@iaforged/context-code 1.0.81 → 1.0.83
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/src/commands.js +0 -2
- package/dist/src/components/PromptInput/PromptInput.js +7 -2
- package/dist/src/components/PromptInput/useMaybeTruncateInput.js +1 -14
- package/dist/src/components/agents/AgentDetail.js +19 -210
- package/dist/src/components/agents/AgentEditor.js +60 -70
- package/dist/src/components/agents/AgentsList.js +3 -2
- package/dist/src/components/agents/AgentsMenu.js +10 -10
- package/dist/src/components/agents/ColorPicker.js +2 -2
- package/dist/src/components/agents/ToolSelector.js +13 -13
- package/dist/src/components/agents/agentFileUtils.js +12 -11
- package/dist/src/components/agents/new-agent-creation/CreateAgentWizard.js +1 -1
- package/dist/src/components/agents/new-agent-creation/wizard-steps/ColorStep.js +22 -68
- package/dist/src/components/agents/new-agent-creation/wizard-steps/ConfirmStep.js +30 -369
- package/dist/src/components/agents/new-agent-creation/wizard-steps/ConfirmStepWrapper.js +13 -15
- package/dist/src/components/agents/new-agent-creation/wizard-steps/DescriptionStep.js +4 -4
- package/dist/src/components/agents/new-agent-creation/wizard-steps/GenerateStep.js +15 -22
- package/dist/src/components/agents/new-agent-creation/wizard-steps/LocationStep.js +2 -2
- package/dist/src/components/agents/new-agent-creation/wizard-steps/MemoryStep.js +9 -9
- package/dist/src/components/agents/new-agent-creation/wizard-steps/MethodStep.js +3 -3
- package/dist/src/components/agents/new-agent-creation/wizard-steps/ModelStep.js +8 -38
- package/dist/src/components/agents/new-agent-creation/wizard-steps/PromptStep.js +4 -4
- package/dist/src/components/agents/new-agent-creation/wizard-steps/ToolsStep.js +1 -1
- package/dist/src/components/agents/new-agent-creation/wizard-steps/TypeStep.js +3 -3
- package/dist/src/components/agents/validateAgent.js +16 -16
- package/dist/src/hooks/usePasteHandler.js +68 -7
- package/dist/src/services/compact/compact.js +22 -20
- package/dist/src/tools/AgentTool/AgentTool.js +12 -8
- package/dist/src/tools/AgentTool/agentDisplay.js +5 -2
- package/dist/src/tools/AgentTool/loadAgentsDir.js +36 -0
- package/dist/src/tools/AgentTool/resumeAgent.js +7 -3
- package/dist/src/utils/model/providers.js +14 -0
- package/package.json +1 -1
|
@@ -41,28 +41,28 @@ export function MemoryStep() {
|
|
|
41
41
|
if ($[1] !== isUserScope) {
|
|
42
42
|
const folder = getProjectMemoryFolderLabel();
|
|
43
43
|
t1 = isUserScope ? [{
|
|
44
|
-
label: "
|
|
44
|
+
label: "Alcance de usuario (~/.context/agent-memory/) (Recomendado)",
|
|
45
45
|
value: "user"
|
|
46
46
|
}, {
|
|
47
|
-
label: "
|
|
47
|
+
label: "Ninguna (sin memoria persistente)",
|
|
48
48
|
value: "none"
|
|
49
49
|
}, {
|
|
50
|
-
label: `
|
|
50
|
+
label: `Alcance de proyecto (${folder}/agent-memory/)`,
|
|
51
51
|
value: "project"
|
|
52
52
|
}, {
|
|
53
|
-
label: `
|
|
53
|
+
label: `Alcance local (${folder}/agent-memory-local/)`,
|
|
54
54
|
value: "local"
|
|
55
55
|
}] : [{
|
|
56
|
-
label: `
|
|
56
|
+
label: `Alcance de proyecto (${folder}/agent-memory/) (Recomendado)`,
|
|
57
57
|
value: "project"
|
|
58
58
|
}, {
|
|
59
|
-
label: "
|
|
59
|
+
label: "Ninguna (sin memoria persistente)",
|
|
60
60
|
value: "none"
|
|
61
61
|
}, {
|
|
62
|
-
label: "
|
|
62
|
+
label: "Alcance de usuario (~/.context/agent-memory/)",
|
|
63
63
|
value: "user"
|
|
64
64
|
}, {
|
|
65
|
-
label: `
|
|
65
|
+
label: `Alcance local (${folder}/agent-memory-local/)`,
|
|
66
66
|
value: "local"
|
|
67
67
|
}];
|
|
68
68
|
$[1] = isUserScope;
|
|
@@ -107,7 +107,7 @@ export function MemoryStep() {
|
|
|
107
107
|
}
|
|
108
108
|
let t4;
|
|
109
109
|
if ($[9] !== goBack || $[10] !== handleSelect || $[11] !== memoryOptions) {
|
|
110
|
-
t4 = _jsx(WizardDialogLayout, { subtitle: "
|
|
110
|
+
t4 = _jsx(WizardDialogLayout, { subtitle: "Configurar memoria del agente", footerText: t3, children: _jsx(Box, { children: _jsx(Select, { options: memoryOptions, onChange: handleSelect, onCancel: goBack }, "memory-select") }) });
|
|
111
111
|
$[9] = goBack;
|
|
112
112
|
$[10] = handleSelect;
|
|
113
113
|
$[11] = memoryOptions;
|
|
@@ -13,10 +13,10 @@ export function MethodStep() {
|
|
|
13
13
|
let t0;
|
|
14
14
|
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
15
15
|
t0 = [{
|
|
16
|
-
label: "
|
|
16
|
+
label: "Generar con Claude (recomendado)",
|
|
17
17
|
value: "generate"
|
|
18
18
|
}, {
|
|
19
|
-
label: "
|
|
19
|
+
label: "Configuracion manual",
|
|
20
20
|
value: "manual"
|
|
21
21
|
}];
|
|
22
22
|
$[0] = t0;
|
|
@@ -67,7 +67,7 @@ export function MethodStep() {
|
|
|
67
67
|
}
|
|
68
68
|
let t4;
|
|
69
69
|
if ($[8] !== t2 || $[9] !== t3) {
|
|
70
|
-
t4 = _jsx(WizardDialogLayout, { subtitle: "
|
|
70
|
+
t4 = _jsx(WizardDialogLayout, { subtitle: "Metodo de creacion", footerText: t1, children: _jsx(Box, { children: _jsx(Select, { options: methodOptions, onChange: t2, onCancel: t3 }, "method-select") }) });
|
|
71
71
|
$[8] = t2;
|
|
72
72
|
$[9] = t3;
|
|
73
73
|
$[10] = t4;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { c as _c } from "react/compiler-runtime";
|
|
3
2
|
import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js';
|
|
4
3
|
import { Byline } from '../../../design-system/Byline.js';
|
|
5
4
|
import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js';
|
|
@@ -7,42 +6,13 @@ import { useWizard } from '../../../wizard/index.js';
|
|
|
7
6
|
import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js';
|
|
8
7
|
import { ModelSelector } from '../../ModelSelector.js';
|
|
9
8
|
export function ModelStep() {
|
|
10
|
-
const $ = _c(8);
|
|
11
9
|
const { goNext, goBack, updateWizardData, wizardData } = useWizard();
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
$[0] = goNext;
|
|
21
|
-
$[1] = updateWizardData;
|
|
22
|
-
$[2] = t0;
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
t0 = $[2];
|
|
26
|
-
}
|
|
27
|
-
const handleComplete = t0;
|
|
28
|
-
let t1;
|
|
29
|
-
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
|
30
|
-
t1 = _jsxs(Byline, { children: [_jsx(KeyboardShortcutHint, { shortcut: "\u2191\u2193", action: "navegar" }), _jsx(KeyboardShortcutHint, { shortcut: "Enter", action: "seleccionar" }), _jsx(ConfigurableShortcutHint, { action: "confirm:no", context: "Confirmation", fallback: "Esc", description: "volver" })] });
|
|
31
|
-
$[3] = t1;
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
t1 = $[3];
|
|
35
|
-
}
|
|
36
|
-
let t2;
|
|
37
|
-
if ($[4] !== goBack || $[5] !== handleComplete || $[6] !== wizardData.selectedModel) {
|
|
38
|
-
t2 = _jsx(WizardDialogLayout, { subtitle: "Select model", footerText: t1, children: _jsx(ModelSelector, { initialModel: wizardData.selectedModel, onComplete: handleComplete, onCancel: goBack }) });
|
|
39
|
-
$[4] = goBack;
|
|
40
|
-
$[5] = handleComplete;
|
|
41
|
-
$[6] = wizardData.selectedModel;
|
|
42
|
-
$[7] = t2;
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
t2 = $[7];
|
|
46
|
-
}
|
|
47
|
-
return t2;
|
|
10
|
+
const handleComplete = (selection) => {
|
|
11
|
+
updateWizardData({
|
|
12
|
+
selectedModel: selection.model,
|
|
13
|
+
selectedProvider: selection.provider,
|
|
14
|
+
});
|
|
15
|
+
goNext();
|
|
16
|
+
};
|
|
17
|
+
return (_jsx(WizardDialogLayout, { subtitle: "Proveedor y modelo", footerText: _jsxs(Byline, { children: [_jsx(KeyboardShortcutHint, { shortcut: "\u2191\u2193", action: "navegar" }), _jsx(KeyboardShortcutHint, { shortcut: "Enter", action: "seleccionar" }), _jsx(ConfigurableShortcutHint, { action: "confirm:no", context: "Confirmation", fallback: "Esc", description: "volver" })] }), children: _jsx(ModelSelector, { initialModel: wizardData.selectedModel, initialProvider: wizardData.selectedProvider, onComplete: handleComplete, onCancel: goBack }) }));
|
|
48
18
|
}
|
|
@@ -59,7 +59,7 @@ export function PromptStep() {
|
|
|
59
59
|
t3 = () => {
|
|
60
60
|
const trimmedPrompt = systemPrompt.trim();
|
|
61
61
|
if (!trimmedPrompt) {
|
|
62
|
-
setError("
|
|
62
|
+
setError("El system prompt es obligatorio");
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
65
|
setError(null);
|
|
@@ -88,8 +88,8 @@ export function PromptStep() {
|
|
|
88
88
|
let t5;
|
|
89
89
|
let t6;
|
|
90
90
|
if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
|
|
91
|
-
t5 = _jsx(Text, { children: "
|
|
92
|
-
t6 = _jsx(Text, { dimColor: true, children: "
|
|
91
|
+
t5 = _jsx(Text, { children: "Escribe el system prompt de tu agente:" });
|
|
92
|
+
t6 = _jsx(Text, { dimColor: true, children: "Se detallado para obtener mejores resultados" });
|
|
93
93
|
$[9] = t5;
|
|
94
94
|
$[10] = t6;
|
|
95
95
|
}
|
|
@@ -99,7 +99,7 @@ export function PromptStep() {
|
|
|
99
99
|
}
|
|
100
100
|
let t7;
|
|
101
101
|
if ($[11] !== cursorOffset || $[12] !== handleSubmit || $[13] !== systemPrompt) {
|
|
102
|
-
t7 = _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: systemPrompt, onChange: setSystemPrompt, onSubmit: handleSubmit, placeholder: "
|
|
102
|
+
t7 = _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: systemPrompt, onChange: setSystemPrompt, onSubmit: handleSubmit, placeholder: "Eres un revisor de codigo util que...", columns: 80, cursorOffset: cursorOffset, onChangeCursorOffset: setCursorOffset, focus: true, showCursor: true }) });
|
|
103
103
|
$[11] = cursorOffset;
|
|
104
104
|
$[12] = handleSubmit;
|
|
105
105
|
$[13] = systemPrompt;
|
|
@@ -37,7 +37,7 @@ export function ToolsStep(t0) {
|
|
|
37
37
|
}
|
|
38
38
|
let t3;
|
|
39
39
|
if ($[4] !== goBack || $[5] !== handleComplete || $[6] !== initialTools || $[7] !== tools) {
|
|
40
|
-
t3 = _jsx(WizardDialogLayout, { subtitle: "
|
|
40
|
+
t3 = _jsx(WizardDialogLayout, { subtitle: "Seleccionar herramientas", footerText: t2, children: _jsx(ToolSelector, { tools: tools, initialTools: initialTools, onComplete: handleComplete, onCancel: goBack }) });
|
|
41
41
|
$[4] = goBack;
|
|
42
42
|
$[5] = handleComplete;
|
|
43
43
|
$[6] = initialTools;
|
|
@@ -60,7 +60,7 @@ export function TypeStep(_props) {
|
|
|
60
60
|
}
|
|
61
61
|
let t3;
|
|
62
62
|
if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
|
|
63
|
-
t3 = _jsx(Text, { children: "
|
|
63
|
+
t3 = _jsx(Text, { children: "Escribe un identificador unico para tu agente:" });
|
|
64
64
|
$[5] = t3;
|
|
65
65
|
}
|
|
66
66
|
else {
|
|
@@ -68,7 +68,7 @@ export function TypeStep(_props) {
|
|
|
68
68
|
}
|
|
69
69
|
let t4;
|
|
70
70
|
if ($[6] !== agentType || $[7] !== cursorOffset || $[8] !== handleSubmit) {
|
|
71
|
-
t4 = _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: agentType, onChange: setAgentType, onSubmit: handleSubmit, placeholder: "
|
|
71
|
+
t4 = _jsx(Box, { marginTop: 1, children: _jsx(TextInput, { value: agentType, onChange: setAgentType, onSubmit: handleSubmit, placeholder: "p. ej., test-runner, tech-lead, etc", columns: 60, cursorOffset: cursorOffset, onChangeCursorOffset: setCursorOffset, focus: true, showCursor: true }) });
|
|
72
72
|
$[6] = agentType;
|
|
73
73
|
$[7] = cursorOffset;
|
|
74
74
|
$[8] = handleSubmit;
|
|
@@ -88,7 +88,7 @@ export function TypeStep(_props) {
|
|
|
88
88
|
}
|
|
89
89
|
let t6;
|
|
90
90
|
if ($[12] !== t4 || $[13] !== t5) {
|
|
91
|
-
t6 = _jsx(WizardDialogLayout, { subtitle: "
|
|
91
|
+
t6 = _jsx(WizardDialogLayout, { subtitle: "Tipo de agente (identificador)", footerText: t2, children: _jsxs(Box, { flexDirection: "column", children: [t3, t4, t5] }) });
|
|
92
92
|
$[12] = t4;
|
|
93
93
|
$[13] = t5;
|
|
94
94
|
$[14] = t6;
|
|
@@ -2,16 +2,16 @@ import { resolveAgentTools } from '../../tools/AgentTool/agentToolUtils.js';
|
|
|
2
2
|
import { getAgentSourceDisplayName } from './utils.js';
|
|
3
3
|
export function validateAgentType(agentType) {
|
|
4
4
|
if (!agentType) {
|
|
5
|
-
return '
|
|
5
|
+
return 'El tipo de agente es obligatorio';
|
|
6
6
|
}
|
|
7
7
|
if (!/^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$/.test(agentType)) {
|
|
8
|
-
return '
|
|
8
|
+
return 'El tipo de agente debe iniciar y terminar con caracteres alfanumericos y solo puede contener letras, numeros y guiones';
|
|
9
9
|
}
|
|
10
10
|
if (agentType.length < 3) {
|
|
11
|
-
return '
|
|
11
|
+
return 'El tipo de agente debe tener al menos 3 caracteres';
|
|
12
12
|
}
|
|
13
13
|
if (agentType.length > 50) {
|
|
14
|
-
return '
|
|
14
|
+
return 'El tipo de agente debe tener menos de 50 caracteres';
|
|
15
15
|
}
|
|
16
16
|
return null;
|
|
17
17
|
}
|
|
@@ -20,7 +20,7 @@ export function validateAgent(agent, availableTools, existingAgents) {
|
|
|
20
20
|
const warnings = [];
|
|
21
21
|
// Validate agent type
|
|
22
22
|
if (!agent.agentType) {
|
|
23
|
-
errors.push('
|
|
23
|
+
errors.push('El tipo de agente es obligatorio');
|
|
24
24
|
}
|
|
25
25
|
else {
|
|
26
26
|
const typeError = validateAgentType(agent.agentType);
|
|
@@ -30,46 +30,46 @@ export function validateAgent(agent, availableTools, existingAgents) {
|
|
|
30
30
|
// Check for duplicates (excluding self for editing)
|
|
31
31
|
const duplicate = existingAgents.find(a => a.agentType === agent.agentType && a.source !== agent.source);
|
|
32
32
|
if (duplicate) {
|
|
33
|
-
errors.push(`
|
|
33
|
+
errors.push(`El tipo de agente "${agent.agentType}" ya existe en ${getAgentSourceDisplayName(duplicate.source)}`);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
// Validate description
|
|
37
37
|
if (!agent.whenToUse) {
|
|
38
|
-
errors.push('
|
|
38
|
+
errors.push('La descripcion es obligatoria');
|
|
39
39
|
}
|
|
40
40
|
else if (agent.whenToUse.length < 10) {
|
|
41
|
-
warnings.push('
|
|
41
|
+
warnings.push('La descripcion deberia ser mas clara (al menos 10 caracteres)');
|
|
42
42
|
}
|
|
43
43
|
else if (agent.whenToUse.length > 5000) {
|
|
44
|
-
warnings.push('
|
|
44
|
+
warnings.push('La descripcion es muy larga (mas de 5000 caracteres)');
|
|
45
45
|
}
|
|
46
46
|
// Validate tools
|
|
47
47
|
if (agent.tools !== undefined && !Array.isArray(agent.tools)) {
|
|
48
|
-
errors.push('
|
|
48
|
+
errors.push('Las herramientas deben ser un arreglo');
|
|
49
49
|
}
|
|
50
50
|
else {
|
|
51
51
|
if (agent.tools === undefined) {
|
|
52
|
-
warnings.push('
|
|
52
|
+
warnings.push('El agente tendra acceso a todas las herramientas');
|
|
53
53
|
}
|
|
54
54
|
else if (agent.tools.length === 0) {
|
|
55
|
-
warnings.push('No
|
|
55
|
+
warnings.push('No hay herramientas seleccionadas: el agente tendra capacidades muy limitadas');
|
|
56
56
|
}
|
|
57
57
|
// Check for invalid tools
|
|
58
58
|
const resolvedTools = resolveAgentTools(agent, availableTools, false);
|
|
59
59
|
if (resolvedTools.invalidTools.length > 0) {
|
|
60
|
-
errors.push(`
|
|
60
|
+
errors.push(`Herramientas no validas: ${resolvedTools.invalidTools.join(', ')}`);
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
// Validate system prompt
|
|
64
64
|
const systemPrompt = agent.getSystemPrompt();
|
|
65
65
|
if (!systemPrompt) {
|
|
66
|
-
errors.push('
|
|
66
|
+
errors.push('El system prompt es obligatorio');
|
|
67
67
|
}
|
|
68
68
|
else if (systemPrompt.length < 20) {
|
|
69
|
-
errors.push('
|
|
69
|
+
errors.push('El system prompt es demasiado corto (minimo 20 caracteres)');
|
|
70
70
|
}
|
|
71
71
|
else if (systemPrompt.length > 10000) {
|
|
72
|
-
warnings.push('
|
|
72
|
+
warnings.push('El system prompt es muy largo (mas de 10,000 caracteres)');
|
|
73
73
|
}
|
|
74
74
|
return {
|
|
75
75
|
isValid: errors.length === 0,
|
|
@@ -2,7 +2,7 @@ import { basename } from 'path';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { logError } from '../utils/log.js';
|
|
4
4
|
import { useDebounceCallback } from 'usehooks-ts';
|
|
5
|
-
import { getImageFromClipboard, isImageFilePath,
|
|
5
|
+
import { getImageFromClipboard, isImageFilePath, tryReadImageFromPath, } from '../utils/imagePaste.js';
|
|
6
6
|
import { getPlatform } from '../utils/platform.js';
|
|
7
7
|
const CLIPBOARD_CHECK_DEBOUNCE_MS = 50;
|
|
8
8
|
const PASTE_COMPLETION_TIMEOUT_MS = 100;
|
|
@@ -16,10 +16,27 @@ export function usePasteHandler({ onPaste, onInput, onImagePaste, }) {
|
|
|
16
16
|
// reads stale pasteState.timeoutId (null) and takes the onInput path. If
|
|
17
17
|
// that key is Enter, it submits the old input and the paste is lost.
|
|
18
18
|
const pastePendingRef = React.useRef(false);
|
|
19
|
+
// For detecting fast non-bracketed pastes
|
|
20
|
+
const typingQueueRef = React.useRef([]);
|
|
21
|
+
const typingTimeoutRef = React.useRef(null);
|
|
22
|
+
const flushTypingQueue = React.useCallback((onInputCallback) => {
|
|
23
|
+
if (typingTimeoutRef.current) {
|
|
24
|
+
clearTimeout(typingTimeoutRef.current);
|
|
25
|
+
typingTimeoutRef.current = null;
|
|
26
|
+
}
|
|
27
|
+
const queue = typingQueueRef.current;
|
|
28
|
+
typingQueueRef.current = [];
|
|
29
|
+
for (const q of queue) {
|
|
30
|
+
onInputCallback(q.input, q.key);
|
|
31
|
+
}
|
|
32
|
+
}, []);
|
|
19
33
|
const isMacOS = React.useMemo(() => getPlatform() === 'macos', []);
|
|
20
34
|
React.useEffect(() => {
|
|
21
35
|
return () => {
|
|
22
36
|
isMountedRef.current = false;
|
|
37
|
+
if (typingTimeoutRef.current) {
|
|
38
|
+
clearTimeout(typingTimeoutRef.current);
|
|
39
|
+
}
|
|
23
40
|
};
|
|
24
41
|
}, []);
|
|
25
42
|
const checkClipboardForImageImpl = React.useCallback(() => {
|
|
@@ -156,13 +173,22 @@ export function usePasteHandler({ onPaste, onInput, onImagePaste, }) {
|
|
|
156
173
|
return;
|
|
157
174
|
}
|
|
158
175
|
// Check if we should handle as paste (from bracketed paste, large input, or continuation)
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (shouldHandleAsPaste) {
|
|
176
|
+
const isAlreadyPaste = pastePendingRef.current ||
|
|
177
|
+
hasImageFilePath ||
|
|
178
|
+
isFromPaste ||
|
|
179
|
+
input.length > 50;
|
|
180
|
+
if (onPaste && isAlreadyPaste) {
|
|
165
181
|
pastePendingRef.current = true;
|
|
182
|
+
// If we had buffered typing that was actually part of this paste, move it!
|
|
183
|
+
if (typingQueueRef.current.length > 0) {
|
|
184
|
+
const queuedText = typingQueueRef.current.map(q => q.input).join('');
|
|
185
|
+
input = queuedText + input;
|
|
186
|
+
if (typingTimeoutRef.current) {
|
|
187
|
+
clearTimeout(typingTimeoutRef.current);
|
|
188
|
+
typingTimeoutRef.current = null;
|
|
189
|
+
}
|
|
190
|
+
typingQueueRef.current = [];
|
|
191
|
+
}
|
|
166
192
|
setPasteState(({ chunks, timeoutId }) => {
|
|
167
193
|
return {
|
|
168
194
|
chunks: [...chunks, input],
|
|
@@ -171,6 +197,41 @@ export function usePasteHandler({ onPaste, onInput, onImagePaste, }) {
|
|
|
171
197
|
});
|
|
172
198
|
return;
|
|
173
199
|
}
|
|
200
|
+
// It might be the start of a paste! (length <= 50)
|
|
201
|
+
// Check if it's pure text (not a control key)
|
|
202
|
+
const isControl = key.ctrl || key.meta || key.option || key.super || key.fn ||
|
|
203
|
+
(key.name !== undefined && key.name.length > 1 && key.name !== 'space' && key.name !== 'number');
|
|
204
|
+
if (onPaste && input.length > 0 && !isControl) {
|
|
205
|
+
typingQueueRef.current.push({ input, key });
|
|
206
|
+
// Calculate total length of queued typing
|
|
207
|
+
const totalLength = typingQueueRef.current.reduce((acc, q) => acc + q.input.length, 0);
|
|
208
|
+
if (totalLength > 50) {
|
|
209
|
+
// Oh! It accumulated to > 50 within the tiny window! It IS a paste!
|
|
210
|
+
pastePendingRef.current = true;
|
|
211
|
+
const queuedText = typingQueueRef.current.map(q => q.input).join('');
|
|
212
|
+
typingQueueRef.current = [];
|
|
213
|
+
if (typingTimeoutRef.current) {
|
|
214
|
+
clearTimeout(typingTimeoutRef.current);
|
|
215
|
+
typingTimeoutRef.current = null;
|
|
216
|
+
}
|
|
217
|
+
setPasteState(({ chunks, timeoutId }) => {
|
|
218
|
+
return {
|
|
219
|
+
chunks: [...chunks, queuedText],
|
|
220
|
+
timeoutId: resetPasteTimeout(timeoutId),
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
// Schedule flush if not already scheduled
|
|
226
|
+
if (!typingTimeoutRef.current) {
|
|
227
|
+
typingTimeoutRef.current = setTimeout(() => {
|
|
228
|
+
flushTypingQueue(onInput);
|
|
229
|
+
}, 10); // 10ms is completely imperceptible
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
// If it's a control key or onPaste is not defined, flush any pending typing immediately
|
|
234
|
+
flushTypingQueue(onInput);
|
|
174
235
|
onInput(input, key);
|
|
175
236
|
if (input.length > 10) {
|
|
176
237
|
// Ensure that setIsPasting is turned off on any other multicharacter
|
|
@@ -49,6 +49,7 @@ import { getCompactPrompt, getCompactUserSummaryMessage, getPartialCompactPrompt
|
|
|
49
49
|
export const POST_COMPACT_MAX_FILES_TO_RESTORE = 5;
|
|
50
50
|
export const POST_COMPACT_TOKEN_BUDGET = 50_000;
|
|
51
51
|
export const POST_COMPACT_MAX_TOKENS_PER_FILE = 5_000;
|
|
52
|
+
export const POST_COMPACT_MAX_TOKENS_FOR_PLAN = 5_000;
|
|
52
53
|
// Skills can be large (verify=18.7KB, claude-api=20.1KB). Previously re-injected
|
|
53
54
|
// unbounded on every compact → 5-10K tok/compact. Per-skill truncation beats
|
|
54
55
|
// dropping — instructions at the top of a skill file are usually the critical
|
|
@@ -120,22 +121,23 @@ export function stripImagesFromMessages(messages) {
|
|
|
120
121
|
};
|
|
121
122
|
});
|
|
122
123
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Strip attachment types that are re-injected post-compaction anyway.
|
|
125
|
-
* skill_discovery/skill_listing are re-surfaced by resetSentSkillNames()
|
|
126
|
-
* + the next turn's discovery signal, so feeding them to the summarizer
|
|
127
|
-
* wastes tokens and pollutes the summary with stale skill suggestions.
|
|
128
|
-
*
|
|
129
|
-
* No-op when EXPERIMENTAL_SKILL_SEARCH is off (the attachment types
|
|
130
|
-
* don't exist on external builds).
|
|
131
|
-
*/
|
|
132
124
|
export function stripReinjectedAttachments(messages) {
|
|
133
|
-
|
|
134
|
-
|
|
125
|
+
const stripSkillSearchAttachments = feature('EXPERIMENTAL_SKILL_SEARCH');
|
|
126
|
+
return messages.filter(m => {
|
|
127
|
+
if (m.type !== 'attachment') {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
if (m.attachment.type === 'plan_file_reference' ||
|
|
131
|
+
m.attachment.type === 'invoked_skills') {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
if (stripSkillSearchAttachments &&
|
|
135
135
|
(m.attachment.type === 'skill_discovery' ||
|
|
136
|
-
m.attachment.type === 'skill_listing'))
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
m.attachment.type === 'skill_listing')) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
});
|
|
139
141
|
}
|
|
140
142
|
export const ERROR_MESSAGE_NOT_ENOUGH_MESSAGES = 'Not enough messages to compact.';
|
|
141
143
|
const MAX_PTL_RETRIES = 3;
|
|
@@ -1070,11 +1072,12 @@ export async function createPostCompactFileAttachments(readFileState, toolUseCon
|
|
|
1070
1072
|
* This ensures the plan is preserved after compaction.
|
|
1071
1073
|
*/
|
|
1072
1074
|
export function createPlanAttachmentIfNeeded(agentId) {
|
|
1073
|
-
const
|
|
1074
|
-
if (!
|
|
1075
|
+
const rawPlanContent = getPlan(agentId);
|
|
1076
|
+
if (!rawPlanContent) {
|
|
1075
1077
|
return null;
|
|
1076
1078
|
}
|
|
1077
1079
|
const planFilePath = getPlanFilePath(agentId);
|
|
1080
|
+
const planContent = truncateToTokens(rawPlanContent, POST_COMPACT_MAX_TOKENS_FOR_PLAN, '\n\n[... plan content truncated for compaction; use Read on the plan path if you need the full text]');
|
|
1078
1081
|
return createAttachmentMessage({
|
|
1079
1082
|
type: 'plan_file_reference',
|
|
1080
1083
|
planFilePath,
|
|
@@ -1216,19 +1219,18 @@ function collectReadToolFilePaths(messages) {
|
|
|
1216
1219
|
}
|
|
1217
1220
|
return paths;
|
|
1218
1221
|
}
|
|
1219
|
-
const SKILL_TRUNCATION_MARKER = '\n\n[... skill content truncated for compaction; use Read on the skill path if you need the full text]';
|
|
1220
1222
|
/**
|
|
1221
1223
|
* Truncate content to roughly maxTokens, keeping the head. roughTokenCountEstimation
|
|
1222
1224
|
* uses ~4 chars/token (its default bytesPerToken), so char budget = maxTokens * 4
|
|
1223
1225
|
* minus the marker so the result stays within budget. Marker tells the model it
|
|
1224
1226
|
* can Read the full file if needed.
|
|
1225
1227
|
*/
|
|
1226
|
-
function truncateToTokens(content, maxTokens) {
|
|
1228
|
+
function truncateToTokens(content, maxTokens, marker = '\n\n[... skill content truncated for compaction; use Read on the skill path if you need the full text]') {
|
|
1227
1229
|
if (roughTokenCountEstimation(content) <= maxTokens) {
|
|
1228
1230
|
return content;
|
|
1229
1231
|
}
|
|
1230
|
-
const charBudget = maxTokens * 4 -
|
|
1231
|
-
return content.slice(0, charBudget) +
|
|
1232
|
+
const charBudget = maxTokens * 4 - marker.length;
|
|
1233
|
+
return content.slice(0, Math.max(0, charBudget)) + marker;
|
|
1232
1234
|
}
|
|
1233
1235
|
function shouldExcludeFromPostCompactRestore(filename, agentId) {
|
|
1234
1236
|
const normalizedFilename = expandPath(filename);
|
|
@@ -25,6 +25,8 @@ import { AbortError, errorMessage, toError } from '../../utils/errors.js';
|
|
|
25
25
|
import { lazySchema } from '../../utils/lazySchema.js';
|
|
26
26
|
import { createUserMessage, extractTextContent, isSyntheticMessage, normalizeMessages } from '../../utils/messages.js';
|
|
27
27
|
import { getAgentModel } from '../../utils/model/agent.js';
|
|
28
|
+
import { runWithProviderOverride } from '../../utils/model/providerOverrideContext.js';
|
|
29
|
+
import { providerPreferenceToApiProvider } from '../../utils/model/providers.js';
|
|
28
30
|
import { permissionModeSchema } from '../../utils/permissions/PermissionMode.js';
|
|
29
31
|
import { filterDeniedAgents, getDenyRuleForAgent } from '../../utils/permissions/permissions.js';
|
|
30
32
|
import { enqueueSdkEvent } from '../../utils/sdkEventQueue.js';
|
|
@@ -316,8 +318,10 @@ export const AgentTool = buildTool({
|
|
|
316
318
|
if (selectedAgent.color) {
|
|
317
319
|
setAgentColor(selectedAgent.agentType, selectedAgent.color);
|
|
318
320
|
}
|
|
321
|
+
const agentProviderOverride = providerPreferenceToApiProvider(selectedAgent.provider);
|
|
322
|
+
const withAgentProvider = (fn => runWithProviderOverride(agentProviderOverride, fn));
|
|
319
323
|
// Resolve agent params for logging (these are already resolved in runAgent)
|
|
320
|
-
const resolvedAgentModel = getAgentModel(selectedAgent.model, toolUseContext.options.mainLoopModel, isForkPath ? undefined : model, permissionMode);
|
|
324
|
+
const resolvedAgentModel = withAgentProvider(() => getAgentModel(selectedAgent.model, toolUseContext.options.mainLoopModel, isForkPath ? undefined : model, permissionMode));
|
|
321
325
|
logEvent('tengu_agent_tool_selected', {
|
|
322
326
|
agent_type: selectedAgent.agentType,
|
|
323
327
|
model: resolvedAgentModel,
|
|
@@ -425,7 +429,7 @@ export const AgentTool = buildTool({
|
|
|
425
429
|
});
|
|
426
430
|
}
|
|
427
431
|
// Apply environment details enhancement
|
|
428
|
-
enhancedSystemPrompt = await enhanceSystemPromptWithEnvDetails([agentPrompt], resolvedAgentModel, additionalWorkingDirectories);
|
|
432
|
+
enhancedSystemPrompt = await withAgentProvider(() => enhanceSystemPromptWithEnvDetails([agentPrompt], resolvedAgentModel, additionalWorkingDirectories));
|
|
429
433
|
}
|
|
430
434
|
catch (error) {
|
|
431
435
|
logForDebugging(`Failed to get system prompt for agent ${selectedAgent.agentType}: ${errorMessage(error)}`);
|
|
@@ -600,7 +604,7 @@ export const AgentTool = buildTool({
|
|
|
600
604
|
// invocation time — when this `void` fires — and survives every await
|
|
601
605
|
// inside. No capture/restore needed; the detached closure sees the
|
|
602
606
|
// parent turn's workload automatically, isolated from its finally.
|
|
603
|
-
void runWithAgentContext(asyncAgentContext, () => wrapWithCwd(() => runAsyncAgentLifecycle({
|
|
607
|
+
void withAgentProvider(() => runWithAgentContext(asyncAgentContext, () => wrapWithCwd(() => runAsyncAgentLifecycle({
|
|
604
608
|
taskId: agentBackgroundTask.agentId,
|
|
605
609
|
abortController: agentBackgroundTask.abortController,
|
|
606
610
|
makeStream: onCacheSafeParams => runAgent({
|
|
@@ -619,7 +623,7 @@ export const AgentTool = buildTool({
|
|
|
619
623
|
agentIdForCleanup: asyncAgentId,
|
|
620
624
|
enableSummarization: isCoordinator || isForkSubagentEnabled() || getSdkAgentProgressSummariesEnabled(),
|
|
621
625
|
getWorktreeResult: cleanupWorktreeIfNeeded
|
|
622
|
-
})));
|
|
626
|
+
}))));
|
|
623
627
|
const canReadOutputFile = toolUseContext.options.tools.some(t => toolMatchesName(t, FILE_READ_TOOL_NAME) || toolMatchesName(t, BASH_TOOL_NAME));
|
|
624
628
|
return {
|
|
625
629
|
data: {
|
|
@@ -651,7 +655,7 @@ export const AgentTool = buildTool({
|
|
|
651
655
|
};
|
|
652
656
|
// Wrap entire sync agent execution in context for analytics attribution
|
|
653
657
|
// and optionally in a worktree cwd override for filesystem isolation
|
|
654
|
-
return runWithAgentContext(syncAgentContext, () => wrapWithCwd(async () => {
|
|
658
|
+
return withAgentProvider(() => runWithAgentContext(syncAgentContext, () => wrapWithCwd(async () => {
|
|
655
659
|
const agentMessages = [];
|
|
656
660
|
const agentStartTime = Date.now();
|
|
657
661
|
const syncTracker = createProgressTracker();
|
|
@@ -761,7 +765,7 @@ export const AgentTool = buildTool({
|
|
|
761
765
|
// Workload: inherited via ALS at `void` invocation time,
|
|
762
766
|
// same as the async-from-start path above.
|
|
763
767
|
// Continue agent in background and return async result
|
|
764
|
-
void runWithAgentContext(syncAgentContext, async () => {
|
|
768
|
+
void withAgentProvider(() => runWithAgentContext(syncAgentContext, async () => {
|
|
765
769
|
let stopBackgroundedSummarization;
|
|
766
770
|
try {
|
|
767
771
|
// Clean up the foreground iterator so its finally block runs
|
|
@@ -883,7 +887,7 @@ export const AgentTool = buildTool({
|
|
|
883
887
|
// Note: worktree cleanup is done before enqueueAgentNotification
|
|
884
888
|
// in both try and catch paths so we can include worktree info
|
|
885
889
|
}
|
|
886
|
-
});
|
|
890
|
+
}));
|
|
887
891
|
// Return async_launched result immediately
|
|
888
892
|
const canReadOutputFile = toolUseContext.options.tools.some(t => toolMatchesName(t, FILE_READ_TOOL_NAME) || toolMatchesName(t, BASH_TOOL_NAME));
|
|
889
893
|
return {
|
|
@@ -1091,7 +1095,7 @@ export const AgentTool = buildTool({
|
|
|
1091
1095
|
...worktreeResult
|
|
1092
1096
|
}
|
|
1093
1097
|
};
|
|
1094
|
-
}));
|
|
1098
|
+
})));
|
|
1095
1099
|
}
|
|
1096
1100
|
},
|
|
1097
1101
|
isReadOnly() {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Shared utilities for displaying agent information.
|
|
3
3
|
* Used by both the CLI `claude agents` handler and the interactive `/agents` command.
|
|
4
4
|
*/
|
|
5
|
-
import { getDefaultSubagentModel } from '../../utils/model/agent.js';
|
|
5
|
+
import { getAgentProviderDisplay, getDefaultSubagentModel, } from '../../utils/model/agent.js';
|
|
6
6
|
const OVERRIDE_SOURCE_LABELS = {
|
|
7
7
|
userSettings: 'usuario',
|
|
8
8
|
projectSettings: 'proyecto',
|
|
@@ -61,7 +61,10 @@ export function resolveAgentModelDisplay(agent) {
|
|
|
61
61
|
const model = agent.model || getDefaultSubagentModel();
|
|
62
62
|
if (!model)
|
|
63
63
|
return undefined;
|
|
64
|
-
return model === 'inherit' ? '
|
|
64
|
+
return model === 'inherit' ? 'Heredar del padre' : model;
|
|
65
|
+
}
|
|
66
|
+
export function resolveAgentProviderDisplay(agent) {
|
|
67
|
+
return getAgentProviderDisplay(agent.provider);
|
|
65
68
|
}
|
|
66
69
|
/**
|
|
67
70
|
* Get a human-readable label for the source that overrides an agent.
|
|
@@ -42,6 +42,19 @@ const AgentJsonSchema = lazySchema(() => z.object({
|
|
|
42
42
|
.min(1, 'Model cannot be empty')
|
|
43
43
|
.transform(m => (m.toLowerCase() === 'inherit' ? 'inherit' : m))
|
|
44
44
|
.optional(),
|
|
45
|
+
provider: z
|
|
46
|
+
.enum([
|
|
47
|
+
'claude',
|
|
48
|
+
'openai',
|
|
49
|
+
'openrouter',
|
|
50
|
+
'ollama',
|
|
51
|
+
'ollama-cloud',
|
|
52
|
+
'gemini-api',
|
|
53
|
+
'gemini-google',
|
|
54
|
+
'zai',
|
|
55
|
+
'minimax',
|
|
56
|
+
])
|
|
57
|
+
.optional(),
|
|
45
58
|
effort: z.union([z.enum(EFFORT_LEVELS), z.number().int()]).optional(),
|
|
46
59
|
permissionMode: z.enum(PERMISSION_MODES).optional(),
|
|
47
60
|
mcpServers: z.array(AgentMcpServerSpecSchema()).optional(),
|
|
@@ -285,6 +298,7 @@ export function parseAgentFromJson(name, definition, source = 'flagSettings') {
|
|
|
285
298
|
},
|
|
286
299
|
source,
|
|
287
300
|
...(parsed.model ? { model: parsed.model } : {}),
|
|
301
|
+
...(parsed.provider ? { provider: parsed.provider } : {}),
|
|
288
302
|
...(parsed.effort !== undefined ? { effort: parsed.effort } : {}),
|
|
289
303
|
...(parsed.permissionMode
|
|
290
304
|
? { permissionMode: parsed.permissionMode }
|
|
@@ -347,6 +361,27 @@ export function parseAgentFromMarkdown(filePath, baseDir, frontmatter, content,
|
|
|
347
361
|
// Unescape newlines in whenToUse that were escaped for YAML parsing
|
|
348
362
|
whenToUse = whenToUse.replace(/\\n/g, '\n');
|
|
349
363
|
const color = frontmatter['color'];
|
|
364
|
+
const providerRaw = frontmatter['provider'];
|
|
365
|
+
const VALID_PROVIDERS = [
|
|
366
|
+
'claude',
|
|
367
|
+
'openai',
|
|
368
|
+
'openrouter',
|
|
369
|
+
'ollama',
|
|
370
|
+
'ollama-cloud',
|
|
371
|
+
'gemini-api',
|
|
372
|
+
'gemini-google',
|
|
373
|
+
'zai',
|
|
374
|
+
'minimax',
|
|
375
|
+
];
|
|
376
|
+
let provider;
|
|
377
|
+
if (typeof providerRaw === 'string') {
|
|
378
|
+
if (VALID_PROVIDERS.includes(providerRaw)) {
|
|
379
|
+
provider = providerRaw;
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
logForDebugging(`Agent file ${filePath} has invalid provider value '${providerRaw}'. Valid options: ${VALID_PROVIDERS.join(', ')}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
350
385
|
const modelRaw = frontmatter['model'];
|
|
351
386
|
let model;
|
|
352
387
|
if (typeof modelRaw === 'string' && modelRaw.trim().length > 0) {
|
|
@@ -477,6 +512,7 @@ export function parseAgentFromMarkdown(filePath, baseDir, frontmatter, content,
|
|
|
477
512
|
? { color }
|
|
478
513
|
: {}),
|
|
479
514
|
...(model !== undefined ? { model } : {}),
|
|
515
|
+
...(provider !== undefined ? { provider } : {}),
|
|
480
516
|
...(parsedEffort !== undefined ? { effort: parsedEffort } : {}),
|
|
481
517
|
...(isValidPermissionMode
|
|
482
518
|
? { permissionMode: permissionModeRaw }
|