@johpaz/hive-sdk 0.2.0 → 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 +306 -0
- package/README.md +11 -3
- package/package.json +10 -4
- package/packages/core/src/agent/agent-catalog.ts +81 -24
- package/packages/core/src/agent/compaction.ts +20 -1
- package/packages/core/src/agent/context-compiler.ts +6 -3
- package/packages/core/src/agent/conversation-store.ts +136 -2
- package/packages/core/src/agent/curator.ts +12 -3
- package/packages/core/src/agent/llm-providers/nvidia.ts +39 -0
- package/packages/core/src/agent/llm-providers/openai-compat-base.ts +38 -2
- package/packages/core/src/agent/playbook-selector.ts +18 -3
- package/packages/core/src/agent/prompt-builder.ts +2 -2
- package/packages/core/src/agent/providers/index.ts +36 -1
- package/packages/core/src/agent/reflector.ts +32 -9
- package/packages/core/src/agent/skill-selector.ts +2 -2
- package/packages/core/src/agent/thread-store.ts +43 -0
- package/packages/core/src/agent/tool-selector.ts +2 -0
- package/packages/core/src/api/createAgent.ts +66 -1
- package/packages/core/src/artifacts/index.ts +15 -0
- package/packages/core/src/artifacts/store.ts +77 -2
- package/packages/core/src/canvas/index.ts +9 -0
- package/packages/core/src/ethics/EthicsGuard.ts +7 -1
- package/packages/core/src/events/index.ts +18 -0
- 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/durable-queue.ts +13 -1
- package/packages/core/src/gateway/index.ts +3 -0
- package/packages/core/src/gateway/job-store.ts +6 -0
- 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/multimodal/vision-service.ts +45 -13
- package/packages/core/src/resilience/index.ts +13 -0
- package/packages/core/src/scheduler/CronScheduler.ts +48 -21
- 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 +16 -5
- 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 +5 -3
- 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 +74 -5
- package/packages/core/src/storage/collections.ts +106 -1
- package/packages/core/src/storage/crypto.ts +24 -7
- package/packages/core/src/storage/index.ts +2 -1
- package/packages/core/src/storage/onboarding.ts +59 -43
- package/packages/core/src/storage/reconcile.ts +6 -1
- package/packages/core/src/storage/seed.ts +89 -11
- 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/agents/index.ts +18 -60
- package/packages/core/src/tools/cli/index.ts +55 -0
- package/packages/core/src/tools/core/index.ts +50 -2
- package/packages/core/src/tools/cron/index.ts +4 -4
- 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
|
@@ -460,22 +460,32 @@ Para gestionar tareas programadas (cron jobs): crear, listar, actualizar, pausar
|
|
|
460
460
|
| \`cron_expression\` | string | Expresión cron (solo para recurring) |
|
|
461
461
|
| \`fire_at\` | string | Datetime ISO (solo para one_shot) |
|
|
462
462
|
| \`channel\` | string | Canal de notificación |
|
|
463
|
-
| \`start_at\` | string | Inicio de ventana opcional
|
|
464
|
-
| \`stop_at\` | string | Fin de ventana opcional
|
|
463
|
+
| \`start_at\` | string | Inicio de ventana opcional |
|
|
464
|
+
| \`stop_at\` | string | Fin de ventana opcional |
|
|
465
465
|
| \`dom_and_dow\` | number | 0=OR (default), 1=AND (día mes + día semana) |
|
|
466
|
+
| \`max_runs\` | number | Deja de correr después de N corridas ("recordámelo 3 veces") |
|
|
467
|
+
| \`payload\` | object | Datos que recibe el agente al ejecutarse |
|
|
468
|
+
| \`agent_id\` | string | Agente concreto que debe ejecutarla. Si se omite, decide el coordinador |
|
|
469
|
+
| \`tool_name\` | string | Ejecutar una tool directamente, sin pasar por un agente |
|
|
470
|
+
|
|
471
|
+
> **La zona horaria no se pasa acá**: sale del perfil del usuario. No la
|
|
472
|
+
> inventes ni la pidas — si el usuario dice "a las 9", son las 9 de su reloj.
|
|
466
473
|
|
|
467
474
|
## Cron Expression Format
|
|
468
475
|
|
|
469
476
|
\`\`\`
|
|
470
|
-
|
|
471
|
-
│
|
|
472
|
-
│ │
|
|
473
|
-
│ │ │
|
|
474
|
-
│ │
|
|
475
|
-
│
|
|
476
|
-
|
|
477
|
+
┌───────── segundos (0-59) ← opcional, sólo si hacen falta
|
|
478
|
+
│ ┌─────── minuto (0-59)
|
|
479
|
+
│ │ ┌───── hora (0-23)
|
|
480
|
+
│ │ │ ┌─── día del mes (1-31)
|
|
481
|
+
│ │ │ │ ┌─ mes (1-12 o JAN-DEC)
|
|
482
|
+
│ │ │ │ │ ┌ día de semana (0-7 o SUN-SAT, 0 y 7 = domingo)
|
|
483
|
+
* * * * * *
|
|
477
484
|
\`\`\`
|
|
478
485
|
|
|
486
|
+
Cinco campos, o seis poniendo los segundos adelante. Acepta \`*\`, listas \`1,15\`,
|
|
487
|
+
rangos \`1-5\`, pasos \`*/2\`, y nombres de mes y de día.
|
|
488
|
+
|
|
479
489
|
## Ejemplos Comunes
|
|
480
490
|
|
|
481
491
|
| Expresión | Significado |
|
|
@@ -834,7 +844,7 @@ Esta skill se activa cuando el usuario necesita:
|
|
|
834
844
|
description: `Automate web workflows with navigation, clicks, form filling, and visual verification`,
|
|
835
845
|
category: "web",
|
|
836
846
|
version: "1.0.0",
|
|
837
|
-
tools: ["browser_navigate","browser_click","browser_type","browser_screenshot"],
|
|
847
|
+
tools: ["browser_navigate","browser_wait","browser_click","browser_type","browser_screenshot","browser_script","computer_use_task"],
|
|
838
848
|
triggers: ["automatizá el navegador","automate browser","completá el formulario","fill form","hacé clic en","click on","iniciá sesión","login","registrate","sign up","interactuá con la web","interact with website","flujo web","web workflow"],
|
|
839
849
|
body: `
|
|
840
850
|
# Browser Automate Skill
|
|
@@ -879,7 +889,7 @@ Esta skill se activa para automatizar flujos de interacción con aplicaciones we
|
|
|
879
889
|
description: `Navigate to web pages and capture rendered content including screenshots for dynamic sites`,
|
|
880
890
|
category: "web",
|
|
881
891
|
version: "1.0.0",
|
|
882
|
-
tools: ["browser_navigate","browser_screenshot","
|
|
892
|
+
tools: ["browser_navigate","browser_screenshot","browser_extract","browser_wait"],
|
|
883
893
|
triggers: ["capturá el contenido","scrape content","obtené la página renderizada","get rendered page","sitios dinámicos","dynamic sites","web con javascript","javascript websites","tomá screenshot y contenido","screenshot and content"],
|
|
884
894
|
body: `
|
|
885
895
|
# Browser Scrape Skill
|
|
@@ -894,7 +904,12 @@ Esta skill se activa para sitios web dinámicos que requieren JavaScript renderi
|
|
|
894
904
|
|------|----------|---------------|
|
|
895
905
|
| \`browser_navigate\` | Navega y renderiza página completa | Sitios con JavaScript/SPA |
|
|
896
906
|
| \`browser_screenshot\` | Captura estado visual | Evidencia de contenido renderizado |
|
|
897
|
-
| \`
|
|
907
|
+
| \`browser_extract\` | Extrae del DOM ya renderizado | Contenido textual o estructurado del SPA |
|
|
908
|
+
| \`browser_wait\` | Espera a que aparezca un selector | Antes de extraer, en páginas que cargan por partes |
|
|
909
|
+
|
|
910
|
+
> **No usar \`web_fetch\` acá.** Vuelve a pedir la URL al servidor y recibe el HTML
|
|
911
|
+
> sin JavaScript ejecutado — en un SPA, una cáscara vacía. Para eso está
|
|
912
|
+
> \`browser_extract\`, que lee el DOM que el navegador ya renderizó.
|
|
898
913
|
|
|
899
914
|
## Workflow
|
|
900
915
|
|
|
@@ -1430,6 +1445,89 @@ Para crear flujos interactivos multi-paso usando A2UI v0.9. Usar cuando se neces
|
|
|
1430
1445
|
- Agregar validación con \`checks\` en TextField
|
|
1431
1446
|
- Mantener el estado del flujo en el data model (\`/data/step\`, \`/data/serviceType\`, etc.)
|
|
1432
1447
|
- Eliminar surfaces con \`a2ui_delete_surface\` al completar o cancelar
|
|
1448
|
+
`,
|
|
1449
|
+
},
|
|
1450
|
+
{
|
|
1451
|
+
name: "image_editor",
|
|
1452
|
+
description: `Convertir, redimensionar y rotar imágenes con Bun.Image. Inspeccionar dimensiones y formato sin cargar la imagen al contexto.`,
|
|
1453
|
+
category: "images",
|
|
1454
|
+
version: "1.0.0",
|
|
1455
|
+
tools: ["image_metadata","image_transform","artifact_inspect"],
|
|
1456
|
+
triggers: ["convertí la imagen","convert image","redimensioná la imagen","resize image","achicá la foto","make it smaller","pasala a webp","convert to webp","rotá la imagen","rotate image","qué tamaño tiene","image dimensions","comprimí la imagen","compress image","hacé una miniatura","make a thumbnail"],
|
|
1457
|
+
body: `
|
|
1458
|
+
# Image Editor Skill
|
|
1459
|
+
|
|
1460
|
+
## Cuándo se Activa
|
|
1461
|
+
|
|
1462
|
+
Cuando el usuario quiere **cambiar** una imagen (formato, tamaño, rotación) o
|
|
1463
|
+
**saber** sus características. Corre sobre \`Bun.Image\`, nativo del runtime.
|
|
1464
|
+
|
|
1465
|
+
## Herramientas Disponibles
|
|
1466
|
+
|
|
1467
|
+
| Tool | Qué hace | Cuándo usarla |
|
|
1468
|
+
|------|----------|---------------|
|
|
1469
|
+
| \`image_metadata\` | Ancho, alto y formato | Siempre antes de transformar |
|
|
1470
|
+
| \`image_transform\` | Convierte, redimensiona, rota | El trabajo en sí |
|
|
1471
|
+
| \`artifact_inspect\` | Tipo MIME, integridad, tamaño | Cuando no está claro si el artefacto es una imagen |
|
|
1472
|
+
|
|
1473
|
+
## Lo Que Hay Que Entender
|
|
1474
|
+
|
|
1475
|
+
**Todo pasa por artefactos.** Una imagen no viaja en el mensaje: vive como
|
|
1476
|
+
artefacto y se la nombra por su \`artifact_id\`. Es lo que evita que una foto de 4
|
|
1477
|
+
MB entre a la ventana de contexto y se reenvíe en cada turno de la conversación.
|
|
1478
|
+
|
|
1479
|
+
**Transformar no destruye.** \`image_transform\` devuelve un artefacto **nuevo**.
|
|
1480
|
+
El original sigue disponible, así que se puede probar un tamaño, ver que no
|
|
1481
|
+
gustó y probar otro sin haber perdido nada.
|
|
1482
|
+
|
|
1483
|
+
**La proporción se pierde en silencio.** Si se pasan \`width\` y \`height\` juntos,
|
|
1484
|
+
la imagen se estira sin avisar. Pasando uno solo, el otro se calcula.
|
|
1485
|
+
|
|
1486
|
+
## Formatos
|
|
1487
|
+
|
|
1488
|
+
\`jpeg\` · \`png\` · \`webp\` · \`avif\` · \`heic\`
|
|
1489
|
+
|
|
1490
|
+
Ante la duda, **webp**: pesa bastante menos que jpeg y png a calidad comparable,
|
|
1491
|
+
y lo entienden todos los navegadores actuales.
|
|
1492
|
+
`,
|
|
1493
|
+
},
|
|
1494
|
+
{
|
|
1495
|
+
name: "artifact_reader",
|
|
1496
|
+
description: `Leer archivos grandes que llegaron como artifact_ref: por tramos o buscando dentro, sin volcarlos enteros al contexto.`,
|
|
1497
|
+
category: "artifacts",
|
|
1498
|
+
version: "1.0.0",
|
|
1499
|
+
tools: ["artifact_read","artifact_inspect"],
|
|
1500
|
+
triggers: ["leé el archivo adjunto","read the attachment","qué dice el documento","what does the document say","buscá en el archivo","search in the file","artifact_ref","el resultado quedó truncado","the result was truncated","seguí leyendo","keep reading"],
|
|
1501
|
+
body: `
|
|
1502
|
+
# Artifact Reader Skill
|
|
1503
|
+
|
|
1504
|
+
## Cuándo se Activa
|
|
1505
|
+
|
|
1506
|
+
Cuando aparece un **\`artifact_ref\`**: un archivo, un adjunto o el resultado de
|
|
1507
|
+
una tool que era demasiado grande para entrar en el contexto.
|
|
1508
|
+
|
|
1509
|
+
## Herramientas Disponibles
|
|
1510
|
+
|
|
1511
|
+
| Tool | Qué hace | Cuándo usarla |
|
|
1512
|
+
|------|----------|---------------|
|
|
1513
|
+
| \`artifact_inspect\` | Tamaño, tipo MIME, integridad | Antes de leer, para decidir cómo |
|
|
1514
|
+
| \`artifact_read\` | Contenido, por tramos o buscando | Para leer de verdad |
|
|
1515
|
+
|
|
1516
|
+
## Lo Que Hay Que Entender
|
|
1517
|
+
|
|
1518
|
+
**Un \`artifact_ref\` no es un error.** Es el mecanismo por el que un archivo
|
|
1519
|
+
grande queda fuera de la ventana de contexto y a la vez disponible. El archivo
|
|
1520
|
+
está entero; lo que cambia es que se lee a pedido en vez de entrar completo en
|
|
1521
|
+
cada turno de la conversación.
|
|
1522
|
+
|
|
1523
|
+
**Buscar cuesta mucho menos que paginar.** \`artifact_read\` con \`search\` recorre
|
|
1524
|
+
el archivo del lado del servidor y devuelve extractos de cada coincidencia con
|
|
1525
|
+
su contexto alrededor. Paginar el mismo archivo con \`offset\`/\`limit\` gasta un
|
|
1526
|
+
turno por tramo y mete en el contexto un montón de texto que no hacía falta.
|
|
1527
|
+
Cuando se sabe qué se busca, se busca.
|
|
1528
|
+
|
|
1529
|
+
**Continuar es explícito.** Cada lectura devuelve \`next_offset\`. Ese es el valor
|
|
1530
|
+
que se pasa para seguir — no se calcula a mano.
|
|
1433
1531
|
`,
|
|
1434
1532
|
},
|
|
1435
1533
|
];
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { getHiveDb, getOpenHiveDb } from "./hivedb.ts";
|
|
15
15
|
import { col } from "./hive.ts";
|
|
16
|
-
import { seedAllData } from "./seed.ts";
|
|
16
|
+
import { seedAllData, type SeedOptions } from "./seed.ts";
|
|
17
17
|
import { ensureSecretsBackend } from "./crypto.ts";
|
|
18
18
|
import { ensureLegacyThread } from "../agent/thread-store.ts";
|
|
19
19
|
|
|
@@ -106,6 +106,10 @@ const INDEXES: IndexSpec[] = [
|
|
|
106
106
|
{ collection: "conversationThreads", field: "user_id" },
|
|
107
107
|
{ collection: "conversationThreads", field: "channel" },
|
|
108
108
|
{ collection: "conversationThreads", field: "archived" },
|
|
109
|
+
{ collection: "swarms", field: "enabled" },
|
|
110
|
+
{ collection: "apiEndpoints", field: "enabled" },
|
|
111
|
+
{ collection: "memory", field: "user_id" },
|
|
112
|
+
{ collection: "playbook", field: "user_id" },
|
|
109
113
|
];
|
|
110
114
|
|
|
111
115
|
async function ensureIndexes(): Promise<void> {
|
|
@@ -123,8 +127,8 @@ async function ensureIndexes(): Promise<void> {
|
|
|
123
127
|
* user-toggleable fields (enabled/active) and doesn't touch `users`, so it's
|
|
124
128
|
* safe to call unconditionally before any onboarding has happened.
|
|
125
129
|
*/
|
|
126
|
-
async function ensureSeedData(): Promise<void> {
|
|
127
|
-
await seedAllData();
|
|
130
|
+
async function ensureSeedData(opts?: SeedOptions): Promise<void> {
|
|
131
|
+
await seedAllData(opts);
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
// Bootstrap state belongs to a specific database instance. A boolean that
|
|
@@ -138,6 +142,69 @@ let bootstrappedDb: Awaited<ReturnType<typeof getHiveDb>> | null = null;
|
|
|
138
142
|
* mensaje— como una conversación más de la web, para que siga siendo legible desde
|
|
139
143
|
* la lista. Idempotente: no hace nada si ya está registrada o si no hay historial.
|
|
140
144
|
*/
|
|
145
|
+
/**
|
|
146
|
+
* Reasigna las memorias anteriores al aislamiento por usuario.
|
|
147
|
+
*
|
|
148
|
+
* Antes la colección era global: `id` era el título y no había `user_id`. Al
|
|
149
|
+
* introducir el aislamiento esas filas quedarían invisibles —nadie las
|
|
150
|
+
* encontraría, porque toda lectura filtra por dueño— así que se les asigna el
|
|
151
|
+
* usuario existente y se re-clavean a `${userId}:${title}`.
|
|
152
|
+
*
|
|
153
|
+
* Idempotente: una fila que ya tiene `user_id` no se toca. Si todavía no hay
|
|
154
|
+
* usuario (onboarding sin terminar) se deja para el próximo arranque, cuando lo
|
|
155
|
+
* haya, en vez de asignarlas a `""` y tener que deshacerlo.
|
|
156
|
+
*/
|
|
157
|
+
async function migrateLegacyMemories(): Promise<void> {
|
|
158
|
+
try {
|
|
159
|
+
const memories = await col<{ id: string; user_id?: string; title: string }>("memory");
|
|
160
|
+
const legacy = (await memories.scan({})).filter((e) => !e.doc.user_id);
|
|
161
|
+
if (legacy.length === 0) return;
|
|
162
|
+
|
|
163
|
+
const users = await col<{ id: string }>("users");
|
|
164
|
+
const primero = (await users.scan({ limit: 1 }))[0];
|
|
165
|
+
if (!primero) return;
|
|
166
|
+
|
|
167
|
+
for (const entry of legacy) {
|
|
168
|
+
const nuevoId = `${primero.id}:${entry.doc.title}`;
|
|
169
|
+
await memories.put(nuevoId, { ...entry.doc, id: nuevoId, user_id: primero.id });
|
|
170
|
+
if (nuevoId !== entry.id) await memories.delete(entry.id).catch(() => {});
|
|
171
|
+
}
|
|
172
|
+
} catch {
|
|
173
|
+
// La memoria no es crítica para arrancar: si falla, se reintenta al próximo boot.
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Reglas del playbook anteriores a `user_id`.
|
|
179
|
+
*
|
|
180
|
+
* Se aprendieron cuando la instalación era de un solo usuario, así que son de
|
|
181
|
+
* él. Dejarlas sin dueño las volvería globales y se las inyectaría a cualquier
|
|
182
|
+
* usuario que se dé de alta después, que es exactamente el aislamiento que este
|
|
183
|
+
* campo viene a cerrar. Las sembradas no pasan por acá: `seedAllData()` les
|
|
184
|
+
* pone `user_id: ""` en cada arranque.
|
|
185
|
+
*/
|
|
186
|
+
async function migrateLegacyPlaybook(): Promise<void> {
|
|
187
|
+
try {
|
|
188
|
+
const playbook = await col<{ id: string; user_id?: string }>("playbook");
|
|
189
|
+
const legacy = (await playbook.scan({})).filter((e) => e.doc.user_id === undefined);
|
|
190
|
+
if (legacy.length === 0) return;
|
|
191
|
+
|
|
192
|
+
const users = await col<{ id: string }>("users");
|
|
193
|
+
const primero = (await users.scan({ limit: 1 }))[0];
|
|
194
|
+
if (!primero) return;
|
|
195
|
+
|
|
196
|
+
for (const entry of legacy) {
|
|
197
|
+
await playbook.put(
|
|
198
|
+
entry.id,
|
|
199
|
+
{ ...entry.doc, user_id: primero.id },
|
|
200
|
+
{ expectedVersion: entry.version },
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
} catch {
|
|
204
|
+
// El playbook no es crítico para arrancar: se reintenta al próximo boot.
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
141
208
|
async function ensureLegacyThreads(): Promise<void> {
|
|
142
209
|
try {
|
|
143
210
|
const users = await col<{ id: string }>("users");
|
|
@@ -154,16 +221,18 @@ async function ensureLegacyThreads(): Promise<void> {
|
|
|
154
221
|
* static catalogs, and records the schema version. Safe to call on every
|
|
155
222
|
* gateway boot.
|
|
156
223
|
*/
|
|
157
|
-
export async function ensureHiveDb(): Promise<void> {
|
|
224
|
+
export async function ensureHiveDb(opts?: SeedOptions): Promise<void> {
|
|
158
225
|
const db = await getHiveDb();
|
|
159
226
|
await ensureIndexes();
|
|
160
227
|
// Mint the master key before anything can save an API key, so a fresh
|
|
161
228
|
// install is durable from the first keystroke rather than after a restart
|
|
162
229
|
// has already dropped the secret.
|
|
163
230
|
ensureSecretsBackend();
|
|
164
|
-
await ensureSeedData();
|
|
231
|
+
await ensureSeedData(opts);
|
|
165
232
|
|
|
166
233
|
await ensureLegacyThreads();
|
|
234
|
+
await migrateLegacyMemories();
|
|
235
|
+
await migrateLegacyPlaybook();
|
|
167
236
|
|
|
168
237
|
const meta = await col<{ value: number }>("meta");
|
|
169
238
|
const existing = await meta.get("schemaVersion");
|
|
@@ -351,6 +351,8 @@ export interface TraceDoc {
|
|
|
351
351
|
export interface ReflectionDoc {
|
|
352
352
|
id: string
|
|
353
353
|
trace_ids: string
|
|
354
|
+
/** Usuario del que salieron las trazas. `""` cuando abarca varios. */
|
|
355
|
+
user_id: string
|
|
354
356
|
insight_type: "success_pattern" | "failure_pattern" | "optimization" | "ethics_violation" | "root_cause" | "learning_proposal"
|
|
355
357
|
description: string
|
|
356
358
|
affected_tools: string | null
|
|
@@ -363,6 +365,16 @@ export interface PlaybookDoc {
|
|
|
363
365
|
id: string
|
|
364
366
|
rule: string
|
|
365
367
|
category: string
|
|
368
|
+
/**
|
|
369
|
+
* De quién es lo aprendido. `""` = global, que es lo que corresponde a las
|
|
370
|
+
* reglas sembradas: son conocimiento del producto, no de nadie en particular.
|
|
371
|
+
*
|
|
372
|
+
* Sin esto el playbook era global a la instalación, así que **lo que el agente
|
|
373
|
+
* aprendía interactuando con una persona se le aplicaba a todas**. En una
|
|
374
|
+
* instalación de un solo usuario da igual; en un host multi-inquilino es una
|
|
375
|
+
* fuga entre workspaces.
|
|
376
|
+
*/
|
|
377
|
+
user_id: string
|
|
366
378
|
applicable_to: string | null
|
|
367
379
|
helpful_count: number
|
|
368
380
|
harmful_count: number
|
|
@@ -521,7 +533,15 @@ export interface ArtifactDoc {
|
|
|
521
533
|
height: number | null
|
|
522
534
|
status: "active" | "expired"
|
|
523
535
|
created_at: number
|
|
524
|
-
|
|
536
|
+
/**
|
|
537
|
+
* `null` = no expira nunca.
|
|
538
|
+
*
|
|
539
|
+
* Los artefactos internos —capturas, resultados de tools grandes— son basura
|
|
540
|
+
* transitoria y se limpian solos a los 7 días. Pero un archivo que el usuario
|
|
541
|
+
* subió o transformó no es basura: borrárselo a la semana convierte un
|
|
542
|
+
* servicio en una pérdida de datos. Quien lo crea decide.
|
|
543
|
+
*/
|
|
544
|
+
expires_at: number | null
|
|
525
545
|
expired_at: number | null
|
|
526
546
|
}
|
|
527
547
|
|
|
@@ -552,8 +572,21 @@ export interface TaskRunDoc {
|
|
|
552
572
|
// ─── Stage 6: orchestration ───────────────────────────────────────────────────
|
|
553
573
|
|
|
554
574
|
/** id = title — the old `notes`/`memory_*` table never existed, so this is a from-scratch fix. */
|
|
575
|
+
/**
|
|
576
|
+
* Memoria de largo plazo de un usuario.
|
|
577
|
+
*
|
|
578
|
+
* `id` es `${user_id}:${title}`. Hasta acá era sólo el título y la colección era
|
|
579
|
+
* global al proceso —coherente con que hive es mono-usuario, pero inservible
|
|
580
|
+
* para un runtime donde cada quien arma su colmena: dos usuarios no podían
|
|
581
|
+
* tener una memoria con el mismo título, y cualquiera veía la del otro.
|
|
582
|
+
*
|
|
583
|
+
* Las filas anteriores (id = título, sin `user_id`) se migran al arrancar
|
|
584
|
+
* asignándolas al usuario existente. Ver `migrateLegacyMemories`.
|
|
585
|
+
*/
|
|
555
586
|
export interface MemoryDoc {
|
|
556
587
|
id: string
|
|
588
|
+
/** Dueño de la memoria. Las filas legacy se migran al usuario único. */
|
|
589
|
+
user_id: string
|
|
557
590
|
title: string
|
|
558
591
|
content: string
|
|
559
592
|
created_at: number
|
|
@@ -613,6 +646,78 @@ export interface DelegationGroupDoc {
|
|
|
613
646
|
notified_at: number | null
|
|
614
647
|
}
|
|
615
648
|
|
|
649
|
+
/**
|
|
650
|
+
* Un enjambre guardado: qué agentes lo componen, con qué rol y en qué orden.
|
|
651
|
+
*
|
|
652
|
+
* Hasta acá un enjambre sólo existía mientras corría — `runRoleSwarm()` recibe
|
|
653
|
+
* los agentes en la llamada y no persiste nada. Quien armara uno desde una
|
|
654
|
+
* interfaz lo perdía al cerrar la ventana, y por eso hive-cloud terminó creando
|
|
655
|
+
* sus propias tablas en Postgres: no le quedaba alternativa.
|
|
656
|
+
*
|
|
657
|
+
* `agents_json` guarda `[{agentId, role, orderIndex}]`. Se deja como JSON y no
|
|
658
|
+
* como colección aparte a propósito: un enjambre se lee y se escribe entero,
|
|
659
|
+
* nunca por agente suelto, así que una tabla de unión sólo agregaría joins.
|
|
660
|
+
*/
|
|
661
|
+
/**
|
|
662
|
+
* Un endpoint HTTP registrado como herramienta.
|
|
663
|
+
*
|
|
664
|
+
* Una tool normal es código con un `execute`, así que desde una interfaz no hay
|
|
665
|
+
* dónde ponerlo. Un endpoint declarativo invierte eso: el usuario aporta
|
|
666
|
+
* **datos** —URL, método, cabeceras, qué parámetros acepta— y el ejecutor es
|
|
667
|
+
* genérico y vive en el SDK. Es la única forma de que alguien sume una
|
|
668
|
+
* capacidad propia desde una UI sin abrir la puerta a ejecutar código
|
|
669
|
+
* arbitrario, y sin levantar un servidor MCP.
|
|
670
|
+
*
|
|
671
|
+
* Las credenciales NO viven acá: van cifradas en el secret store bajo
|
|
672
|
+
* `endpoint:<id>:headers`, igual que las de proveedores y canales. Esta fila es
|
|
673
|
+
* pública y se muestra en la UI; la clave, no.
|
|
674
|
+
*/
|
|
675
|
+
export interface ApiEndpointDoc {
|
|
676
|
+
id: string
|
|
677
|
+
name: string
|
|
678
|
+
description: string
|
|
679
|
+
method: string
|
|
680
|
+
url: string
|
|
681
|
+
/** Cabeceras no sensibles. Las que llevan credenciales van al secret store. */
|
|
682
|
+
headers_json: string | null
|
|
683
|
+
/** Query fijos que siempre acompañan la llamada. */
|
|
684
|
+
query_json: string | null
|
|
685
|
+
/**
|
|
686
|
+
* Cuerpo con marcadores `{{param}}` que se reemplazan por lo que pase el
|
|
687
|
+
* modelo. Sin esto un endpoint POST sólo serviría con cuerpo fijo.
|
|
688
|
+
*/
|
|
689
|
+
body_template: string | null
|
|
690
|
+
/** JSON Schema de lo que el modelo puede pasar — lo que ve como parámetros. */
|
|
691
|
+
param_schema_json: string | null
|
|
692
|
+
enabled: boolean
|
|
693
|
+
created_at: number
|
|
694
|
+
updated_at: number
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
export interface SwarmDoc {
|
|
698
|
+
id: string
|
|
699
|
+
name: string
|
|
700
|
+
description: string | null
|
|
701
|
+
/** Cómo se coordinan: en cadena, en paralelo, o con un orquestador que delega. */
|
|
702
|
+
strategy: "sequential" | "parallel" | "hierarchical"
|
|
703
|
+
/** Requerido por `hierarchical`; null en las otras dos. */
|
|
704
|
+
orchestrator_agent_id: string | null
|
|
705
|
+
/** `[{ agentId, role, orderIndex }]` — ver SwarmMemberSpec. */
|
|
706
|
+
agents_json: string
|
|
707
|
+
enabled: boolean
|
|
708
|
+
/** Tope de delegaciones en la estrategia jerárquica; null = el del runtime. */
|
|
709
|
+
max_delegations: number | null
|
|
710
|
+
created_at: number
|
|
711
|
+
updated_at: number
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/** Un integrante del enjambre, tal como se serializa en `SwarmDoc.agents_json`. */
|
|
715
|
+
export interface SwarmMemberSpec {
|
|
716
|
+
agentId: string
|
|
717
|
+
role: "orchestrator" | "worker"
|
|
718
|
+
orderIndex: number
|
|
719
|
+
}
|
|
720
|
+
|
|
616
721
|
export interface AgentBusMessageDoc {
|
|
617
722
|
id: string
|
|
618
723
|
event_type: string
|
|
@@ -29,6 +29,22 @@ interface SecretDoc {
|
|
|
29
29
|
const _mem = new Map<string, string>()
|
|
30
30
|
let _keychainOk: boolean | null = null // null = untested
|
|
31
31
|
|
|
32
|
+
let _keychainApi: unknown = undefined
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A test double or a recovered runtime may replace Bun.secrets. Reset the
|
|
36
|
+
* cached availability result when that API object changes so a previous
|
|
37
|
+
* headless failure cannot poison the replacement backend forever.
|
|
38
|
+
*/
|
|
39
|
+
function _getKeychainApi(): any {
|
|
40
|
+
const api = (Bun as any).secrets
|
|
41
|
+
if (api !== _keychainApi) {
|
|
42
|
+
_keychainApi = api
|
|
43
|
+
_keychainOk = null
|
|
44
|
+
}
|
|
45
|
+
return api
|
|
46
|
+
}
|
|
47
|
+
|
|
32
48
|
async function _get(name: string): Promise<string | null> {
|
|
33
49
|
const cached = _mem.get(name)
|
|
34
50
|
if (cached !== undefined) return cached
|
|
@@ -82,11 +98,10 @@ async function _readCollectionSecret(name: string): Promise<string | null> {
|
|
|
82
98
|
/**
|
|
83
99
|
* Olvida si el keychain del SO respondió o no.
|
|
84
100
|
*
|
|
85
|
-
* `
|
|
86
|
-
*
|
|
87
|
-
* que
|
|
88
|
-
*
|
|
89
|
-
* antes y falló. Resetear acá lo vuelve a dejar sin probar.
|
|
101
|
+
* Sustituir `Bun.secrets` por un doble ya NO necesita esto: `_getKeychainApi()`
|
|
102
|
+
* detecta que el objeto cambió e invalida el sondeo solo. Queda para el caso
|
|
103
|
+
* que aquello no cubre —vaciar además el caché en memoria (`_mem`)— y porque
|
|
104
|
+
* ya salió publicado.
|
|
90
105
|
*/
|
|
91
106
|
export function resetKeychainProbe(): void {
|
|
92
107
|
_keychainOk = null
|
|
@@ -94,9 +109,10 @@ export function resetKeychainProbe(): void {
|
|
|
94
109
|
}
|
|
95
110
|
|
|
96
111
|
async function _keychainGet(name: string): Promise<string | null> {
|
|
112
|
+
const keychain = _getKeychainApi()
|
|
97
113
|
if (_keychainOk === false) return null
|
|
98
114
|
try {
|
|
99
|
-
const val = await
|
|
115
|
+
const val = await keychain.get({ service: SERVICE, name })
|
|
100
116
|
_keychainOk = true
|
|
101
117
|
return val ?? null
|
|
102
118
|
} catch {
|
|
@@ -106,9 +122,10 @@ async function _keychainGet(name: string): Promise<string | null> {
|
|
|
106
122
|
}
|
|
107
123
|
|
|
108
124
|
async function _keychainSet(name: string, value: string): Promise<boolean> {
|
|
125
|
+
const keychain = _getKeychainApi()
|
|
109
126
|
if (_keychainOk === false) return false
|
|
110
127
|
try {
|
|
111
|
-
await
|
|
128
|
+
await keychain.set({ service: SERVICE, name, value })
|
|
112
129
|
_keychainOk = true
|
|
113
130
|
return true
|
|
114
131
|
} catch {
|
|
@@ -34,7 +34,7 @@ export type * from "./collections.ts";
|
|
|
34
34
|
export { catalogModelKey, wireModelId, isResellerProvider } from "./model-id.ts";
|
|
35
35
|
|
|
36
36
|
// ─── Seed del catálogo ───────────────────────────────────────────────────────
|
|
37
|
-
export type { SeedData } from "./seed.ts";
|
|
37
|
+
export type { SeedData, SeedOptions, SpecialistSeedMode } from "./seed.ts";
|
|
38
38
|
export {
|
|
39
39
|
SEED_DATA,
|
|
40
40
|
seedAllData,
|
|
@@ -88,6 +88,7 @@ export {
|
|
|
88
88
|
|
|
89
89
|
// ─── Onboarding e identidad ──────────────────────────────────────────────────
|
|
90
90
|
export type { OnboardingSection } from "./onboarding.ts";
|
|
91
|
+
export { activateBrowserTools } from "./onboarding.ts";
|
|
91
92
|
export {
|
|
92
93
|
resolveUserId,
|
|
93
94
|
resolveAgentId,
|