@vetala/vetala 0.1.0-dev → 0.2.0-dev
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 +25 -1
- package/dist/src/agent.d.ts +12 -1
- package/dist/src/agent.js +92 -14
- package/dist/src/agent.js.map +1 -1
- package/dist/src/app-meta.d.ts +3 -0
- package/dist/src/app-meta.js +4 -0
- package/dist/src/app-meta.js.map +1 -0
- package/dist/src/cli.js +28 -10
- package/dist/src/cli.js.map +1 -1
- package/dist/src/config.d.ts +10 -1
- package/dist/src/config.js +160 -67
- package/dist/src/config.js.map +1 -1
- package/dist/src/edit-history.d.ts +4 -0
- package/dist/src/edit-history.js +77 -0
- package/dist/src/edit-history.js.map +1 -0
- package/dist/src/edits/diff.d.ts +1 -0
- package/dist/src/edits/diff.js +176 -0
- package/dist/src/edits/diff.js.map +1 -0
- package/dist/src/ink/command-suggestions.js +1 -0
- package/dist/src/ink/command-suggestions.js.map +1 -1
- package/dist/src/ink/ink-terminal-ui.d.ts +2 -2
- package/dist/src/ink/ink-terminal-ui.js +7 -3
- package/dist/src/ink/ink-terminal-ui.js.map +1 -1
- package/dist/src/ink/repl-app.d.ts +3 -2
- package/dist/src/ink/repl-app.js +333 -111
- package/dist/src/ink/repl-app.js.map +1 -1
- package/dist/src/process-utils.d.ts +1 -0
- package/dist/src/process-utils.js +3 -1
- package/dist/src/process-utils.js.map +1 -1
- package/dist/src/providers/catalog.d.ts +29 -0
- package/dist/src/providers/catalog.js +121 -0
- package/dist/src/providers/catalog.js.map +1 -0
- package/dist/src/providers/index.d.ts +9 -0
- package/dist/src/providers/index.js +11 -0
- package/dist/src/providers/index.js.map +1 -0
- package/dist/src/providers/openai-compatible-client.d.ts +23 -0
- package/dist/src/providers/openai-compatible-client.js +183 -0
- package/dist/src/providers/openai-compatible-client.js.map +1 -0
- package/dist/src/repl.d.ts +2 -1
- package/dist/src/repl.js +1 -1
- package/dist/src/repl.js.map +1 -1
- package/dist/src/runtime-profile.d.ts +16 -0
- package/dist/src/runtime-profile.js +112 -0
- package/dist/src/runtime-profile.js.map +1 -0
- package/dist/src/search-provider.d.ts +6 -1
- package/dist/src/search-provider.js +257 -0
- package/dist/src/search-provider.js.map +1 -1
- package/dist/src/session-store.d.ts +5 -3
- package/dist/src/session-store.js +75 -4
- package/dist/src/session-store.js.map +1 -1
- package/dist/src/terminal-ui.d.ts +3 -1
- package/dist/src/terminal-ui.js +13 -3
- package/dist/src/terminal-ui.js.map +1 -1
- package/dist/src/tools/filesystem.js +141 -143
- package/dist/src/tools/filesystem.js.map +1 -1
- package/dist/src/tools/index.js +2 -0
- package/dist/src/tools/index.js.map +1 -1
- package/dist/src/tools/repo-search.d.ts +23 -0
- package/dist/src/tools/repo-search.js +221 -0
- package/dist/src/tools/repo-search.js.map +1 -0
- package/dist/src/tools/shell.js +26 -3
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/timing.d.ts +2 -0
- package/dist/src/tools/timing.js +58 -0
- package/dist/src/tools/timing.js.map +1 -0
- package/dist/src/tools/web.js +70 -5
- package/dist/src/tools/web.js.map +1 -1
- package/dist/src/types.d.ts +89 -17
- package/package.json +11 -1
- package/terminal.png +0 -0
package/dist/src/ink/repl-app.js
CHANGED
|
@@ -4,17 +4,28 @@ import { Box, Text, useApp, useInput } from "ink";
|
|
|
4
4
|
import SelectInput from "ink-select-input";
|
|
5
5
|
import Spinner from "ink-spinner";
|
|
6
6
|
import TextInput from "ink-text-input";
|
|
7
|
-
import { Agent } from "../agent.js";
|
|
7
|
+
import { Agent, isAgentInterruptedError } from "../agent.js";
|
|
8
|
+
import { latestUndoableEdit, undoLastEdit } from "../edit-history.js";
|
|
8
9
|
import { ApprovalManager } from "../approvals.js";
|
|
9
|
-
import {
|
|
10
|
+
import { clearProviderSavedAuth, loadConfig, providerConfigFor, saveProviderDefaults, saveProviderPersistentAuth, withProviderSessionAuth, withProviderStoredAuth } from "../config.js";
|
|
10
11
|
import { PathPolicy } from "../path-policy.js";
|
|
11
|
-
import {
|
|
12
|
+
import { getProviderDefinition, listProviders, providerLabel } from "../providers/index.js";
|
|
12
13
|
import { SkillRuntime } from "../skills/runtime.js";
|
|
13
14
|
import { createToolRegistry } from "../tools/index.js";
|
|
15
|
+
import { buildTranscriptCards } from "./transcript-cards.js";
|
|
14
16
|
import { InkTerminalUI } from "./ink-terminal-ui.js";
|
|
15
17
|
import { buildSlashSuggestions } from "./command-suggestions.js";
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
const ASSISTANT_FLUSH_INTERVAL_MS = 33;
|
|
19
|
+
const MAX_LIVE_ASSISTANT_LINES = 24;
|
|
20
|
+
const MAX_VISIBLE_TRANSCRIPT_TURNS = 6;
|
|
21
|
+
const UI_COLORS = {
|
|
22
|
+
accent: "blue",
|
|
23
|
+
muted: "gray",
|
|
24
|
+
border: "gray",
|
|
25
|
+
warning: "yellow",
|
|
26
|
+
danger: "red"
|
|
27
|
+
};
|
|
28
|
+
export function ReplApp({ initialConfig, initialSession, runtimeProfile, store }) {
|
|
18
29
|
const { exit } = useApp();
|
|
19
30
|
const [config, setConfig] = useState(initialConfig);
|
|
20
31
|
const [session, setSession] = useState(() => cloneSession(initialSession));
|
|
@@ -26,18 +37,27 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
26
37
|
const [spinnerLabel, setSpinnerLabel] = useState(null);
|
|
27
38
|
const [status, setStatus] = useState("Ready");
|
|
28
39
|
const [pendingApproval, setPendingApproval] = useState(null);
|
|
40
|
+
const [pendingBusyPrompt, setPendingBusyPrompt] = useState(null);
|
|
29
41
|
const [modelPickerOpen, setModelPickerOpen] = useState(false);
|
|
42
|
+
const [pendingSarvamModelPicker, setPendingSarvamModelPicker] = useState(null);
|
|
43
|
+
const [pendingOpenRouterModelInput, setPendingOpenRouterModelInput] = useState(null);
|
|
30
44
|
const [pendingReasoningSetup, setPendingReasoningSetup] = useState(null);
|
|
31
45
|
const [pendingAuthInput, setPendingAuthInput] = useState(null);
|
|
32
46
|
const [pendingAuthRetention, setPendingAuthRetention] = useState(null);
|
|
33
47
|
const [availableSkills, setAvailableSkills] = useState([]);
|
|
34
48
|
const [paused, setPaused] = useState(false);
|
|
35
49
|
const [pendingExitConfirm, setPendingExitConfirm] = useState(false);
|
|
50
|
+
const [queuedPrompt, setQueuedPrompt] = useState(null);
|
|
51
|
+
const [turnRunning, setTurnRunning] = useState(false);
|
|
36
52
|
const assistantBufferRef = useRef("");
|
|
53
|
+
const assistantFlushTimerRef = useRef(null);
|
|
37
54
|
const nextEntryIdRef = useRef(0);
|
|
55
|
+
const queuedPromptRef = useRef(null);
|
|
38
56
|
const sessionRef = useRef(session);
|
|
57
|
+
const turnRunningRef = useRef(false);
|
|
39
58
|
const uiRef = useRef(null);
|
|
40
59
|
const skillRuntimeRef = useRef(null);
|
|
60
|
+
const activeAgentRef = useRef(null);
|
|
41
61
|
sessionRef.current = session;
|
|
42
62
|
const pushEntry = (kind, text) => {
|
|
43
63
|
setEntries((current) => [
|
|
@@ -49,7 +69,24 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
49
69
|
}
|
|
50
70
|
]);
|
|
51
71
|
};
|
|
72
|
+
const flushAssistantBuffer = () => {
|
|
73
|
+
if (assistantFlushTimerRef.current) {
|
|
74
|
+
clearTimeout(assistantFlushTimerRef.current);
|
|
75
|
+
assistantFlushTimerRef.current = null;
|
|
76
|
+
}
|
|
77
|
+
setAssistantBuffer(assistantBufferRef.current);
|
|
78
|
+
};
|
|
79
|
+
const scheduleAssistantFlush = () => {
|
|
80
|
+
if (assistantFlushTimerRef.current) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
assistantFlushTimerRef.current = setTimeout(() => {
|
|
84
|
+
assistantFlushTimerRef.current = null;
|
|
85
|
+
setAssistantBuffer(assistantBufferRef.current);
|
|
86
|
+
}, ASSISTANT_FLUSH_INTERVAL_MS);
|
|
87
|
+
};
|
|
52
88
|
const finalizeAssistant = () => {
|
|
89
|
+
flushAssistantBuffer();
|
|
53
90
|
const buffered = assistantBufferRef.current.trimEnd();
|
|
54
91
|
if (!buffered) {
|
|
55
92
|
assistantBufferRef.current = "";
|
|
@@ -64,7 +101,7 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
64
101
|
uiRef.current = new InkTerminalUI({
|
|
65
102
|
appendAssistant: (text) => {
|
|
66
103
|
assistantBufferRef.current += text;
|
|
67
|
-
|
|
104
|
+
scheduleAssistantFlush();
|
|
68
105
|
},
|
|
69
106
|
finalizeAssistant,
|
|
70
107
|
pushEntry,
|
|
@@ -75,7 +112,7 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
75
112
|
setSpinnerLabel(label);
|
|
76
113
|
setStatus(label ?? "Ready");
|
|
77
114
|
}
|
|
78
|
-
});
|
|
115
|
+
}, runtimeProfile);
|
|
79
116
|
}
|
|
80
117
|
const ui = uiRef.current;
|
|
81
118
|
if (!skillRuntimeRef.current) {
|
|
@@ -85,17 +122,21 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
85
122
|
});
|
|
86
123
|
}
|
|
87
124
|
const skills = skillRuntimeRef.current;
|
|
88
|
-
const busy = spinnerLabel !== null;
|
|
89
|
-
const transcriptCards = buildTranscriptCards(entries).slice(-8);
|
|
90
|
-
const liveCardId = transcriptCards.at(-1)?.id ?? null;
|
|
91
125
|
const visibleStatus = paused ? "Paused" : status;
|
|
126
|
+
const visibleAssistantBuffer = renderLiveAssistantBuffer(assistantBuffer);
|
|
127
|
+
const transcriptCards = buildTranscriptCards(entries);
|
|
128
|
+
const visibleTranscriptCards = transcriptCards.slice(-MAX_VISIBLE_TRANSCRIPT_TURNS);
|
|
129
|
+
const hiddenTranscriptTurnCount = Math.max(0, transcriptCards.length - visibleTranscriptCards.length);
|
|
92
130
|
const slashSuggestions = buildSlashSuggestions(input, availableSkills).slice(0, 8);
|
|
93
131
|
const showSlashSuggestions = Boolean(trusted &&
|
|
94
|
-
!
|
|
132
|
+
!turnRunning &&
|
|
95
133
|
!paused &&
|
|
96
134
|
!pendingExitConfirm &&
|
|
97
135
|
!pendingApproval &&
|
|
136
|
+
!pendingBusyPrompt &&
|
|
98
137
|
!modelPickerOpen &&
|
|
138
|
+
!pendingSarvamModelPicker &&
|
|
139
|
+
!pendingOpenRouterModelInput &&
|
|
99
140
|
!pendingReasoningSetup &&
|
|
100
141
|
!pendingAuthInput &&
|
|
101
142
|
!pendingAuthRetention &&
|
|
@@ -123,6 +164,11 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
123
164
|
cancelled = true;
|
|
124
165
|
};
|
|
125
166
|
}, [skills]);
|
|
167
|
+
useEffect(() => () => {
|
|
168
|
+
if (assistantFlushTimerRef.current) {
|
|
169
|
+
clearTimeout(assistantFlushTimerRef.current);
|
|
170
|
+
}
|
|
171
|
+
}, []);
|
|
126
172
|
useInput((inputValue, key) => {
|
|
127
173
|
if (showSlashSuggestions && isTabInput(inputValue, key)) {
|
|
128
174
|
const firstSuggestion = slashSuggestions[0];
|
|
@@ -154,41 +200,67 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
154
200
|
return cloned;
|
|
155
201
|
};
|
|
156
202
|
const resetTranscript = () => {
|
|
203
|
+
flushAssistantBuffer();
|
|
157
204
|
assistantBufferRef.current = "";
|
|
158
205
|
setAssistantBuffer("");
|
|
159
206
|
setActivityLabel(null);
|
|
160
207
|
nextEntryIdRef.current = 0;
|
|
161
208
|
setEntries([]);
|
|
162
209
|
};
|
|
163
|
-
const mergeLoadedConfig = (loaded) => {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
210
|
+
const mergeLoadedConfig = (loaded, skipProvider) => {
|
|
211
|
+
let merged = loaded;
|
|
212
|
+
for (const provider of listProviders()) {
|
|
213
|
+
if (provider.name === skipProvider) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const currentProfile = config.providers[provider.name];
|
|
217
|
+
if (currentProfile.authSource === "session" &&
|
|
218
|
+
currentProfile.authValue &&
|
|
219
|
+
currentProfile.authMode !== "missing") {
|
|
220
|
+
merged = withProviderSessionAuth(merged, provider.name, currentProfile.authMode, currentProfile.authValue);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return merged;
|
|
174
224
|
};
|
|
175
225
|
const closeCommandModals = () => {
|
|
176
226
|
setModelPickerOpen(false);
|
|
227
|
+
setPendingSarvamModelPicker(null);
|
|
228
|
+
setPendingOpenRouterModelInput(null);
|
|
177
229
|
setPendingReasoningSetup(null);
|
|
178
230
|
setPendingAuthInput(null);
|
|
179
231
|
setPendingAuthRetention(null);
|
|
180
232
|
};
|
|
181
|
-
const
|
|
233
|
+
const setQueuedPromptState = (prompt) => {
|
|
234
|
+
queuedPromptRef.current = prompt;
|
|
235
|
+
setQueuedPrompt(prompt);
|
|
236
|
+
};
|
|
237
|
+
const setTurnRunningState = (value) => {
|
|
238
|
+
turnRunningRef.current = value;
|
|
239
|
+
setTurnRunning(value);
|
|
240
|
+
};
|
|
241
|
+
const beginQueuedPrompt = (prompt) => {
|
|
242
|
+
setQueuedPromptState(null);
|
|
243
|
+
setPendingBusyPrompt(null);
|
|
244
|
+
void runPrompt(prompt, { forceRun: true });
|
|
245
|
+
};
|
|
246
|
+
const runPrompt = async (prompt, options = {}) => {
|
|
182
247
|
const trimmed = prompt.trim();
|
|
183
248
|
if (!trimmed) {
|
|
184
249
|
return;
|
|
185
250
|
}
|
|
251
|
+
if (turnRunningRef.current && !options.forceRun) {
|
|
252
|
+
setPendingBusyPrompt({ prompt: trimmed });
|
|
253
|
+
setInput("");
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
186
256
|
if (trimmed.startsWith("/")) {
|
|
257
|
+
setInput("");
|
|
187
258
|
await handleCommand(trimmed);
|
|
188
259
|
return;
|
|
189
260
|
}
|
|
190
261
|
setInput("");
|
|
191
262
|
setPendingExitConfirm(false);
|
|
263
|
+
setPendingBusyPrompt(null);
|
|
192
264
|
setActivityLabel(null);
|
|
193
265
|
pushEntry("user", summarizeUserPrompt(trimmed));
|
|
194
266
|
setStatus("Running agent");
|
|
@@ -199,23 +271,40 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
199
271
|
sessionStore: store,
|
|
200
272
|
approvals,
|
|
201
273
|
pathPolicy: new PathPolicy(session.workspaceRoot, approvals),
|
|
274
|
+
runtimeProfile,
|
|
202
275
|
skills,
|
|
203
276
|
tools: createTools(),
|
|
204
277
|
ui
|
|
205
278
|
});
|
|
279
|
+
activeAgentRef.current = agent;
|
|
280
|
+
setTurnRunningState(true);
|
|
206
281
|
try {
|
|
207
282
|
await agent.runTurn(trimmed, true);
|
|
208
283
|
syncSession(session);
|
|
209
284
|
setActivityLabel(null);
|
|
210
|
-
setStatus("Ready");
|
|
285
|
+
setStatus(queuedPromptRef.current ? "Running queued prompt" : "Ready");
|
|
211
286
|
}
|
|
212
287
|
catch (error) {
|
|
288
|
+
if (isAgentInterruptedError(error)) {
|
|
289
|
+
setActivityLabel(null);
|
|
290
|
+
syncSession(session);
|
|
291
|
+
setStatus(queuedPromptRef.current ? "Running queued prompt" : "Interrupted");
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
213
294
|
finalizeAssistant();
|
|
214
295
|
setActivityLabel(null);
|
|
215
296
|
pushEntry("error", error instanceof Error ? error.message : String(error));
|
|
216
297
|
setStatus("Failed");
|
|
217
298
|
syncSession(session);
|
|
218
299
|
}
|
|
300
|
+
finally {
|
|
301
|
+
activeAgentRef.current = null;
|
|
302
|
+
setTurnRunningState(false);
|
|
303
|
+
const nextQueuedPrompt = queuedPromptRef.current;
|
|
304
|
+
if (nextQueuedPrompt) {
|
|
305
|
+
beginQueuedPrompt(nextQueuedPrompt);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
219
308
|
};
|
|
220
309
|
const handleCommand = async (commandLine) => {
|
|
221
310
|
const [command, ...args] = commandLine.slice(1).split(/\s+/);
|
|
@@ -227,6 +316,7 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
227
316
|
pushEntry("info", [
|
|
228
317
|
"/help",
|
|
229
318
|
"/model",
|
|
319
|
+
"/undo",
|
|
230
320
|
"/skill",
|
|
231
321
|
"/tools",
|
|
232
322
|
"/history",
|
|
@@ -245,8 +335,18 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
245
335
|
}
|
|
246
336
|
closeCommandModals();
|
|
247
337
|
setModelPickerOpen(true);
|
|
248
|
-
setStatus("Select a model");
|
|
338
|
+
setStatus("Select a provider and model");
|
|
339
|
+
return;
|
|
340
|
+
case "undo": {
|
|
341
|
+
const result = await undoLastEdit(session, store, (request) => {
|
|
342
|
+
const approvals = new ApprovalManager(session, store, null, requestApprovalDecision);
|
|
343
|
+
return approvals.requestApproval(request);
|
|
344
|
+
});
|
|
345
|
+
syncSession(session);
|
|
346
|
+
pushEntry(result.isError ? "warn" : "info", result.content);
|
|
347
|
+
setStatus(result.isError ? "Undo blocked" : "Ready");
|
|
249
348
|
return;
|
|
349
|
+
}
|
|
250
350
|
case "skill":
|
|
251
351
|
case "skills":
|
|
252
352
|
await handleSkillsCommand(args);
|
|
@@ -282,7 +382,7 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
282
382
|
return;
|
|
283
383
|
}
|
|
284
384
|
case "new": {
|
|
285
|
-
const nextSession = await store.createSession(session.workspaceRoot, session.model);
|
|
385
|
+
const nextSession = await store.createSession(session.workspaceRoot, session.provider, session.model);
|
|
286
386
|
const nextCloned = syncSession(nextSession);
|
|
287
387
|
closeCommandModals();
|
|
288
388
|
resetTranscript();
|
|
@@ -300,22 +400,22 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
300
400
|
ui.printConfig(config);
|
|
301
401
|
return;
|
|
302
402
|
case "logout": {
|
|
303
|
-
const
|
|
304
|
-
await
|
|
403
|
+
const previousProfile = providerConfigFor(config, session.provider);
|
|
404
|
+
await clearProviderSavedAuth(session.provider);
|
|
305
405
|
closeCommandModals();
|
|
306
|
-
const reloaded = await loadConfig();
|
|
406
|
+
const reloaded = mergeLoadedConfig(await loadConfig(), session.provider);
|
|
307
407
|
setConfig(reloaded);
|
|
308
|
-
if (reloaded.authSource === "env") {
|
|
309
|
-
pushEntry("warn",
|
|
408
|
+
if (providerConfigFor(reloaded, session.provider).authSource === "env") {
|
|
409
|
+
pushEntry("warn", `Cleared local ${providerLabel(session.provider)} auth state, but environment credentials are still active for this process.`);
|
|
310
410
|
}
|
|
311
|
-
else if (
|
|
312
|
-
pushEntry("info",
|
|
411
|
+
else if (previousProfile.authSource === "stored" || previousProfile.authSource === "stored_hash") {
|
|
412
|
+
pushEntry("info", `Cleared the saved ${providerLabel(session.provider)} credential for future launches.`);
|
|
313
413
|
}
|
|
314
|
-
else if (
|
|
315
|
-
pushEntry("info",
|
|
414
|
+
else if (previousProfile.authSource === "session") {
|
|
415
|
+
pushEntry("info", `Cleared the ${providerLabel(session.provider)} credential that was only active in this session.`);
|
|
316
416
|
}
|
|
317
417
|
else {
|
|
318
|
-
pushEntry("info",
|
|
418
|
+
pushEntry("info", `No saved ${providerLabel(session.provider)} credential remained after logout.`);
|
|
319
419
|
}
|
|
320
420
|
setStatus("Logged out");
|
|
321
421
|
return;
|
|
@@ -423,6 +523,51 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
423
523
|
setPendingApproval(null);
|
|
424
524
|
promptState.resolve(value);
|
|
425
525
|
};
|
|
526
|
+
const onBusyPromptSelect = async (choice) => {
|
|
527
|
+
const current = pendingBusyPrompt;
|
|
528
|
+
if (!current) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
if (choice === "cancel") {
|
|
532
|
+
setPendingBusyPrompt(null);
|
|
533
|
+
setInput(current.prompt);
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
const replacedExistingQueue = queuedPromptRef.current !== null;
|
|
537
|
+
setQueuedPromptState(current.prompt);
|
|
538
|
+
setPendingBusyPrompt(null);
|
|
539
|
+
pushEntry("info", choice === "force"
|
|
540
|
+
? `Stopping the current turn and sending next: ${summarizeUserPrompt(current.prompt)}`
|
|
541
|
+
: `${replacedExistingQueue ? "Replaced" : "Queued"} next prompt: ${summarizeUserPrompt(current.prompt)}`);
|
|
542
|
+
if (choice === "force") {
|
|
543
|
+
setStatus("Stopping current turn");
|
|
544
|
+
activeAgentRef.current?.requestStop();
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
setStatus("Queued next prompt");
|
|
548
|
+
};
|
|
549
|
+
const applyModelSelection = async (nextSettings) => {
|
|
550
|
+
const definition = getProviderDefinition(nextSettings.provider);
|
|
551
|
+
await store.updateModel(session, nextSettings.provider, nextSettings.model);
|
|
552
|
+
await saveProviderDefaults(nextSettings.provider, nextSettings.model, definition.supportsReasoningEffort
|
|
553
|
+
? { reasoningEffort: nextSettings.reasoningEffort }
|
|
554
|
+
: {});
|
|
555
|
+
syncSession(session);
|
|
556
|
+
const nextConfig = mergeLoadedConfig(await loadConfig());
|
|
557
|
+
setConfig(nextConfig);
|
|
558
|
+
const nextProfile = providerConfigFor(nextConfig, nextSettings.provider);
|
|
559
|
+
if (nextProfile.authSource === "missing" || nextProfile.authSource === "stored_hash") {
|
|
560
|
+
setPendingAuthInput({
|
|
561
|
+
...nextSettings,
|
|
562
|
+
authMode: definition.auth.defaultMode,
|
|
563
|
+
value: ""
|
|
564
|
+
});
|
|
565
|
+
setStatus(`Enter ${definition.auth.inputLabel.toLowerCase()} for ${providerLabel(nextSettings.provider)} / ${nextSettings.model}`);
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
pushEntry("info", formatModelSetupSummary(nextSettings, nextConfig));
|
|
569
|
+
setStatus("Ready");
|
|
570
|
+
};
|
|
426
571
|
const onModelSelect = async (value) => {
|
|
427
572
|
if (value === "cancel") {
|
|
428
573
|
setModelPickerOpen(false);
|
|
@@ -430,11 +575,30 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
430
575
|
return;
|
|
431
576
|
}
|
|
432
577
|
setModelPickerOpen(false);
|
|
578
|
+
if (value === "sarvam") {
|
|
579
|
+
setPendingSarvamModelPicker("sarvam");
|
|
580
|
+
setStatus("Select a Sarvam model");
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
setPendingOpenRouterModelInput({
|
|
584
|
+
provider: "openrouter",
|
|
585
|
+
value: session.provider === "openrouter" ? session.model : providerConfigFor(config, "openrouter").defaultModel
|
|
586
|
+
});
|
|
587
|
+
setStatus("Enter an OpenRouter model id");
|
|
588
|
+
};
|
|
589
|
+
const onSarvamModelSelect = async (value) => {
|
|
590
|
+
if (value === "cancel") {
|
|
591
|
+
setPendingSarvamModelPicker(null);
|
|
592
|
+
setStatus("Ready");
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
setPendingSarvamModelPicker(null);
|
|
433
596
|
setPendingReasoningSetup({
|
|
597
|
+
provider: "sarvam",
|
|
434
598
|
model: value,
|
|
435
599
|
reasoningEffort: config.reasoningEffort
|
|
436
600
|
});
|
|
437
|
-
setStatus(`Select reasoning effort for ${value}`);
|
|
601
|
+
setStatus(`Select reasoning effort for Sarvam / ${value}`);
|
|
438
602
|
};
|
|
439
603
|
const onReasoningSelect = async (value) => {
|
|
440
604
|
const current = pendingReasoningSetup;
|
|
@@ -451,23 +615,28 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
451
615
|
...current,
|
|
452
616
|
reasoningEffort: value === "none" ? null : value
|
|
453
617
|
};
|
|
454
|
-
await
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
...nextSettings,
|
|
463
|
-
authMode: "subscription_key",
|
|
464
|
-
value: ""
|
|
465
|
-
});
|
|
466
|
-
setStatus(`Enter API key for ${nextSettings.model}`);
|
|
618
|
+
await applyModelSelection(nextSettings);
|
|
619
|
+
};
|
|
620
|
+
const onOpenRouterModelInputChange = (value) => {
|
|
621
|
+
setPendingOpenRouterModelInput((current) => (current ? { ...current, value } : current));
|
|
622
|
+
};
|
|
623
|
+
const onOpenRouterModelInputSubmit = async (value) => {
|
|
624
|
+
const trimmed = value.trim();
|
|
625
|
+
if (!pendingOpenRouterModelInput) {
|
|
467
626
|
return;
|
|
468
627
|
}
|
|
469
|
-
|
|
470
|
-
|
|
628
|
+
if (!trimmed) {
|
|
629
|
+
setPendingOpenRouterModelInput(null);
|
|
630
|
+
pushEntry("warn", "OpenRouter model selection cancelled.");
|
|
631
|
+
setStatus("Ready");
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
setPendingOpenRouterModelInput(null);
|
|
635
|
+
await applyModelSelection({
|
|
636
|
+
provider: "openrouter",
|
|
637
|
+
model: trimmed,
|
|
638
|
+
reasoningEffort: null
|
|
639
|
+
});
|
|
471
640
|
};
|
|
472
641
|
const onAuthInputChange = (value) => {
|
|
473
642
|
setPendingAuthInput((current) => (current ? { ...current, value } : current));
|
|
@@ -480,7 +649,7 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
480
649
|
const trimmed = value.trim();
|
|
481
650
|
if (!trimmed) {
|
|
482
651
|
setPendingAuthInput(null);
|
|
483
|
-
pushEntry("warn",
|
|
652
|
+
pushEntry("warn", `Credential entry cancelled. Model settings were saved, but no usable ${providerLabel(current.provider)} credential is active.`);
|
|
484
653
|
setStatus("Ready");
|
|
485
654
|
return;
|
|
486
655
|
}
|
|
@@ -489,7 +658,7 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
489
658
|
...current,
|
|
490
659
|
value: trimmed
|
|
491
660
|
});
|
|
492
|
-
setStatus("Choose how long to keep this
|
|
661
|
+
setStatus("Choose how long to keep this credential");
|
|
493
662
|
};
|
|
494
663
|
const onAuthRetentionSelect = async (choice) => {
|
|
495
664
|
const current = pendingAuthRetention;
|
|
@@ -504,15 +673,15 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
504
673
|
}
|
|
505
674
|
const loadedConfig = await loadConfig();
|
|
506
675
|
const nextConfig = choice === "persist"
|
|
507
|
-
?
|
|
508
|
-
:
|
|
676
|
+
? withProviderStoredAuth(loadedConfig, current.provider, current.authMode, current.value)
|
|
677
|
+
: withProviderSessionAuth(loadedConfig, current.provider, current.authMode, current.value);
|
|
509
678
|
if (choice === "persist") {
|
|
510
|
-
await
|
|
679
|
+
await saveProviderPersistentAuth(current.provider, current.authMode, current.value);
|
|
511
680
|
}
|
|
512
681
|
setConfig(nextConfig);
|
|
513
682
|
setPendingAuthRetention(null);
|
|
514
683
|
pushEntry("info", formatModelSetupSummary(current, nextConfig, choice));
|
|
515
|
-
if (choice === "persist" && loadedConfig.authSource === "env") {
|
|
684
|
+
if (choice === "persist" && providerConfigFor(loadedConfig, current.provider).authSource === "env") {
|
|
516
685
|
pushEntry("warn", "Environment credentials are still set in this shell. They may take precedence on future launches.");
|
|
517
686
|
}
|
|
518
687
|
setStatus("Ready");
|
|
@@ -524,51 +693,68 @@ export function ReplApp({ initialConfig, initialSession, store }) {
|
|
|
524
693
|
}
|
|
525
694
|
setPendingExitConfirm(false);
|
|
526
695
|
};
|
|
527
|
-
return (_jsx(Box, { flexDirection: "column", paddingX: 1, children: !trusted ? (_jsxs(_Fragment, { children: [_jsx(TrustScreen, { workspaceRoot: session.workspaceRoot, onSelect: onTrustSelect }), pendingExitConfirm ? _jsx(ExitConfirmBox, { onSelect: onExitConfirmSelect }) : null] })) : (_jsxs(_Fragment, { children: [_jsx(Dashboard, { config: config, session: session, status: visibleStatus }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
|
|
696
|
+
return (_jsx(Box, { flexDirection: "column", paddingX: 1, children: !trusted ? (_jsxs(_Fragment, { children: [_jsx(TrustScreen, { workspaceRoot: session.workspaceRoot, onSelect: onTrustSelect }), pendingExitConfirm ? _jsx(ExitConfirmBox, { onSelect: onExitConfirmSelect }) : null] })) : (_jsxs(_Fragment, { children: [_jsx(Dashboard, { config: config, session: session, status: visibleStatus }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [entries.length === 0 && !assistantBuffer && !spinnerLabel ? (_jsx(Box, { borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, children: _jsx(Text, { color: UI_COLORS.muted, children: "New transcript. Use /history if you want earlier session messages." }) })) : null, hiddenTranscriptTurnCount > 0 ? (_jsx(Box, { marginBottom: 1, borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, children: _jsxs(Text, { color: UI_COLORS.muted, children: [hiddenTranscriptTurnCount, " earlier turn", hiddenTranscriptTurnCount === 1 ? "" : "s", " hidden. Use /history to inspect older messages or /clear to reset the visible transcript."] }) })) : null, visibleTranscriptCards.map((card) => (_jsx(TranscriptTurnCard, { card: card }, card.id))), (assistantBuffer || activityLabel || spinnerLabel) ? (_jsx(LiveStatusCard, { assistantBuffer: visibleAssistantBuffer, liveLabel: activityLabel ?? spinnerLabel })) : null] }), pendingExitConfirm ? (_jsx(ExitConfirmBox, { onSelect: onExitConfirmSelect })) : paused ? (_jsx(PauseBox, {})) : pendingApproval ? (_jsx(ApprovalBox, { request: pendingApproval.request, onSelect: onApprovalSelect })) : pendingBusyPrompt ? (_jsx(BusyPromptBox, { prompt: pendingBusyPrompt.prompt, onSelect: onBusyPromptSelect })) : modelPickerOpen ? (_jsx(ModelPicker, { currentProvider: session.provider, onSelect: onModelSelect })) : pendingSarvamModelPicker ? (_jsx(SarvamModelPicker, { currentModel: session.provider === "sarvam" ? session.model : null, onSelect: onSarvamModelSelect })) : pendingOpenRouterModelInput ? (_jsx(OpenRouterModelIdBox, { state: pendingOpenRouterModelInput, onChange: onOpenRouterModelInputChange, onSubmit: onOpenRouterModelInputSubmit })) : pendingReasoningSetup ? (_jsx(ReasoningEffortPicker, { currentValue: pendingReasoningSetup.reasoningEffort, provider: pendingReasoningSetup.provider, model: pendingReasoningSetup.model, onSelect: onReasoningSelect })) : pendingAuthInput ? (_jsx(AuthInputBox, { state: pendingAuthInput, onChange: onAuthInputChange, onSubmit: onAuthInputSubmit })) : pendingAuthRetention ? (_jsx(AuthRetentionBox, { state: pendingAuthRetention, onSelect: onAuthRetentionSelect })) : (_jsxs(_Fragment, { children: [_jsx(InputBox, { busy: turnRunning, value: input, onChange: setInput, onSubmit: runPrompt }), showSlashSuggestions ? _jsx(SlashSuggestionBox, { suggestions: slashSuggestions }) : null] })), _jsx(Footer, { config: config, queuedPrompt: queuedPrompt, status: visibleStatus, session: session })] })) }));
|
|
528
697
|
}
|
|
529
698
|
function Dashboard({ config, session, status }) {
|
|
699
|
+
const activeProvider = providerConfigFor(config, session.provider);
|
|
530
700
|
const infoRows = [
|
|
701
|
+
{ item: "provider", value: providerLabel(session.provider) },
|
|
531
702
|
{ item: "model", value: session.model },
|
|
532
703
|
{ item: "directory", value: session.workspaceRoot },
|
|
533
704
|
{ item: "session", value: session.id.slice(0, 8) },
|
|
534
705
|
{ item: "updated", value: formatTimestamp(session.updatedAt) }
|
|
535
706
|
];
|
|
536
707
|
const stateRows = [
|
|
537
|
-
{ item: "auth", value: describeAuth(
|
|
538
|
-
{ item: "reasoning", value: formatReasoningEffort(config.reasoningEffort) },
|
|
708
|
+
{ item: "auth", value: describeAuth(activeProvider) },
|
|
709
|
+
{ item: "reasoning", value: formatReasoningEffort(config.reasoningEffort, session.provider) },
|
|
539
710
|
{ item: "skills", value: describeSkills(session.pinnedSkills.length) },
|
|
540
|
-
{ item: "
|
|
711
|
+
{ item: "undo", value: latestUndoableEdit(session) ? "ready" : "none" },
|
|
712
|
+
{ item: "sha256", value: activeProvider.authFingerprint?.slice(0, 12) ?? "(none)" },
|
|
541
713
|
{ item: "context", value: describeContext(session.messages.length) }
|
|
542
714
|
];
|
|
543
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Box, { borderStyle: "round", borderColor:
|
|
715
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Box, { borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, flexDirection: "row", children: [_jsxs(Box, { flexDirection: "column", width: "60%", paddingRight: 2, children: [_jsx(Text, { color: UI_COLORS.accent, children: "Vetala" }), _jsx(Text, { bold: true, children: "Ready." }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: infoRows.map((row) => (_jsx(InfoRow, { item: row.item, value: row.value }, row.item))) })] }), _jsxs(Box, { flexDirection: "column", width: "40%", children: [_jsx(Text, { color: UI_COLORS.accent, children: "Tips" }), _jsx(Text, { children: "/help for commands" }), _jsx(Text, { children: "/model for provider + model" }), _jsx(Text, { children: "/undo to revert last edit" }), _jsx(Text, { children: "/skill to inspect local skills" }), _jsx(Text, { children: "/logout to clear local auth" }), _jsx(Text, { children: "Ctrl+C to pause" }), _jsx(Text, { children: "Ctrl+D to exit" }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: UI_COLORS.muted, children: ["status: ", status] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: UI_COLORS.accent, children: "Context" }) }), stateRows.map((row) => (_jsx(InfoRow, { item: row.item, value: row.value }, row.item)))] })] }), _jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, children: _jsx(Text, { children: "Try \"explain this codebase\" or \"write a test for <filepath>\"" }) })] }));
|
|
544
716
|
}
|
|
545
717
|
function InfoRow({ item, value }) {
|
|
546
|
-
return (_jsxs(Box, { children: [_jsx(Box, { width: 12, children: _jsx(Text, { color:
|
|
718
|
+
return (_jsxs(Box, { children: [_jsx(Box, { width: 12, children: _jsx(Text, { color: UI_COLORS.muted, children: item }) }), _jsx(Text, { children: value })] }));
|
|
547
719
|
}
|
|
548
720
|
function TrustScreen({ workspaceRoot, onSelect }) {
|
|
549
|
-
return (_jsxs(Box, { borderStyle: "round", borderColor:
|
|
721
|
+
return (_jsxs(Box, { borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: UI_COLORS.accent, children: "Accessing workspace" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: workspaceRoot }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "Quick safety check: is this a project you created or one you trust? If not, review it before continuing." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: "Vetala will be able to read, edit, and execute files here." }) })] }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: [
|
|
550
722
|
{ label: "Yes, I trust this folder", value: "trust" },
|
|
551
723
|
{ label: "No, exit", value: "exit" }
|
|
552
724
|
], onSelect: (item) => void onSelect(item.value) }) })] }));
|
|
553
725
|
}
|
|
554
726
|
function ApprovalBox({ request, onSelect }) {
|
|
555
|
-
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor:
|
|
727
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: UI_COLORS.warning, paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: UI_COLORS.warning, children: "Approval required" }), request.label.split("\n").map((line) => (_jsx(Text, { children: line }, line))), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: [
|
|
556
728
|
{ label: "Allow once", value: "once" },
|
|
557
729
|
{ label: "Allow for session", value: "session" },
|
|
558
730
|
{ label: "Deny", value: "deny" }
|
|
559
731
|
], onSelect: (item) => void onSelect(item.value) }) })] }));
|
|
560
732
|
}
|
|
561
|
-
function ModelPicker({
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
733
|
+
function ModelPicker({ currentProvider, onSelect }) {
|
|
734
|
+
const items = [
|
|
735
|
+
...listProviders().map((provider) => ({
|
|
736
|
+
label: provider.name === currentProvider ? `${provider.label} (current)` : provider.label,
|
|
737
|
+
value: provider.name
|
|
738
|
+
})),
|
|
739
|
+
{ label: "Cancel", value: "cancel" }
|
|
740
|
+
];
|
|
741
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: UI_COLORS.accent, children: "Select provider" }), _jsxs(Text, { color: UI_COLORS.muted, children: ["Current: ", providerLabel(currentProvider)] }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: items, onSelect: (item) => void onSelect(item.value) }) })] }));
|
|
742
|
+
}
|
|
743
|
+
function SarvamModelPicker({ currentModel, onSelect }) {
|
|
744
|
+
const items = [
|
|
745
|
+
...getProviderDefinition("sarvam").suggestedModels.map((model) => ({
|
|
746
|
+
label: model === currentModel ? `${model} (current)` : model,
|
|
747
|
+
value: model
|
|
748
|
+
})),
|
|
749
|
+
{ label: "Cancel", value: "cancel" }
|
|
750
|
+
];
|
|
751
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: UI_COLORS.accent, children: "Select a Sarvam model" }), _jsx(Text, { color: UI_COLORS.muted, children: "After model selection, Vetala will ask for reasoning effort." }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: items, onSelect: (item) => void onSelect(item.value) }) })] }));
|
|
752
|
+
}
|
|
753
|
+
function OpenRouterModelIdBox({ state, onChange, onSubmit }) {
|
|
754
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: UI_COLORS.accent, children: "Enter an OpenRouter model id" }), _jsx(Text, { color: UI_COLORS.muted, children: "Examples: `openai/gpt-4o-mini`, `anthropic/claude-3.5-haiku`, `google/gemini-2.0-flash-001`" }), _jsx(Text, { color: UI_COLORS.muted, children: "Reasoning differs by OpenRouter model. Vetala will use the provider default instead of forcing one global reasoning setting." }), _jsx(Text, { color: UI_COLORS.muted, children: "Press Enter on an empty field to cancel." }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: UI_COLORS.accent, children: "model " }), _jsx(TextInput, { highlightPastedText: false, value: state.value, onChange: onChange, onSubmit: (value) => void onSubmit(value) })] })] }));
|
|
569
755
|
}
|
|
570
|
-
function ReasoningEffortPicker({ currentValue, model, onSelect }) {
|
|
571
|
-
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor:
|
|
756
|
+
function ReasoningEffortPicker({ currentValue, provider, model, onSelect }) {
|
|
757
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: UI_COLORS.accent, children: "Select reasoning effort" }), _jsxs(Text, { color: UI_COLORS.muted, children: ["Provider: ", providerLabel(provider), " \u00B7 Model: ", model, " \u00B7 Current: ", formatReasoningEffort(currentValue, provider)] }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: [
|
|
572
758
|
{ label: "None (null / let Sarvam decide)", value: "none" },
|
|
573
759
|
{ label: "Low", value: "low" },
|
|
574
760
|
{ label: "Medium", value: "medium" },
|
|
@@ -577,10 +763,11 @@ function ReasoningEffortPicker({ currentValue, model, onSelect }) {
|
|
|
577
763
|
], onSelect: (item) => void onSelect(item.value) }) })] }));
|
|
578
764
|
}
|
|
579
765
|
function AuthInputBox({ state, onChange, onSubmit }) {
|
|
580
|
-
|
|
766
|
+
const definition = getProviderDefinition(state.provider);
|
|
767
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, flexDirection: "column", children: [_jsxs(Text, { color: UI_COLORS.accent, children: ["Enter ", definition.auth.inputLabel.toLowerCase(), " for ", providerLabel(state.provider), " / ", state.model] }), _jsxs(Text, { color: UI_COLORS.muted, children: [definition.auth.helpText, " After you press Enter, choose whether Vetala keeps it for all sessions or only for this session."] }), _jsxs(Text, { color: UI_COLORS.muted, children: ["Reasoning: ", formatReasoningEffort(state.reasoningEffort, state.provider)] }), _jsx(Text, { color: UI_COLORS.muted, children: "Press Enter on an empty field to cancel." }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: UI_COLORS.accent, children: "key " }), _jsx(TextInput, { mask: "*", highlightPastedText: false, value: state.value, onChange: onChange, onSubmit: (value) => void onSubmit(value) })] })] }));
|
|
581
768
|
}
|
|
582
769
|
function AuthRetentionBox({ state, onSelect }) {
|
|
583
|
-
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor:
|
|
770
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, flexDirection: "column", children: [_jsxs(Text, { color: UI_COLORS.accent, children: ["Keep credential for ", providerLabel(state.provider), " / ", state.model] }), _jsxs(Text, { color: UI_COLORS.muted, children: ["Key preview: ", maskSecretPreview(state.value)] }), _jsxs(Text, { color: UI_COLORS.muted, children: ["Reasoning: ", formatReasoningEffort(state.reasoningEffort, state.provider)] }), _jsx(Text, { color: UI_COLORS.muted, children: "Future-session mode stores the raw key locally until you run /logout." }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsx(Text, { children: "Choose how long Vetala should keep this key:" }) }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: [
|
|
584
771
|
{
|
|
585
772
|
label: "Keep for all sessions until /logout",
|
|
586
773
|
value: "persist"
|
|
@@ -596,36 +783,54 @@ function AuthRetentionBox({ state, onSelect }) {
|
|
|
596
783
|
], onSelect: (item) => void onSelect(item.value) }) })] }));
|
|
597
784
|
}
|
|
598
785
|
function InputBox({ busy, value, onChange, onSubmit }) {
|
|
599
|
-
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor:
|
|
786
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, children: [_jsx(Text, { color: UI_COLORS.accent, children: "\u276F " }), _jsx(TextInput, { highlightPastedText: false, value: value, onChange: onChange, onSubmit: (nextValue) => void onSubmit(nextValue) }), busy ? _jsx(Text, { color: UI_COLORS.muted, children: " Enter to queue or force-send." }) : null] }));
|
|
787
|
+
}
|
|
788
|
+
function BusyPromptBox({ prompt, onSelect }) {
|
|
789
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: UI_COLORS.warning, paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: UI_COLORS.warning, children: "Current turn is still running" }), _jsx(Text, { color: UI_COLORS.muted, children: "Choose what to do with the next prompt:" }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: summarizeUserPrompt(prompt).split("\n").map((line) => (_jsx(Text, { children: line }, line))) }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: [
|
|
790
|
+
{
|
|
791
|
+
label: "Send now (stop current turn)",
|
|
792
|
+
value: "force"
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
label: "Send after current turn",
|
|
796
|
+
value: "queue"
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
label: "Cancel",
|
|
800
|
+
value: "cancel"
|
|
801
|
+
}
|
|
802
|
+
], onSelect: (item) => void onSelect(item.value) }) })] }));
|
|
600
803
|
}
|
|
601
804
|
function SlashSuggestionBox({ suggestions }) {
|
|
602
|
-
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor:
|
|
805
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: UI_COLORS.accent, children: "Commands" }), _jsx(Text, { color: UI_COLORS.muted, children: "Tab autocompletes the first match." }), suggestions.map((suggestion, index) => (_jsxs(Box, { children: [_jsx(Box, { width: "45%", children: index === 0 ? (_jsxs(Text, { color: UI_COLORS.accent, children: ["\u276F ", suggestion.label] })) : (_jsxs(Text, { children: [" ", suggestion.label] })) }), _jsx(Text, { color: UI_COLORS.muted, children: suggestion.detail })] }, suggestion.label)))] }));
|
|
603
806
|
}
|
|
604
807
|
function PauseBox() {
|
|
605
|
-
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor:
|
|
808
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: UI_COLORS.border, paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: UI_COLORS.accent, children: "Paused" }), _jsx(Text, { children: "Press Ctrl+C again to resume." }), _jsx(Text, { children: "Press Ctrl+D if you want to exit." })] }));
|
|
606
809
|
}
|
|
607
810
|
function ExitConfirmBox({ onSelect }) {
|
|
608
|
-
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "red", paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: "red", children: "Exit Vetala?" }), _jsx(Text, { color:
|
|
811
|
+
return (_jsxs(Box, { marginTop: 1, borderStyle: "round", borderColor: "red", paddingX: 1, flexDirection: "column", children: [_jsx(Text, { color: "red", children: "Exit Vetala?" }), _jsx(Text, { color: UI_COLORS.muted, children: "Current session state is already written to disk as it changes." }), _jsx(Box, { marginTop: 1, children: _jsx(SelectInput, { items: [
|
|
609
812
|
{ label: "Exit", value: "exit" },
|
|
610
813
|
{ label: "Stay", value: "stay" }
|
|
611
814
|
], onSelect: (item) => void onSelect(item.value) }) })] }));
|
|
612
815
|
}
|
|
613
|
-
function Footer({ config, status, session }) {
|
|
614
|
-
|
|
816
|
+
function Footer({ config, queuedPrompt, status, session }) {
|
|
817
|
+
const activeProvider = providerConfigFor(config, session.provider);
|
|
818
|
+
return (_jsxs(Box, { marginTop: 1, justifyContent: "space-between", children: [_jsx(Text, { color: UI_COLORS.muted, children: "/help for commands \u00B7 /undo reverts last edit \u00B7 Ctrl+C pause \u00B7 Ctrl+D exit" }), _jsxs(Text, { color: UI_COLORS.muted, children: [status, queuedPrompt ? " · queued next prompt" : "", " \u00B7 ", describeAuth(activeProvider), " \u00B7 ", describeContext(session.messages.length)] })] }));
|
|
615
819
|
}
|
|
616
|
-
function
|
|
820
|
+
function TranscriptTurnCard({ card }) {
|
|
617
821
|
const borderColor = transcriptCardBorder(card.entries);
|
|
618
|
-
return (
|
|
822
|
+
return (_jsx(Box, { marginBottom: 1, borderStyle: "round", borderColor: borderColor, paddingX: 1, flexDirection: "column", children: card.entries.map((entry) => (_jsx(TranscriptSection, { entry: entry }, entry.id))) }));
|
|
619
823
|
}
|
|
620
824
|
function LiveStatusCard({ assistantBuffer, liveLabel }) {
|
|
621
|
-
return (_jsxs(Box, { marginBottom: 1, borderStyle: "round", borderColor:
|
|
825
|
+
return (_jsxs(Box, { marginBottom: 1, borderStyle: "round", borderColor: UI_COLORS.accent, paddingX: 1, flexDirection: "column", children: [liveLabel ? _jsx(LiveActivitySection, { label: liveLabel }) : null, assistantBuffer ? (_jsx(TranscriptSection, { entry: { id: "live:assistant", kind: "assistant", text: assistantBuffer } })) : null] }));
|
|
622
826
|
}
|
|
623
827
|
function TranscriptSection({ entry }) {
|
|
624
828
|
const isActivity = entry.kind === "activity";
|
|
625
|
-
|
|
829
|
+
const labelColor = entryColor(entry.kind);
|
|
830
|
+
return (_jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [labelColor ? _jsx(Text, { color: labelColor, children: entryLabel(entry.kind) }) : _jsx(Text, { children: entryLabel(entry.kind) }), entry.text.split("\n").map((line, index) => isActivity ? (_jsx(Text, { color: UI_COLORS.muted, children: line.length > 0 ? line : " " }, `${entry.id}:${index}`)) : (_jsx(Text, { children: line.length > 0 ? line : " " }, `${entry.id}:${index}`)))] }));
|
|
626
831
|
}
|
|
627
832
|
function LiveActivitySection({ label }) {
|
|
628
|
-
return (_jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsx(Text, { color:
|
|
833
|
+
return (_jsxs(Box, { marginBottom: 1, flexDirection: "column", children: [_jsx(Text, { color: UI_COLORS.muted, children: "doing" }), _jsxs(Box, { children: [_jsx(Text, { color: UI_COLORS.accent, children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { color: UI_COLORS.muted, children: [" ", label] })] })] }));
|
|
629
834
|
}
|
|
630
835
|
function cloneSession(session) {
|
|
631
836
|
return {
|
|
@@ -638,14 +843,15 @@ function cloneSession(session) {
|
|
|
638
843
|
messages: [...session.messages],
|
|
639
844
|
referencedFiles: [...session.referencedFiles],
|
|
640
845
|
readFiles: [...session.readFiles],
|
|
641
|
-
pinnedSkills: [...session.pinnedSkills]
|
|
846
|
+
pinnedSkills: [...session.pinnedSkills],
|
|
847
|
+
edits: session.edits.map((edit) => ({ ...edit }))
|
|
642
848
|
};
|
|
643
849
|
}
|
|
644
850
|
function formatSessionList(sessions) {
|
|
645
851
|
return sessions.length > 0
|
|
646
852
|
? sessions
|
|
647
853
|
.slice(0, 10)
|
|
648
|
-
.map((item) => `${item.id} ${item.updatedAt} ${item.workspaceRoot}`)
|
|
854
|
+
.map((item) => `${item.id} ${item.provider}/${item.model} ${item.updatedAt} ${item.workspaceRoot}`)
|
|
649
855
|
.join("\n")
|
|
650
856
|
: "(no sessions)";
|
|
651
857
|
}
|
|
@@ -667,18 +873,23 @@ function describeAuth(config) {
|
|
|
667
873
|
return "missing";
|
|
668
874
|
}
|
|
669
875
|
}
|
|
670
|
-
function formatReasoningEffort(value) {
|
|
876
|
+
function formatReasoningEffort(value, provider) {
|
|
877
|
+
if (!getProviderDefinition(provider).supportsReasoningEffort) {
|
|
878
|
+
return "provider default (model-specific)";
|
|
879
|
+
}
|
|
671
880
|
return value ?? "(none)";
|
|
672
881
|
}
|
|
673
882
|
function describeSkills(pinnedCount) {
|
|
674
883
|
return pinnedCount > 0 ? `${pinnedCount} pinned` : "none pinned";
|
|
675
884
|
}
|
|
676
885
|
function formatModelSetupSummary(state, config, authRetention) {
|
|
886
|
+
const profile = providerConfigFor(config, state.provider);
|
|
677
887
|
const lines = [
|
|
888
|
+
`Provider: ${providerLabel(state.provider)}`,
|
|
678
889
|
`Model: ${state.model}`,
|
|
679
|
-
`Reasoning effort: ${formatReasoningEffort(state.reasoningEffort)}`,
|
|
680
|
-
`Credential: ${describeAuth(
|
|
681
|
-
`Stored SHA-256: ${
|
|
890
|
+
`Reasoning effort: ${formatReasoningEffort(state.reasoningEffort, state.provider)}`,
|
|
891
|
+
`Credential: ${describeAuth(profile)}`,
|
|
892
|
+
`Stored SHA-256: ${profile.authFingerprint?.slice(0, 16) ?? "(none)"}`
|
|
682
893
|
];
|
|
683
894
|
if (authRetention === "persist") {
|
|
684
895
|
lines.push("Raw key is stored locally for all future sessions until /logout.");
|
|
@@ -706,6 +917,20 @@ function summarizeUserPrompt(prompt) {
|
|
|
706
917
|
`Preview: ${preview}${preview.length < prompt.replace(/\s+/g, " ").trim().length ? "..." : ""}`
|
|
707
918
|
].join("\n");
|
|
708
919
|
}
|
|
920
|
+
function renderLiveAssistantBuffer(buffer) {
|
|
921
|
+
if (!buffer) {
|
|
922
|
+
return "";
|
|
923
|
+
}
|
|
924
|
+
const lines = buffer.split("\n");
|
|
925
|
+
if (lines.length <= MAX_LIVE_ASSISTANT_LINES) {
|
|
926
|
+
return buffer;
|
|
927
|
+
}
|
|
928
|
+
const hiddenLineCount = lines.length - MAX_LIVE_ASSISTANT_LINES;
|
|
929
|
+
return [
|
|
930
|
+
`[${hiddenLineCount} earlier line${hiddenLineCount === 1 ? "" : "s"} hidden while streaming]`,
|
|
931
|
+
...lines.slice(-MAX_LIVE_ASSISTANT_LINES)
|
|
932
|
+
].join("\n");
|
|
933
|
+
}
|
|
709
934
|
function renderAuthMode(authMode) {
|
|
710
935
|
switch (authMode) {
|
|
711
936
|
case "bearer":
|
|
@@ -732,19 +957,19 @@ function maskSecretPreview(value) {
|
|
|
732
957
|
function entryColor(kind) {
|
|
733
958
|
switch (kind) {
|
|
734
959
|
case "assistant":
|
|
735
|
-
return
|
|
960
|
+
return UI_COLORS.accent;
|
|
736
961
|
case "user":
|
|
737
|
-
return
|
|
962
|
+
return undefined;
|
|
738
963
|
case "tool":
|
|
739
|
-
return
|
|
964
|
+
return UI_COLORS.accent;
|
|
740
965
|
case "activity":
|
|
741
|
-
return
|
|
966
|
+
return UI_COLORS.muted;
|
|
742
967
|
case "info":
|
|
743
|
-
return
|
|
968
|
+
return UI_COLORS.accent;
|
|
744
969
|
case "warn":
|
|
745
|
-
return
|
|
970
|
+
return UI_COLORS.warning;
|
|
746
971
|
case "error":
|
|
747
|
-
return
|
|
972
|
+
return UI_COLORS.danger;
|
|
748
973
|
}
|
|
749
974
|
}
|
|
750
975
|
function entryLabel(kind) {
|
|
@@ -767,23 +992,20 @@ function entryLabel(kind) {
|
|
|
767
992
|
}
|
|
768
993
|
function transcriptCardBorder(entries) {
|
|
769
994
|
if (entries.some((entry) => entry.kind === "error")) {
|
|
770
|
-
return
|
|
995
|
+
return UI_COLORS.danger;
|
|
771
996
|
}
|
|
772
997
|
if (entries.some((entry) => entry.kind === "warn")) {
|
|
773
|
-
return
|
|
998
|
+
return UI_COLORS.warning;
|
|
774
999
|
}
|
|
775
1000
|
if (entries.some((entry) => entry.kind === "tool")) {
|
|
776
|
-
return
|
|
777
|
-
}
|
|
778
|
-
if (entries.some((entry) => entry.kind === "assistant")) {
|
|
779
|
-
return "white";
|
|
1001
|
+
return UI_COLORS.accent;
|
|
780
1002
|
}
|
|
781
1003
|
if (entries.some((entry) => entry.kind === "info")) {
|
|
782
|
-
return
|
|
1004
|
+
return UI_COLORS.accent;
|
|
783
1005
|
}
|
|
784
1006
|
if (entries.some((entry) => entry.kind === "activity")) {
|
|
785
|
-
return
|
|
1007
|
+
return UI_COLORS.muted;
|
|
786
1008
|
}
|
|
787
|
-
return
|
|
1009
|
+
return UI_COLORS.border;
|
|
788
1010
|
}
|
|
789
1011
|
//# sourceMappingURL=repl-app.js.map
|