@danieltmn/tamnora 0.25.0 → 0.25.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/CHANGELOG.md +6 -0
- package/harness/packages/cli/src/commands/repl.js +18 -2
- package/harness/packages/cli/src/commands/run.js +5 -1
- package/harness/packages/cli/src/commands/tui.js +5 -1
- package/harness/packages/cli/src/lib/runtime.js +2 -0
- package/harness/packages/cli/src/tui/app.js +22 -2
- package/harness/packages/cli/src/tui/layout.js +1 -1
- package/harness/packages/core/src/agent/runAgent.js +5 -2
- package/harness/packages/core/src/agents/index.js +141 -0
- package/harness/packages/core/src/config/index.js +1 -0
- package/harness/packages/core/src/index.js +10 -0
- package/harness/packages/core/src/providers/index.js +3 -1
- package/harness/packages/core/src/providers/ollama.js +4 -1
- package/harness/packages/core/src/providers/openaiCompatible.js +6 -1
- package/harness/packages/core/src/providers/openrouter.js +5 -1
- package/harness/packages/core/src/providers/presets.js +4 -3
- package/harness/packages/core/src/providers/reasoning.js +58 -0
- package/harness/packages/core/test/agents.test.js +111 -0
- package/harness/packages/core/test/reasoning.test.js +43 -0
- package/harness/packages/server/public/app.js +37 -0
- package/harness/packages/server/public/chat.js +1 -0
- package/harness/packages/server/public/chrome.js +94 -1
- package/harness/packages/server/public/core.js +1 -1
- package/harness/packages/server/public/icons (1).zip +0 -0
- package/harness/packages/server/public/index.html +90 -1
- package/harness/packages/server/public/sessions.js +1 -0
- package/harness/packages/server/public/settings.js +167 -0
- package/harness/packages/server/public/settingsNav.js +1 -0
- package/harness/packages/server/src/lib/agents.js +74 -0
- package/harness/packages/server/src/lib/channels/telegram/run.js +1 -0
- package/harness/packages/server/src/lib/prompt.js +11 -3
- package/harness/packages/server/src/lib/scheduler.js +4 -0
- package/harness/packages/server/src/lib/sessionView.js +1 -0
- package/harness/packages/server/src/lib/skills.js +13 -0
- package/harness/packages/server/src/lib/tasks.js +71 -23
- package/harness/packages/server/src/routes/agents.js +88 -0
- package/harness/packages/server/src/routes/chat.js +9 -1
- package/harness/packages/server/src/routes/providers.js +7 -0
- package/harness/packages/server/src/routes/sessions.js +3 -1
- package/harness/packages/server/src/server.js +7 -0
- package/harness/packages/server/test/agents.test.js +194 -0
- package/harness/packages/server/test/consoleTabs.test.js +2 -2
- package/harness/packages/server/test/settingsNav.test.js +2 -2
- package/harness/tamnora.example.json +1 -0
- package/package.json +1 -1
|
@@ -3,11 +3,13 @@ import {
|
|
|
3
3
|
runAgent,
|
|
4
4
|
createTools,
|
|
5
5
|
resolveMode,
|
|
6
|
+
normalizeEffort,
|
|
6
7
|
MAX_TOOL_ROUNDS,
|
|
7
8
|
} from '../lib/core.js';
|
|
8
9
|
import { openSse, readJson, sendJson, sendError } from '../lib/http.js';
|
|
9
10
|
import { workspaceForRequest, resolveExistingDir, localOk } from '../lib/workspace.js';
|
|
10
11
|
import { createSubagentTool } from '../lib/tasks.js';
|
|
12
|
+
import { enabledAgents } from '../lib/agents.js';
|
|
11
13
|
import {
|
|
12
14
|
combineToolset,
|
|
13
15
|
combineLifecycle,
|
|
@@ -162,6 +164,8 @@ export function registerChat(route, ctx) {
|
|
|
162
164
|
const model = body.model || session?.model || ctx.effectiveModel();
|
|
163
165
|
if (!model) return sendError(res, 400, 'Falta "model" (en el cuerpo, en Ajustes o en tamnora.json).');
|
|
164
166
|
const mode = resolveMode(body.mode ?? session?.meta?.mode ?? ctx.store.getSetting('activeMode'));
|
|
167
|
+
// Esfuerzo de razonamiento: cuerpo > sesión > default global/config.
|
|
168
|
+
const effort = normalizeEffort(body.effort ?? session?.meta?.effort ?? ctx.effectiveEffort?.());
|
|
165
169
|
|
|
166
170
|
if (!sessionId) {
|
|
167
171
|
try {
|
|
@@ -174,7 +178,7 @@ export function registerChat(route, ctx) {
|
|
|
174
178
|
provider: providerId,
|
|
175
179
|
model,
|
|
176
180
|
cwd: sessionCwd,
|
|
177
|
-
meta: { mode: mode.id },
|
|
181
|
+
meta: { mode: mode.id, effort },
|
|
178
182
|
});
|
|
179
183
|
}
|
|
180
184
|
|
|
@@ -209,6 +213,7 @@ export function registerChat(route, ctx) {
|
|
|
209
213
|
provider: providerId,
|
|
210
214
|
model,
|
|
211
215
|
mode: mode.id,
|
|
216
|
+
effort,
|
|
212
217
|
cwd: sessionCwd,
|
|
213
218
|
say: fraseInicio(),
|
|
214
219
|
});
|
|
@@ -271,6 +276,8 @@ export function registerChat(route, ctx) {
|
|
|
271
276
|
providerId,
|
|
272
277
|
model,
|
|
273
278
|
parentMode: mode,
|
|
279
|
+
effort,
|
|
280
|
+
agents: enabledAgents(ctx),
|
|
274
281
|
permissions: ctx.config.permissions,
|
|
275
282
|
mcp,
|
|
276
283
|
mcpMode: ctx.effectiveMcpMode(),
|
|
@@ -347,6 +354,7 @@ export function registerChat(route, ctx) {
|
|
|
347
354
|
maxDurationMs: Number(body.maxDurationMs) || ctx.effectiveMaxDurationMs?.() || 0,
|
|
348
355
|
maxOutputTokens: Number(body.maxOutputTokens) || ctx.effectiveMaxOutputTokens?.() || 0,
|
|
349
356
|
promptCaching: ctx.effectivePromptCaching?.() ?? ctx.config.promptCaching === true,
|
|
357
|
+
reasoningEffort: effort,
|
|
350
358
|
stallWarnMs: body.stallWarnMs ?? ctx.config.stallWarnMs,
|
|
351
359
|
contextTokens,
|
|
352
360
|
onEvent(event) {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
readApp,
|
|
8
8
|
writeApp,
|
|
9
9
|
maskSecret,
|
|
10
|
+
normalizeEffort,
|
|
10
11
|
} from '../lib/core.js';
|
|
11
12
|
import { resolveAppearance, sanitizeAppearance } from '../lib/appearance.js';
|
|
12
13
|
import { readJson, sendJson, sendError } from '../lib/http.js';
|
|
@@ -26,6 +27,7 @@ export function providerList(ctx) {
|
|
|
26
27
|
baseUrl: row?.baseUrl || '',
|
|
27
28
|
defaultBaseUrl: meta.defaultBaseUrl,
|
|
28
29
|
requiresKey: row?.requiresKey ?? meta.requiresKey,
|
|
30
|
+
reasoning: meta.reasoning ?? 'none',
|
|
29
31
|
custom: false,
|
|
30
32
|
builtin: true,
|
|
31
33
|
stored: Boolean(row),
|
|
@@ -44,6 +46,7 @@ export function providerList(ctx) {
|
|
|
44
46
|
baseUrl: row.baseUrl,
|
|
45
47
|
defaultBaseUrl: '',
|
|
46
48
|
requiresKey: row.requiresKey ?? true,
|
|
49
|
+
reasoning: 'none',
|
|
47
50
|
custom: true,
|
|
48
51
|
builtin: false,
|
|
49
52
|
stored: true,
|
|
@@ -83,6 +86,7 @@ export function registerProviders(route, ctx) {
|
|
|
83
86
|
maxDurationMs: ctx.effectiveMaxDurationMs(),
|
|
84
87
|
maxOutputTokens: ctx.effectiveMaxOutputTokens(),
|
|
85
88
|
promptCaching: ctx.effectivePromptCaching(),
|
|
89
|
+
effort: ctx.effectiveEffort(),
|
|
86
90
|
defaultCwd: ctx.store.getSetting('defaultCwd') || '',
|
|
87
91
|
strictScope: ctx.effectiveStrictScope(),
|
|
88
92
|
mcpMode: ctx.effectiveMcpMode(),
|
|
@@ -101,6 +105,9 @@ export function registerProviders(route, ctx) {
|
|
|
101
105
|
if (body.mode !== undefined) ctx.store.setSetting('activeMode', resolveMode(String(body.mode)).id);
|
|
102
106
|
if (body.defaultCwd !== undefined) ctx.store.setSetting('defaultCwd', String(body.defaultCwd ?? '').trim() || null);
|
|
103
107
|
if (body.strictScope !== undefined) ctx.store.setSetting('strictScope', Boolean(body.strictScope));
|
|
108
|
+
if (body.effort !== undefined) {
|
|
109
|
+
ctx.store.setSetting('activeEffort', normalizeEffort(body.effort) || null);
|
|
110
|
+
}
|
|
104
111
|
if (body.promptCaching !== undefined) ctx.store.setSetting('promptCaching', Boolean(body.promptCaching));
|
|
105
112
|
if (body.mcpMode !== undefined) {
|
|
106
113
|
const mode = String(body.mcpMode);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readJson, sendJson, sendError } from '../lib/http.js';
|
|
2
|
-
import { resolveMode } from '../lib/core.js';
|
|
2
|
+
import { resolveMode, normalizeEffort } from '../lib/core.js';
|
|
3
3
|
import { workspaceForRequest } from '../lib/workspace.js';
|
|
4
4
|
import { sessionView, sessionState, awaitingBySession } from '../lib/sessionView.js';
|
|
5
5
|
import { summarizeSession, compactSession, SUMMARY_INSTRUCTION } from '../lib/compact.js';
|
|
@@ -49,6 +49,7 @@ export function registerSessions(route, ctx) {
|
|
|
49
49
|
const meta = {};
|
|
50
50
|
if (body.goal) meta.goal = String(body.goal);
|
|
51
51
|
if (body.mode) meta.mode = resolveMode(String(body.mode)).id;
|
|
52
|
+
if ('effort' in body) meta.effort = normalizeEffort(body.effort);
|
|
52
53
|
const id = ctx.store.createSession({
|
|
53
54
|
title: String(body.title || 'Nueva sesión').slice(0, 80),
|
|
54
55
|
provider: body.provider || ctx.effectiveProviderId(),
|
|
@@ -105,6 +106,7 @@ export function registerSessions(route, ctx) {
|
|
|
105
106
|
const meta = {};
|
|
106
107
|
if ('goal' in body) meta.goal = body.goal ? String(body.goal) : undefined;
|
|
107
108
|
if ('mode' in body) meta.mode = resolveMode(String(body.mode)).id;
|
|
109
|
+
if ('effort' in body) meta.effort = normalizeEffort(body.effort);
|
|
108
110
|
if (Object.keys(meta).length) patch.meta = meta;
|
|
109
111
|
ctx.store.touchSession(params.id, patch);
|
|
110
112
|
sendJson(res, 200, { session: sessionView(ctx.store.getSession(params.id)) });
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
MCP_AUTO_THRESHOLD,
|
|
18
18
|
readPackageVersion,
|
|
19
19
|
scopeDirective,
|
|
20
|
+
normalizeEffort,
|
|
20
21
|
} from './lib/core.js';
|
|
21
22
|
import { sendJson, sendError } from './lib/http.js';
|
|
22
23
|
import { requireTrustedHost, requireSameOrigin } from './lib/workspace.js';
|
|
@@ -24,6 +25,7 @@ import { applySecurityHeaders, cspMode } from './lib/securityHeaders.js';
|
|
|
24
25
|
import { createPromptBuilder, DEFAULT_SYSTEM_PROMPT } from './lib/prompt.js';
|
|
25
26
|
import { profilePrompt, resolveProfile } from './lib/profile.js';
|
|
26
27
|
import { skillsBlock } from './lib/skills.js';
|
|
28
|
+
import { agentsIndexPrompt } from './lib/agents.js';
|
|
27
29
|
import { createApprovalBroker } from './lib/approvals.js';
|
|
28
30
|
import { createQuestionBroker } from './lib/questions.js';
|
|
29
31
|
import { createPresence } from './lib/presence.js';
|
|
@@ -42,6 +44,7 @@ import { registerModels } from './routes/models.js';
|
|
|
42
44
|
import { registerProviders } from './routes/providers.js';
|
|
43
45
|
import { registerMcp } from './routes/mcp.js';
|
|
44
46
|
import { registerSkills } from './routes/skills.js';
|
|
47
|
+
import { registerAgents } from './routes/agents.js';
|
|
45
48
|
import { registerMemory } from './routes/memory.js';
|
|
46
49
|
import { registerChannels } from './routes/channels.js';
|
|
47
50
|
import { registerProfile } from './routes/profile.js';
|
|
@@ -183,6 +186,7 @@ export async function createTamnoraServer({
|
|
|
183
186
|
const effectiveMaxDurationMs = () => settingNumber('maxDurationMs', config.maxDurationMs);
|
|
184
187
|
const effectiveMaxOutputTokens = () => settingNumber('maxOutputTokens', config.maxOutputTokens);
|
|
185
188
|
const effectiveStrictScope = () => store.getSetting('strictScope') === true;
|
|
189
|
+
const effectiveEffort = () => normalizeEffort(store.getSetting('activeEffort') ?? config.effort);
|
|
186
190
|
const effectivePromptCaching = () => {
|
|
187
191
|
const stored = store.getSetting('promptCaching');
|
|
188
192
|
if (stored === null || stored === undefined) return config.promptCaching === true;
|
|
@@ -228,6 +232,7 @@ export async function createTamnoraServer({
|
|
|
228
232
|
effectiveMaxDurationMs,
|
|
229
233
|
effectiveMaxOutputTokens,
|
|
230
234
|
effectiveStrictScope,
|
|
235
|
+
effectiveEffort,
|
|
231
236
|
effectivePromptCaching,
|
|
232
237
|
effectiveMcpMode,
|
|
233
238
|
effectiveMcpPinned,
|
|
@@ -269,6 +274,7 @@ export async function createTamnoraServer({
|
|
|
269
274
|
files: config.instructions,
|
|
270
275
|
profileText: () => profilePrompt(resolveProfile(ctx.app)),
|
|
271
276
|
skillsText: () => skillsBlock(ctx),
|
|
277
|
+
agentsText: () => agentsIndexPrompt(ctx),
|
|
272
278
|
mcpText: () => ctx.mcpText(),
|
|
273
279
|
scopeText: () => scopeDirective(ctx.effectiveStrictScope()),
|
|
274
280
|
ruleFiles: config.rules ?? [],
|
|
@@ -326,6 +332,7 @@ export async function createTamnoraServer({
|
|
|
326
332
|
registerProviders(route, ctx);
|
|
327
333
|
registerMcp(route, ctx);
|
|
328
334
|
registerSkills(route, ctx);
|
|
335
|
+
registerAgents(route, ctx);
|
|
329
336
|
registerMemory(route, ctx);
|
|
330
337
|
registerChannels(route, ctx);
|
|
331
338
|
registerProfile(route, ctx);
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { startServer } from './helpers.js';
|
|
7
|
+
|
|
8
|
+
const json = { 'Content-Type': 'application/json' };
|
|
9
|
+
|
|
10
|
+
test('manages subagents from the console (CRUD + toggle)', async () => {
|
|
11
|
+
const { instance, base } = await startServer();
|
|
12
|
+
try {
|
|
13
|
+
let data = await (await fetch(`${base}/api/agents`)).json();
|
|
14
|
+
assert.deepEqual(data.agents, []);
|
|
15
|
+
|
|
16
|
+
let res = await fetch(`${base}/api/agents/revisor`, {
|
|
17
|
+
method: 'PUT',
|
|
18
|
+
headers: json,
|
|
19
|
+
body: JSON.stringify({
|
|
20
|
+
name: 'Revisor',
|
|
21
|
+
description: 'Busca bugs',
|
|
22
|
+
provider: 'openai',
|
|
23
|
+
model: 'gpt',
|
|
24
|
+
mode: 'plan',
|
|
25
|
+
effort: 'high',
|
|
26
|
+
maxRounds: 4,
|
|
27
|
+
tools: ['read', 'grep'],
|
|
28
|
+
skills: ['code-review'],
|
|
29
|
+
body: '# Rol\nMirá el diff.',
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
assert.equal(res.status, 200);
|
|
33
|
+
data = await res.json();
|
|
34
|
+
const agent = data.agents.find((item) => item.id === 'revisor');
|
|
35
|
+
assert.equal(agent.name, 'Revisor');
|
|
36
|
+
assert.equal(agent.effort, 'high');
|
|
37
|
+
assert.equal(agent.mode, 'plan');
|
|
38
|
+
assert.equal(agent.maxRounds, 4);
|
|
39
|
+
assert.deepEqual(agent.tools, ['read', 'grep']);
|
|
40
|
+
assert.deepEqual(agent.skills, ['code-review']);
|
|
41
|
+
assert.equal(agent.enabled, true);
|
|
42
|
+
|
|
43
|
+
const detail = await (await fetch(`${base}/api/agents/revisor`)).json();
|
|
44
|
+
assert.match(detail.agent.body, /Mirá el diff/);
|
|
45
|
+
|
|
46
|
+
res = await fetch(`${base}/api/agents/revisor/toggle`, { method: 'POST' });
|
|
47
|
+
data = await res.json();
|
|
48
|
+
assert.equal(data.enabled, false);
|
|
49
|
+
data = await (await fetch(`${base}/api/agents`)).json();
|
|
50
|
+
assert.equal(data.agents[0].enabled, false);
|
|
51
|
+
|
|
52
|
+
res = await fetch(`${base}/api/agents/revisor/toggle`, { method: 'POST' });
|
|
53
|
+
assert.equal((await res.json()).enabled, true);
|
|
54
|
+
|
|
55
|
+
res = await fetch(`${base}/api/agents/revisor`, { method: 'DELETE' });
|
|
56
|
+
assert.equal(res.status, 200);
|
|
57
|
+
data = await res.json();
|
|
58
|
+
assert.deepEqual(data.agents, []);
|
|
59
|
+
} finally {
|
|
60
|
+
await instance.close();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('rejects invalid subagent payloads', async () => {
|
|
65
|
+
const { instance, base } = await startServer();
|
|
66
|
+
try {
|
|
67
|
+
let res = await fetch(`${base}/api/agents/${encodeURIComponent('../escape')}`, {
|
|
68
|
+
method: 'PUT',
|
|
69
|
+
headers: json,
|
|
70
|
+
body: JSON.stringify({ body: 'x' }),
|
|
71
|
+
});
|
|
72
|
+
assert.equal(res.status, 400);
|
|
73
|
+
|
|
74
|
+
res = await fetch(`${base}/api/agents/vacio`, {
|
|
75
|
+
method: 'PUT',
|
|
76
|
+
headers: json,
|
|
77
|
+
body: JSON.stringify({ mode: 'plan' }),
|
|
78
|
+
});
|
|
79
|
+
assert.equal(res.status, 400);
|
|
80
|
+
assert.match((await res.json()).error, /cuerpo o una descripción/);
|
|
81
|
+
} finally {
|
|
82
|
+
await instance.close();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('effort persists globally and per session', async () => {
|
|
87
|
+
const { instance, base } = await startServer();
|
|
88
|
+
try {
|
|
89
|
+
let data = await (await fetch(`${base}/api/settings`)).json();
|
|
90
|
+
assert.equal(data.settings.effort, '');
|
|
91
|
+
|
|
92
|
+
let res = await fetch(`${base}/api/settings`, { method: 'PUT', headers: json, body: JSON.stringify({ effort: 'max' }) });
|
|
93
|
+
data = await res.json();
|
|
94
|
+
assert.equal(data.settings.effort, 'max');
|
|
95
|
+
|
|
96
|
+
res = await fetch(`${base}/api/settings`, { method: 'PUT', headers: json, body: JSON.stringify({ effort: 'auto' }) });
|
|
97
|
+
assert.equal((await res.json()).settings.effort, '');
|
|
98
|
+
|
|
99
|
+
res = await fetch(`${base}/api/sessions`, {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
headers: json,
|
|
102
|
+
body: JSON.stringify({ title: 'con esfuerzo', model: 'm', effort: 'low' }),
|
|
103
|
+
});
|
|
104
|
+
const created = await res.json();
|
|
105
|
+
assert.equal(created.session.effort, 'low');
|
|
106
|
+
|
|
107
|
+
res = await fetch(`${base}/api/sessions/${created.session.id}`, {
|
|
108
|
+
method: 'PUT',
|
|
109
|
+
headers: json,
|
|
110
|
+
body: JSON.stringify({ effort: 'high' }),
|
|
111
|
+
});
|
|
112
|
+
assert.equal((await res.json()).session.effort, 'high');
|
|
113
|
+
} finally {
|
|
114
|
+
await instance.close();
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('a named subagent runs with its own model, effort and instructions', async () => {
|
|
119
|
+
const cwd = await mkdtemp(path.join(tmpdir(), 'tamnora-subagent-'));
|
|
120
|
+
const calls = { parent: 0 };
|
|
121
|
+
const sub = {};
|
|
122
|
+
const parent = {
|
|
123
|
+
id: 'parent',
|
|
124
|
+
async streamChat() {
|
|
125
|
+
calls.parent += 1;
|
|
126
|
+
if (calls.parent === 1) {
|
|
127
|
+
return {
|
|
128
|
+
content: '',
|
|
129
|
+
reasoning: '',
|
|
130
|
+
toolCalls: [
|
|
131
|
+
{
|
|
132
|
+
id: 't1',
|
|
133
|
+
type: 'function',
|
|
134
|
+
function: {
|
|
135
|
+
name: 'task',
|
|
136
|
+
arguments: JSON.stringify({ agent: 'revisor', description: 'revisión', prompt: 'revisá el cambio' }),
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return { content: 'listo', reasoning: '', toolCalls: [] };
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
const revisor = {
|
|
146
|
+
id: 'revisor',
|
|
147
|
+
async streamChat(_config, { model, messages, reasoningEffort }) {
|
|
148
|
+
Object.assign(sub, { model, reasoningEffort, system: messages?.[0]?.content ?? '' });
|
|
149
|
+
return { content: 'resumen del revisor', reasoning: '', toolCalls: [] };
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
const providers = { parent, revisor };
|
|
153
|
+
const { instance, base } = await startServer({
|
|
154
|
+
cwd,
|
|
155
|
+
resolveProvider: ({ providerId }) => ({ provider: providers[providerId] ?? parent, config: {} }),
|
|
156
|
+
});
|
|
157
|
+
try {
|
|
158
|
+
await fetch(`${base}/api/agents/revisor`, {
|
|
159
|
+
method: 'PUT',
|
|
160
|
+
headers: json,
|
|
161
|
+
body: JSON.stringify({
|
|
162
|
+
name: 'Revisor',
|
|
163
|
+
description: 'Busca bugs',
|
|
164
|
+
provider: 'revisor',
|
|
165
|
+
model: 'revisor-model',
|
|
166
|
+
mode: 'plan',
|
|
167
|
+
effort: 'high',
|
|
168
|
+
body: 'MARCADOR_REVISOR',
|
|
169
|
+
}),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const created = await (
|
|
173
|
+
await fetch(`${base}/api/sessions`, {
|
|
174
|
+
method: 'POST',
|
|
175
|
+
headers: json,
|
|
176
|
+
body: JSON.stringify({ title: 'delegar', model: 'parent-model', provider: 'parent' }),
|
|
177
|
+
})
|
|
178
|
+
).json();
|
|
179
|
+
await (
|
|
180
|
+
await fetch(`${base}/api/chat`, {
|
|
181
|
+
method: 'POST',
|
|
182
|
+
headers: json,
|
|
183
|
+
body: JSON.stringify({ sessionId: created.session.id, input: 'delegá al revisor', model: 'parent-model', provider: 'parent' }),
|
|
184
|
+
})
|
|
185
|
+
).text();
|
|
186
|
+
|
|
187
|
+
assert.equal(sub.model, 'revisor-model');
|
|
188
|
+
assert.equal(sub.reasoningEffort, 'high');
|
|
189
|
+
assert.match(sub.system, /MARCADOR_REVISOR/);
|
|
190
|
+
assert.equal(calls.parent >= 2, true);
|
|
191
|
+
} finally {
|
|
192
|
+
await instance.close();
|
|
193
|
+
}
|
|
194
|
+
});
|
|
@@ -159,13 +159,13 @@ for (const [label, width, height] of [['escritorio', 1280, 800], ['movil', 390,
|
|
|
159
159
|
const r = shot.preScriptResult;
|
|
160
160
|
assert.ok(r, 'el script no devolvio resultado');
|
|
161
161
|
|
|
162
|
-
assert.equal(r.todas,
|
|
162
|
+
assert.equal(r.todas, 14, 'sin busqueda se ven las 14 secciones');
|
|
163
163
|
assert.deepEqual(r.filtradoVoz, ['voz']);
|
|
164
164
|
assert.deepEqual(r.gruposVoz, ['canales'], 'los grupos sin resultados se ocultan');
|
|
165
165
|
assert.ok(r.filtradoTema.includes('apariencia'), 'busca por palabra clave');
|
|
166
166
|
assert.deepEqual(r.sinResultados, { tabs: 0, mensaje: true, grupos: 0 });
|
|
167
167
|
assert.deepEqual(r.enter, { activo: 'limites', selected: 'limites', nivel: 'panel' });
|
|
168
|
-
assert.deepEqual(r.escape, { valor: '', tabs:
|
|
168
|
+
assert.deepEqual(r.escape, { valor: '', tabs: 14 }, 'Escape limpia la busqueda y vuelve a mostrar todo');
|
|
169
169
|
assert.ok(r.palette.some((label) => label === 'Ajustes · Apariencia'), `la palette no encontro Apariencia: ${r.palette}`);
|
|
170
170
|
assert.deepEqual(r.paletteSalto, { vista: true, panel: 'apariencia', nivel: 'panel' });
|
|
171
171
|
|
|
@@ -16,7 +16,7 @@ test('foldText ignora mayusculas y acentos', () => {
|
|
|
16
16
|
test('sin consulta devuelve todas las secciones, en el orden de los grupos', () => {
|
|
17
17
|
assert.deepEqual(ids(filterSettings('')), ids(SETTINGS_SECTIONS));
|
|
18
18
|
assert.deepEqual(ids(filterSettings(' ')), ids(SETTINGS_SECTIONS));
|
|
19
|
-
assert.equal(SETTINGS_SECTIONS.length,
|
|
19
|
+
assert.equal(SETTINGS_SECTIONS.length, 14);
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
test('busca por nombre sin importar acentos ni mayusculas', () => {
|
|
@@ -34,7 +34,7 @@ test('busca por palabra clave y por grupo', () => {
|
|
|
34
34
|
assert.ok(ids(filterSettings('tema')).includes('apariencia'));
|
|
35
35
|
assert.ok(ids(filterSettings('dictado')).includes('transcripcion'));
|
|
36
36
|
assert.ok(ids(filterSettings('cron')).includes('tareas'));
|
|
37
|
-
assert.deepEqual(ids(filterSettings('contexto')), ['perfil', 'memoria', 'skills', 'mcp']);
|
|
37
|
+
assert.deepEqual(ids(filterSettings('contexto')), ['perfil', 'memoria', 'skills', 'agentes', 'mcp']);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
test('todas las palabras de la consulta tienen que coincidir', () => {
|
package/package.json
CHANGED