agent-mp 0.5.40 → 0.5.42
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/commands/repl.js +10 -5
- package/dist/core/engine.js +107 -18
- package/dist/utils/deepseek.d.ts +1 -1
- package/dist/utils/deepseek.js +14 -11
- package/package.json +1 -1
package/dist/commands/repl.js
CHANGED
|
@@ -12,7 +12,7 @@ import { log } from '../utils/logger.js';
|
|
|
12
12
|
import { AgentEngine, ExitError } from '../core/engine.js';
|
|
13
13
|
import { qwenAuthStatus, QWEN_AGENT_HOME, fetchQwenModels, loadApiKeyConfig, saveApiKeyConfig, fetchApiKeyModels, DEFAULT_API_BASE_URL } from '../utils/qwen-auth.js';
|
|
14
14
|
import { loadGeminiApiKey, saveGeminiApiKey, geminiAuthStatus, deleteGeminiApiKey, GEMINI_MODELS, fetchGeminiModels } from '../utils/gemini.js';
|
|
15
|
-
import { loadDeepSeekApiKey, saveDeepSeekApiKey, deleteDeepSeekApiKey,
|
|
15
|
+
import { loadDeepSeekApiKey, saveDeepSeekApiKey, deleteDeepSeekApiKey, fetchDeepSeekModels, DEEPSEEK_DEFAULT_MODELS } from '../utils/deepseek.js';
|
|
16
16
|
import { renderWelcomePanel, renderHelpHint, renderSectionBox, renderMultiSectionBox } from '../ui/theme.js';
|
|
17
17
|
import { FixedInput } from '../ui/input.js';
|
|
18
18
|
import { newSession, saveSession } from '../utils/sessions.js';
|
|
@@ -416,11 +416,16 @@ async function promptDeepseekKeySetup(rl, askFn) {
|
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
else {
|
|
419
|
-
console.log(chalk.yellow(' No se pudieron cargar modelos de la API — usando
|
|
420
|
-
|
|
419
|
+
console.log(chalk.yellow(' No se pudieron cargar modelos de la API — usando defaults.'));
|
|
420
|
+
DEEPSEEK_DEFAULT_MODELS.forEach((m, i) => console.log(chalk.dim(` ${i + 1}. ${m}`)));
|
|
421
421
|
const pick = await askFn(` Modelo [${chosenModel}]: `);
|
|
422
|
-
|
|
422
|
+
const num = parseInt(pick.trim());
|
|
423
|
+
if (!isNaN(num) && num >= 1 && num <= DEEPSEEK_DEFAULT_MODELS.length) {
|
|
424
|
+
chosenModel = DEEPSEEK_DEFAULT_MODELS[num - 1];
|
|
425
|
+
}
|
|
426
|
+
else if (pick.trim()) {
|
|
423
427
|
chosenModel = pick.trim();
|
|
428
|
+
}
|
|
424
429
|
}
|
|
425
430
|
await saveDeepSeekApiKey({ api_key: resolvedKey, model: chosenModel });
|
|
426
431
|
console.log(chalk.green(`\n ✓ DeepSeek API key guardada — ${chosenModel}\n`));
|
|
@@ -805,7 +810,7 @@ async function cmdModels(roleArg, fi, rl) {
|
|
|
805
810
|
console.log(chalk.dim(' Fetching DeepSeek models...'));
|
|
806
811
|
models = await fetchDeepSeekModels(deepseekCfg);
|
|
807
812
|
if (!models.length)
|
|
808
|
-
models =
|
|
813
|
+
models = DEEPSEEK_DEFAULT_MODELS;
|
|
809
814
|
}
|
|
810
815
|
else if (activeProvider === 'qwen') {
|
|
811
816
|
console.log(chalk.dim(' Fetching Qwen models...'));
|
package/dist/core/engine.js
CHANGED
|
@@ -237,8 +237,9 @@ async function walkForKeyFiles(root) {
|
|
|
237
237
|
entry: null,
|
|
238
238
|
controllers: [],
|
|
239
239
|
schemas: [],
|
|
240
|
+
allSourceFiles: [],
|
|
240
241
|
};
|
|
241
|
-
const SOURCE_EXT = /\.(ts|tsx|js|jsx|py|go|java|kt|rs|cs)$/;
|
|
242
|
+
const SOURCE_EXT = /\.(ts|tsx|js|jsx|py|go|java|kt|rs|cs|rb|ex|exs|swift|php|vue|svelte)$/;
|
|
242
243
|
const SKIP_FILE = /\.(test|spec|d)\.[a-z]+$/i;
|
|
243
244
|
const ENTRY_NAMES = new Set([
|
|
244
245
|
'main.ts', 'main.js', 'main.py', 'main.go', 'Main.java',
|
|
@@ -255,8 +256,6 @@ async function walkForKeyFiles(root) {
|
|
|
255
256
|
async function walk(dir, depth) {
|
|
256
257
|
if (depth > 6)
|
|
257
258
|
return;
|
|
258
|
-
if (result.controllers.length >= 5 && result.schemas.length >= 5 && result.entry)
|
|
259
|
-
return;
|
|
260
259
|
let entries;
|
|
261
260
|
try {
|
|
262
261
|
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
@@ -280,6 +279,8 @@ async function walkForKeyFiles(root) {
|
|
|
280
279
|
continue;
|
|
281
280
|
if (SKIP_FILE.test(e.name))
|
|
282
281
|
continue;
|
|
282
|
+
// Collect all source files
|
|
283
|
+
result.allSourceFiles.push(p);
|
|
283
284
|
// entry point detection
|
|
284
285
|
if (!result.entry && (ENTRY_NAMES.has(e.name) || ENTRY_REGEX.test(e.name))) {
|
|
285
286
|
result.entry = p;
|
|
@@ -856,33 +857,66 @@ INSTRUCCIONES GENERALES:
|
|
|
856
857
|
}
|
|
857
858
|
};
|
|
858
859
|
// Role binaries (agent-orch, agent-impl, etc.) require an interactive TTY and can't
|
|
859
|
-
// be spawned as subprocesses. Instead,
|
|
860
|
-
// (
|
|
861
|
-
// the Qwen API directly with those creds. Otherwise skip to fallback.
|
|
860
|
+
// be spawned as subprocesses. Instead, call the appropriate API directly
|
|
861
|
+
// (Qwen via oauth_creds.json, DeepSeek via deepseek_key.json).
|
|
862
862
|
const ROLE_BINARIES = new Set(['agent-orch', 'agent-impl', 'agent-rev', 'agent-explorer']);
|
|
863
|
+
const DEEPSEEK_MODEL_PREFIX = /^deepseek-/;
|
|
863
864
|
const tryRoleBinaryCreds = async (cliName, model) => {
|
|
864
|
-
const
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
|
|
865
|
+
const roleDir = path.join(os.homedir(), `.${cliName}`);
|
|
866
|
+
const credsPath = path.join(roleDir, 'oauth_creds.json');
|
|
867
|
+
const deepseekRoleKeyPath = path.join(roleDir, 'deepseek_key.json');
|
|
868
|
+
const deepseekGlobalKeyPath = path.join(os.homedir(), '.agent-mp', 'deepseek_key.json');
|
|
869
|
+
const isDeepSeekModel = DEEPSEEK_MODEL_PREFIX.test(model);
|
|
870
|
+
const hasQwenCreds = await fileExists(credsPath);
|
|
871
|
+
const hasDeepSeekCreds = await fileExists(deepseekRoleKeyPath) || await fileExists(deepseekGlobalKeyPath);
|
|
872
|
+
// Determine primary provider: DeepSeek model with DeepSeek creds -> DeepSeek, else Qwen
|
|
873
|
+
if (isDeepSeekModel && hasDeepSeekCreds) {
|
|
874
|
+
const sp = this._startSpinner(`${cliName} ${model} (deepseek)`);
|
|
875
|
+
const onChunk = (delta) => sp.push(delta);
|
|
876
|
+
try {
|
|
877
|
+
log.info(`${cliName}: calling DeepSeek API (${model})`);
|
|
878
|
+
const result = await callDeepSeekAPI(rolePrompt, model, onChunk);
|
|
879
|
+
sp.stop();
|
|
880
|
+
return result;
|
|
881
|
+
}
|
|
882
|
+
catch (err) {
|
|
883
|
+
sp.stop();
|
|
884
|
+
if (err.message?.startsWith('DEEPSEEK_NOT_CONFIGURED')) {
|
|
885
|
+
log.warn(`${cliName} DeepSeek not configured — using fallback`);
|
|
886
|
+
}
|
|
887
|
+
else if (err.message?.startsWith('DEEPSEEK_QUOTA_EXCEEDED')) {
|
|
888
|
+
log.warn(`${cliName} DeepSeek quota exhausted — using fallback`);
|
|
889
|
+
}
|
|
890
|
+
else if (err.message?.startsWith('DEEPSEEK_NO_BALANCE')) {
|
|
891
|
+
log.warn(`${cliName} DeepSeek balance insufficient — using fallback`);
|
|
892
|
+
}
|
|
893
|
+
else {
|
|
894
|
+
log.warn(`${cliName} DeepSeek API call failed: ${err.message}`);
|
|
895
|
+
}
|
|
896
|
+
return null; // don't try Qwen with a DeepSeek model — let caller fallback handle it
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
// Qwen path (for non-DeepSeek models only)
|
|
900
|
+
if (!hasQwenCreds) {
|
|
868
901
|
const { loadApiKeyConfig } = await import('../utils/qwen-auth.js');
|
|
869
902
|
const apiKeyCfg = await loadApiKeyConfig();
|
|
870
903
|
if (!apiKeyCfg) {
|
|
871
|
-
|
|
904
|
+
if (!isDeepSeekModel) {
|
|
905
|
+
log.warn(`${cliName} has no credentials — run: agent-mp setup api-key or ${cliName} --login`);
|
|
906
|
+
}
|
|
872
907
|
return null;
|
|
873
908
|
}
|
|
874
|
-
// Fall through: callQwenAPIFromCreds will use the API key config
|
|
875
909
|
}
|
|
876
|
-
const
|
|
877
|
-
const
|
|
910
|
+
const sp2 = this._startSpinner(`${cliName} ${model} ${isDeepSeekModel ? '(qwen fallback)' : ''}`);
|
|
911
|
+
const onChunk2 = (delta) => sp2.push(delta);
|
|
878
912
|
try {
|
|
879
913
|
log.info(`${cliName}: calling Qwen API with own credentials (${model})`);
|
|
880
|
-
const result = await callQwenAPIFromCreds(rolePrompt, model, credsPath,
|
|
881
|
-
|
|
914
|
+
const result = await callQwenAPIFromCreds(rolePrompt, model, credsPath, onChunk2);
|
|
915
|
+
sp2.stop();
|
|
882
916
|
return result;
|
|
883
917
|
}
|
|
884
918
|
catch (err) {
|
|
885
|
-
|
|
919
|
+
sp2.stop();
|
|
886
920
|
if (err.message?.startsWith('QWEN_AUTH_EXPIRED')) {
|
|
887
921
|
log.warn(`${cliName} session expired — using fallback`);
|
|
888
922
|
return null;
|
|
@@ -1580,6 +1614,23 @@ INSTRUCCIONES:
|
|
|
1580
1614
|
compSection.push(`### SCHEMA/MODEL: ${rel}\n\`\`\`\n${content}\n\`\`\``);
|
|
1581
1615
|
}
|
|
1582
1616
|
}
|
|
1617
|
+
// Read ALL source files for function-level documentation
|
|
1618
|
+
const MAX_SOURCE_FILE = 2000;
|
|
1619
|
+
const MAX_TOTAL_SOURCE = 20000;
|
|
1620
|
+
let totalSourceRead = 0;
|
|
1621
|
+
const alreadyRead = new Set([found.entry, ...found.controllers, ...found.schemas].filter(Boolean));
|
|
1622
|
+
for (const srcFile of found.allSourceFiles) {
|
|
1623
|
+
if (totalSourceRead >= MAX_TOTAL_SOURCE)
|
|
1624
|
+
break;
|
|
1625
|
+
if (alreadyRead.has(srcFile))
|
|
1626
|
+
continue;
|
|
1627
|
+
const content = await readFileSafe(srcFile, MAX_SOURCE_FILE);
|
|
1628
|
+
if (content) {
|
|
1629
|
+
const rel = path.relative(compDir, srcFile);
|
|
1630
|
+
compSection.push(`### SOURCE: ${rel}\n\`\`\`\n${content}\n\`\`\``);
|
|
1631
|
+
totalSourceRead += content.length;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1583
1634
|
// Only add the component if we read at least the manifest or one source file
|
|
1584
1635
|
if (manifestFound || found.entry || found.controllers.length > 0 || found.schemas.length > 0) {
|
|
1585
1636
|
sections.push(compSection.join('\n\n'));
|
|
@@ -1774,7 +1825,7 @@ Lo concreto: acceso a hosts externos, herramientas requeridas, secrets esperados
|
|
|
1774
1825
|
(scripts reales: package.json scripts, Makefile targets, run.sh, mvnw, gradlew, etc.)
|
|
1775
1826
|
|
|
1776
1827
|
|
|
1777
|
-
────── NIVEL 1 (.agent/context/<componente>/architecture.md,
|
|
1828
|
+
────── NIVEL 1 (.agent/context/<componente>/architecture.md, 150-400 lineas) ──────
|
|
1778
1829
|
|
|
1779
1830
|
# <componente> — Arquitectura
|
|
1780
1831
|
|
|
@@ -1806,6 +1857,44 @@ src/
|
|
|
1806
1857
|
├── ... → ...
|
|
1807
1858
|
\`\`\`
|
|
1808
1859
|
|
|
1860
|
+
## Flujo interno y funciones
|
|
1861
|
+
|
|
1862
|
+
REGLAS:
|
|
1863
|
+
- Lee cada archivo fuente del componente (no solo entry/controller, TODOS los .ts/.js/.py/.go/.java/.rs/.rb/.ex/.cs que haya).
|
|
1864
|
+
- Por cada archivo, lista sus funciones exportadas y las internas importantes.
|
|
1865
|
+
- NO escribas el codigo completo — solo descripcion resumida de que hace.
|
|
1866
|
+
- Para cada funcion, indica con que se conecta (mismo archivo, otro modulo, API externa, BD).
|
|
1867
|
+
- Inclui un ejemplo concreto de datos de entrada y salida.
|
|
1868
|
+
|
|
1869
|
+
### <ruta/archivo.ext>
|
|
1870
|
+
| Funcion | Que hace | Recibe (ejemplo) | Devuelve (ejemplo) | Conecta con |
|
|
1871
|
+
|---------|----------|-------------------|---------------------|-------------|
|
|
1872
|
+
| nombreFunc | 1-linea que hace | {campo: valor, ...} | {campo: valor, ...} | modulo.func(), this.otra(), API externa |
|
|
1873
|
+
|
|
1874
|
+
#### Conecta con — leyenda
|
|
1875
|
+
| Notacion | Significado |
|
|
1876
|
+
|---|---|
|
|
1877
|
+
| \`this.func()\` | Mismo archivo |
|
|
1878
|
+
| \`Modulo.func()\` | Otro archivo del componente |
|
|
1879
|
+
| \`@comp/Otro.func()\` | Otro componente del proyecto |
|
|
1880
|
+
| \`HTTP POST /api/x\` | Llamada HTTP externa |
|
|
1881
|
+
| \`DB: tabla.columna\` | Base de datos |
|
|
1882
|
+
| \`COLA: nombre\` | Mensajeria / eventos |
|
|
1883
|
+
|
|
1884
|
+
### Grafo de dependencias (obligatorio)
|
|
1885
|
+
Diagrama ASCII que muestre como se llaman las funciones entre si.
|
|
1886
|
+
Usa nombres reales de funciones, no de archivos.
|
|
1887
|
+
\`\`\`text
|
|
1888
|
+
controlador.createUser(req)
|
|
1889
|
+
│
|
|
1890
|
+
├──> UserService.validate(data) ── mismo archivo
|
|
1891
|
+
│ └──> UserModel.findByEmail(e) ── DB
|
|
1892
|
+
│
|
|
1893
|
+
├──> UserModel.create(user) ── DB: users.insert
|
|
1894
|
+
│
|
|
1895
|
+
└──> EmailService.sendWelcome(email) ── @notification/EmailService.send()
|
|
1896
|
+
\`\`\`
|
|
1897
|
+
|
|
1809
1898
|
## Endpoints / Rutas / Pantallas
|
|
1810
1899
|
- Backend (api): tabla con metodo, ruta, auth, proposito.
|
|
1811
1900
|
- Frontend (web): tabla con ruta de pagina, proposito, datos que consume.
|
package/dist/utils/deepseek.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ export declare function deepseekAuthStatus(): Promise<{
|
|
|
10
10
|
model?: string;
|
|
11
11
|
}>;
|
|
12
12
|
export declare function callDeepSeekAPI(prompt: string, model?: string, onData?: (chunk: string) => void): Promise<string>;
|
|
13
|
+
export declare const DEEPSEEK_DEFAULT_MODELS: string[];
|
|
13
14
|
export declare function fetchDeepSeekModels(cfg?: DeepSeekKeyConfig | null): Promise<string[]>;
|
|
14
|
-
export declare const DEEPSEEK_MODELS: string[];
|
package/dist/utils/deepseek.js
CHANGED
|
@@ -35,6 +35,7 @@ export async function callDeepSeekAPI(prompt, model = 'deepseek-chat', onData) {
|
|
|
35
35
|
throw new Error('DEEPSEEK_NOT_CONFIGURED: Run /login y elegí DeepSeek');
|
|
36
36
|
}
|
|
37
37
|
const useModel = (model && model !== 'deepseek-model') ? model : cfg.model;
|
|
38
|
+
const isV4 = useModel.startsWith('deepseek-v4');
|
|
38
39
|
const client = new OpenAI({
|
|
39
40
|
apiKey: cfg.api_key,
|
|
40
41
|
baseURL: DEEPSEEK_BASE_URL,
|
|
@@ -43,21 +44,26 @@ export async function callDeepSeekAPI(prompt, model = 'deepseek-chat', onData) {
|
|
|
43
44
|
let lastErr;
|
|
44
45
|
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
45
46
|
try {
|
|
47
|
+
const baseParams = {
|
|
48
|
+
model: useModel,
|
|
49
|
+
messages: [{ role: 'user', content: prompt }],
|
|
50
|
+
};
|
|
51
|
+
if (isV4) {
|
|
52
|
+
baseParams.reasoning_effort = 'high';
|
|
53
|
+
baseParams.extra_body = { thinking: { type: 'enabled' } };
|
|
54
|
+
}
|
|
46
55
|
if (!onData) {
|
|
47
|
-
const completion = await client.chat.completions.create(
|
|
48
|
-
model: useModel,
|
|
49
|
-
messages: [{ role: 'user', content: prompt }],
|
|
50
|
-
});
|
|
56
|
+
const completion = await client.chat.completions.create(baseParams);
|
|
51
57
|
return completion.choices[0]?.message?.content ?? '';
|
|
52
58
|
}
|
|
53
59
|
let fullText = '';
|
|
54
60
|
const stream = await client.chat.completions.create({
|
|
55
|
-
|
|
56
|
-
messages: [{ role: 'user', content: prompt }],
|
|
61
|
+
...baseParams,
|
|
57
62
|
stream: true,
|
|
63
|
+
stream_options: { include_usage: true },
|
|
58
64
|
});
|
|
59
65
|
for await (const chunk of stream) {
|
|
60
|
-
const delta = chunk.choices[0]?.delta?.content ?? '';
|
|
66
|
+
const delta = chunk.choices?.[0]?.delta?.content ?? '';
|
|
61
67
|
if (delta) {
|
|
62
68
|
fullText += delta;
|
|
63
69
|
onData(delta);
|
|
@@ -88,6 +94,7 @@ export async function callDeepSeekAPI(prompt, model = 'deepseek-chat', onData) {
|
|
|
88
94
|
}
|
|
89
95
|
throw lastErr;
|
|
90
96
|
}
|
|
97
|
+
export const DEEPSEEK_DEFAULT_MODELS = ['deepseek-chat', 'deepseek-reasoner'];
|
|
91
98
|
export async function fetchDeepSeekModels(cfg) {
|
|
92
99
|
const resolved = cfg ?? await loadDeepSeekApiKey();
|
|
93
100
|
if (!resolved?.api_key)
|
|
@@ -101,7 +108,3 @@ export async function fetchDeepSeekModels(cfg) {
|
|
|
101
108
|
return [];
|
|
102
109
|
}
|
|
103
110
|
}
|
|
104
|
-
export const DEEPSEEK_MODELS = [
|
|
105
|
-
'deepseek-chat',
|
|
106
|
-
'deepseek-reasoner',
|
|
107
|
-
];
|