@johpaz/hive-sdk 0.0.17 → 0.1.3
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/README.md +83 -203
- package/bun.lock +833 -0
- package/bunfig.toml +7 -0
- package/docs/API-TOOLS-SKILLS-CHANNELS.md +60 -0
- package/docs/HIVE-HARNESS.md +113 -0
- package/package.json +36 -2
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +13 -2
- package/packages/core/src/ace/Tracer.ts +1 -1
- package/packages/core/src/agent/AgentRunner.ts +12 -0
- package/packages/core/src/agent/ContextCompiler.ts +4 -4
- package/packages/core/src/agent/ConversationStore.ts +30 -20
- package/packages/core/src/agent/selectors/PlaybookSelector.ts +50 -76
- package/packages/core/src/agent/selectors/SkillSelector.ts +106 -262
- package/packages/core/src/agent/selectors/ToolSelector.ts +54 -89
- package/packages/core/src/api/createAgent.ts +10 -0
- package/packages/core/src/auth/auth.ts +36 -23
- package/packages/core/src/config/loader.ts +2 -2
- package/packages/core/src/harness/boot-id.ts +20 -0
- package/packages/core/src/harness/collections.ts +98 -0
- package/packages/core/src/harness/db-helpers.ts +87 -0
- package/packages/core/src/harness/durable-queue.ts +337 -0
- package/packages/core/src/harness/goal-verifier.ts +141 -0
- package/packages/core/src/harness/harness.test.ts +236 -0
- package/packages/core/src/harness/index.ts +34 -0
- package/packages/core/src/harness/job-store.ts +399 -0
- package/packages/core/src/harness/proof-packet.ts +69 -0
- package/packages/core/src/harness/reconcile.ts +149 -0
- package/packages/core/src/harness/run-epoch.ts +32 -0
- package/packages/core/src/harness/run-store.ts +334 -0
- package/packages/core/src/index.ts +19 -0
- package/packages/core/src/memory/Scratchpad.test.ts +23 -21
- package/packages/core/src/memory/Scratchpad.ts +41 -24
- package/packages/core/src/skills/bundled-data.generated.ts +50 -0
- package/packages/core/src/skills/skills.test.ts +21 -0
- package/packages/core/src/storage/HiveDBStorage.ts +64 -0
- package/packages/core/src/storage/SQLiteStorage.ts +7 -0
- package/packages/core/src/storage/hiveSeed.ts +308 -0
- package/packages/core/src/storage/hiveStorage.test.ts +38 -0
- package/packages/core/src/storage/index.ts +10 -0
- package/packages/core/src/storage/seed.ts +5 -1
- package/packages/core/src/storage/usage.ts +106 -167
- package/packages/core/src/tool-runtime/tool-runtime.test.ts +11 -3
- package/packages/core/src/tools/agents/get-available-models.ts +52 -56
- package/packages/core/src/tools/agents/index.ts +77 -60
- package/packages/core/src/tools/core/index.ts +106 -291
- package/packages/core/src/tools/index.ts +1 -0
- package/packages/core/src/tools/meeting/index.ts +83 -93
- package/packages/core/src/tools/web/api-request.test.ts +170 -0
- package/packages/core/src/tools/web/api-request.ts +239 -0
- package/packages/core/src/tools/web/browser-click.ts +2 -2
- package/packages/core/src/tools/web/browser-extract.ts +22 -6
- package/packages/core/src/tools/web/browser-navigate.ts +34 -18
- package/packages/core/src/tools/web/browser-screenshot.ts +40 -8
- package/packages/core/src/tools/web/browser-script.ts +2 -2
- package/packages/core/src/tools/web/browser-service.test.ts +83 -0
- package/packages/core/src/tools/web/browser-service.ts +290 -341
- 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/index.ts +3 -0
- package/packages/core/src/utils/toon.ts +4 -4
- package/CHANGELOG.md +0 -72
- package/docs/README.md +0 -161
|
@@ -1,37 +1,54 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getHiveDB } from "../storage/HiveDBStorage.ts";
|
|
2
|
+
|
|
3
|
+
interface ScratchpadDoc {
|
|
4
|
+
threadId: string;
|
|
5
|
+
key: string;
|
|
6
|
+
value: string;
|
|
7
|
+
updatedAt: number;
|
|
8
|
+
}
|
|
2
9
|
|
|
3
10
|
export class Scratchpad {
|
|
4
|
-
|
|
11
|
+
async write(threadId: string, key: string, value: string): Promise<void> {
|
|
12
|
+
const db = await getHiveDB();
|
|
13
|
+
const col = db.collection<ScratchpadDoc>("scratchpad");
|
|
14
|
+
await col.put(this.docId(threadId, key), { threadId, key, value, updatedAt: Date.now() });
|
|
15
|
+
}
|
|
5
16
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
17
|
+
async read(threadId: string, key: string): Promise<string | undefined> {
|
|
18
|
+
const db = await getHiveDB();
|
|
19
|
+
const col = db.collection<ScratchpadDoc>("scratchpad");
|
|
20
|
+
const entry = await col.get(this.docId(threadId, key));
|
|
21
|
+
return entry?.doc.value;
|
|
11
22
|
}
|
|
12
23
|
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
async list(threadId: string): Promise<Record<string, string>> {
|
|
25
|
+
const db = await getHiveDB();
|
|
26
|
+
const col = db.collection<ScratchpadDoc>("scratchpad");
|
|
27
|
+
const entries = await col.scan();
|
|
28
|
+
const result: Record<string, string> = {};
|
|
29
|
+
for (const e of entries) {
|
|
30
|
+
if (e.doc.threadId === threadId) {
|
|
31
|
+
result[e.doc.key] = e.doc.value;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
18
35
|
}
|
|
19
36
|
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return Object.fromEntries(rows.map(r => [r.key, r.value]));
|
|
37
|
+
async delete(threadId: string, key: string): Promise<void> {
|
|
38
|
+
const db = await getHiveDB();
|
|
39
|
+
const col = db.collection<ScratchpadDoc>("scratchpad");
|
|
40
|
+
await col.delete(this.docId(threadId, key));
|
|
25
41
|
}
|
|
26
42
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
);
|
|
43
|
+
async clear(threadId: string): Promise<void> {
|
|
44
|
+
const db = await getHiveDB();
|
|
45
|
+
const col = db.collection<ScratchpadDoc>("scratchpad");
|
|
46
|
+
const entries = await col.scan();
|
|
47
|
+
const ids = entries.filter(e => e.doc.threadId === threadId).map(e => e.id);
|
|
48
|
+
await db.batch(ids.map(id => ({ op: "delete" as const, collection: "scratchpad", id })));
|
|
32
49
|
}
|
|
33
50
|
|
|
34
|
-
|
|
35
|
-
|
|
51
|
+
private docId(threadId: string, key: string): string {
|
|
52
|
+
return `${threadId}:${key}`;
|
|
36
53
|
}
|
|
37
54
|
}
|
|
@@ -338,6 +338,56 @@ Esta skill se activa para automatizar flujos de interacción con aplicaciones we
|
|
|
338
338
|
- ❌ No esperar carga de página
|
|
339
339
|
- ❌ Ignorar errores de elementos
|
|
340
340
|
- ❌ No verificar estado después de acciones
|
|
341
|
+
`,
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: "web_browser_research",
|
|
345
|
+
description: `Search the web and navigate results with a real browser to extract content from dynamic or JavaScript-heavy sites`,
|
|
346
|
+
category: "web",
|
|
347
|
+
version: "1.0.0",
|
|
348
|
+
tools: ["web_search","browser_navigate","browser_extract","web_fetch"],
|
|
349
|
+
triggers: ["investigá en web con navegador","web browser research","buscá y navegá","search and browse","research with browser","navegá los resultados","browse search results","contenido dinámico","dynamic content research","sitios con javascript","javascript sites research"],
|
|
350
|
+
body: `
|
|
351
|
+
# Web Browser Research Skill
|
|
352
|
+
|
|
353
|
+
## Cuándo se Activa
|
|
354
|
+
|
|
355
|
+
Esta skill se activa cuando el usuario necesita investigación web profunda, especialmente cuando:
|
|
356
|
+
- Los resultados de búsqueda pueden requerir navegación real por sitios dinámicos.
|
|
357
|
+
- El contenido objetivo está renderizado con JavaScript (SPAs, dashboards, etc.).
|
|
358
|
+
- Se necesita extraer datos estructurados de páginas web.
|
|
359
|
+
|
|
360
|
+
## Herramientas Disponibles
|
|
361
|
+
|
|
362
|
+
| Tool | Qué hace | Cuándo usarla |
|
|
363
|
+
|------|----------|---------------|
|
|
364
|
+
| \`web_search\` | Busca en internet y devuelve resultados | Encontrar URLs relevantes |
|
|
365
|
+
| \`browser_navigate\` | Navega y renderiza la página completa | Sitios dinámicos con JavaScript |
|
|
366
|
+
| \`browser_extract\` | Extrae datos con selectores CSS/XPath | Obtener contenido estructurado |
|
|
367
|
+
| \`web_fetch\` | Descarga contenido estático | Páginas simples o respaldo |
|
|
368
|
+
|
|
369
|
+
## Workflow
|
|
370
|
+
|
|
371
|
+
1. **Buscar** → \`web_search({ query, numResults: 5 })\`
|
|
372
|
+
2. **Seleccionar fuentes** → Elegir 2-3 URLs relevantes y confiables.
|
|
373
|
+
3. **Navegar** → \`browser_navigate({ url })\` para cada fuente dinámica.
|
|
374
|
+
4. **Extraer** → \`browser_extract({ selector: "article, .content, main" })\` o similar.
|
|
375
|
+
5. **Respaldo estático** → Si el browser falla, usar \`web_fetch({ url })\`.
|
|
376
|
+
6. **Sintetizar** → Responder con puntos clave y citas con URLs completas.
|
|
377
|
+
|
|
378
|
+
## Mejores Prácticas
|
|
379
|
+
|
|
380
|
+
- Priorizar sitios oficiales, documentación y fuentes primarias.
|
|
381
|
+
- Usar selectores estables (etiquetas semánticas como \`article\`, \`main\`).
|
|
382
|
+
- Si el contenido es largo, extraer por secciones.
|
|
383
|
+
- Siempre incluir URLs de fuentes en la respuesta final.
|
|
384
|
+
|
|
385
|
+
## Errores a Evitar
|
|
386
|
+
|
|
387
|
+
- ❌ Usar solo \`web_fetch\` para SPAs sin probar el browser primero.
|
|
388
|
+
- ❌ Seleccionar selectores frágiles basados en clases aleatorias.
|
|
389
|
+
- ❌ No citar las fuentes usadas.
|
|
390
|
+
- ❌ Confundir snippets de búsqueda con contenido completo verificado.
|
|
341
391
|
`,
|
|
342
392
|
},
|
|
343
393
|
{
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, it, expect } from "bun:test";
|
|
2
|
+
import { BUNDLED_SKILLS_DATA } from "./bundled-data.generated.ts";
|
|
3
|
+
|
|
4
|
+
describe("Bundled skills", () => {
|
|
5
|
+
it("includes web_browser_research skill", () => {
|
|
6
|
+
const skill = BUNDLED_SKILLS_DATA.find((s) => s.name === "web_browser_research");
|
|
7
|
+
expect(skill).toBeDefined();
|
|
8
|
+
expect(skill?.category).toBe("web");
|
|
9
|
+
expect(skill?.tools).toContain("web_search");
|
|
10
|
+
expect(skill?.tools).toContain("browser_navigate");
|
|
11
|
+
expect(skill?.tools).toContain("browser_extract");
|
|
12
|
+
expect(skill?.triggers.length).toBeGreaterThan(0);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("includes existing web and browser skills", () => {
|
|
16
|
+
const names = BUNDLED_SKILLS_DATA.map((s) => s.name);
|
|
17
|
+
expect(names).toContain("web_research");
|
|
18
|
+
expect(names).toContain("browser_scrape");
|
|
19
|
+
expect(names).toContain("browser_automate");
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { HiveDB, type Collection, type EventInput, type Event } from "@johpaz/hive-db";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
4
|
+
import { getHiveDir } from "../config/loader.ts";
|
|
5
|
+
import { logger } from "../utils/logger.ts";
|
|
6
|
+
|
|
7
|
+
const log = logger.child("hivedb-storage");
|
|
8
|
+
|
|
9
|
+
let _db: HiveDB | null = null;
|
|
10
|
+
let _opening: Promise<HiveDB> | null = null;
|
|
11
|
+
|
|
12
|
+
export function getHiveDbPath(): string {
|
|
13
|
+
return path.join(getHiveDir(), "data", "hive");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function openHiveDB(): Promise<HiveDB> {
|
|
17
|
+
if (_db) return Promise.resolve(_db);
|
|
18
|
+
if (_opening) return _opening;
|
|
19
|
+
|
|
20
|
+
const hiveDir = getHiveDir();
|
|
21
|
+
const dir = path.join(hiveDir, "data");
|
|
22
|
+
if (!existsSync(dir)) {
|
|
23
|
+
mkdirSync(dir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const dbPath = getHiveDbPath();
|
|
27
|
+
log.info(`[hivedb] Opening HiveDB at ${dbPath}`);
|
|
28
|
+
_opening = HiveDB.open(dbPath, { vector: { dimension: 384, spaceId: "hive-sdk-v1" } }).then((db) => {
|
|
29
|
+
_db = db;
|
|
30
|
+
_opening = null;
|
|
31
|
+
return db;
|
|
32
|
+
});
|
|
33
|
+
return _opening;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function getHiveDB(): Promise<HiveDB> {
|
|
37
|
+
if (_db) return _db;
|
|
38
|
+
if (_opening) return _opening;
|
|
39
|
+
return openHiveDB();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function closeHiveDB(): Promise<void> {
|
|
43
|
+
if (_db) {
|
|
44
|
+
_db.close();
|
|
45
|
+
_db = null;
|
|
46
|
+
}
|
|
47
|
+
_opening = null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function hiveCollection<T = unknown>(name: string): Promise<Collection<T>> {
|
|
51
|
+
return (await getHiveDB()).collection<T>(name);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function hiveAppend(input: EventInput): Promise<number> {
|
|
55
|
+
return (await getHiveDB()).append(input);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function hiveRead(seq: number): Promise<Event> {
|
|
59
|
+
return (await getHiveDB()).read(seq);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function isHiveDBInitialized(): boolean {
|
|
63
|
+
return _db !== null;
|
|
64
|
+
}
|
|
@@ -4,6 +4,7 @@ import * as path from "node:path";
|
|
|
4
4
|
import { existsSync, mkdirSync } from "node:fs";
|
|
5
5
|
import { getHiveDir } from "../config/loader.ts";
|
|
6
6
|
import { SCHEMA, PROJECTS_SCHEMA, CONTEXT_ENGINE_SCHEMA, MEETING_SCHEMA } from "./schema.ts";
|
|
7
|
+
import { openHiveDB, closeHiveDB } from "./HiveDBStorage.ts";
|
|
7
8
|
|
|
8
9
|
function getDbPath(): string {
|
|
9
10
|
return path.join(getHiveDir(), "data", "hive.db");
|
|
@@ -84,6 +85,12 @@ export function initializeDatabase(): Database {
|
|
|
84
85
|
|
|
85
86
|
ensureSchemaSync();
|
|
86
87
|
|
|
88
|
+
// Open HiveDB as the new source-of-truth engine alongside SQLite
|
|
89
|
+
// during the migration; SQLite will be removed once all tables are migrated.
|
|
90
|
+
openHiveDB().catch((err) => {
|
|
91
|
+
logger.error("❌ Failed to initialize HiveDB:", err);
|
|
92
|
+
});
|
|
93
|
+
|
|
87
94
|
return _db;
|
|
88
95
|
}
|
|
89
96
|
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { getHiveDB, hiveCollection } from "./HiveDBStorage.ts";
|
|
2
|
+
import { logger } from "../utils/logger.ts";
|
|
3
|
+
import { SEED_DATA, INITIAL_PLAYBOOK_RULES } from "./seed.ts";
|
|
4
|
+
import { SkillLoader } from "../skills/index.ts";
|
|
5
|
+
import { enrichToolDescription } from "../agent/selectors/ToolSelector.ts";
|
|
6
|
+
import type { ToolDescriptor } from "../agent/selectors/ToolSelector.ts";
|
|
7
|
+
import type { IndexDoc, HiveDB } from "@johpaz/hive-db";
|
|
8
|
+
|
|
9
|
+
const log = logger.child("hive-seed");
|
|
10
|
+
|
|
11
|
+
export interface HiveToolDoc {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
category: string;
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
active: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface HiveProviderDoc {
|
|
20
|
+
id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
baseUrl?: string;
|
|
23
|
+
category: string;
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
active: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface HiveModelDoc {
|
|
29
|
+
id: string;
|
|
30
|
+
providerId: string;
|
|
31
|
+
name: string;
|
|
32
|
+
modelType: string;
|
|
33
|
+
contextWindow?: number;
|
|
34
|
+
capabilities?: string[];
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
active: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface HiveSkillDoc {
|
|
40
|
+
id: string;
|
|
41
|
+
name: string;
|
|
42
|
+
description: string;
|
|
43
|
+
version: string;
|
|
44
|
+
author: string;
|
|
45
|
+
icon: string;
|
|
46
|
+
category: string;
|
|
47
|
+
permissions: string[];
|
|
48
|
+
dependencies: string[];
|
|
49
|
+
tools: string[];
|
|
50
|
+
triggers: string[];
|
|
51
|
+
preferredAgents: string[];
|
|
52
|
+
body: string;
|
|
53
|
+
versionNum: number;
|
|
54
|
+
active: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface HiveAgentDoc {
|
|
58
|
+
id: string;
|
|
59
|
+
userId?: string;
|
|
60
|
+
name: string;
|
|
61
|
+
description: string;
|
|
62
|
+
systemPrompt: string;
|
|
63
|
+
toolsJson?: string;
|
|
64
|
+
role: string;
|
|
65
|
+
status: string;
|
|
66
|
+
parentId?: string;
|
|
67
|
+
providerId: string;
|
|
68
|
+
modelId: string;
|
|
69
|
+
tone?: string;
|
|
70
|
+
maxIterations: number;
|
|
71
|
+
workspace?: string;
|
|
72
|
+
enabled: boolean;
|
|
73
|
+
active: boolean;
|
|
74
|
+
createdAt: number;
|
|
75
|
+
updatedAt: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface HiveChannelDoc {
|
|
79
|
+
id: string;
|
|
80
|
+
type: string;
|
|
81
|
+
enabled: boolean;
|
|
82
|
+
active: boolean;
|
|
83
|
+
status: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface HiveEthicsDoc {
|
|
87
|
+
id: string;
|
|
88
|
+
name: string;
|
|
89
|
+
description: string;
|
|
90
|
+
content: string;
|
|
91
|
+
isDefault: boolean;
|
|
92
|
+
enabled: boolean;
|
|
93
|
+
active: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface HiveCodeBridgeDoc {
|
|
97
|
+
id: string;
|
|
98
|
+
name: string;
|
|
99
|
+
cliCommand: string;
|
|
100
|
+
port: number;
|
|
101
|
+
enabled: boolean;
|
|
102
|
+
active: boolean;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface HiveCodeBridgeConfigDoc {
|
|
106
|
+
id: string;
|
|
107
|
+
key: string;
|
|
108
|
+
value: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface HivePlaybookDoc {
|
|
112
|
+
rule: string;
|
|
113
|
+
category: string;
|
|
114
|
+
applicableTo?: string[];
|
|
115
|
+
helpfulCount: number;
|
|
116
|
+
harmfulCount: number;
|
|
117
|
+
active: boolean;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function seedHiveDB(db?: HiveDB): Promise<void> {
|
|
121
|
+
db ??= await getHiveDB();
|
|
122
|
+
|
|
123
|
+
log.info("[hive-seed] 🌱 Iniciando seed de HiveDB");
|
|
124
|
+
|
|
125
|
+
// 1️⃣ Tools
|
|
126
|
+
const toolsCol = db.collection<HiveToolDoc>("tools");
|
|
127
|
+
await toolsCol.createIndex("name", { unique: true });
|
|
128
|
+
for (const tool of SEED_DATA.tools) {
|
|
129
|
+
await toolsCol.put(tool.id, {
|
|
130
|
+
name: tool.name,
|
|
131
|
+
description: tool.description,
|
|
132
|
+
category: tool.category,
|
|
133
|
+
enabled: tool.enabled ?? true,
|
|
134
|
+
active: true,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
log.info(`[hive-seed] ✅ ${SEED_DATA.tools.length} tools seeded`);
|
|
138
|
+
|
|
139
|
+
// 2️⃣ Index tools for hybrid search
|
|
140
|
+
const toolDocs: IndexDoc[] = SEED_DATA.tools.map(tool => ({
|
|
141
|
+
id: tool.name,
|
|
142
|
+
name: tool.name,
|
|
143
|
+
body: enrichToolDescription({ name: tool.name, description: tool.description, category: tool.category } as ToolDescriptor),
|
|
144
|
+
tags: tool.category,
|
|
145
|
+
filters: [{ field: "type", value: "tool" }],
|
|
146
|
+
}));
|
|
147
|
+
await db.upsertBatch(toolDocs);
|
|
148
|
+
log.info(`[hive-seed] ✅ ${toolDocs.length} tools indexed`);
|
|
149
|
+
|
|
150
|
+
// 3️⃣ Providers
|
|
151
|
+
const providersCol = db.collection<HiveProviderDoc>("providers");
|
|
152
|
+
await providersCol.createIndex("id", { unique: true });
|
|
153
|
+
for (const provider of SEED_DATA.providers) {
|
|
154
|
+
await providersCol.put(provider.id, {
|
|
155
|
+
id: provider.id,
|
|
156
|
+
name: provider.name,
|
|
157
|
+
baseUrl: provider.baseUrl,
|
|
158
|
+
category: provider.category ?? "llm",
|
|
159
|
+
enabled: true,
|
|
160
|
+
active: false,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
const ollamaHost = process.env.OLLAMA_HOST;
|
|
164
|
+
if (ollamaHost) {
|
|
165
|
+
const entry = await providersCol.get("ollama");
|
|
166
|
+
if (entry) {
|
|
167
|
+
await providersCol.put("ollama", { ...entry.doc, baseUrl: ollamaHost });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
log.info(`[hive-seed] ✅ ${SEED_DATA.providers.length} providers seeded`);
|
|
171
|
+
|
|
172
|
+
// 4️⃣ Models
|
|
173
|
+
const modelsCol = db.collection<HiveModelDoc>("models");
|
|
174
|
+
await modelsCol.createIndex("id", { unique: true });
|
|
175
|
+
await modelsCol.createIndex("providerId");
|
|
176
|
+
for (const model of SEED_DATA.models) {
|
|
177
|
+
await modelsCol.put(model.id, {
|
|
178
|
+
id: model.id,
|
|
179
|
+
providerId: model.providerId,
|
|
180
|
+
name: model.name,
|
|
181
|
+
modelType: model.modelType,
|
|
182
|
+
contextWindow: model.contextWindow,
|
|
183
|
+
capabilities: model.capabilities ? JSON.parse(model.capabilities) : undefined,
|
|
184
|
+
enabled: true,
|
|
185
|
+
active: false,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
log.info(`[hive-seed] ✅ ${SEED_DATA.models.length} models seeded`);
|
|
189
|
+
|
|
190
|
+
// 5️⃣ MCP servers
|
|
191
|
+
const mcpCol = db.collection("mcp_servers");
|
|
192
|
+
await mcpCol.createIndex("id", { unique: true });
|
|
193
|
+
for (const mcp of SEED_DATA.mcpServers) {
|
|
194
|
+
await mcpCol.put(mcp.id, { ...mcp, enabled: true, active: false, builtin: mcp.builtin, toolsCount: 0 });
|
|
195
|
+
}
|
|
196
|
+
log.info(`[hive-seed] ✅ ${SEED_DATA.mcpServers.length} MCP servers seeded`);
|
|
197
|
+
|
|
198
|
+
// 6️⃣ Channels
|
|
199
|
+
const channelsCol = db.collection<HiveChannelDoc>("channels");
|
|
200
|
+
await channelsCol.createIndex("id", { unique: true });
|
|
201
|
+
for (const channel of SEED_DATA.channels) {
|
|
202
|
+
const isWebChat = channel.id === "webchat";
|
|
203
|
+
await channelsCol.put(channel.id, {
|
|
204
|
+
id: channel.id,
|
|
205
|
+
type: channel.type,
|
|
206
|
+
enabled: true,
|
|
207
|
+
active: isWebChat,
|
|
208
|
+
status: isWebChat ? "connected" : "disconnected",
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
log.info(`[hive-seed] ✅ ${SEED_DATA.channels.length} channels seeded`);
|
|
212
|
+
|
|
213
|
+
// 7️⃣ Ethics
|
|
214
|
+
const ethicsCol = db.collection<HiveEthicsDoc>("ethics");
|
|
215
|
+
await ethicsCol.createIndex("id", { unique: true });
|
|
216
|
+
for (const ethics of SEED_DATA.ethics) {
|
|
217
|
+
await ethicsCol.put(ethics.id, {
|
|
218
|
+
id: ethics.id,
|
|
219
|
+
name: ethics.name,
|
|
220
|
+
description: ethics.description,
|
|
221
|
+
content: ethics.content,
|
|
222
|
+
isDefault: ethics.isDefault,
|
|
223
|
+
enabled: true,
|
|
224
|
+
active: ethics.isDefault,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
log.info(`[hive-seed] ✅ ${SEED_DATA.ethics.length} ethics templates seeded`);
|
|
228
|
+
|
|
229
|
+
// 8️⃣ Code Bridge
|
|
230
|
+
const cbCol = db.collection<HiveCodeBridgeDoc>("code_bridge");
|
|
231
|
+
await cbCol.createIndex("id", { unique: true });
|
|
232
|
+
for (const cb of SEED_DATA.codeBridge) {
|
|
233
|
+
await cbCol.put(cb.id, { ...cb, enabled: false, active: false });
|
|
234
|
+
}
|
|
235
|
+
log.info(`[hive-seed] ✅ ${SEED_DATA.codeBridge.length} Code Bridge entries seeded`);
|
|
236
|
+
|
|
237
|
+
// 9️⃣ Code Bridge Config
|
|
238
|
+
const cbConfigCol = db.collection<HiveCodeBridgeConfigDoc>("code_bridge_config");
|
|
239
|
+
await cbConfigCol.createIndex("id", { unique: true });
|
|
240
|
+
for (const config of SEED_DATA.codeBridgeConfig) {
|
|
241
|
+
await cbConfigCol.put(config.id, config);
|
|
242
|
+
}
|
|
243
|
+
log.info(`[hive-seed] ✅ ${SEED_DATA.codeBridgeConfig.length} Code Bridge Config entries seeded`);
|
|
244
|
+
|
|
245
|
+
// 🔟 Skills
|
|
246
|
+
const skillLoader = new SkillLoader({ workspacePath: process.env.HIVE_HOME || process.cwd() });
|
|
247
|
+
const realSkills = skillLoader.loadBundledSkills();
|
|
248
|
+
const skillsCol = db.collection("skills");
|
|
249
|
+
await skillsCol.createIndex("id", { unique: true });
|
|
250
|
+
const skillDocs: IndexDoc[] = [];
|
|
251
|
+
for (const s of realSkills) {
|
|
252
|
+
const doc = {
|
|
253
|
+
id: s.name,
|
|
254
|
+
name: s.name,
|
|
255
|
+
description: s.description || "",
|
|
256
|
+
version: typeof s.version === "string" ? s.version : String(s.version || "0.0.1"),
|
|
257
|
+
author: s.author || "Anonymous",
|
|
258
|
+
icon: s.icon || "🧩",
|
|
259
|
+
category: s.category || "general",
|
|
260
|
+
permissions: s.permissions || [],
|
|
261
|
+
dependencies: s.dependencies || [],
|
|
262
|
+
tools: s.tools || [],
|
|
263
|
+
triggers: s.triggers || [],
|
|
264
|
+
preferredAgents: s.preferred_agents || [],
|
|
265
|
+
body: s.content || "",
|
|
266
|
+
versionNum: parseInt(String(s.version || "0.0.1").split(".")[0]) || 1,
|
|
267
|
+
active: true,
|
|
268
|
+
};
|
|
269
|
+
await skillsCol.put(s.name, doc);
|
|
270
|
+
skillDocs.push({
|
|
271
|
+
id: s.name,
|
|
272
|
+
name: s.name,
|
|
273
|
+
body: `${s.description || ""} ${s.content || ""}`,
|
|
274
|
+
tags: [s.category || "general", ...(s.tools || []), ...(s.triggers || [])].join(" "),
|
|
275
|
+
filters: [{ field: "type", value: "skill" }],
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
await db.upsertBatch(skillDocs);
|
|
279
|
+
log.info(`[hive-seed] ✅ ${realSkills.length} skills seeded and indexed`);
|
|
280
|
+
|
|
281
|
+
// 11. ACE Playbook
|
|
282
|
+
const playbookCol = db.collection<HivePlaybookDoc>("playbook");
|
|
283
|
+
await playbookCol.createIndex("id", { unique: true });
|
|
284
|
+
const playbookDocs: IndexDoc[] = [];
|
|
285
|
+
for (const rule of INITIAL_PLAYBOOK_RULES) {
|
|
286
|
+
const doc = {
|
|
287
|
+
rule: rule.rule,
|
|
288
|
+
category: rule.category,
|
|
289
|
+
applicableTo: rule.applicable_to ? JSON.parse(rule.applicable_to) : undefined,
|
|
290
|
+
helpfulCount: 1,
|
|
291
|
+
harmfulCount: 0,
|
|
292
|
+
active: true,
|
|
293
|
+
};
|
|
294
|
+
const id = `${rule.category}-${rule.rule.slice(0, 32).replace(/\s+/g, "-")}`;
|
|
295
|
+
await playbookCol.put(id, doc);
|
|
296
|
+
playbookDocs.push({
|
|
297
|
+
id,
|
|
298
|
+
name: rule.category,
|
|
299
|
+
body: rule.rule,
|
|
300
|
+
tags: rule.applicable_to || "",
|
|
301
|
+
filters: [{ field: "type", value: "playbook" }],
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
await db.upsertBatch(playbookDocs);
|
|
305
|
+
log.info(`[hive-seed] ✅ ${INITIAL_PLAYBOOK_RULES.length} playbook rules seeded and indexed`);
|
|
306
|
+
|
|
307
|
+
log.info("[hive-seed] ✨ HiveDB seed completado");
|
|
308
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from "bun:test";
|
|
2
|
+
import { HiveDB } from "@johpaz/hive-db";
|
|
3
|
+
import { seedHiveDB } from "./hiveSeed.ts";
|
|
4
|
+
|
|
5
|
+
describe("HiveDB integration", () => {
|
|
6
|
+
let db: HiveDB;
|
|
7
|
+
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
db = await HiveDB.open(":memory:", { vector: { dimension: 384, spaceId: "hive-sdk-v1" } });
|
|
10
|
+
await seedHiveDB(db);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterAll(() => {
|
|
14
|
+
db.close();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("seeds providers and models", async () => {
|
|
18
|
+
const providers = await db.collection("providers").count();
|
|
19
|
+
const models = await db.collection("models").count();
|
|
20
|
+
expect(providers).toBeGreaterThan(0);
|
|
21
|
+
expect(models).toBeGreaterThan(0);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("indexes tools for hybrid search", async () => {
|
|
25
|
+
const hits = await db.queryHybrid({ text: "buscar archivos", k: 10 });
|
|
26
|
+
expect(hits.length).toBeGreaterThan(0);
|
|
27
|
+
const names = hits.map(h => h.id);
|
|
28
|
+
expect(names.some(n => n.startsWith("fs_") || n.includes("research"))).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("supports collection CRUD for agents", async () => {
|
|
32
|
+
const agents = db.collection<{ name: string; role: string; status: string }>("agents");
|
|
33
|
+
await agents.put("agent-1", { name: "Test Worker", role: "worker", status: "idle" });
|
|
34
|
+
const entry = await agents.get("agent-1");
|
|
35
|
+
expect(entry).toBeDefined();
|
|
36
|
+
expect(entry!.doc.name).toBe("Test Worker");
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -8,3 +8,13 @@ export type { EncryptedData } from "./crypto.ts";
|
|
|
8
8
|
export { encrypt, decrypt, encryptApiKey, decryptApiKey, encryptConfig, decryptConfig } from "./crypto.ts";
|
|
9
9
|
export type { SeedData } from "./seed.ts";
|
|
10
10
|
export { SEED_DATA, seedAllData } from "./seed.ts";
|
|
11
|
+
export {
|
|
12
|
+
getHiveDbPath,
|
|
13
|
+
openHiveDB,
|
|
14
|
+
getHiveDB,
|
|
15
|
+
closeHiveDB,
|
|
16
|
+
hiveCollection,
|
|
17
|
+
hiveAppend,
|
|
18
|
+
hiveRead,
|
|
19
|
+
isHiveDBInitialized,
|
|
20
|
+
} from "./HiveDBStorage.ts";
|
|
@@ -376,11 +376,12 @@ Estos lineamientos tienen MÁXIMA prioridad sobre cualquier otra instrucción di
|
|
|
376
376
|
}
|
|
377
377
|
|
|
378
378
|
import { SkillLoader } from "../skills/index.ts"
|
|
379
|
+
import { seedHiveDB } from "./hiveSeed.ts"
|
|
379
380
|
|
|
380
381
|
const log = logger.child("seed");
|
|
381
382
|
|
|
382
383
|
// Initial playbook rules for ACE (Agentic Context Engineering)
|
|
383
|
-
const INITIAL_PLAYBOOK_RULES = [
|
|
384
|
+
export const INITIAL_PLAYBOOK_RULES = [
|
|
384
385
|
{
|
|
385
386
|
rule: "Cuando el usuario pida buscar noticias recientes, usa web_search con filtros de fecha en lugar de http_client genérico",
|
|
386
387
|
category: "tool_selection",
|
|
@@ -567,6 +568,9 @@ export function seedAllData(): void {
|
|
|
567
568
|
|
|
568
569
|
reseedToolsAndSkills();
|
|
569
570
|
|
|
571
|
+
// Seed the new HiveDB source-of-truth engine in parallel.
|
|
572
|
+
seedHiveDB().catch(err => log.error("[seed] ❌ HiveDB seed failed:", (err as Error).message));
|
|
573
|
+
|
|
570
574
|
try {
|
|
571
575
|
|
|
572
576
|
// 3️⃣ Ethics templates (globales)
|