@johpaz/hive-sdk 0.1.6 → 0.2.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 +135 -0
- package/README.md +11 -1
- package/package.json +18 -2
- package/packages/core/src/agent/acceptance-checks.ts +9 -9
- package/packages/core/src/agent/agent-catalog.ts +3 -3
- 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 +10 -9
- package/packages/core/src/agent/context-compiler.ts +58 -34
- package/packages/core/src/agent/conversation-store.ts +31 -7
- package/packages/core/src/agent/curator.ts +4 -4
- 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 +1 -1
- package/packages/core/src/agent/llm-providers/ollama.ts +4 -4
- package/packages/core/src/agent/llm-providers/openai-compat-base.ts +9 -5
- 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 +4 -4
- 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 +4 -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 +6 -6
- 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 +250 -0
- package/packages/core/src/agent/tool-selector.ts +7 -5
- package/packages/core/src/agent/tracer.ts +5 -5
- package/packages/core/src/api/createAgent.ts +1 -1
- package/packages/core/src/artifacts/store.ts +84 -3
- package/packages/core/src/canvas/emitter.ts +2 -2
- 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/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/narration.ts +3 -3
- package/packages/core/src/gateway/delegation-groups.ts +4 -4
- package/packages/core/src/gateway/durable-queue.ts +5 -5
- package/packages/core/src/gateway/job-store.ts +5 -5
- package/packages/core/src/gateway/notification-inbox.ts +2 -2
- package/packages/core/src/gateway/server.ts +2 -2
- 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 +6 -6
- package/packages/core/src/plugins/loader.ts +4 -1
- package/packages/core/src/resilience/circuit-breaker.ts +16 -5
- package/packages/core/src/resilience/retry.ts +1 -1
- package/packages/core/src/scheduler/CronScheduler.ts +6 -6
- package/packages/core/src/scheduler/integration.ts +9 -9
- package/packages/core/src/sessions/index.ts +266 -0
- package/packages/core/src/storage/bootstrap.ts +34 -8
- package/packages/core/src/storage/causal-events.ts +1 -1
- package/packages/core/src/storage/collections.ts +32 -1
- package/packages/core/src/storage/crypto.ts +16 -2
- package/packages/core/src/storage/hive.ts +1 -1
- package/packages/core/src/storage/hivedb.ts +10 -1
- package/packages/core/src/storage/onboarding.ts +6 -6
- package/packages/core/src/storage/reconcile.ts +5 -5
- package/packages/core/src/storage/seed.ts +103 -13
- 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/tools/ToolExecutor.ts +7 -3
- package/packages/core/src/tools/core/index.ts +2 -2
- package/packages/core/src/tools/cron/index.ts +4 -4
- 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
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `createAgent` persiste su configuración en la fila del agente.
|
|
3
|
-
*
|
|
4
|
-
* Hasta 0.1.5 aceptaba `provider`, `model`, `maxIterations`, `workspace` y
|
|
5
|
-
* `systemPrompt` y los descartaba: construía el loop global y corría con lo que
|
|
6
|
-
* hubiera en la base. Estos tests fijan que ahora sí lleguen.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
process.env.HIVE_DB_PATH = ":memory:";
|
|
10
|
-
|
|
11
|
-
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
12
|
-
import { z } from "zod";
|
|
13
|
-
import { closeHiveDb } from "../storage/hivedb.ts";
|
|
14
|
-
import { ensureHiveDb } from "../storage/bootstrap.ts";
|
|
15
|
-
import { col, fromIndexable } from "../storage/hive.ts";
|
|
16
|
-
import { clearAppTools, createAllTools } from "../tools/index.ts";
|
|
17
|
-
import { selectTools } from "../agent/tool-selector.ts";
|
|
18
|
-
import { loadConfig } from "../config/loader.ts";
|
|
19
|
-
import type { AgentDoc, ModelDoc } from "../storage/collections.ts";
|
|
20
|
-
import { createAgent } from "./createAgent.ts";
|
|
21
|
-
import { defineTool } from "../tools/ToolRegistry.ts";
|
|
22
|
-
import { defineSkill } from "../skills/defineSkill.ts";
|
|
23
|
-
|
|
24
|
-
beforeEach(async () => {
|
|
25
|
-
closeHiveDb();
|
|
26
|
-
clearAppTools();
|
|
27
|
-
await ensureHiveDb();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
afterEach(() => {
|
|
31
|
-
clearAppTools();
|
|
32
|
-
closeHiveDb();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe("createAgent", () => {
|
|
36
|
-
it("persiste provider, modelo, prompt e iteraciones en la fila del agente", async () => {
|
|
37
|
-
const agent = await createAgent({
|
|
38
|
-
name: "Test Agent",
|
|
39
|
-
provider: "anthropic",
|
|
40
|
-
model: "claude-opus-5",
|
|
41
|
-
systemPrompt: "Sos un asistente de prueba.",
|
|
42
|
-
maxIterations: 7,
|
|
43
|
-
workspace: "/tmp/ws",
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
expect(agent.name).toBe("Test Agent");
|
|
47
|
-
expect(agent.id).toBe("test_agent");
|
|
48
|
-
|
|
49
|
-
const agentsCol = await col<AgentDoc>("agents");
|
|
50
|
-
const row = (await agentsCol.get("test_agent"))!.doc;
|
|
51
|
-
|
|
52
|
-
expect(fromIndexable(row.provider_id)).toBe("anthropic");
|
|
53
|
-
expect(fromIndexable(row.model_id)).toBe("claude-opus-5");
|
|
54
|
-
expect(row.system_prompt).toBe("Sos un asistente de prueba.");
|
|
55
|
-
expect(row.max_iterations).toBe(7);
|
|
56
|
-
expect(row.workspace).toBe("/tmp/ws");
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("prefija el id del modelo cuando el provider es revendedor", async () => {
|
|
60
|
-
await createAgent({
|
|
61
|
-
name: "reseller",
|
|
62
|
-
provider: "modelscope",
|
|
63
|
-
model: "Qwen-Ambassador/Qwen3.8-Max",
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const agentsCol = await col<AgentDoc>("agents");
|
|
67
|
-
const row = (await agentsCol.get("reseller"))!.doc;
|
|
68
|
-
|
|
69
|
-
expect(fromIndexable(row.model_id)).toBe("modelscope/Qwen-Ambassador/Qwen3.8-Max");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("activa el modelo elegido, o resolveProviderConfig no encuentra su base_url", async () => {
|
|
73
|
-
await createAgent({ name: "activador", provider: "anthropic", model: "claude-opus-5" });
|
|
74
|
-
|
|
75
|
-
const modelsCol = await col<ModelDoc>("models");
|
|
76
|
-
const model = (await modelsCol.get("claude-opus-5"))!.doc;
|
|
77
|
-
|
|
78
|
-
expect(model.active).toBe(true);
|
|
79
|
-
expect(model.enabled).toBe(true);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("rechaza un modelo que no está en el catálogo", async () => {
|
|
83
|
-
await expect(
|
|
84
|
-
createAgent({ name: "fantasma", provider: "anthropic", model: "claude-que-no-existe" })
|
|
85
|
-
).rejects.toThrow(/no está en el catálogo/);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("pide provider cuando se especifica un modelo", async () => {
|
|
89
|
-
// El mismo modelo lo sirven varios providers y la clave del catálogo
|
|
90
|
-
// depende de cuál, así que adivinarlo sería elegir por el usuario.
|
|
91
|
-
await expect(createAgent({ name: "sin-provider", model: "claude-opus-5" })).rejects.toThrow(
|
|
92
|
-
/necesita también `provider`/
|
|
93
|
-
);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it("registra las tools de la app con su ejecutor, no sólo la declaración", async () => {
|
|
97
|
-
const greet = defineTool({
|
|
98
|
-
name: "greet_person",
|
|
99
|
-
description: "Saluda a alguien",
|
|
100
|
-
schema: z.object({ name: z.string().describe("a quién saludar") }),
|
|
101
|
-
execute: async (args: { name: string }) => `Hola ${args.name}`,
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
await createAgent({ name: "con-tools", tools: [greet] });
|
|
105
|
-
|
|
106
|
-
const registered = createAllTools(await loadConfig()).find((t) => t.name === "greet_person");
|
|
107
|
-
expect(registered, "la tool no llegó al registry que lee el context compiler").toBeDefined();
|
|
108
|
-
expect(registered!.parameters.properties).toHaveProperty("name");
|
|
109
|
-
expect(registered!.parameters.required).toEqual(["name"]);
|
|
110
|
-
expect(await registered!.execute({ name: "Ana" })).toBe("Hola Ana");
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("deja la tool de la app descubrible por búsqueda de capacidad", async () => {
|
|
114
|
-
// El loadout inicial es mínimo a propósito; el resto se encuentra vía
|
|
115
|
-
// `search_knowledge`, que corre sobre el índice BM25. Si createAgent no
|
|
116
|
-
// siembra la fila en `tools` y no reindexa, la tool tiene ejecutor pero el
|
|
117
|
-
// modelo no se entera de que existe.
|
|
118
|
-
const weather = defineTool({
|
|
119
|
-
name: "get_weather",
|
|
120
|
-
description: "Consulta el clima actual de una ciudad. Sinónimos: tiempo, temperatura, pronóstico",
|
|
121
|
-
schema: z.object({ city: z.string() }),
|
|
122
|
-
execute: async (a: { city: string }) => `Soleado en ${a.city}`,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
await createAgent({ name: "descubrible", tools: [weather] });
|
|
126
|
-
|
|
127
|
-
const found = await selectTools("clima de una ciudad", undefined, 5);
|
|
128
|
-
expect(found.map((t) => t.name)).toContain("get_weather");
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
it("reusa la fila al recrear un agente con el mismo nombre", async () => {
|
|
132
|
-
const first = await createAgent({ name: "estable", provider: "anthropic", model: "claude-opus-5" });
|
|
133
|
-
const second = await createAgent({ name: "estable", provider: "anthropic", model: "claude-sonnet-5" });
|
|
134
|
-
|
|
135
|
-
expect(second.id).toBe(first.id);
|
|
136
|
-
|
|
137
|
-
const agentsCol = await col<AgentDoc>("agents");
|
|
138
|
-
const all = (await agentsCol.scan({})).filter((e) => e.id === "estable");
|
|
139
|
-
expect(all).toHaveLength(1);
|
|
140
|
-
expect(fromIndexable(all[0].doc.model_id)).toBe("claude-sonnet-5");
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe("defineTool / defineSkill", () => {
|
|
145
|
-
it("siguen siendo funciones de declaración puras", () => {
|
|
146
|
-
const tool = defineTool({
|
|
147
|
-
name: "greet",
|
|
148
|
-
description: "Says hello",
|
|
149
|
-
execute: async (args: { name: string }) => `Hello ${args.name}`,
|
|
150
|
-
});
|
|
151
|
-
expect(tool.name).toBe("greet");
|
|
152
|
-
|
|
153
|
-
const skill = defineSkill({
|
|
154
|
-
name: "greeting-skill",
|
|
155
|
-
description: "Greeting skill",
|
|
156
|
-
steps: [{ action: "greet", instruction: "Say hello" }],
|
|
157
|
-
});
|
|
158
|
-
expect(skill.name).toBe("greeting-skill");
|
|
159
|
-
});
|
|
160
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import { emitCanvas, subscribeCanvas, unsubscribeCanvas } from "./emitter.ts";
|
|
3
|
-
|
|
4
|
-
// `canvas:render` desapareció en 0.1.5: el canvas dejó de emitir componentes
|
|
5
|
-
// sueltos y pasó a un modelo de grafo (`node_add` / `node_update` / `edge_*`),
|
|
6
|
-
// que es el que consume la vista de oficina 3D.
|
|
7
|
-
|
|
8
|
-
describe("canvas emitter", () => {
|
|
9
|
-
it("subscribes and unsubscribes websocket-like objects", () => {
|
|
10
|
-
let received: any = null;
|
|
11
|
-
const ws = {
|
|
12
|
-
send: (data: string) => { received = JSON.parse(data); },
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
subscribeCanvas(ws);
|
|
16
|
-
emitCanvas("canvas:node_update", { nodeId: "test", changes: { status: "thinking" } });
|
|
17
|
-
unsubscribeCanvas(ws);
|
|
18
|
-
|
|
19
|
-
expect(received).toBeDefined();
|
|
20
|
-
expect(received.type).toBe("canvas:node_update");
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("stops receiving after unsubscribe", () => {
|
|
24
|
-
let count = 0;
|
|
25
|
-
const ws = {
|
|
26
|
-
send: () => { count++; },
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
subscribeCanvas(ws);
|
|
30
|
-
emitCanvas("canvas:node_update", { nodeId: "a", changes: { status: "thinking" } });
|
|
31
|
-
unsubscribeCanvas(ws);
|
|
32
|
-
emitCanvas("canvas:node_update", { nodeId: "b", changes: { status: "idle" } });
|
|
33
|
-
|
|
34
|
-
expect(count).toBe(1);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import { ChannelManager } from "./manager.ts";
|
|
3
|
-
import { loadConfig } from "../config/loader.ts";
|
|
4
|
-
|
|
5
|
-
describe("ChannelManager", () => {
|
|
6
|
-
it("creates a channel manager", () => {
|
|
7
|
-
const config = loadConfig();
|
|
8
|
-
const manager = new ChannelManager(config);
|
|
9
|
-
expect(manager).toBeDefined();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it("has onMessage handler", () => {
|
|
13
|
-
const config = loadConfig();
|
|
14
|
-
const manager = new ChannelManager(config);
|
|
15
|
-
manager.onMessage(async () => {});
|
|
16
|
-
expect(manager).toBeDefined();
|
|
17
|
-
});
|
|
18
|
-
});
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
process.env.HIVE_DB_PATH = ":memory:";
|
|
2
|
-
|
|
3
|
-
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
4
|
-
import { EthicsGuard } from "./EthicsGuard.ts";
|
|
5
|
-
import { closeHiveDb } from "../storage/hivedb.ts";
|
|
6
|
-
import { ensureHiveDb } from "../storage/bootstrap.ts";
|
|
7
|
-
import { col } from "../storage/hive.ts";
|
|
8
|
-
import { toIndexable } from "../storage/hive.ts";
|
|
9
|
-
import type { PlaybookDoc } from "../storage/collections.ts";
|
|
10
|
-
|
|
11
|
-
async function addRule(id: string, rule: string, category: string, opts?: {
|
|
12
|
-
applicableTo?: string;
|
|
13
|
-
helpfulCount?: number;
|
|
14
|
-
active?: boolean;
|
|
15
|
-
}) {
|
|
16
|
-
const playbookCol = await col<PlaybookDoc>("playbook");
|
|
17
|
-
const now = Date.now();
|
|
18
|
-
await playbookCol.put(id, {
|
|
19
|
-
id,
|
|
20
|
-
rule,
|
|
21
|
-
category,
|
|
22
|
-
applicable_to: opts?.applicableTo ?? null,
|
|
23
|
-
helpful_count: opts?.helpfulCount ?? 0,
|
|
24
|
-
harmful_count: 0,
|
|
25
|
-
active: opts?.active ?? true,
|
|
26
|
-
source_reflection_id: toIndexable(null),
|
|
27
|
-
created_at: now,
|
|
28
|
-
updated_at: now,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
beforeEach(async () => {
|
|
33
|
-
closeHiveDb();
|
|
34
|
-
await ensureHiveDb();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
afterEach(() => {
|
|
38
|
-
closeHiveDb();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe("EthicsGuard", () => {
|
|
42
|
-
it("sólo devuelve reglas de response_quality activas", async () => {
|
|
43
|
-
await addRule("rq-1", "Verificá las fuentes antes de responder", "response_quality");
|
|
44
|
-
await addRule("rq-2", "Regla apagada", "response_quality", { active: false });
|
|
45
|
-
await addRule("otra", "Usá web_search para noticias", "tool_selection");
|
|
46
|
-
|
|
47
|
-
const rules = await new EthicsGuard().getRules();
|
|
48
|
-
|
|
49
|
-
expect(rules.map((r) => r.id)).toEqual(["rq-1"]);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("ordena por helpful_count descendente", async () => {
|
|
53
|
-
await addRule("poco", "poco útil", "response_quality", { helpfulCount: 1 });
|
|
54
|
-
await addRule("mucho", "muy útil", "response_quality", { helpfulCount: 9 });
|
|
55
|
-
|
|
56
|
-
const rules = await new EthicsGuard().getRules();
|
|
57
|
-
|
|
58
|
-
expect(rules.map((r) => r.id)).toEqual(["mucho", "poco"]);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("filtra por agentRole cuando alguna regla lo declara", async () => {
|
|
62
|
-
await addRule("para-coord", "regla del coordinador", "response_quality", {
|
|
63
|
-
applicableTo: JSON.stringify(["coordinator"]),
|
|
64
|
-
});
|
|
65
|
-
await addRule("para-worker", "regla del worker", "response_quality", {
|
|
66
|
-
applicableTo: JSON.stringify(["worker"]),
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
const rules = await new EthicsGuard().getRules("coordinator");
|
|
70
|
-
|
|
71
|
-
expect(rules.map((r) => r.id)).toEqual(["para-coord"]);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("cae a todas las reglas si ninguna declara ese rol", async () => {
|
|
75
|
-
// Sin esto, un `applicable_to` mal cargado dejaría al agente sin capa
|
|
76
|
-
// de calidad en vez de con una de más.
|
|
77
|
-
await addRule("generica", "regla general", "response_quality", {
|
|
78
|
-
applicableTo: JSON.stringify(["worker"]),
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
const rules = await new EthicsGuard().getRules("coordinator");
|
|
82
|
-
|
|
83
|
-
expect(rules.map((r) => r.id)).toEqual(["generica"]);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("injectIntoPrompt agrega las reglas y conserva el prompt original", async () => {
|
|
87
|
-
await addRule("rq-1", "Verificá las fuentes", "response_quality");
|
|
88
|
-
|
|
89
|
-
const guard = new EthicsGuard();
|
|
90
|
-
const result = guard.injectIntoPrompt("Eres un asistente.", await guard.getRules());
|
|
91
|
-
|
|
92
|
-
expect(result).toContain("Eres un asistente.");
|
|
93
|
-
expect(result).toContain("## Reglas de Calidad de Respuesta");
|
|
94
|
-
expect(result).toContain("- Verificá las fuentes");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("injectIntoPrompt devuelve el prompt intacto sin reglas", () => {
|
|
98
|
-
expect(new EthicsGuard().injectIntoPrompt("Eres un asistente.", [])).toBe("Eres un asistente.");
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("hasEthicsLayer refleja si hay reglas cargadas", async () => {
|
|
102
|
-
const guard = new EthicsGuard();
|
|
103
|
-
expect(await guard.hasEthicsLayer()).toBe(false);
|
|
104
|
-
|
|
105
|
-
await addRule("rq-1", "Verificá las fuentes", "response_quality");
|
|
106
|
-
expect(await guard.hasEthicsLayer()).toBe(true);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, beforeAll, afterAll } from "bun:test";
|
|
2
|
-
import { startGateway } from "./index.ts";
|
|
3
|
-
|
|
4
|
-
describe("gateway", () => {
|
|
5
|
-
let server: ReturnType<typeof startGateway> extends Promise<infer T> ? T : never;
|
|
6
|
-
|
|
7
|
-
beforeAll(async () => {
|
|
8
|
-
server = await startGateway({ host: "127.0.0.1", port: 18791, agentId: "test" });
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
afterAll(() => {
|
|
12
|
-
server?.stop(true);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("returns health status", async () => {
|
|
16
|
-
const res = await fetch("http://127.0.0.1:18791/status");
|
|
17
|
-
expect(res.status).toBe(200);
|
|
18
|
-
const body = await res.json();
|
|
19
|
-
expect(body.status).toBe("ok");
|
|
20
|
-
expect(body.gateway).toBe(true);
|
|
21
|
-
expect(body.agentId).toBe("test");
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("returns 404 for unknown routes", async () => {
|
|
25
|
-
const res = await fetch("http://127.0.0.1:18791/unknown");
|
|
26
|
-
expect(res.status).toBe(404);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("chat endpoint requires database setup", async () => {
|
|
30
|
-
const res = await fetch("http://127.0.0.1:18791/chat", {
|
|
31
|
-
method: "POST",
|
|
32
|
-
headers: { "Content-Type": "application/json" },
|
|
33
|
-
body: JSON.stringify({ message: "hello" }),
|
|
34
|
-
});
|
|
35
|
-
// Will fail because no DB/agent configured, but endpoint exists
|
|
36
|
-
expect(res.status).toBe(500);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
process.env.HIVE_DB_PATH = ":memory:";
|
|
2
|
-
|
|
3
|
-
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
4
|
-
import { Scratchpad } from "./Scratchpad.ts";
|
|
5
|
-
import { closeHiveDb } from "../storage/hivedb.ts";
|
|
6
|
-
import { ensureHiveDb } from "../storage/bootstrap.ts";
|
|
7
|
-
import { getScratchpad } from "../agent/conversation-store.ts";
|
|
8
|
-
|
|
9
|
-
const THREAD = "test-thread";
|
|
10
|
-
|
|
11
|
-
let pad: Scratchpad;
|
|
12
|
-
|
|
13
|
-
beforeEach(async () => {
|
|
14
|
-
closeHiveDb();
|
|
15
|
-
await ensureHiveDb();
|
|
16
|
-
pad = new Scratchpad();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterEach(() => {
|
|
20
|
-
closeHiveDb();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe("Scratchpad", () => {
|
|
24
|
-
it("writes and reads a note", async () => {
|
|
25
|
-
await pad.write(THREAD, "test-1", "hello world");
|
|
26
|
-
expect(await pad.read(THREAD, "test-1")).toBe("hello world");
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("lists notes as key-value map", async () => {
|
|
30
|
-
await pad.write(THREAD, "list-a", "aaa");
|
|
31
|
-
await pad.write(THREAD, "list-b", "bbb");
|
|
32
|
-
|
|
33
|
-
const notes = await pad.list(THREAD);
|
|
34
|
-
expect(notes["list-a"]).toBe("aaa");
|
|
35
|
-
expect(notes["list-b"]).toBe("bbb");
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("deletes a note", async () => {
|
|
39
|
-
await pad.write(THREAD, "to-delete", "delete me");
|
|
40
|
-
await pad.delete(THREAD, "to-delete");
|
|
41
|
-
expect(await pad.read(THREAD, "to-delete")).toBeUndefined();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("clear removes all notes for a thread", async () => {
|
|
45
|
-
await pad.write(THREAD, "clear-a", "a");
|
|
46
|
-
await pad.write(THREAD, "clear-b", "b");
|
|
47
|
-
await pad.clear(THREAD);
|
|
48
|
-
expect(Object.keys(await pad.list(THREAD))).toHaveLength(0);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("no toca las notas de otros threads", async () => {
|
|
52
|
-
await pad.write("thread-uno", "k", "uno");
|
|
53
|
-
await pad.write("thread-dos", "k", "dos");
|
|
54
|
-
await pad.clear("thread-uno");
|
|
55
|
-
|
|
56
|
-
expect(await pad.read("thread-dos", "k")).toBe("dos");
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("escribe el mismo documento que lee el context compiler", async () => {
|
|
60
|
-
// La clase y `conversation-store` comparten colección e id. Antes la clase
|
|
61
|
-
// guardaba un doc incompleto (sin `source`, `createdAt` ni `seq`), así que
|
|
62
|
-
// una nota escrita por acá se ordenaba mal dentro del prompt.
|
|
63
|
-
await pad.write(THREAD, "compartida", "valor", "test");
|
|
64
|
-
|
|
65
|
-
const notes = await getScratchpad(THREAD);
|
|
66
|
-
expect(notes).toEqual([{ key: "compartida", value: "valor" }]);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
process.env.HIVE_DB_PATH = ":memory:";
|
|
2
|
-
|
|
3
|
-
import { describe, expect, it } from "bun:test";
|
|
4
|
-
import { CronScheduler } from "./CronScheduler.ts";
|
|
5
|
-
|
|
6
|
-
// `new CronScheduler(db, handler)` pasó a `new CronScheduler(handler)`: los jobs
|
|
7
|
-
// viven en la colección `cronJobs` de HiveDB y el scheduler la abre solo, así
|
|
8
|
-
// que ya no recibe un handle de base.
|
|
9
|
-
|
|
10
|
-
describe("CronScheduler", () => {
|
|
11
|
-
it("creates a scheduler instance with a handler", () => {
|
|
12
|
-
const scheduler = new CronScheduler(async () => ({ success: true }));
|
|
13
|
-
expect(scheduler).toBeDefined();
|
|
14
|
-
});
|
|
15
|
-
});
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* El catálogo de skills bundled se genera desde los SKILL.md de `skills/bundled/`
|
|
3
|
-
* con `bun run skills:bundle`. Hasta 0.1.5 el SDK traía 44 skills copiadas a
|
|
4
|
-
* mano, 21 de las cuales invocaban tools que ya no existen (`voice_*`,
|
|
5
|
-
* `meeting_transcription`, `canvas_*`, `code_*`, `project_*`): el selector se
|
|
6
|
-
* las podía ofrecer al modelo y la ejecución moría sin ejecutor.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { describe, it, expect } from "bun:test";
|
|
10
|
-
import { BUNDLED_SKILLS_DATA } from "./bundled-data.generated.ts";
|
|
11
|
-
import { createAllTools } from "../tools/index.ts";
|
|
12
|
-
import { loadConfig } from "../config/loader.ts";
|
|
13
|
-
|
|
14
|
-
describe("Bundled skills", () => {
|
|
15
|
-
it("incluye las skills web y de browser", () => {
|
|
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
|
-
|
|
22
|
-
it("no arrastra las skills retiradas", () => {
|
|
23
|
-
const names = BUNDLED_SKILLS_DATA.map((s) => s.name);
|
|
24
|
-
for (const retired of [
|
|
25
|
-
"web_browser_research",
|
|
26
|
-
"voice_input",
|
|
27
|
-
"voice_output",
|
|
28
|
-
"voice_assistant",
|
|
29
|
-
"meeting_transcription",
|
|
30
|
-
"canvas_report",
|
|
31
|
-
"canvas_dashboard",
|
|
32
|
-
"code_review",
|
|
33
|
-
"code_generate",
|
|
34
|
-
"project_planner",
|
|
35
|
-
"busqueda_fts5",
|
|
36
|
-
"ai_coder",
|
|
37
|
-
"data_analyst",
|
|
38
|
-
]) {
|
|
39
|
-
expect(names, `la skill retirada "${retired}" volvió al bundle`).not.toContain(retired);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("cada skill declara sólo tools que existen en el registry", async () => {
|
|
44
|
-
const available = new Set(createAllTools(await loadConfig()).map((t) => t.name));
|
|
45
|
-
|
|
46
|
-
const dangling = BUNDLED_SKILLS_DATA.flatMap((skill) =>
|
|
47
|
-
(skill.tools ?? [])
|
|
48
|
-
.filter((tool) => !available.has(tool))
|
|
49
|
-
.map((tool) => `${skill.name} → ${tool}`)
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
expect(dangling, `skills apuntando a tools inexistentes:\n${dangling.join("\n")}`).toEqual([]);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("toda skill tiene nombre, descripción y categoría", () => {
|
|
56
|
-
for (const skill of BUNDLED_SKILLS_DATA) {
|
|
57
|
-
expect(skill.name, "skill sin nombre").toBeTruthy();
|
|
58
|
-
expect(skill.description, `${skill.name} sin descripción`).toBeTruthy();
|
|
59
|
-
expect(skill.category, `${skill.name} sin categoría`).toBeTruthy();
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
import { TaskGraph } from "./TaskGraph.ts";
|
|
3
|
-
|
|
4
|
-
describe("TaskGraph", () => {
|
|
5
|
-
it("creates a graph with nodes", () => {
|
|
6
|
-
const graph = new TaskGraph([
|
|
7
|
-
{ id: "a", agentId: "agent-a", name: "Task A", taskDescription: "Task A", deps: [] },
|
|
8
|
-
{ id: "b", agentId: "agent-b", name: "Task B", taskDescription: "Task B", deps: ["a"] },
|
|
9
|
-
]);
|
|
10
|
-
|
|
11
|
-
expect(graph.nodes.size).toBe(2);
|
|
12
|
-
expect(graph.nodes.get("a")?.deps).toEqual([]);
|
|
13
|
-
expect(graph.nodes.get("b")?.deps).toEqual(["a"]);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("validates node existence", () => {
|
|
17
|
-
const graph = new TaskGraph([
|
|
18
|
-
{ id: "a", agentId: "agent-a", name: "Task A", taskDescription: "Task A", deps: [] },
|
|
19
|
-
]);
|
|
20
|
-
|
|
21
|
-
expect(graph.nodes.has("a")).toBe(true);
|
|
22
|
-
expect(graph.nodes.has("missing")).toBe(false);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it } from "bun:test";
|
|
2
|
-
import { executeToolBatch, shutdownToolRuntime, type RuntimeTool, type ToolCallLike } from "./index.ts";
|
|
3
|
-
import { loadConfig } from "../config/loader.ts";
|
|
4
|
-
|
|
5
|
-
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
6
|
-
|
|
7
|
-
function toolCall(id: string, name: string, args: unknown = {}): ToolCallLike {
|
|
8
|
-
return {
|
|
9
|
-
id,
|
|
10
|
-
function: {
|
|
11
|
-
name,
|
|
12
|
-
arguments: JSON.stringify(args),
|
|
13
|
-
},
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
describe("tool runtime worker pool", () => {
|
|
18
|
-
afterEach(() => {
|
|
19
|
-
shutdownToolRuntime();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("runs multiple tools in parallel through worker scheduling", async () => {
|
|
23
|
-
// Per-tool delay + threshold sized for CI headroom: worker spawn/scheduling
|
|
24
|
-
// overhead is roughly fixed, so a larger delay keeps that overhead a small
|
|
25
|
-
// fraction of the budget instead of dominating it on a loaded runner.
|
|
26
|
-
// Serial execution would take ~3x the delay; the threshold stays well
|
|
27
|
-
// under that so the assertion still proves parallelism, not just patience.
|
|
28
|
-
const TOOL_DELAY_MS = 200;
|
|
29
|
-
const PARALLEL_THRESHOLD_MS = 450;
|
|
30
|
-
|
|
31
|
-
const tools: RuntimeTool[] = ["slow_a", "slow_b", "slow_c"].map((name) => ({
|
|
32
|
-
name,
|
|
33
|
-
execute: async () => {
|
|
34
|
-
await delay(TOOL_DELAY_MS);
|
|
35
|
-
return { name };
|
|
36
|
-
},
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
|
-
const startedAt = performance.now();
|
|
40
|
-
const results = await executeToolBatch({
|
|
41
|
-
toolCalls: [
|
|
42
|
-
toolCall("1", "slow_a"),
|
|
43
|
-
toolCall("2", "slow_b"),
|
|
44
|
-
toolCall("3", "slow_c"),
|
|
45
|
-
],
|
|
46
|
-
allTools: tools,
|
|
47
|
-
toolConfig: {},
|
|
48
|
-
hiveConfig: loadConfig(),
|
|
49
|
-
workerPool: { enabled: true, maxWorkers: 3, toolTimeoutMs: 5000, parallelToolCalls: true },
|
|
50
|
-
});
|
|
51
|
-
const elapsed = performance.now() - startedAt;
|
|
52
|
-
|
|
53
|
-
expect(results.map((result) => (result.result as any).name)).toEqual(["slow_a", "slow_b", "slow_c"]);
|
|
54
|
-
expect(elapsed).toBeLessThan(PARALLEL_THRESHOLD_MS);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("preserves input order when tools complete out of order", async () => {
|
|
58
|
-
const tools: RuntimeTool[] = [
|
|
59
|
-
{ name: "first", execute: async () => { await delay(120); return { value: 1 } } },
|
|
60
|
-
{ name: "second", execute: async () => { await delay(20); return { value: 2 } } },
|
|
61
|
-
{ name: "third", execute: async () => { await delay(60); return { value: 3 } } },
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
const results = await executeToolBatch({
|
|
65
|
-
toolCalls: [toolCall("1", "first"), toolCall("2", "second"), toolCall("3", "third")],
|
|
66
|
-
allTools: tools,
|
|
67
|
-
toolConfig: {},
|
|
68
|
-
hiveConfig: loadConfig(),
|
|
69
|
-
workerPool: { enabled: true, maxWorkers: 3, toolTimeoutMs: 1000, parallelToolCalls: true },
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
expect(results.map((r) => (r.result as any).value)).toEqual([1, 2, 3]);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("handles tool errors gracefully", async () => {
|
|
76
|
-
const tools: RuntimeTool[] = [
|
|
77
|
-
{
|
|
78
|
-
name: "fail",
|
|
79
|
-
execute: async () => {
|
|
80
|
-
throw new Error("intentional failure");
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
];
|
|
84
|
-
|
|
85
|
-
const results = await executeToolBatch({
|
|
86
|
-
toolCalls: [toolCall("1", "fail")],
|
|
87
|
-
allTools: tools,
|
|
88
|
-
toolConfig: {},
|
|
89
|
-
hiveConfig: loadConfig(),
|
|
90
|
-
workerPool: { enabled: true, maxWorkers: 1, toolTimeoutMs: 1000, parallelToolCalls: false },
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
expect(results[0].ok).toBe(false);
|
|
94
|
-
expect(results[0].result).toBeDefined();
|
|
95
|
-
const result = results[0].result as any;
|
|
96
|
-
expect(result.error).toBe(true);
|
|
97
|
-
expect(result.message).toContain("intentional failure");
|
|
98
|
-
});
|
|
99
|
-
});
|