dexto 1.5.0 → 1.5.1
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/commands/interactive-commands/command-parser.d.ts +1 -1
- package/dist/cli/commands/interactive-commands/command-parser.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/command-parser.js +11 -1
- package/dist/cli/commands/interactive-commands/general-commands.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/general-commands.js +63 -0
- package/dist/cli/commands/interactive-commands/system/system-commands.d.ts.map +1 -1
- package/dist/cli/commands/interactive-commands/system/system-commands.js +3 -2
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +163 -28
- package/dist/cli/ink-cli/components/TextBufferInput.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/TextBufferInput.js +7 -3
- package/dist/cli/ink-cli/components/chat/Header.js +1 -1
- package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/chat/MessageItem.js +4 -1
- package/dist/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.js +1 -1
- package/dist/cli/ink-cli/components/overlays/CustomModelWizard.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/CustomModelWizard.js +19 -8
- package/dist/cli/ink-cli/components/overlays/LogLevelSelector.js +1 -1
- package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts +2 -1
- package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.js +64 -2
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.d.ts.map +1 -1
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.js +44 -1
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/types.d.ts +6 -0
- package/dist/cli/ink-cli/components/overlays/custom-model-wizard/types.d.ts.map +1 -1
- package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
- package/dist/cli/ink-cli/containers/OverlayContainer.js +2 -2
- package/dist/cli/ink-cli/hooks/useAgentEvents.d.ts +8 -1
- package/dist/cli/ink-cli/hooks/useAgentEvents.d.ts.map +1 -1
- package/dist/cli/ink-cli/hooks/useAgentEvents.js +144 -6
- package/dist/cli/ink-cli/hooks/useCLIState.d.ts.map +1 -1
- package/dist/cli/ink-cli/hooks/useCLIState.js +3 -0
- package/dist/cli/ink-cli/hooks/useTokenCounter.d.ts +11 -7
- package/dist/cli/ink-cli/hooks/useTokenCounter.d.ts.map +1 -1
- package/dist/cli/ink-cli/hooks/useTokenCounter.js +41 -18
- package/dist/cli/ink-cli/services/processStream.d.ts.map +1 -1
- package/dist/cli/ink-cli/services/processStream.js +20 -8
- package/dist/cli/ink-cli/state/types.d.ts +2 -2
- package/dist/cli/ink-cli/state/types.d.ts.map +1 -1
- package/dist/index.js +24 -3
- package/package.json +7 -7
|
@@ -37,8 +37,17 @@ const CustomModelWizard = forwardRef(function CustomModelWizard({ isVisible, onC
|
|
|
37
37
|
const localModelWizardRef = useRef(null);
|
|
38
38
|
// Get provider config (data-driven, no conditionals)
|
|
39
39
|
const providerConfig = selectedProvider ? getProviderConfig(selectedProvider) : null;
|
|
40
|
-
const
|
|
41
|
-
|
|
40
|
+
const allWizardSteps = providerConfig?.steps ?? [];
|
|
41
|
+
/**
|
|
42
|
+
* Get visible steps based on current values.
|
|
43
|
+
* Steps with a condition function are only shown if the condition returns true.
|
|
44
|
+
*/
|
|
45
|
+
const getVisibleSteps = useCallback((currentValues) => {
|
|
46
|
+
return allWizardSteps.filter((step) => !step.condition || step.condition(currentValues));
|
|
47
|
+
}, [allWizardSteps]);
|
|
48
|
+
// Current visible steps based on accumulated values
|
|
49
|
+
const visibleSteps = getVisibleSteps(values);
|
|
50
|
+
const currentStepConfig = visibleSteps[currentStep];
|
|
42
51
|
// Reset when becoming visible
|
|
43
52
|
useEffect(() => {
|
|
44
53
|
if (isVisible) {
|
|
@@ -117,15 +126,17 @@ const CustomModelWizard = forwardRef(function CustomModelWizard({ isVisible, onC
|
|
|
117
126
|
setValues(newValues);
|
|
118
127
|
setError(null);
|
|
119
128
|
setCurrentInput('');
|
|
129
|
+
// Get updated visible steps with new values
|
|
130
|
+
const updatedVisibleSteps = getVisibleSteps(newValues);
|
|
120
131
|
// Check if we're done
|
|
121
|
-
if (currentStep >=
|
|
132
|
+
if (currentStep >= updatedVisibleSteps.length - 1) {
|
|
122
133
|
await saveModel(newValues);
|
|
123
134
|
}
|
|
124
135
|
else {
|
|
125
136
|
const nextStep = currentStep + 1;
|
|
126
137
|
setCurrentStep(nextStep);
|
|
127
138
|
// Pre-populate next step from stored values (for edit mode)
|
|
128
|
-
const nextStepConfig =
|
|
139
|
+
const nextStepConfig = updatedVisibleSteps[nextStep];
|
|
129
140
|
const nextValue = nextStepConfig ? newValues[nextStepConfig.field] : undefined;
|
|
130
141
|
setCurrentInput(nextValue ?? '');
|
|
131
142
|
}
|
|
@@ -133,11 +144,11 @@ const CustomModelWizard = forwardRef(function CustomModelWizard({ isVisible, onC
|
|
|
133
144
|
currentInput,
|
|
134
145
|
currentStep,
|
|
135
146
|
currentStepConfig,
|
|
147
|
+
getVisibleSteps,
|
|
136
148
|
isSaving,
|
|
137
149
|
isValidating,
|
|
138
150
|
selectedProvider,
|
|
139
151
|
values,
|
|
140
|
-
wizardSteps,
|
|
141
152
|
]);
|
|
142
153
|
/**
|
|
143
154
|
* Build and save the model using provider config's buildModel function.
|
|
@@ -191,7 +202,7 @@ const CustomModelWizard = forwardRef(function CustomModelWizard({ isVisible, onC
|
|
|
191
202
|
if (currentStep > 0) {
|
|
192
203
|
setCurrentStep(currentStep - 1);
|
|
193
204
|
// Restore previous value
|
|
194
|
-
const prevStep =
|
|
205
|
+
const prevStep = visibleSteps[currentStep - 1];
|
|
195
206
|
if (prevStep) {
|
|
196
207
|
setCurrentInput(values[prevStep.field] || '');
|
|
197
208
|
}
|
|
@@ -205,7 +216,7 @@ const CustomModelWizard = forwardRef(function CustomModelWizard({ isVisible, onC
|
|
|
205
216
|
else {
|
|
206
217
|
onClose();
|
|
207
218
|
}
|
|
208
|
-
}, [currentStep, onClose, selectedProvider, values,
|
|
219
|
+
}, [currentStep, onClose, selectedProvider, values, visibleSteps]);
|
|
209
220
|
// Handle keyboard input
|
|
210
221
|
useImperativeHandle(ref, () => ({
|
|
211
222
|
handleInput: (input, key) => {
|
|
@@ -281,6 +292,6 @@ const CustomModelWizard = forwardRef(function CustomModelWizard({ isVisible, onC
|
|
|
281
292
|
// Wizard steps screen for other providers
|
|
282
293
|
if (!currentStepConfig || !providerConfig)
|
|
283
294
|
return null;
|
|
284
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, marginTop: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "green", children: isEditing ? 'Edit Custom Model' : 'Add Custom Model' }), _jsxs(Text, { color: "gray", children: [' ', "(", providerConfig.displayName, ") Step ", currentStep + 1, "/",
|
|
295
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, marginTop: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "green", children: isEditing ? 'Edit Custom Model' : 'Add Custom Model' }), _jsxs(Text, { color: "gray", children: [' ', "(", providerConfig.displayName, ") Step ", currentStep + 1, "/", visibleSteps.length] })] }), providerConfig.setupInfo && currentStep === 0 && (_jsx(SetupInfoBanner, { title: providerConfig.setupInfo.title, description: providerConfig.setupInfo.description, docsUrl: providerConfig.setupInfo.docsUrl })), _jsx(WizardStepInput, { step: currentStepConfig, currentInput: currentInput, error: error, isValidating: isValidating, isSaving: isSaving, additionalContent: currentStepConfig.field === 'apiKey' ? (_jsx(ApiKeyStep, { provider: selectedProvider })) : undefined }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", children: ["Enter to continue \u2022 Esc to", ' ', currentStep > 0 ? 'go back' : 'back to provider'] }) })] }));
|
|
285
296
|
});
|
|
286
297
|
export default CustomModelWizard;
|
|
@@ -54,6 +54,6 @@ const LogLevelSelector = forwardRef(function LogLevelSelector({ isVisible, onSel
|
|
|
54
54
|
const handleSelect = (option) => {
|
|
55
55
|
onSelect(option.level);
|
|
56
56
|
};
|
|
57
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(BaseSelector, { ref: baseSelectorRef, items: levels, isVisible: isVisible, isLoading: false, selectedIndex: selectedIndex, onSelectIndex: setSelectedIndex, onSelect: handleSelect, onClose: onClose, formatItem: formatItem, title: "Select Log Level", borderColor: "yellowBright", emptyMessage: "No log levels available" }), logFilePath && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", children: ["\uD83D\uDCC1 Log file: ", logFilePath] }) }))] }));
|
|
57
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(BaseSelector, { ref: baseSelectorRef, items: levels, isVisible: isVisible, isLoading: false, selectedIndex: selectedIndex, onSelectIndex: setSelectedIndex, onSelect: handleSelect, onClose: onClose, formatItem: formatItem, title: "Select Log Level", borderColor: "yellowBright", emptyMessage: "No log levels available" }), logFilePath && process.env.DEXTO_PRIVACY_MODE !== 'true' && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "gray", children: ["\uD83D\uDCC1 Log file: ", logFilePath] }) }))] }));
|
|
58
58
|
});
|
|
59
59
|
export default LogLevelSelector;
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
import type { Key } from '../../hooks/useInputOrchestrator.js';
|
|
8
8
|
import type { DextoAgent, LLMProvider } from '@dexto/core';
|
|
9
9
|
import { type CustomModel } from '@dexto/agent-management';
|
|
10
|
+
type ReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
|
|
10
11
|
interface ModelSelectorProps {
|
|
11
12
|
isVisible: boolean;
|
|
12
|
-
onSelectModel: (provider: LLMProvider, model: string, displayName?: string, baseURL?: string) => void;
|
|
13
|
+
onSelectModel: (provider: LLMProvider, model: string, displayName?: string, baseURL?: string, reasoningEffort?: ReasoningEffort) => void;
|
|
13
14
|
onClose: () => void;
|
|
14
15
|
onAddCustomModel: () => void;
|
|
15
16
|
onEditCustomModel: (model: CustomModel) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModelSelectorRefactored.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/ModelSelectorRefactored.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ModelSelectorRefactored.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/ModelSelectorRefactored.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO3D,OAAO,EAIH,KAAK,WAAW,EACnB,MAAM,yBAAyB,CAAC;AAEjC,KAAK,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhF,UAAU,kBAAkB;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,CACX,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,EAChB,eAAe,CAAC,EAAE,eAAe,KAChC,IAAI,CAAC;IACV,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,iBAAiB,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAChD,KAAK,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAChC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAiDD;;GAEG;AACH,QAAA,MAAM,aAAa,oHA4rBjB,CAAC;AAEH,eAAe,aAAa,CAAC"}
|
|
@@ -7,12 +7,30 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
7
7
|
*/
|
|
8
8
|
import { useState, useEffect, forwardRef, useRef, useImperativeHandle, useMemo, useCallback, } from 'react';
|
|
9
9
|
import { Box, Text } from 'ink';
|
|
10
|
-
import { listOllamaModels, DEFAULT_OLLAMA_URL, getLocalModelById } from '@dexto/core';
|
|
10
|
+
import { listOllamaModels, DEFAULT_OLLAMA_URL, getLocalModelById, isReasoningCapableModel, } from '@dexto/core';
|
|
11
11
|
import { loadCustomModels, deleteCustomModel, getAllInstalledModels, } from '@dexto/agent-management';
|
|
12
12
|
function isAddCustomOption(item) {
|
|
13
13
|
return 'type' in item && item.type === 'add-custom';
|
|
14
14
|
}
|
|
15
15
|
const MAX_VISIBLE_ITEMS = 10;
|
|
16
|
+
// Reasoning effort options - defined at module scope to avoid recreation on each render
|
|
17
|
+
const REASONING_EFFORT_OPTIONS = [
|
|
18
|
+
{
|
|
19
|
+
value: 'auto',
|
|
20
|
+
label: 'Auto',
|
|
21
|
+
description: 'Let the model decide (recommended for most tasks)',
|
|
22
|
+
},
|
|
23
|
+
{ value: 'none', label: 'None', description: 'No reasoning, fastest responses' },
|
|
24
|
+
{ value: 'minimal', label: 'Minimal', description: 'Barely any reasoning, very fast' },
|
|
25
|
+
{ value: 'low', label: 'Low', description: 'Light reasoning, fast responses' },
|
|
26
|
+
{
|
|
27
|
+
value: 'medium',
|
|
28
|
+
label: 'Medium',
|
|
29
|
+
description: 'Balanced reasoning (OpenAI recommended)',
|
|
30
|
+
},
|
|
31
|
+
{ value: 'high', label: 'High', description: 'Thorough reasoning for complex tasks' },
|
|
32
|
+
{ value: 'xhigh', label: 'Extra High', description: 'Maximum quality, slower/costlier' },
|
|
33
|
+
];
|
|
16
34
|
/**
|
|
17
35
|
* Model selector with search and custom model support
|
|
18
36
|
*/
|
|
@@ -27,6 +45,9 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
|
|
|
27
45
|
const [pendingDeleteConfirm, setPendingDeleteConfirm] = useState(false);
|
|
28
46
|
const selectedIndexRef = useRef(selectedIndex);
|
|
29
47
|
const deleteTimeoutRef = useRef(null);
|
|
48
|
+
// Reasoning effort sub-step state
|
|
49
|
+
const [pendingReasoningModel, setPendingReasoningModel] = useState(null);
|
|
50
|
+
const [reasoningEffortIndex, setReasoningEffortIndex] = useState(0); // Default to 'Auto' (index 0)
|
|
30
51
|
// Keep ref in sync
|
|
31
52
|
selectedIndexRef.current = selectedIndex;
|
|
32
53
|
// Clear delete confirmation timeout on unmount
|
|
@@ -48,6 +69,8 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
|
|
|
48
69
|
setScrollOffset(0);
|
|
49
70
|
setCustomModelAction(null);
|
|
50
71
|
setPendingDeleteConfirm(false);
|
|
72
|
+
setPendingReasoningModel(null);
|
|
73
|
+
setReasoningEffortIndex(0); // Default to 'Auto'
|
|
51
74
|
if (deleteTimeoutRef.current) {
|
|
52
75
|
clearTimeout(deleteTimeoutRef.current);
|
|
53
76
|
deleteTimeoutRef.current = null;
|
|
@@ -256,6 +279,30 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
|
|
|
256
279
|
handleInput: (input, key) => {
|
|
257
280
|
if (!isVisible)
|
|
258
281
|
return false;
|
|
282
|
+
// Handle reasoning effort sub-step
|
|
283
|
+
if (pendingReasoningModel) {
|
|
284
|
+
if (key.escape) {
|
|
285
|
+
// Go back to model selection
|
|
286
|
+
setPendingReasoningModel(null);
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
if (key.upArrow) {
|
|
290
|
+
setReasoningEffortIndex((prev) => prev > 0 ? prev - 1 : REASONING_EFFORT_OPTIONS.length - 1);
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
if (key.downArrow) {
|
|
294
|
+
setReasoningEffortIndex((prev) => prev < REASONING_EFFORT_OPTIONS.length - 1 ? prev + 1 : 0);
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
if (key.return) {
|
|
298
|
+
const selectedOption = REASONING_EFFORT_OPTIONS[reasoningEffortIndex];
|
|
299
|
+
const reasoningEffort = selectedOption?.value === 'auto' ? undefined : selectedOption?.value;
|
|
300
|
+
onSelectModel(pendingReasoningModel.provider, pendingReasoningModel.name, pendingReasoningModel.displayName, pendingReasoningModel.baseURL, reasoningEffort);
|
|
301
|
+
setPendingReasoningModel(null);
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
return true; // Consume all input in reasoning effort mode
|
|
305
|
+
}
|
|
259
306
|
// Escape always works
|
|
260
307
|
if (key.escape) {
|
|
261
308
|
// If in action mode, just clear it first
|
|
@@ -408,7 +455,13 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
|
|
|
408
455
|
}
|
|
409
456
|
return true;
|
|
410
457
|
}
|
|
411
|
-
// Normal selection
|
|
458
|
+
// Normal selection - check if reasoning-capable
|
|
459
|
+
if (isReasoningCapableModel(item.name)) {
|
|
460
|
+
// Show reasoning effort sub-step
|
|
461
|
+
setPendingReasoningModel(item);
|
|
462
|
+
setReasoningEffortIndex(0); // Default to 'Auto'
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
412
465
|
onSelectModel(item.provider, item.name, item.displayName, item.baseURL);
|
|
413
466
|
return true;
|
|
414
467
|
}
|
|
@@ -426,12 +479,21 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
|
|
|
426
479
|
pendingDeleteConfirm,
|
|
427
480
|
customModels,
|
|
428
481
|
handleDeleteCustomModel,
|
|
482
|
+
pendingReasoningModel,
|
|
483
|
+
reasoningEffortIndex,
|
|
429
484
|
]);
|
|
430
485
|
if (!isVisible)
|
|
431
486
|
return null;
|
|
432
487
|
if (isLoading) {
|
|
433
488
|
return (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: "Loading models..." }) }));
|
|
434
489
|
}
|
|
490
|
+
// Reasoning effort sub-step UI
|
|
491
|
+
if (pendingReasoningModel) {
|
|
492
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "cyan", bold: true, children: "Configure Reasoning Effort" }) }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Text, { color: "gray", children: ["for ", pendingReasoningModel.displayName || pendingReasoningModel.name] }) }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: "\u2191\u2193 navigate, Enter select, Esc back" }) }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: '─'.repeat(50) }) }), REASONING_EFFORT_OPTIONS.map((option, index) => {
|
|
493
|
+
const isSelected = index === reasoningEffortIndex;
|
|
494
|
+
return (_jsxs(Box, { paddingX: 0, paddingY: 0, children: [_jsxs(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, children: [isSelected ? '› ' : ' ', option.label] }), _jsxs(Text, { color: isSelected ? 'white' : 'gray', children: [' ', "- ", option.description] })] }, option.value));
|
|
495
|
+
})] }));
|
|
496
|
+
}
|
|
435
497
|
const visibleItems = filteredItems.slice(scrollOffset, scrollOffset + MAX_VISIBLE_ITEMS);
|
|
436
498
|
const hasCustomModels = customModels.length > 0;
|
|
437
499
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Text, { color: "cyan", bold: true, children: ["Select Model (", selectedIndex + 1, "/", filteredItems.length, ")"] }) }), _jsxs(Box, { paddingX: 0, paddingY: 0, children: [_jsx(Text, { color: "gray", children: "\u2191\u2193 navigate, Enter select, Esc close" }), hasCustomModels && _jsx(Text, { color: "gray", children: ", \u2192\u2190 for custom actions" })] }), _jsxs(Box, { paddingX: 0, paddingY: 0, children: [_jsx(Text, { color: "gray", children: "\uD83D\uDD0D " }), _jsx(Text, { color: searchQuery ? 'white' : 'gray', children: searchQuery || 'Type to search...' }), _jsx(Text, { color: "cyan", children: "\u258C" })] }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: '─'.repeat(50) }) }), visibleItems.map((item, visibleIndex) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider-config.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAe,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"provider-config.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/ink-cli/components/overlays/custom-model-wizard/provider-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAe,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAQhF,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,YAAY,CAAC;AA4D7D;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,mBAAmB,EAAE,cAAc,CA4XxE,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,cAAc,CAE/E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,CAGtE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,SAAS,mBAAmB,EAAE,CAEtE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAEzE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACpC,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Each provider has its own config with display name, description, steps, and model builder.
|
|
4
4
|
*/
|
|
5
5
|
import { CUSTOM_MODEL_PROVIDERS } from '@dexto/agent-management';
|
|
6
|
-
import { lookupOpenRouterModel, refreshOpenRouterModelCache, getLocalModelById } from '@dexto/core';
|
|
6
|
+
import { lookupOpenRouterModel, refreshOpenRouterModelCache, getLocalModelById, isReasoningCapableModel, } from '@dexto/core';
|
|
7
7
|
import { validators } from './types.js';
|
|
8
8
|
import * as fs from 'fs';
|
|
9
9
|
import * as os from 'os';
|
|
@@ -36,6 +36,29 @@ const DISPLAY_NAME_STEP = {
|
|
|
36
36
|
placeholder: 'e.g., My Custom Model',
|
|
37
37
|
required: false,
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* Common reasoning effort step - for OpenAI reasoning models (o1, o3, codex, gpt-5.x).
|
|
41
|
+
* Only shown when the model name indicates reasoning capability.
|
|
42
|
+
*/
|
|
43
|
+
const REASONING_EFFORT_STEP = {
|
|
44
|
+
field: 'reasoningEffort',
|
|
45
|
+
label: 'Reasoning Effort (optional)',
|
|
46
|
+
placeholder: 'none | minimal | low | medium | high | xhigh (blank for auto)',
|
|
47
|
+
required: false,
|
|
48
|
+
validate: (value) => {
|
|
49
|
+
if (!value?.trim())
|
|
50
|
+
return null;
|
|
51
|
+
const validValues = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh'];
|
|
52
|
+
if (!validValues.includes(value.toLowerCase())) {
|
|
53
|
+
return `Invalid reasoning effort. Use: ${validValues.join(', ')}`;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
},
|
|
57
|
+
condition: (values) => {
|
|
58
|
+
const modelName = values.name || '';
|
|
59
|
+
return isReasoningCapableModel(modelName);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
39
62
|
/**
|
|
40
63
|
* Provider configuration registry.
|
|
41
64
|
* Keys are CustomModelProvider values.
|
|
@@ -61,6 +84,7 @@ export const PROVIDER_CONFIGS = {
|
|
|
61
84
|
},
|
|
62
85
|
{ ...DISPLAY_NAME_STEP, placeholder: 'e.g., My Local Llama 3' },
|
|
63
86
|
MAX_INPUT_TOKENS_STEP,
|
|
87
|
+
REASONING_EFFORT_STEP,
|
|
64
88
|
API_KEY_STEP,
|
|
65
89
|
],
|
|
66
90
|
buildModel: (values, provider) => {
|
|
@@ -77,6 +101,10 @@ export const PROVIDER_CONFIGS = {
|
|
|
77
101
|
if (values.maxInputTokens?.trim()) {
|
|
78
102
|
model.maxInputTokens = parseInt(values.maxInputTokens, 10);
|
|
79
103
|
}
|
|
104
|
+
if (values.reasoningEffort?.trim()) {
|
|
105
|
+
model.reasoningEffort =
|
|
106
|
+
values.reasoningEffort.toLowerCase();
|
|
107
|
+
}
|
|
80
108
|
return model;
|
|
81
109
|
},
|
|
82
110
|
},
|
|
@@ -92,6 +120,7 @@ export const PROVIDER_CONFIGS = {
|
|
|
92
120
|
validate: validators.slashFormat,
|
|
93
121
|
},
|
|
94
122
|
{ ...DISPLAY_NAME_STEP, placeholder: 'e.g., Claude 3.5 Sonnet' },
|
|
123
|
+
REASONING_EFFORT_STEP,
|
|
95
124
|
{
|
|
96
125
|
...API_KEY_STEP,
|
|
97
126
|
placeholder: 'Saved as OPENROUTER_API_KEY if not set, otherwise per-model',
|
|
@@ -105,6 +134,10 @@ export const PROVIDER_CONFIGS = {
|
|
|
105
134
|
if (values.displayName?.trim()) {
|
|
106
135
|
model.displayName = values.displayName.trim();
|
|
107
136
|
}
|
|
137
|
+
if (values.reasoningEffort?.trim()) {
|
|
138
|
+
model.reasoningEffort =
|
|
139
|
+
values.reasoningEffort.toLowerCase();
|
|
140
|
+
}
|
|
108
141
|
return model;
|
|
109
142
|
},
|
|
110
143
|
asyncValidation: {
|
|
@@ -141,6 +174,7 @@ export const PROVIDER_CONFIGS = {
|
|
|
141
174
|
validate: validators.slashFormat,
|
|
142
175
|
},
|
|
143
176
|
{ ...DISPLAY_NAME_STEP, placeholder: 'e.g., GPT-4o via Glama' },
|
|
177
|
+
REASONING_EFFORT_STEP,
|
|
144
178
|
{
|
|
145
179
|
...API_KEY_STEP,
|
|
146
180
|
placeholder: 'Saved as GLAMA_API_KEY if not set, otherwise per-model',
|
|
@@ -154,6 +188,10 @@ export const PROVIDER_CONFIGS = {
|
|
|
154
188
|
if (values.displayName?.trim()) {
|
|
155
189
|
model.displayName = values.displayName.trim();
|
|
156
190
|
}
|
|
191
|
+
if (values.reasoningEffort?.trim()) {
|
|
192
|
+
model.reasoningEffort =
|
|
193
|
+
values.reasoningEffort.toLowerCase();
|
|
194
|
+
}
|
|
157
195
|
return model;
|
|
158
196
|
},
|
|
159
197
|
},
|
|
@@ -177,6 +215,7 @@ export const PROVIDER_CONFIGS = {
|
|
|
177
215
|
},
|
|
178
216
|
{ ...DISPLAY_NAME_STEP, placeholder: 'e.g., My LiteLLM GPT-4' },
|
|
179
217
|
MAX_INPUT_TOKENS_STEP,
|
|
218
|
+
REASONING_EFFORT_STEP,
|
|
180
219
|
{
|
|
181
220
|
...API_KEY_STEP,
|
|
182
221
|
placeholder: 'Saved as LITELLM_API_KEY if not set, otherwise per-model',
|
|
@@ -196,6 +235,10 @@ export const PROVIDER_CONFIGS = {
|
|
|
196
235
|
if (values.maxInputTokens?.trim()) {
|
|
197
236
|
model.maxInputTokens = parseInt(values.maxInputTokens, 10);
|
|
198
237
|
}
|
|
238
|
+
if (values.reasoningEffort?.trim()) {
|
|
239
|
+
model.reasoningEffort =
|
|
240
|
+
values.reasoningEffort.toLowerCase();
|
|
241
|
+
}
|
|
199
242
|
return model;
|
|
200
243
|
},
|
|
201
244
|
},
|
|
@@ -12,6 +12,12 @@ export interface WizardStep {
|
|
|
12
12
|
placeholder: string;
|
|
13
13
|
required: boolean;
|
|
14
14
|
validate?: (value: string) => string | null;
|
|
15
|
+
/**
|
|
16
|
+
* Optional condition to determine if this step should be shown.
|
|
17
|
+
* Takes the current accumulated values and returns true if the step should be shown.
|
|
18
|
+
* If omitted, the step is always shown.
|
|
19
|
+
*/
|
|
20
|
+
condition?: (values: Record<string, string>) => boolean;
|
|
15
21
|
}
|
|
16
22
|
/**
|
|
17
23
|
* Props passed to each provider-specific wizard component.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/ink-cli/components/overlays/custom-model-wizard/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,wCAAwC,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/ink-cli/components/overlays/custom-model-wizard/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,wCAAwC,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC5C;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC;CAC3D;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,8CAA8C;IAC9C,YAAY,EAAE,OAAO,CAAC;IACtB,uCAAuC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,8CAA8C;IAC9C,SAAS,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,sDAAsD;IACtD,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;IAClD,sCAAsC;IACtC,QAAQ,EAAE,MAAM,UAAU,EAAE,CAAC;IAC7B,8BAA8B;IAC9B,oBAAoB,EAAE,MAAM,UAAU,GAAG,SAAS,CAAC;CACtD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,kDAAkD;IAClD,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,mBAAmB,KAAK,WAAW,CAAC;IAC3F,oDAAoD;IACpD,eAAe,CAAC,EAAE;QACd,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;KACvD,CAAC;IACF,sDAAsD;IACtD,SAAS,CAAC,EAAE;QACR,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACL;AAED;;GAEG;AACH,eAAO,MAAM,UAAU;sBACD,MAAM,MAAM,GAAG,MAAM;aAE9B,MAAM;wBAaK,MAAM;qBAOT,MAAM;CAK1B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OverlayContainer.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/containers/OverlayContainer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAyE,MAAM,OAAO,CAAC;AAE9F,OAAO,KAAK,EAAE,UAAU,EAAmD,MAAM,aAAa,CAAC;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAE5D,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAGH,KAAK,eAAe,EACvB,MAAM,iCAAiC,CAAC;AA0EzC,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAK3D,MAAM,WAAW,sBAAsB;IACnC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,qBAAqB;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAC1E,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"OverlayContainer.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/containers/OverlayContainer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAyE,MAAM,OAAO,CAAC;AAE9F,OAAO,KAAK,EAAE,UAAU,EAAmD,MAAM,aAAa,CAAC;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAE5D,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAGH,KAAK,eAAe,EACvB,MAAM,iCAAiC,CAAC;AA0EzC,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAK3D,MAAM,WAAW,sBAAsB;IACnC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,qBAAqB;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAC1E,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,MAAM,EAAE,UAAU,CAAC;IACnB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,4EAA4E;IAC5E,qBAAqB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,sGAm1D5B,CAAC"}
|
|
@@ -200,7 +200,7 @@ export const OverlayContainer = forwardRef(function OverlayContainer({ ui, input
|
|
|
200
200
|
return null;
|
|
201
201
|
};
|
|
202
202
|
// Handle model selection
|
|
203
|
-
const handleModelSelect = useCallback(async (provider, model, displayName, baseURL) => {
|
|
203
|
+
const handleModelSelect = useCallback(async (provider, model, displayName, baseURL, reasoningEffort) => {
|
|
204
204
|
setUi((prev) => ({ ...prev, activeOverlay: 'none', mcpWizardServerType: null }));
|
|
205
205
|
buffer.setText('');
|
|
206
206
|
setInput((prev) => ({ ...prev, historyIndex: -1 }));
|
|
@@ -214,7 +214,7 @@ export const OverlayContainer = forwardRef(function OverlayContainer({ ui, input
|
|
|
214
214
|
timestamp: new Date(),
|
|
215
215
|
},
|
|
216
216
|
]);
|
|
217
|
-
await agent.switchLLM({ provider: provider, model, baseURL }, session.id || undefined);
|
|
217
|
+
await agent.switchLLM({ provider: provider, model, baseURL, reasoningEffort }, session.id || undefined);
|
|
218
218
|
// Update session state with display name (fallback to model ID)
|
|
219
219
|
setSession((prev) => ({ ...prev, modelName: displayName || model }));
|
|
220
220
|
setMessages((prev) => [
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* - session:created - New session creation (e.g., from /clear)
|
|
12
12
|
* - message:queued - New message added to queue
|
|
13
13
|
* - message:removed - Message removed from queue (e.g., up arrow edit)
|
|
14
|
+
* - run:invoke - External trigger (scheduler, A2A, API) starting a run
|
|
15
|
+
* - run:complete (for external triggers) - External run finished
|
|
14
16
|
*
|
|
15
17
|
* Uses AbortController pattern for cleanup.
|
|
16
18
|
*/
|
|
@@ -21,16 +23,21 @@ import type { ApprovalRequest } from '../components/ApprovalPrompt.js';
|
|
|
21
23
|
interface UseAgentEventsProps {
|
|
22
24
|
agent: DextoAgent;
|
|
23
25
|
setMessages: React.Dispatch<React.SetStateAction<Message[]>>;
|
|
26
|
+
setPendingMessages: React.Dispatch<React.SetStateAction<Message[]>>;
|
|
24
27
|
setUi: React.Dispatch<React.SetStateAction<UIState>>;
|
|
25
28
|
setSession: React.Dispatch<React.SetStateAction<SessionState>>;
|
|
26
29
|
setApproval: React.Dispatch<React.SetStateAction<ApprovalRequest | null>>;
|
|
27
30
|
setApprovalQueue: React.Dispatch<React.SetStateAction<ApprovalRequest[]>>;
|
|
28
31
|
setQueuedMessages: React.Dispatch<React.SetStateAction<QueuedMessage[]>>;
|
|
32
|
+
/** Current session ID for filtering events */
|
|
33
|
+
currentSessionId: string | null;
|
|
29
34
|
}
|
|
30
35
|
/**
|
|
31
36
|
* Subscribes to agent event bus for non-streaming events only.
|
|
32
37
|
* Streaming events are handled via agent.stream() iterator.
|
|
38
|
+
*
|
|
39
|
+
* Also handles external triggers (run:invoke) from scheduler, A2A, etc.
|
|
33
40
|
*/
|
|
34
|
-
export declare function useAgentEvents({ agent, setMessages, setUi, setSession, setApproval, setApprovalQueue, setQueuedMessages, }: UseAgentEventsProps): void;
|
|
41
|
+
export declare function useAgentEvents({ agent, setMessages, setPendingMessages, setUi, setSession, setApproval, setApprovalQueue, setQueuedMessages, currentSessionId, }: UseAgentEventsProps): void;
|
|
35
42
|
export {};
|
|
36
43
|
//# sourceMappingURL=useAgentEvents.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAgentEvents.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/hooks/useAgentEvents.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"useAgentEvents.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/hooks/useAgentEvents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAEH,KAAK,UAAU,EACf,KAAK,aAAa,EAErB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAGvE,UAAU,mBAAmB;IACzB,KAAK,EAAE,UAAU,CAAC;IAClB,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACpE,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/D,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAC1E,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IACzE,8CAA8C;IAC9C,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAYD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAC3B,KAAK,EACL,WAAW,EACX,kBAAkB,EAClB,KAAK,EACL,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACnB,EAAE,mBAAmB,GAAG,IAAI,CAqS5B"}
|
|
@@ -11,23 +11,39 @@
|
|
|
11
11
|
* - session:created - New session creation (e.g., from /clear)
|
|
12
12
|
* - message:queued - New message added to queue
|
|
13
13
|
* - message:removed - Message removed from queue (e.g., up arrow edit)
|
|
14
|
+
* - run:invoke - External trigger (scheduler, A2A, API) starting a run
|
|
15
|
+
* - run:complete (for external triggers) - External run finished
|
|
14
16
|
*
|
|
15
17
|
* Uses AbortController pattern for cleanup.
|
|
16
18
|
*/
|
|
17
|
-
import { useEffect } from 'react';
|
|
19
|
+
import { useEffect, useRef } from 'react';
|
|
18
20
|
import { setMaxListeners } from 'events';
|
|
19
|
-
import { getModelDisplayName } from '@dexto/core';
|
|
21
|
+
import { getModelDisplayName, } from '@dexto/core';
|
|
22
|
+
import { generateMessageId } from '../utils/idGenerator.js';
|
|
23
|
+
/**
|
|
24
|
+
* Extract text content from ContentPart array
|
|
25
|
+
*/
|
|
26
|
+
function extractTextContent(content) {
|
|
27
|
+
return content
|
|
28
|
+
.filter((part) => part.type === 'text')
|
|
29
|
+
.map((part) => part.text)
|
|
30
|
+
.join('\n');
|
|
31
|
+
}
|
|
20
32
|
/**
|
|
21
33
|
* Subscribes to agent event bus for non-streaming events only.
|
|
22
34
|
* Streaming events are handled via agent.stream() iterator.
|
|
35
|
+
*
|
|
36
|
+
* Also handles external triggers (run:invoke) from scheduler, A2A, etc.
|
|
23
37
|
*/
|
|
24
|
-
export function useAgentEvents({ agent, setMessages, setUi, setSession, setApproval, setApprovalQueue, setQueuedMessages, }) {
|
|
38
|
+
export function useAgentEvents({ agent, setMessages, setPendingMessages, setUi, setSession, setApproval, setApprovalQueue, setQueuedMessages, currentSessionId, }) {
|
|
39
|
+
// Track if an external trigger is active (scheduler, A2A, etc.)
|
|
40
|
+
const externalTriggerRef = useRef({ active: false, sessionId: null, messageId: null });
|
|
25
41
|
useEffect(() => {
|
|
26
42
|
const bus = agent.agentEventBus;
|
|
27
43
|
const controller = new AbortController();
|
|
28
44
|
const { signal } = controller;
|
|
29
|
-
// Increase listener limit for safety
|
|
30
|
-
setMaxListeners(
|
|
45
|
+
// Increase listener limit for safety (added more for external trigger events)
|
|
46
|
+
setMaxListeners(25, signal);
|
|
31
47
|
// NOTE: approval:request is now handled in processStream (via iterator) for proper
|
|
32
48
|
// event ordering. Direct bus subscription here caused a race condition where
|
|
33
49
|
// approval UI showed before text messages were added.
|
|
@@ -97,9 +113,131 @@ export function useAgentEvents({ agent, setMessages, setUi, setSession, setAppro
|
|
|
97
113
|
}, { signal });
|
|
98
114
|
// Note: message:dequeued is handled in processStream (via iterator) for proper synchronization
|
|
99
115
|
// with streaming events. Don't handle it here via event bus.
|
|
116
|
+
// ============================================================================
|
|
117
|
+
// EXTERNAL TRIGGER HANDLING (scheduler, A2A, API)
|
|
118
|
+
// When an external source invokes the agent, we receive events here instead of
|
|
119
|
+
// through processStream (which only handles user-initiated streams).
|
|
120
|
+
// ============================================================================
|
|
121
|
+
// Handle external trigger invocation (scheduler, A2A, API)
|
|
122
|
+
bus.on('run:invoke', (payload) => {
|
|
123
|
+
// Only handle if this is for the current session
|
|
124
|
+
if (payload.sessionId !== currentSessionId) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// Mark external trigger as active
|
|
128
|
+
const messageId = generateMessageId('assistant');
|
|
129
|
+
externalTriggerRef.current = {
|
|
130
|
+
active: true,
|
|
131
|
+
sessionId: payload.sessionId,
|
|
132
|
+
messageId,
|
|
133
|
+
};
|
|
134
|
+
// Extract prompt text from content parts
|
|
135
|
+
const promptText = extractTextContent(payload.content);
|
|
136
|
+
// Add the scheduled prompt as a "user" message with a source indicator
|
|
137
|
+
const sourceLabel = payload.source === 'scheduler'
|
|
138
|
+
? '⏰ Scheduled Task'
|
|
139
|
+
: payload.source === 'a2a'
|
|
140
|
+
? '🤖 A2A Request'
|
|
141
|
+
: payload.source === 'api'
|
|
142
|
+
? '🔌 API Request'
|
|
143
|
+
: '📥 External Request';
|
|
144
|
+
setMessages((prev) => [
|
|
145
|
+
...prev,
|
|
146
|
+
{
|
|
147
|
+
id: generateMessageId('system'),
|
|
148
|
+
role: 'system',
|
|
149
|
+
content: `${sourceLabel}`,
|
|
150
|
+
timestamp: new Date(),
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: generateMessageId('user'),
|
|
154
|
+
role: 'user',
|
|
155
|
+
content: promptText,
|
|
156
|
+
timestamp: new Date(),
|
|
157
|
+
},
|
|
158
|
+
]);
|
|
159
|
+
// Set processing state
|
|
160
|
+
setUi((prev) => ({
|
|
161
|
+
...prev,
|
|
162
|
+
isProcessing: true,
|
|
163
|
+
isThinking: true,
|
|
164
|
+
}));
|
|
165
|
+
// Add assistant pending message for streaming
|
|
166
|
+
setPendingMessages([
|
|
167
|
+
{
|
|
168
|
+
id: messageId,
|
|
169
|
+
role: 'assistant',
|
|
170
|
+
content: '',
|
|
171
|
+
timestamp: new Date(),
|
|
172
|
+
isStreaming: true,
|
|
173
|
+
},
|
|
174
|
+
]);
|
|
175
|
+
}, { signal });
|
|
176
|
+
// Handle streaming chunks for external triggers
|
|
177
|
+
bus.on('llm:chunk', (payload) => {
|
|
178
|
+
// Only handle if this is for an active external trigger
|
|
179
|
+
if (!externalTriggerRef.current.active ||
|
|
180
|
+
payload.sessionId !== externalTriggerRef.current.sessionId) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Only handle text chunks (not reasoning)
|
|
184
|
+
if (payload.chunkType !== 'text') {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
// Update pending message with new content
|
|
188
|
+
setPendingMessages((prev) => prev.map((msg) => msg.id === externalTriggerRef.current.messageId
|
|
189
|
+
? { ...msg, content: msg.content + payload.content }
|
|
190
|
+
: msg));
|
|
191
|
+
// Clear thinking state once we start receiving chunks
|
|
192
|
+
setUi((prev) => (prev.isThinking ? { ...prev, isThinking: false } : prev));
|
|
193
|
+
}, { signal });
|
|
194
|
+
// Handle LLM thinking for external triggers
|
|
195
|
+
bus.on('llm:thinking', (payload) => {
|
|
196
|
+
if (!externalTriggerRef.current.active ||
|
|
197
|
+
payload.sessionId !== externalTriggerRef.current.sessionId) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
setUi((prev) => ({ ...prev, isThinking: true }));
|
|
201
|
+
}, { signal });
|
|
202
|
+
// Handle run completion for external triggers
|
|
203
|
+
bus.on('run:complete', (payload) => {
|
|
204
|
+
// Only handle if this is for an active external trigger
|
|
205
|
+
if (!externalTriggerRef.current.active ||
|
|
206
|
+
payload.sessionId !== externalTriggerRef.current.sessionId) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
// Finalize the pending message
|
|
210
|
+
setPendingMessages((prev) => {
|
|
211
|
+
const pendingMsg = prev.find((m) => m.id === externalTriggerRef.current.messageId);
|
|
212
|
+
if (pendingMsg) {
|
|
213
|
+
// Move to finalized messages
|
|
214
|
+
setMessages((msgs) => [...msgs, { ...pendingMsg, isStreaming: false }]);
|
|
215
|
+
}
|
|
216
|
+
// Clear pending
|
|
217
|
+
return prev.filter((m) => m.id !== externalTriggerRef.current.messageId);
|
|
218
|
+
});
|
|
219
|
+
// Clear processing state
|
|
220
|
+
setUi((prev) => ({
|
|
221
|
+
...prev,
|
|
222
|
+
isProcessing: false,
|
|
223
|
+
isThinking: false,
|
|
224
|
+
}));
|
|
225
|
+
// Reset external trigger tracking
|
|
226
|
+
externalTriggerRef.current = { active: false, sessionId: null, messageId: null };
|
|
227
|
+
}, { signal });
|
|
100
228
|
// Cleanup: abort controller removes all listeners at once
|
|
101
229
|
return () => {
|
|
102
230
|
controller.abort();
|
|
103
231
|
};
|
|
104
|
-
}, [
|
|
232
|
+
}, [
|
|
233
|
+
agent,
|
|
234
|
+
setMessages,
|
|
235
|
+
setPendingMessages,
|
|
236
|
+
setUi,
|
|
237
|
+
setSession,
|
|
238
|
+
setApproval,
|
|
239
|
+
setApprovalQueue,
|
|
240
|
+
setQueuedMessages,
|
|
241
|
+
currentSessionId,
|
|
242
|
+
]);
|
|
105
243
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCLIState.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/hooks/useCLIState.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"useCLIState.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/hooks/useCLIState.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAA4D,MAAM,OAAO,CAAC;AAEjF,OAAO,EAAuB,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAGvE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEpE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAChF,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAGrF,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAE3E,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;IACzB,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC;CACzD;AAED,MAAM,WAAW,cAAc;IAE3B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAE7D,eAAe,EAAE,OAAO,EAAE,CAAC;IAC3B,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAGpE,cAAc,EAAE,OAAO,EAAE,CAAC;IAC1B,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAEnE,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;IACzE,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3D,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/D,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,aAAa,EAAE,eAAe,EAAE,CAAC;IACjC,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAG1E,MAAM,EAAE,UAAU,CAAC;IAGnB,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAG/B,mBAAmB,EAAE,KAAK,CAAC,SAAS,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;IAGpE,eAAe,EAAE,OAAO,EAAE,CAAC;IAG3B,KAAK,EAAE,UAAU,CAAC;IAClB,WAAW,EAAE,WAAW,CAAC;CAC5B;AAED,wBAAgB,WAAW,CAAC,EACxB,KAAK,EACL,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAAE,iBAAiB,GACtC,EAAE,gBAAgB,GAAG,cAAc,CAmMnC"}
|