@johpaz/hive-sdk 0.1.6 → 0.3.0
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 +441 -0
- package/README.md +21 -3
- package/package.json +27 -5
- package/packages/core/src/agent/acceptance-checks.ts +9 -9
- package/packages/core/src/agent/agent-catalog.ts +82 -25
- package/packages/core/src/agent/agent-loop.ts +115 -26
- package/packages/core/src/agent/capability-search.ts +2 -2
- package/packages/core/src/agent/catalog-selector.ts +4 -4
- package/packages/core/src/agent/compaction.ts +30 -10
- package/packages/core/src/agent/context-compiler.ts +63 -36
- package/packages/core/src/agent/conversation-store.ts +167 -9
- package/packages/core/src/agent/curator.ts +16 -7
- package/packages/core/src/agent/delegation-runtime.ts +5 -5
- package/packages/core/src/agent/goal-runner.ts +9 -9
- package/packages/core/src/agent/index.ts +1 -0
- package/packages/core/src/agent/llm-client.ts +98 -37
- package/packages/core/src/agent/llm-providers/anthropic.ts +4 -4
- package/packages/core/src/agent/llm-providers/deepseek.ts +1 -1
- package/packages/core/src/agent/llm-providers/gemini.ts +4 -4
- package/packages/core/src/agent/llm-providers/groq.ts +1 -1
- package/packages/core/src/agent/llm-providers/hiveagents.ts +3 -3
- package/packages/core/src/agent/llm-providers/interface.ts +2 -2
- package/packages/core/src/agent/llm-providers/kimi.ts +1 -1
- package/packages/core/src/agent/llm-providers/minimax.ts +1 -1
- package/packages/core/src/agent/llm-providers/mistral.ts +1 -1
- package/packages/core/src/agent/llm-providers/modelscope.ts +1 -1
- package/packages/core/src/agent/llm-providers/nvidia.ts +40 -1
- package/packages/core/src/agent/llm-providers/ollama.ts +4 -4
- package/packages/core/src/agent/llm-providers/openai-compat-base.ts +47 -7
- package/packages/core/src/agent/llm-providers/openai.ts +1 -1
- package/packages/core/src/agent/llm-providers/opencode-go.ts +1 -1
- package/packages/core/src/agent/llm-providers/openrouter.ts +1 -1
- package/packages/core/src/agent/llm-providers/qwen.ts +1 -1
- package/packages/core/src/agent/llm-providers/z-ai.ts +1 -1
- package/packages/core/src/agent/mcp-result-normalizer.ts +192 -0
- package/packages/core/src/agent/playbook-selector.ts +22 -7
- package/packages/core/src/agent/prompt-builder.ts +5 -5
- package/packages/core/src/agent/proof-packet.ts +5 -5
- package/packages/core/src/agent/providers/index.ts +39 -4
- package/packages/core/src/agent/realtime-providers/gemini-live.ts +238 -0
- package/packages/core/src/agent/realtime-providers/index.ts +29 -0
- package/packages/core/src/agent/realtime-providers/interface.ts +108 -0
- package/packages/core/src/agent/reflector.ts +38 -15
- package/packages/core/src/agent/run-store.ts +8 -8
- package/packages/core/src/agent/service.ts +10 -10
- package/packages/core/src/agent/skill-selector.ts +6 -6
- package/packages/core/src/agent/thread-id.ts +71 -0
- package/packages/core/src/agent/thread-store.ts +293 -0
- package/packages/core/src/agent/tool-selector.ts +9 -5
- package/packages/core/src/agent/tracer.ts +5 -5
- package/packages/core/src/api/createAgent.ts +67 -2
- package/packages/core/src/artifacts/index.ts +15 -0
- package/packages/core/src/artifacts/store.ts +161 -5
- package/packages/core/src/canvas/emitter.ts +2 -2
- package/packages/core/src/canvas/index.ts +9 -0
- package/packages/core/src/channels/telegram.ts +1 -1
- package/packages/core/src/channels/webchat.ts +1 -1
- package/packages/core/src/config/loader.ts +14 -5
- package/packages/core/src/ethics/EthicsGuard.ts +7 -1
- package/packages/core/src/events/agent-bus.ts +3 -3
- package/packages/core/src/events/channel-narration.ts +3 -3
- package/packages/core/src/events/event-bus.ts +1 -1
- package/packages/core/src/events/index.ts +18 -0
- package/packages/core/src/events/narration.ts +3 -3
- package/packages/core/src/events/tool-narration.ts +4 -0
- package/packages/core/src/gateway/channel-notify.ts +103 -6
- package/packages/core/src/gateway/delegation-groups.ts +4 -4
- package/packages/core/src/gateway/durable-queue.ts +18 -6
- package/packages/core/src/gateway/index.ts +3 -0
- package/packages/core/src/gateway/job-store.ts +11 -5
- package/packages/core/src/gateway/notification-inbox.ts +2 -2
- package/packages/core/src/gateway/server.ts +2 -2
- package/packages/core/src/harness/executors.ts +493 -0
- package/packages/core/src/harness/index.ts +12 -2
- package/packages/core/src/hooks/index.ts +203 -0
- package/packages/core/src/images/index.ts +161 -0
- package/packages/core/src/index.ts +1 -0
- package/packages/core/src/mcp/MCPClient.ts +3 -3
- package/packages/core/src/mcp/hot-reload.ts +5 -5
- package/packages/core/src/mcp/tool-sync.ts +5 -5
- package/packages/core/src/mcp/transports/index.ts +2 -2
- package/packages/core/src/mcp/transports/sse.ts +1 -1
- package/packages/core/src/models/index.ts +36 -0
- package/packages/core/src/multimodal/index.ts +2 -2
- package/packages/core/src/multimodal/vision-service.ts +51 -19
- package/packages/core/src/plugins/loader.ts +4 -1
- package/packages/core/src/resilience/circuit-breaker.ts +16 -5
- package/packages/core/src/resilience/index.ts +13 -0
- package/packages/core/src/resilience/retry.ts +1 -1
- package/packages/core/src/scheduler/CronScheduler.ts +54 -27
- package/packages/core/src/scheduler/cron/expression.ts +165 -0
- package/packages/core/src/scheduler/cron/index.ts +10 -0
- package/packages/core/src/scheduler/cron/job.ts +339 -0
- package/packages/core/src/scheduler/cron/next-run.ts +121 -0
- package/packages/core/src/scheduler/cron/zoned-time.ts +138 -0
- package/packages/core/src/scheduler/index.ts +21 -3
- package/packages/core/src/scheduler/integration.ts +25 -14
- package/packages/core/src/scheduler/types.ts +3 -18
- package/packages/core/src/services/agents.ts +268 -0
- package/packages/core/src/services/cron.ts +257 -0
- package/packages/core/src/services/endpoints.ts +289 -0
- package/packages/core/src/services/ethics.ts +107 -0
- package/packages/core/src/services/images.ts +212 -0
- package/packages/core/src/services/index.ts +112 -0
- package/packages/core/src/services/mcp.ts +201 -0
- package/packages/core/src/services/memory.ts +133 -0
- package/packages/core/src/services/models.ts +179 -0
- package/packages/core/src/services/providers.ts +152 -0
- package/packages/core/src/services/setup.ts +222 -0
- package/packages/core/src/services/skills.ts +241 -0
- package/packages/core/src/services/swarms.ts +307 -0
- package/packages/core/src/services/tools.ts +106 -0
- package/packages/core/src/sessions/index.ts +268 -0
- package/packages/core/src/sessions/resolve.ts +108 -0
- package/packages/core/src/skills/SkillLoader.ts +8 -1
- package/packages/core/src/skills/bundled/artifacts/artifact_reader/SKILL.md +105 -0
- package/packages/core/src/skills/bundled/cron_manager/SKILL.md +21 -11
- package/packages/core/src/skills/bundled/images/image_editor/SKILL.md +120 -0
- package/packages/core/src/skills/bundled/web/browser_automate/SKILL.md +12 -3
- package/packages/core/src/skills/bundled/web/browser_scrape/SKILL.md +22 -7
- package/packages/core/src/skills/bundled-data.generated.ts +110 -12
- package/packages/core/src/storage/bootstrap.ts +107 -12
- package/packages/core/src/storage/causal-events.ts +1 -1
- package/packages/core/src/storage/collections.ts +138 -2
- package/packages/core/src/storage/crypto.ts +35 -4
- package/packages/core/src/storage/hive.ts +1 -1
- package/packages/core/src/storage/hivedb.ts +10 -1
- package/packages/core/src/storage/index.ts +2 -1
- package/packages/core/src/storage/onboarding.ts +61 -45
- package/packages/core/src/storage/reconcile.ts +11 -6
- package/packages/core/src/storage/seed.ts +191 -23
- package/packages/core/src/storage/usage.ts +3 -3
- package/packages/core/src/swarm/AgentExecutor.ts +2 -2
- package/packages/core/src/swarm/Coordinator.ts +8 -8
- package/packages/core/src/swarm/EventBridge.ts +2 -2
- package/packages/core/src/swarm/RoleSwarm.ts +234 -0
- package/packages/core/src/swarm/TaskGraph.ts +2 -2
- package/packages/core/src/swarm/index.ts +7 -0
- package/packages/core/src/swarm/presets/HiveLearnPreset.ts +2 -2
- package/packages/core/src/swarm/presets/ResearchPreset.ts +2 -2
- package/packages/core/src/swarm/strategies/ParallelStrategy.ts +1 -1
- package/packages/core/src/swarm/strategies/PriorityStrategy.ts +3 -3
- package/packages/core/src/swarm/types.ts +3 -18
- package/packages/core/src/tool-runtime/embedded-worker.generated.ts +21 -0
- package/packages/core/src/tool-runtime/index.ts +129 -14
- package/packages/core/src/tools/ToolExecutor.ts +7 -3
- package/packages/core/src/tools/agents/index.ts +18 -60
- package/packages/core/src/tools/cli/index.ts +55 -0
- package/packages/core/src/tools/core/index.ts +52 -4
- package/packages/core/src/tools/cron/index.ts +8 -8
- package/packages/core/src/tools/images/index.ts +130 -0
- package/packages/core/src/tools/index.ts +14 -1
- package/packages/core/src/tools/office/office-escribir-xlsx.ts +2 -1
- package/packages/core/src/tools/office/office-leer-xlsx.ts +2 -1
- package/packages/core/src/tools/office/xlsx-loader.ts +19 -0
- package/packages/core/src/tools/web/artifact-inspect.ts +2 -2
- package/packages/core/src/tools/web/artifact-read.ts +162 -0
- package/packages/core/src/tools/web/browser-backend.ts +141 -44
- package/packages/core/src/tools/web/browser-click.ts +2 -2
- package/packages/core/src/tools/web/browser-extract.ts +2 -2
- package/packages/core/src/tools/web/browser-navigate.ts +2 -2
- package/packages/core/src/tools/web/browser-screenshot.ts +12 -5
- package/packages/core/src/tools/web/browser-script.ts +2 -2
- package/packages/core/src/tools/web/browser-service.ts +63 -384
- package/packages/core/src/tools/web/browser-session.ts +125 -0
- package/packages/core/src/tools/web/browser-type.ts +2 -2
- package/packages/core/src/tools/web/browser-wait.ts +2 -2
- package/packages/core/src/tools/web/computer-use.ts +553 -0
- package/packages/core/src/tools/web/index.ts +8 -1
- package/packages/core/src/tools/web/webview-backend.ts +460 -21
- package/packages/core/src/utils/index.ts +1 -0
- package/packages/core/src/utils/logger.ts +12 -4
- package/packages/core/src/utils/redact-binary.ts +17 -0
- package/packages/core/src/utils/toon.ts +1 -1
- package/packages/core/src/voice/index.ts +6 -6
- package/bun.lock +0 -859
- package/bunfig.toml +0 -9
- package/docs/API-AGENTS.md +0 -367
- package/docs/API-CONTEXT-COMPILER.md +0 -249
- package/docs/API-DAG-SCHEDULER.md +0 -273
- package/docs/API-TOOLS-SKILLS-CHANNELS.md +0 -446
- package/docs/API-WORKERS-EVENTS.md +0 -299
- package/docs/HIVE-HARNESS.md +0 -113
- package/docs/INDEX.md +0 -190
- package/docs/TEMPLATE-HIVE-APP.md +0 -360
- package/packages/cli/package.json +0 -17
- package/packages/cli/src/commands/create-app.test.ts +0 -180
- package/packages/core/package.json +0 -70
- package/packages/core/src/api/createAgent.test.ts +0 -160
- package/packages/core/src/canvas/canvas.test.ts +0 -36
- package/packages/core/src/channels/channels.test.ts +0 -18
- package/packages/core/src/ethics/EthicsGuard.test.ts +0 -108
- package/packages/core/src/gateway/gateway.test.ts +0 -38
- package/packages/core/src/memory/Scratchpad.test.ts +0 -68
- package/packages/core/src/scheduler/scheduler.test.ts +0 -15
- package/packages/core/src/skills/skills.test.ts +0 -62
- package/packages/core/src/swarm/swarm.test.ts +0 -24
- package/packages/core/src/tool-runtime/tool-runtime.test.ts +0 -99
- package/packages/core/src/tools/ToolRegistry.test.ts +0 -98
- package/packages/core/src/tools/api/api-request.test.ts +0 -164
- package/packages/core/src/tools/web/browser-service.test.ts +0 -83
- package/packages/core/src/workers/workers.test.ts +0 -41
- package/scripts/bump-version.ts +0 -248
- package/scripts/generate-skill-bundle.ts +0 -108
- package/test/acceptance-checks.test.ts +0 -403
- package/test/agent-loop-terminal-synthesis.test.ts +0 -32
- package/test/browser-backend.test.ts +0 -308
- package/test/catalog-agents-stay-enabled.test.ts +0 -117
- package/test/causal-events.test.ts +0 -117
- package/test/compaction.test.ts +0 -105
- package/test/context-compiler.test.ts +0 -269
- package/test/curator.test.ts +0 -130
- package/test/durable-queue.test.ts +0 -114
- package/test/harness-barrel.test.ts +0 -64
- package/test/hive-helpers.test.ts +0 -130
- package/test/hivedb-search.test.ts +0 -189
- package/test/internal-turns.test.ts +0 -166
- package/test/job-idempotency.test.ts +0 -68
- package/test/job-retry-backoff.test.ts +0 -184
- package/test/job-store.test.ts +0 -381
- package/test/llm-retry.test.ts +0 -97
- package/test/memory-perf.test.ts +0 -774
- package/test/minimal-loadout.test.ts +0 -78
- package/test/model-catalog.test.ts +0 -105
- package/test/preload.ts +0 -12
- package/test/reflector.test.ts +0 -320
- package/test/retention-cap.test.ts +0 -91
- package/test/retired-capabilities-pruned.test.ts +0 -192
- package/test/run-store.test.ts +0 -355
- package/test/scratchpad.test.ts +0 -74
- package/test/secrets-durability.test.ts +0 -119
- package/test/seed-model-reseed.test.ts +0 -155
- package/test/setup-agent-seed.test.ts +0 -264
- package/test/tool-inventory.test.ts +0 -65
- package/test/tool-runtime.test.ts +0 -258
- package/test/tool-selector-runtime-tools.test.ts +0 -117
- package/test/toon.test.ts +0 -429
- package/tsconfig.json +0 -42
|
@@ -4,8 +4,8 @@ import type {
|
|
|
4
4
|
AgentModelOverride,
|
|
5
5
|
AgentWorkspaceScope,
|
|
6
6
|
} from "../storage/collections";
|
|
7
|
-
import { col, toIndexable, fromIndexable } from "../storage/hive";
|
|
8
|
-
import { expandToolAllowlist } from "./delegation-runtime";
|
|
7
|
+
import { col, toIndexable, fromIndexable } from "../storage/hive.ts";
|
|
8
|
+
import { expandToolAllowlist } from "./delegation-runtime.ts";
|
|
9
9
|
|
|
10
10
|
interface CatalogPersona {
|
|
11
11
|
id: string;
|
|
@@ -37,7 +37,7 @@ ${s.workflow.map((step, index) => `${index + 1}. ${step}`).join("\n")}
|
|
|
37
37
|
|
|
38
38
|
# QUÉ NO HACES
|
|
39
39
|
${s.prohibitions.map((rule) => `- ${rule}`).join("\n")}
|
|
40
|
-
- No
|
|
40
|
+
- No hablas con el usuario, no pedís confirmaciones directas y no delegas a otros agentes.
|
|
41
41
|
- No ampliás el alcance ni usás tools fuera del loadout autorizado.
|
|
42
42
|
- No declarás éxito sin evidencia comprobable para cada criterio.
|
|
43
43
|
|
|
@@ -72,10 +72,10 @@ const CATALOG_PERSONAS: CatalogPersona[] = [
|
|
|
72
72
|
role: "Tu dominio es investigación web, contraste de fuentes y síntesis basada en evidencia.",
|
|
73
73
|
receives: "Una pregunta acotada, contexto relevante, restricciones de actualidad y criterios de aceptación.",
|
|
74
74
|
workflow: [
|
|
75
|
-
"Convertí la pregunta en consultas concretas y
|
|
76
|
-
"
|
|
75
|
+
"Convertí la pregunta en consultas concretas y busca fuentes primarias o autorizadas.",
|
|
76
|
+
"Lee las fuentes relevantes y separa hechos, inferencias y datos no confirmados.",
|
|
77
77
|
"Contrastá afirmaciones sensibles o discutidas con más de una fuente.",
|
|
78
|
-
"
|
|
78
|
+
"Entrega una síntesis concisa con referencias y fechas cuando sean relevantes.",
|
|
79
79
|
],
|
|
80
80
|
prohibitions: [...COMMON_PROHIBITIONS, "No automatizás formularios ni realizás acciones en sitios."],
|
|
81
81
|
quality: "Cada afirmación material debe poder rastrearse a una fuente accesible; los desacuerdos se presentan explícitamente.",
|
|
@@ -92,10 +92,10 @@ const CATALOG_PERSONAS: CatalogPersona[] = [
|
|
|
92
92
|
role: "Tu dominio es navegación y automatización web renderizada.",
|
|
93
93
|
receives: "Una acción web autorizada, URL inicial, datos permitidos, estado final esperado y límites de seguridad.",
|
|
94
94
|
workflow: [
|
|
95
|
-
"
|
|
96
|
-
"Inspeccioná el estado antes de interactuar y
|
|
97
|
-
"
|
|
98
|
-
"
|
|
95
|
+
"Abre el sitio y verifica que corresponda al objetivo.",
|
|
96
|
+
"Inspeccioná el estado antes de interactuar y usa selectores estables.",
|
|
97
|
+
"Ejecuta solamente los clicks, escritura y esperas necesarios.",
|
|
98
|
+
"Verifica el estado final mediante extracción y captura de pantalla.",
|
|
99
99
|
],
|
|
100
100
|
prohibitions: [...COMMON_PROHIBITIONS, "No confirmás compras, envíos, borrados o publicaciones si el principal no autorizó explícitamente ese efecto."],
|
|
101
101
|
quality: "La entrega incluye URL final, estado observado y evidencia visual o estructurada posterior a la acción.",
|
|
@@ -112,10 +112,10 @@ const CATALOG_PERSONAS: CatalogPersona[] = [
|
|
|
112
112
|
role: "Tu dominio es operaciones seguras sobre archivos y carpetas del workspace.",
|
|
113
113
|
receives: "Paths relativos o autorizados, contenido solicitado, operación exacta y estado final esperado.",
|
|
114
114
|
workflow: [
|
|
115
|
-
"
|
|
115
|
+
"Resuelve todos los paths contra el workspace y comprueba su estado inicial.",
|
|
116
116
|
"Aplicá la mínima operación necesaria sin tocar paths ajenos.",
|
|
117
117
|
"Volvé a leer o listar el resultado para comprobarlo.",
|
|
118
|
-
"
|
|
118
|
+
"Reporta paths exactos, cambios y evidencia de readback.",
|
|
119
119
|
],
|
|
120
120
|
prohibitions: [...COMMON_PROHIBITIONS, "No ejecutás comandos shell ni modificás repositorios fuera de la operación de archivos pedida."],
|
|
121
121
|
quality: "Todos los paths permanecen dentro del workspace y su contenido o ausencia final se comprueba después de la operación.",
|
|
@@ -134,10 +134,10 @@ const CATALOG_PERSONAS: CatalogPersona[] = [
|
|
|
134
134
|
workflow: [
|
|
135
135
|
"Inspeccioná el repositorio, convenciones y estado antes de editar.",
|
|
136
136
|
"Determiná la causa o el diseño mínimo y modificá solo archivos pertinentes.",
|
|
137
|
-
"
|
|
138
|
-
"
|
|
137
|
+
"Ejecuta checks, tests o builds proporcionales al riesgo.",
|
|
138
|
+
"Entrega archivos cambiados, evidencia de validación y riesgos restantes.",
|
|
139
139
|
],
|
|
140
|
-
prohibitions: [...COMMON_PROHIBITIONS, "No sobrescribís cambios ajenos, no publicás y no
|
|
140
|
+
prohibitions: [...COMMON_PROHIBITIONS, "No sobrescribís cambios ajenos, no publicás y no delegas a subagentes CLI."],
|
|
141
141
|
quality: "El cambio satisface el comportamiento pedido, preserva compatibilidad y pasa las validaciones relevantes.",
|
|
142
142
|
routingExamples: ["implementar una función", "arreglar un bug", "ejecutar tests de un proyecto"],
|
|
143
143
|
tools: ["fs_*", "cli_exec"],
|
|
@@ -153,10 +153,10 @@ const CATALOG_PERSONAS: CatalogPersona[] = [
|
|
|
153
153
|
role: "Tu dominio es lectura y generación de archivos Office estructurados.",
|
|
154
154
|
receives: "Archivo de entrada o especificación del documento, formato final, contenido y path autorizado.",
|
|
155
155
|
workflow: [
|
|
156
|
-
"Inspeccioná entradas y
|
|
156
|
+
"Inspeccioná entradas y confirma el formato solicitado.",
|
|
157
157
|
"Generá o extraé contenido preservando estructura y datos.",
|
|
158
|
-
"
|
|
159
|
-
"
|
|
158
|
+
"Comprueba que el archivo existe, no está vacío y puede reabrirse.",
|
|
159
|
+
"Entrega el path final, resumen de contenido y prueba de reapertura.",
|
|
160
160
|
],
|
|
161
161
|
prohibitions: [...COMMON_PROHIBITIONS, "No editás formatos binarios con tools genéricas de filesystem."],
|
|
162
162
|
quality: "El artefacto debe abrir sin error con la tool lectora correspondiente y contener la estructura solicitada.",
|
|
@@ -174,9 +174,9 @@ const CATALOG_PERSONAS: CatalogPersona[] = [
|
|
|
174
174
|
receives: "Sesión, surfaceId, flujo solicitado, datos, acciones permitidas y criterios visuales.",
|
|
175
175
|
workflow: [
|
|
176
176
|
"Diseñá una jerarquía pequeña con IDs únicos y un root explícito.",
|
|
177
|
-
"
|
|
177
|
+
"Crea la superficie antes de enviar componentes.",
|
|
178
178
|
"Enviá componentes válidos y después el data model enlazado.",
|
|
179
|
-
"
|
|
179
|
+
"Comprueba acknowledgements, IDs y paths; liberá la superficie al cancelar.",
|
|
180
180
|
],
|
|
181
181
|
prohibitions: [...COMMON_PROHIBITIONS, "No interpretás acciones del usuario ni conversás; los eventos vuelven al principal."],
|
|
182
182
|
quality: "La superficie usa el catálogo v0.9, no tiene referencias rotas y sus bindings apuntan a paths válidos.",
|
|
@@ -194,9 +194,9 @@ const CATALOG_PERSONAS: CatalogPersona[] = [
|
|
|
194
194
|
receives: "Una automatización que Hive debe ejecutar después, su horario o recurrencia, timezone, canal y comportamiento esperado.",
|
|
195
195
|
workflow: [
|
|
196
196
|
"Normalizá fecha, recurrencia y timezone sin cambiar la intención.",
|
|
197
|
-
"
|
|
197
|
+
"Crea o modificá únicamente el job solicitado.",
|
|
198
198
|
"Consultá el job persistido y su próxima ejecución.",
|
|
199
|
-
"
|
|
199
|
+
"Entrega ID, estado, timezone y next_run_at.",
|
|
200
200
|
],
|
|
201
201
|
prohibitions: [
|
|
202
202
|
...COMMON_PROHIBITIONS,
|
|
@@ -223,9 +223,9 @@ const CATALOG_PERSONAS: CatalogPersona[] = [
|
|
|
223
223
|
receives: "Endpoint autorizado, método, headers permitidos, payload, status esperado y esquema relevante.",
|
|
224
224
|
workflow: [
|
|
225
225
|
"Validá método, host, payload y alcance antes del request.",
|
|
226
|
-
"
|
|
227
|
-
"
|
|
228
|
-
"
|
|
226
|
+
"Ejecuta una sola operación idempotente o explícitamente autorizada.",
|
|
227
|
+
"Comprueba status, headers y forma de la respuesta.",
|
|
228
|
+
"Entrega evidencia saneada sin secretos.",
|
|
229
229
|
],
|
|
230
230
|
prohibitions: [...COMMON_PROHIBITIONS, "No repetís mutaciones automáticamente ni cambiás método, host o payload para forzar éxito."],
|
|
231
231
|
quality: "El status y contrato observados coinciden con los criterios y la evidencia no contiene credenciales.",
|
|
@@ -284,6 +284,63 @@ export function createSeedCatalogAgents(now = Date.now()): AgentDoc[] {
|
|
|
284
284
|
|
|
285
285
|
export const CATALOG_AGENT_IDS = CATALOG_PERSONAS.map((s) => s.id);
|
|
286
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Qué tools y skills necesita un subconjunto de agentes de catálogo.
|
|
289
|
+
*
|
|
290
|
+
* Devuelve la **unión**, no la lista de cada uno, y ahí está todo el asunto: las
|
|
291
|
+
* tools se comparten. `web_fetch` lo declaran `web_researcher` y
|
|
292
|
+
* `browser_operator`; `fs_*` lo declaran `workspace_file_operator` y
|
|
293
|
+
* `software_engineer`. Sembrar "sólo lo de este agente" dejaría a los demás sin
|
|
294
|
+
* capacidades que sí necesitan, y el fallo aparecería recién cuando el modelo
|
|
295
|
+
* descubra por BM25 una tool que no puede ejecutar.
|
|
296
|
+
*
|
|
297
|
+
* Los patrones se devuelven **sin expandir** a propósito: `expandToolAllowlist`
|
|
298
|
+
* necesita el registro vivo de tools, que este módulo no debe conocer. Quien
|
|
299
|
+
* siembre expande.
|
|
300
|
+
*
|
|
301
|
+
* `MINIMAL_TOOLS` no se incluye acá: son del coordinador, existan o no estos
|
|
302
|
+
* agentes, y quien siembra las añade siempre.
|
|
303
|
+
*/
|
|
304
|
+
export function requiredCapabilitiesFor(agentIds: string[]): {
|
|
305
|
+
toolPatterns: string[];
|
|
306
|
+
skills: string[];
|
|
307
|
+
} {
|
|
308
|
+
const conocidos = new Set(CATALOG_AGENT_IDS);
|
|
309
|
+
const desconocidos = agentIds.filter((id) => !conocidos.has(id));
|
|
310
|
+
if (desconocidos.length > 0) {
|
|
311
|
+
throw new Error(`No existen en el catálogo: ${desconocidos.join(", ")}`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const pedidos = new Set(agentIds);
|
|
315
|
+
const toolPatterns = new Set<string>();
|
|
316
|
+
const skills = new Set<string>();
|
|
317
|
+
|
|
318
|
+
for (const persona of CATALOG_PERSONAS) {
|
|
319
|
+
if (!pedidos.has(persona.id)) continue;
|
|
320
|
+
for (const t of persona.tools) toolPatterns.add(t);
|
|
321
|
+
for (const s of persona.skills) skills.add(s);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return { toolPatterns: [...toolPatterns], skills: [...skills] };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** Las personas del catálogo, para que una UI pueda ofrecerlas al configurar. */
|
|
328
|
+
export function listCatalogPersonas(): Array<{
|
|
329
|
+
id: string;
|
|
330
|
+
name: string;
|
|
331
|
+
description: string;
|
|
332
|
+
tools: string[];
|
|
333
|
+
skills: string[];
|
|
334
|
+
}> {
|
|
335
|
+
return CATALOG_PERSONAS.map((p) => ({
|
|
336
|
+
id: p.id,
|
|
337
|
+
name: p.name,
|
|
338
|
+
description: p.description,
|
|
339
|
+
tools: p.tools,
|
|
340
|
+
skills: p.skills,
|
|
341
|
+
}));
|
|
342
|
+
}
|
|
343
|
+
|
|
287
344
|
/**
|
|
288
345
|
* Writes the coordinator's provider/model onto every other agent row, so the
|
|
289
346
|
* whole hive is explicitly configured instead of relying on the runtime
|
|
@@ -13,24 +13,25 @@
|
|
|
13
13
|
* Also used directly by runAgentIsolated() for worker tasks.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { logger } from "../utils/logger"
|
|
17
|
-
import { col, fromIndexable } from "../storage/hive"
|
|
18
|
-
import { getHiveDb } from "../storage/hivedb"
|
|
16
|
+
import { logger } from "../utils/logger.ts"
|
|
17
|
+
import { col, fromIndexable } from "../storage/hive.ts"
|
|
18
|
+
import { getHiveDb } from "../storage/hivedb.ts"
|
|
19
19
|
import type { HiveDB, EventInput } from "@johpaz/hive-db"
|
|
20
|
-
import type { AgentDoc, TurnSource } from "../storage/collections"
|
|
21
|
-
import { callLLM, resolveProviderConfig, getDefaultLLM, type LLMMessage } from "./llm-client"
|
|
22
|
-
import { addMessage } from "./conversation-store"
|
|
23
|
-
import { saveTrace, recordLLMUsage } from "./tracer"
|
|
24
|
-
import { maybeCompact, clearOldToolResults } from "./compaction"
|
|
25
|
-
import { emitCanvas } from "../canvas/emitter"
|
|
20
|
+
import type { AgentDoc, TurnSource } from "../storage/collections.ts"
|
|
21
|
+
import { callLLM, resolveProviderConfig, getDefaultLLM, type LLMMessage, type ProviderCredentials } from "./llm-client.ts"
|
|
22
|
+
import { addMessage } from "./conversation-store.ts"
|
|
23
|
+
import { saveTrace, recordLLMUsage } from "./tracer.ts"
|
|
24
|
+
import { maybeCompact, clearOldToolResults } from "./compaction.ts"
|
|
25
|
+
import { emitCanvas } from "../canvas/emitter.ts"
|
|
26
26
|
import type { MCPClientManager } from "../mcp/index.ts"
|
|
27
|
-
import { compileContext } from "./context-compiler"
|
|
28
|
-
import { formatToolResult } from "../utils/toon"
|
|
29
|
-
import {
|
|
30
|
-
import
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
27
|
+
import { compileContext } from "./context-compiler.ts"
|
|
28
|
+
import { formatToolResult } from "../utils/toon.ts"
|
|
29
|
+
import { redactBinaryStrings } from "../utils/redact-binary.ts"
|
|
30
|
+
import { resolveUserId, resolveAgentId } from "../storage/onboarding.ts"
|
|
31
|
+
import type { ContentPart } from "../multimodal/types.ts"
|
|
32
|
+
import { loadConfig } from "../config/loader.ts"
|
|
33
|
+
import { executeToolBatch } from "../tool-runtime/index.ts"
|
|
34
|
+
import { createStuckLoopDetector, getInterventionMessage, type StuckLoopState } from "./stuck-loop.ts"
|
|
34
35
|
import {
|
|
35
36
|
createRun as createAgentRun,
|
|
36
37
|
checkpoint as checkpointRun,
|
|
@@ -44,9 +45,9 @@ import {
|
|
|
44
45
|
startLeaseRenewal,
|
|
45
46
|
stopLeaseRenewal,
|
|
46
47
|
type RunCheckpointState,
|
|
47
|
-
} from "./run-store"
|
|
48
|
-
import { publishNarration } from "../events/narration"
|
|
49
|
-
import { getNarration } from "../events/tool-narration"
|
|
48
|
+
} from "./run-store.ts"
|
|
49
|
+
import { publishNarration } from "../events/narration.ts"
|
|
50
|
+
import { getNarration } from "../events/tool-narration.ts"
|
|
50
51
|
|
|
51
52
|
const log = logger.child("agent-loop")
|
|
52
53
|
|
|
@@ -94,6 +95,50 @@ export async function synthesizeFinalResponse(
|
|
|
94
95
|
)
|
|
95
96
|
}
|
|
96
97
|
|
|
98
|
+
type LoadoutContext = {
|
|
99
|
+
tools: Array<{ type: string; function?: { name?: string } }>
|
|
100
|
+
allTools: Array<{ name: string }>
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Adds artifact_read to the loadout the moment a tool result hands the model an
|
|
105
|
+
* `artifact_ref` it will need to open.
|
|
106
|
+
*
|
|
107
|
+
* mcp-result-normalizer.ts moves oversized MCP text results out of the context
|
|
108
|
+
* window and leaves a reference behind. Discovery (search_knowledge) can find
|
|
109
|
+
* the reader, but that costs an iteration and assumes the model thinks to look:
|
|
110
|
+
* in the incident this comes from, it reached for artifact_inspect instead, got
|
|
111
|
+
* metadata, and burned the rest of its budget on `find` and `env` before the
|
|
112
|
+
* turn died on an empty synthesis. Image refs are excluded — those are carried
|
|
113
|
+
* to the UI, not read back by the model.
|
|
114
|
+
*
|
|
115
|
+
* Returns true when the loadout changed.
|
|
116
|
+
*/
|
|
117
|
+
export function injectArtifactReadIfNeeded(toolResult: unknown, ctx: LoadoutContext): boolean {
|
|
118
|
+
if (!Array.isArray(toolResult)) return false
|
|
119
|
+
|
|
120
|
+
const needsReader = toolResult.some((block) =>
|
|
121
|
+
!!block && typeof block === "object" &&
|
|
122
|
+
(block as { type?: unknown }).type === "artifact_ref" &&
|
|
123
|
+
!String((block as { mime_type?: unknown }).mime_type ?? "").startsWith("image/")
|
|
124
|
+
)
|
|
125
|
+
if (!needsReader) return false
|
|
126
|
+
if (ctx.tools.some((t) => t.function?.name === "artifact_read")) return false
|
|
127
|
+
|
|
128
|
+
const reader = ctx.allTools.find((t) => t.name === "artifact_read")
|
|
129
|
+
if (!reader) return false
|
|
130
|
+
|
|
131
|
+
ctx.tools.push({
|
|
132
|
+
type: "function",
|
|
133
|
+
function: {
|
|
134
|
+
name: reader.name,
|
|
135
|
+
description: (reader as any).description ?? "",
|
|
136
|
+
parameters: (reader as any).parameters ?? { type: "object", properties: {} },
|
|
137
|
+
},
|
|
138
|
+
} as LoadoutContext["tools"][number])
|
|
139
|
+
return true
|
|
140
|
+
}
|
|
141
|
+
|
|
97
142
|
/** Bounds a single async operation to its own timeout window, independent of any caller. */
|
|
98
143
|
export async function withTimeout<T>(op: () => Promise<T>, timeoutMs: number): Promise<T> {
|
|
99
144
|
let timer: ReturnType<typeof setTimeout>
|
|
@@ -171,6 +216,14 @@ export interface AgentLoopOptions {
|
|
|
171
216
|
taskId?: string
|
|
172
217
|
/** External routing/session id for progress delivery. */
|
|
173
218
|
sessionId?: string
|
|
219
|
+
/**
|
|
220
|
+
* Credenciales del proveedor para ESTA llamada, en vez de las del proceso.
|
|
221
|
+
*
|
|
222
|
+
* Un host multi-tenant resuelve la key de su inquilino y la pasa acá. Sin esto
|
|
223
|
+
* la única fuente era el secret store de HiveDB o `process.env`, ambos globales:
|
|
224
|
+
* dos inquilinos concurrentes en el mismo proceso compartían credencial.
|
|
225
|
+
*/
|
|
226
|
+
credentials?: ProviderCredentials
|
|
174
227
|
/** Whether to resume from a previously saved checkpoint */
|
|
175
228
|
resume?: boolean
|
|
176
229
|
/** Run budget — overrides agent.max_iterations when set */
|
|
@@ -215,6 +268,8 @@ export interface StreamChunk {
|
|
|
215
268
|
agent?: { messages: any[]; streamed?: boolean }
|
|
216
269
|
tools?: { messages: any[] }
|
|
217
270
|
usage?: { input_tokens: number; output_tokens: number }
|
|
271
|
+
/** Image artifacts (mcp-result-normalizer.ts) produced by tools this turn. */
|
|
272
|
+
artifacts?: { images: Array<{ artifactId: string; mimeType: string }> }
|
|
218
273
|
}
|
|
219
274
|
|
|
220
275
|
// ─── Main agent loop ──────────────────────────────────────────────────────────
|
|
@@ -280,7 +335,7 @@ export async function* runAgent(
|
|
|
280
335
|
agentProvider = agentProvider || defaultLLM.provider
|
|
281
336
|
agentModel = agentModel || defaultLLM.model
|
|
282
337
|
}
|
|
283
|
-
const providerCfg = await resolveProviderConfig(agentProvider, agentModel)
|
|
338
|
+
const providerCfg = await resolveProviderConfig(agentProvider, agentModel, opts.credentials)
|
|
284
339
|
|
|
285
340
|
const cleanModel = providerCfg.model.replace(new RegExp(`^${providerCfg.provider}\\/`), "")
|
|
286
341
|
log.info(`[agent-loop] Starting: agent=${agentName} thread=${opts.threadId} provider=${providerCfg.provider}/${cleanModel}`)
|
|
@@ -360,6 +415,10 @@ export async function* runAgent(
|
|
|
360
415
|
let iterations = 0
|
|
361
416
|
let totalInputTokens = 0
|
|
362
417
|
let totalOutputTokens = 0
|
|
418
|
+
// Image artifacts produced by tool results this turn (post mcp-result-normalizer.ts) —
|
|
419
|
+
// surfaced in the final chunk so callers (webchat-turn.ts) can attach them to the
|
|
420
|
+
// outbound message instead of the model having to describe them in text.
|
|
421
|
+
const turnImageArtifacts: Array<{ artifactId: string; mimeType: string }> = []
|
|
363
422
|
let lastToolSignature = ""
|
|
364
423
|
let consecutiveRepeat = 0
|
|
365
424
|
let idleIterations = 0
|
|
@@ -452,7 +511,7 @@ export async function* runAgent(
|
|
|
452
511
|
iterations++
|
|
453
512
|
|
|
454
513
|
const delegationGroupAtCall = opts.turnId && !opts.isolated
|
|
455
|
-
? await import("../gateway/delegation-groups").then((mod) => mod.getDelegationGroup(opts.turnId!))
|
|
514
|
+
? await import("../gateway/delegation-groups.ts").then((mod) => mod.getDelegationGroup(opts.turnId!))
|
|
456
515
|
: null
|
|
457
516
|
let streamedThisCall = false
|
|
458
517
|
let response: Awaited<ReturnType<typeof callLLM>>
|
|
@@ -648,6 +707,27 @@ export async function* runAgent(
|
|
|
648
707
|
const toolResultJS = batchResult.result
|
|
649
708
|
const toolMs = batchResult.durationMs
|
|
650
709
|
|
|
710
|
+
// Surface image artifacts (see mcp-result-normalizer.ts) so the final
|
|
711
|
+
// response can carry them to the UI/channel — before TOON-encoding,
|
|
712
|
+
// while toolResultJS is still structured.
|
|
713
|
+
if (Array.isArray(toolResultJS)) {
|
|
714
|
+
for (const block of toolResultJS) {
|
|
715
|
+
if (
|
|
716
|
+
block && typeof block === "object" &&
|
|
717
|
+
(block as { type?: unknown }).type === "artifact_ref" &&
|
|
718
|
+
typeof (block as { mime_type?: unknown }).mime_type === "string" &&
|
|
719
|
+
(block as { mime_type: string }).mime_type.startsWith("image/")
|
|
720
|
+
) {
|
|
721
|
+
const ref = block as { artifact_id: string; mime_type: string }
|
|
722
|
+
turnImageArtifacts.push({ artifactId: ref.artifact_id, mimeType: ref.mime_type })
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
if (injectArtifactReadIfNeeded(toolResultJS, ctx)) {
|
|
728
|
+
log.info("[agent-loop] Tool result carries an artifact_ref — injected artifact_read into loadout")
|
|
729
|
+
}
|
|
730
|
+
|
|
651
731
|
// Encode TOON only for LLM consumption (with cost calculation)
|
|
652
732
|
const toolResultLLM = formatToolResult(toolResultJS, cleanModel)
|
|
653
733
|
|
|
@@ -812,7 +892,7 @@ export async function* runAgent(
|
|
|
812
892
|
// Inject skills associated with the injected tools
|
|
813
893
|
if (injectedTools.length > 0) {
|
|
814
894
|
try {
|
|
815
|
-
const skillsCol = await col<import("../storage/collections").SkillDoc>("skills")
|
|
895
|
+
const skillsCol = await col<import("../storage/collections.ts").SkillDoc>("skills")
|
|
816
896
|
// Find skills that use any of the injected tools
|
|
817
897
|
const activeSkills = (await skillsCol.scan({})).filter(e => e.doc.active)
|
|
818
898
|
const skillsWithTools = activeSkills
|
|
@@ -1032,7 +1112,7 @@ export async function* runAgent(
|
|
|
1032
1112
|
// Make one extra call without tools so it summarizes what it did.
|
|
1033
1113
|
if (!finalContent) {
|
|
1034
1114
|
const pendingDelegation = opts.turnId && !opts.isolated
|
|
1035
|
-
? await import("../gateway/delegation-groups").then((mod) => mod.getDelegationGroup(opts.turnId!))
|
|
1115
|
+
? await import("../gateway/delegation-groups.ts").then((mod) => mod.getDelegationGroup(opts.turnId!))
|
|
1036
1116
|
: null
|
|
1037
1117
|
if (pendingDelegation) {
|
|
1038
1118
|
log.info(`[agent-loop] Suppressing terminal synthesis while delegation group ${opts.turnId} is pending`)
|
|
@@ -1085,6 +1165,10 @@ export async function* runAgent(
|
|
|
1085
1165
|
yield { usage: { input_tokens: totalInputTokens, output_tokens: totalOutputTokens } }
|
|
1086
1166
|
}
|
|
1087
1167
|
|
|
1168
|
+
if (turnImageArtifacts.length > 0) {
|
|
1169
|
+
yield { artifacts: { images: turnImageArtifacts } }
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1088
1172
|
// ── Post-loop ────────────────────────────────────────────────────────────
|
|
1089
1173
|
const durationMs = Math.round(performance.now() - t0)
|
|
1090
1174
|
|
|
@@ -1203,6 +1287,12 @@ export interface IsolatedAgentOptions {
|
|
|
1203
1287
|
userId?: string
|
|
1204
1288
|
channel?: string
|
|
1205
1289
|
sessionId?: string
|
|
1290
|
+
/**
|
|
1291
|
+
* Se hereda del turno que delegó. Sin propagarla, un worker delegado volvía a
|
|
1292
|
+
* caer en la credencial global del proceso y la fuga entre inquilinos se
|
|
1293
|
+
* reabría justo en el camino de delegación.
|
|
1294
|
+
*/
|
|
1295
|
+
credentials?: ProviderCredentials
|
|
1206
1296
|
}
|
|
1207
1297
|
|
|
1208
1298
|
export async function runAgentIsolatedDetailed(
|
|
@@ -1226,15 +1316,14 @@ export async function runAgentIsolatedDetailed(
|
|
|
1226
1316
|
userId: opts.userId,
|
|
1227
1317
|
channel: opts.channel,
|
|
1228
1318
|
sessionId: opts.sessionId,
|
|
1319
|
+
credentials: opts.credentials,
|
|
1229
1320
|
})) {
|
|
1230
1321
|
if (chunk.agent?.messages?.[0]?.content) {
|
|
1231
1322
|
lastContent = chunk.agent.messages[0].content
|
|
1232
1323
|
}
|
|
1233
1324
|
for (const message of chunk.tools?.messages ?? []) {
|
|
1234
1325
|
const raw = typeof message.content === "string" ? message.content : JSON.stringify(message.content)
|
|
1235
|
-
const safe = raw
|
|
1236
|
-
.replace(/data:[a-z0-9.+-]+\/[a-z0-9.+-]+;base64,[a-z0-9+/=\s]+/gi, "[REDACTED_BINARY]")
|
|
1237
|
-
.replace(/[A-Za-z0-9+/]{1000,}={0,2}/g, "[REDACTED_BINARY]")
|
|
1326
|
+
const safe = redactBinaryStrings(raw)
|
|
1238
1327
|
toolEvidence.push(`${message.name ?? "tool"}: ${safe.slice(0, 4000)}`)
|
|
1239
1328
|
if (toolEvidence.length > 8) toolEvidence.shift()
|
|
1240
1329
|
}
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import type { IndexDoc } from "@johpaz/hive-db";
|
|
22
|
-
import { getHiveDb } from "../storage/hivedb";
|
|
23
|
-
import { logger } from "../utils/logger";
|
|
22
|
+
import { getHiveDb } from "../storage/hivedb.ts";
|
|
23
|
+
import { logger } from "../utils/logger.ts";
|
|
24
24
|
|
|
25
25
|
const log = logger.child("capability-search");
|
|
26
26
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { col } from "../storage/hive";
|
|
2
|
-
import type { AgentDoc } from "../storage/collections";
|
|
1
|
+
import { col } from "../storage/hive.ts";
|
|
2
|
+
import type { AgentDoc } from "../storage/collections.ts";
|
|
3
3
|
import {
|
|
4
4
|
applyRelativeCutoff,
|
|
5
5
|
replaceCapabilityDocs,
|
|
6
6
|
searchCapabilities,
|
|
7
7
|
type CapabilityDoc,
|
|
8
|
-
} from "./capability-search";
|
|
9
|
-
import { logger } from "../utils/logger";
|
|
8
|
+
} from "./capability-search.ts";
|
|
9
|
+
import { logger } from "../utils/logger.ts";
|
|
10
10
|
|
|
11
11
|
const log = logger.child("catalog-selector");
|
|
12
12
|
const RELEVANCE_RATIO = 0.35;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* short summaries in the in-memory message array before model calls.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { logger } from "../utils/logger"
|
|
18
|
+
import { logger } from "../utils/logger.ts"
|
|
19
19
|
import {
|
|
20
20
|
getTotalTokens,
|
|
21
21
|
getHistory,
|
|
@@ -24,11 +24,13 @@ import {
|
|
|
24
24
|
getMessageCount,
|
|
25
25
|
isInternalSource,
|
|
26
26
|
type StoredMessage,
|
|
27
|
-
} from "./conversation-store"
|
|
28
|
-
import { estimateTokens } from "../utils/toon"
|
|
29
|
-
import { callLLM, resolveProviderConfig, getDefaultLLM, type ContentPart } from "./llm-client"
|
|
30
|
-
import { col, fromIndexable } from "../storage/hive"
|
|
31
|
-
import type { AgentDoc, ModelDoc } from "../storage/collections"
|
|
27
|
+
} from "./conversation-store.ts"
|
|
28
|
+
import { estimateTokens } from "../utils/toon.ts"
|
|
29
|
+
import { callLLM, resolveProviderConfig, getDefaultLLM, type ContentPart } from "./llm-client.ts"
|
|
30
|
+
import { col, fromIndexable } from "../storage/hive.ts"
|
|
31
|
+
import type { AgentDoc, ModelDoc } from "../storage/collections.ts"
|
|
32
|
+
import { loadConfig } from "../config/loader.ts"
|
|
33
|
+
import { runBeforeCompaction } from "../hooks/index.ts"
|
|
32
34
|
|
|
33
35
|
const log = logger.child("compaction")
|
|
34
36
|
|
|
@@ -51,8 +53,15 @@ export async function maybeCompact(
|
|
|
51
53
|
try {
|
|
52
54
|
const totalTokens = await getTotalTokens(threadId)
|
|
53
55
|
|
|
54
|
-
//
|
|
56
|
+
// Orden de precedencia: lo que el usuario configuró gana sobre lo que se
|
|
57
|
+
// deduce del modelo, y eso gana sobre la constante.
|
|
58
|
+
//
|
|
59
|
+
// `agent.context.compactionThreshold` estaba en el esquema de configuración
|
|
60
|
+
// y **no lo leía nadie**: alguien podía ajustarlo y no pasaba nada. Una
|
|
61
|
+
// opción que no hace nada es peor que no tenerla, porque el usuario cree
|
|
62
|
+
// que cambió algo.
|
|
55
63
|
let effectiveThreshold = COMPACT_TOKEN_THRESHOLD
|
|
64
|
+
const configurado = loadConfig().agent?.context?.compactionThreshold
|
|
56
65
|
try {
|
|
57
66
|
const agentsCol = await col<AgentDoc>("agents")
|
|
58
67
|
const coordinators = await agentsCol.findBy("role", "coordinator", { limit: 1 })
|
|
@@ -69,8 +78,18 @@ export async function maybeCompact(
|
|
|
69
78
|
}
|
|
70
79
|
} catch { /* use default threshold */ }
|
|
71
80
|
|
|
81
|
+
if (configurado && configurado > 0) effectiveThreshold = configurado
|
|
82
|
+
|
|
72
83
|
if (totalTokens < effectiveThreshold) return
|
|
73
84
|
|
|
85
|
+
// Avisar antes de comprimir: es la última oportunidad de que alguien
|
|
86
|
+
// conserve algo del historial que está por resumirse.
|
|
87
|
+
await runBeforeCompaction({
|
|
88
|
+
threadId,
|
|
89
|
+
messageCount: await getMessageCount(threadId),
|
|
90
|
+
totalTokens,
|
|
91
|
+
}).catch(() => {})
|
|
92
|
+
|
|
74
93
|
const summary = await getSummary(threadId)
|
|
75
94
|
const totalMessages = await getMessageCount(threadId)
|
|
76
95
|
|
|
@@ -118,7 +137,7 @@ export function renderTranscript(rows: StoredMessage[], maxMsgChars = MAX_MSG_CH
|
|
|
118
137
|
/**
|
|
119
138
|
* Compress a thread's history into a summary.
|
|
120
139
|
*/
|
|
121
|
-
async function compactThread(
|
|
140
|
+
export async function compactThread(
|
|
122
141
|
threadId: string,
|
|
123
142
|
notify?: { channel: string; userId: string }
|
|
124
143
|
): Promise<void> {
|
|
@@ -187,11 +206,12 @@ async function compactThread(
|
|
|
187
206
|
// Notify user in their active channel (non-critical)
|
|
188
207
|
if (notify?.channel && notify?.userId) {
|
|
189
208
|
try {
|
|
190
|
-
const { sendToUserChannel } = await import("../gateway/channel-notify")
|
|
209
|
+
const { sendToUserChannel } = await import("../gateway/channel-notify.ts")
|
|
191
210
|
await sendToUserChannel(
|
|
192
211
|
notify.channel,
|
|
193
212
|
notify.userId,
|
|
194
|
-
`🗜️ Resumí ${toSummarize.length} mensajes anteriores para mantener el contexto limpio
|
|
213
|
+
`🗜️ Resumí ${toSummarize.length} mensajes anteriores para mantener el contexto limpio.`,
|
|
214
|
+
{ threadId }
|
|
195
215
|
)
|
|
196
216
|
} catch {
|
|
197
217
|
// Non-critical — don't break the flow if notification fails
|