@johpaz/hive-sdk 0.1.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +441 -0
- package/README.md +21 -3
- package/package.json +27 -5
- package/packages/core/src/agent/acceptance-checks.ts +9 -9
- package/packages/core/src/agent/agent-catalog.ts +82 -25
- package/packages/core/src/agent/agent-loop.ts +115 -26
- package/packages/core/src/agent/capability-search.ts +2 -2
- package/packages/core/src/agent/catalog-selector.ts +4 -4
- package/packages/core/src/agent/compaction.ts +30 -10
- package/packages/core/src/agent/context-compiler.ts +63 -36
- package/packages/core/src/agent/conversation-store.ts +167 -9
- package/packages/core/src/agent/curator.ts +16 -7
- package/packages/core/src/agent/delegation-runtime.ts +5 -5
- package/packages/core/src/agent/goal-runner.ts +9 -9
- package/packages/core/src/agent/index.ts +1 -0
- package/packages/core/src/agent/llm-client.ts +98 -37
- package/packages/core/src/agent/llm-providers/anthropic.ts +4 -4
- package/packages/core/src/agent/llm-providers/deepseek.ts +1 -1
- package/packages/core/src/agent/llm-providers/gemini.ts +4 -4
- package/packages/core/src/agent/llm-providers/groq.ts +1 -1
- package/packages/core/src/agent/llm-providers/hiveagents.ts +3 -3
- package/packages/core/src/agent/llm-providers/interface.ts +2 -2
- package/packages/core/src/agent/llm-providers/kimi.ts +1 -1
- package/packages/core/src/agent/llm-providers/minimax.ts +1 -1
- package/packages/core/src/agent/llm-providers/mistral.ts +1 -1
- package/packages/core/src/agent/llm-providers/modelscope.ts +1 -1
- package/packages/core/src/agent/llm-providers/nvidia.ts +40 -1
- package/packages/core/src/agent/llm-providers/ollama.ts +4 -4
- package/packages/core/src/agent/llm-providers/openai-compat-base.ts +47 -7
- package/packages/core/src/agent/llm-providers/openai.ts +1 -1
- package/packages/core/src/agent/llm-providers/opencode-go.ts +1 -1
- package/packages/core/src/agent/llm-providers/openrouter.ts +1 -1
- package/packages/core/src/agent/llm-providers/qwen.ts +1 -1
- package/packages/core/src/agent/llm-providers/z-ai.ts +1 -1
- package/packages/core/src/agent/mcp-result-normalizer.ts +192 -0
- package/packages/core/src/agent/playbook-selector.ts +22 -7
- package/packages/core/src/agent/prompt-builder.ts +5 -5
- package/packages/core/src/agent/proof-packet.ts +5 -5
- package/packages/core/src/agent/providers/index.ts +39 -4
- package/packages/core/src/agent/realtime-providers/gemini-live.ts +238 -0
- package/packages/core/src/agent/realtime-providers/index.ts +29 -0
- package/packages/core/src/agent/realtime-providers/interface.ts +108 -0
- package/packages/core/src/agent/reflector.ts +38 -15
- package/packages/core/src/agent/run-store.ts +8 -8
- package/packages/core/src/agent/service.ts +10 -10
- package/packages/core/src/agent/skill-selector.ts +6 -6
- package/packages/core/src/agent/thread-id.ts +71 -0
- package/packages/core/src/agent/thread-store.ts +293 -0
- package/packages/core/src/agent/tool-selector.ts +9 -5
- package/packages/core/src/agent/tracer.ts +5 -5
- package/packages/core/src/api/createAgent.ts +67 -2
- package/packages/core/src/artifacts/index.ts +15 -0
- package/packages/core/src/artifacts/store.ts +161 -5
- package/packages/core/src/canvas/emitter.ts +2 -2
- package/packages/core/src/canvas/index.ts +9 -0
- package/packages/core/src/channels/telegram.ts +1 -1
- package/packages/core/src/channels/webchat.ts +1 -1
- package/packages/core/src/config/loader.ts +14 -5
- package/packages/core/src/ethics/EthicsGuard.ts +7 -1
- package/packages/core/src/events/agent-bus.ts +3 -3
- package/packages/core/src/events/channel-narration.ts +3 -3
- package/packages/core/src/events/event-bus.ts +1 -1
- package/packages/core/src/events/index.ts +18 -0
- package/packages/core/src/events/narration.ts +3 -3
- package/packages/core/src/events/tool-narration.ts +4 -0
- package/packages/core/src/gateway/channel-notify.ts +103 -6
- package/packages/core/src/gateway/delegation-groups.ts +4 -4
- package/packages/core/src/gateway/durable-queue.ts +18 -6
- package/packages/core/src/gateway/index.ts +3 -0
- package/packages/core/src/gateway/job-store.ts +11 -5
- package/packages/core/src/gateway/notification-inbox.ts +2 -2
- package/packages/core/src/gateway/server.ts +2 -2
- package/packages/core/src/harness/executors.ts +493 -0
- package/packages/core/src/harness/index.ts +12 -2
- package/packages/core/src/hooks/index.ts +203 -0
- package/packages/core/src/images/index.ts +161 -0
- package/packages/core/src/index.ts +1 -0
- package/packages/core/src/mcp/MCPClient.ts +3 -3
- package/packages/core/src/mcp/hot-reload.ts +5 -5
- package/packages/core/src/mcp/tool-sync.ts +5 -5
- package/packages/core/src/mcp/transports/index.ts +2 -2
- package/packages/core/src/mcp/transports/sse.ts +1 -1
- package/packages/core/src/models/index.ts +36 -0
- package/packages/core/src/multimodal/index.ts +2 -2
- package/packages/core/src/multimodal/vision-service.ts +51 -19
- package/packages/core/src/plugins/loader.ts +4 -1
- package/packages/core/src/resilience/circuit-breaker.ts +16 -5
- package/packages/core/src/resilience/index.ts +13 -0
- package/packages/core/src/resilience/retry.ts +1 -1
- package/packages/core/src/scheduler/CronScheduler.ts +54 -27
- package/packages/core/src/scheduler/cron/expression.ts +165 -0
- package/packages/core/src/scheduler/cron/index.ts +10 -0
- package/packages/core/src/scheduler/cron/job.ts +339 -0
- package/packages/core/src/scheduler/cron/next-run.ts +121 -0
- package/packages/core/src/scheduler/cron/zoned-time.ts +138 -0
- package/packages/core/src/scheduler/index.ts +21 -3
- package/packages/core/src/scheduler/integration.ts +25 -14
- package/packages/core/src/scheduler/types.ts +3 -18
- package/packages/core/src/services/agents.ts +268 -0
- package/packages/core/src/services/cron.ts +257 -0
- package/packages/core/src/services/endpoints.ts +289 -0
- package/packages/core/src/services/ethics.ts +107 -0
- package/packages/core/src/services/images.ts +212 -0
- package/packages/core/src/services/index.ts +112 -0
- package/packages/core/src/services/mcp.ts +201 -0
- package/packages/core/src/services/memory.ts +133 -0
- package/packages/core/src/services/models.ts +179 -0
- package/packages/core/src/services/providers.ts +152 -0
- package/packages/core/src/services/setup.ts +222 -0
- package/packages/core/src/services/skills.ts +241 -0
- package/packages/core/src/services/swarms.ts +307 -0
- package/packages/core/src/services/tools.ts +106 -0
- package/packages/core/src/sessions/index.ts +268 -0
- package/packages/core/src/sessions/resolve.ts +108 -0
- package/packages/core/src/skills/SkillLoader.ts +8 -1
- package/packages/core/src/skills/bundled/artifacts/artifact_reader/SKILL.md +105 -0
- package/packages/core/src/skills/bundled/cron_manager/SKILL.md +21 -11
- package/packages/core/src/skills/bundled/images/image_editor/SKILL.md +120 -0
- package/packages/core/src/skills/bundled/web/browser_automate/SKILL.md +12 -3
- package/packages/core/src/skills/bundled/web/browser_scrape/SKILL.md +22 -7
- package/packages/core/src/skills/bundled-data.generated.ts +110 -12
- package/packages/core/src/storage/bootstrap.ts +107 -12
- package/packages/core/src/storage/causal-events.ts +1 -1
- package/packages/core/src/storage/collections.ts +138 -2
- package/packages/core/src/storage/crypto.ts +35 -4
- package/packages/core/src/storage/hive.ts +1 -1
- package/packages/core/src/storage/hivedb.ts +10 -1
- package/packages/core/src/storage/index.ts +2 -1
- package/packages/core/src/storage/onboarding.ts +61 -45
- package/packages/core/src/storage/reconcile.ts +11 -6
- package/packages/core/src/storage/seed.ts +191 -23
- package/packages/core/src/storage/usage.ts +3 -3
- package/packages/core/src/swarm/AgentExecutor.ts +2 -2
- package/packages/core/src/swarm/Coordinator.ts +8 -8
- package/packages/core/src/swarm/EventBridge.ts +2 -2
- package/packages/core/src/swarm/RoleSwarm.ts +234 -0
- package/packages/core/src/swarm/TaskGraph.ts +2 -2
- package/packages/core/src/swarm/index.ts +7 -0
- package/packages/core/src/swarm/presets/HiveLearnPreset.ts +2 -2
- package/packages/core/src/swarm/presets/ResearchPreset.ts +2 -2
- package/packages/core/src/swarm/strategies/ParallelStrategy.ts +1 -1
- package/packages/core/src/swarm/strategies/PriorityStrategy.ts +3 -3
- package/packages/core/src/swarm/types.ts +3 -18
- package/packages/core/src/tool-runtime/embedded-worker.generated.ts +21 -0
- package/packages/core/src/tool-runtime/index.ts +129 -14
- package/packages/core/src/tools/ToolExecutor.ts +7 -3
- package/packages/core/src/tools/agents/index.ts +18 -60
- package/packages/core/src/tools/cli/index.ts +55 -0
- package/packages/core/src/tools/core/index.ts +52 -4
- package/packages/core/src/tools/cron/index.ts +8 -8
- package/packages/core/src/tools/images/index.ts +130 -0
- package/packages/core/src/tools/index.ts +14 -1
- package/packages/core/src/tools/office/office-escribir-xlsx.ts +2 -1
- package/packages/core/src/tools/office/office-leer-xlsx.ts +2 -1
- package/packages/core/src/tools/office/xlsx-loader.ts +19 -0
- package/packages/core/src/tools/web/artifact-inspect.ts +2 -2
- package/packages/core/src/tools/web/artifact-read.ts +162 -0
- package/packages/core/src/tools/web/browser-backend.ts +141 -44
- package/packages/core/src/tools/web/browser-click.ts +2 -2
- package/packages/core/src/tools/web/browser-extract.ts +2 -2
- package/packages/core/src/tools/web/browser-navigate.ts +2 -2
- package/packages/core/src/tools/web/browser-screenshot.ts +12 -5
- package/packages/core/src/tools/web/browser-script.ts +2 -2
- package/packages/core/src/tools/web/browser-service.ts +63 -384
- package/packages/core/src/tools/web/browser-session.ts +125 -0
- package/packages/core/src/tools/web/browser-type.ts +2 -2
- package/packages/core/src/tools/web/browser-wait.ts +2 -2
- package/packages/core/src/tools/web/computer-use.ts +553 -0
- package/packages/core/src/tools/web/index.ts +8 -1
- package/packages/core/src/tools/web/webview-backend.ts +460 -21
- package/packages/core/src/utils/index.ts +1 -0
- package/packages/core/src/utils/logger.ts +12 -4
- package/packages/core/src/utils/redact-binary.ts +17 -0
- package/packages/core/src/utils/toon.ts +1 -1
- package/packages/core/src/voice/index.ts +6 -6
- package/bun.lock +0 -859
- package/bunfig.toml +0 -9
- package/docs/API-AGENTS.md +0 -367
- package/docs/API-CONTEXT-COMPILER.md +0 -249
- package/docs/API-DAG-SCHEDULER.md +0 -273
- package/docs/API-TOOLS-SKILLS-CHANNELS.md +0 -446
- package/docs/API-WORKERS-EVENTS.md +0 -299
- package/docs/HIVE-HARNESS.md +0 -113
- package/docs/INDEX.md +0 -190
- package/docs/TEMPLATE-HIVE-APP.md +0 -360
- package/packages/cli/package.json +0 -17
- package/packages/cli/src/commands/create-app.test.ts +0 -180
- package/packages/core/package.json +0 -70
- package/packages/core/src/api/createAgent.test.ts +0 -160
- package/packages/core/src/canvas/canvas.test.ts +0 -36
- package/packages/core/src/channels/channels.test.ts +0 -18
- package/packages/core/src/ethics/EthicsGuard.test.ts +0 -108
- package/packages/core/src/gateway/gateway.test.ts +0 -38
- package/packages/core/src/memory/Scratchpad.test.ts +0 -68
- package/packages/core/src/scheduler/scheduler.test.ts +0 -15
- package/packages/core/src/skills/skills.test.ts +0 -62
- package/packages/core/src/swarm/swarm.test.ts +0 -24
- package/packages/core/src/tool-runtime/tool-runtime.test.ts +0 -99
- package/packages/core/src/tools/ToolRegistry.test.ts +0 -98
- package/packages/core/src/tools/api/api-request.test.ts +0 -164
- package/packages/core/src/tools/web/browser-service.test.ts +0 -83
- package/packages/core/src/workers/workers.test.ts +0 -41
- package/scripts/bump-version.ts +0 -248
- package/scripts/generate-skill-bundle.ts +0 -108
- package/test/acceptance-checks.test.ts +0 -403
- package/test/agent-loop-terminal-synthesis.test.ts +0 -32
- package/test/browser-backend.test.ts +0 -308
- package/test/catalog-agents-stay-enabled.test.ts +0 -117
- package/test/causal-events.test.ts +0 -117
- package/test/compaction.test.ts +0 -105
- package/test/context-compiler.test.ts +0 -269
- package/test/curator.test.ts +0 -130
- package/test/durable-queue.test.ts +0 -114
- package/test/harness-barrel.test.ts +0 -64
- package/test/hive-helpers.test.ts +0 -130
- package/test/hivedb-search.test.ts +0 -189
- package/test/internal-turns.test.ts +0 -166
- package/test/job-idempotency.test.ts +0 -68
- package/test/job-retry-backoff.test.ts +0 -184
- package/test/job-store.test.ts +0 -381
- package/test/llm-retry.test.ts +0 -97
- package/test/memory-perf.test.ts +0 -774
- package/test/minimal-loadout.test.ts +0 -78
- package/test/model-catalog.test.ts +0 -105
- package/test/preload.ts +0 -12
- package/test/reflector.test.ts +0 -320
- package/test/retention-cap.test.ts +0 -91
- package/test/retired-capabilities-pruned.test.ts +0 -192
- package/test/run-store.test.ts +0 -355
- package/test/scratchpad.test.ts +0 -74
- package/test/secrets-durability.test.ts +0 -119
- package/test/seed-model-reseed.test.ts +0 -155
- package/test/setup-agent-seed.test.ts +0 -264
- package/test/tool-inventory.test.ts +0 -65
- package/test/tool-runtime.test.ts +0 -258
- package/test/tool-selector-runtime-tools.test.ts +0 -117
- package/test/toon.test.ts +0 -429
- package/tsconfig.json +0 -42
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The coordinator starts every turn with MINIMAL_TOOLS and the skills derived
|
|
3
|
-
* from it. Everything else is discovered, and discovery injects a tool together
|
|
4
|
-
* with the skills that document it (agent-loop.ts).
|
|
5
|
-
*
|
|
6
|
-
* So an always-injected skill that teaches a tool outside the loadout is pure
|
|
7
|
-
* cost: the agent reads instructions for something it cannot call, and gets the
|
|
8
|
-
* same skill again later when it discovers the tool. That is exactly what had
|
|
9
|
-
* drifted — 3 of the 4 hand-listed "minimal" skills were in that state — hence
|
|
10
|
-
* the derivation in minimal-loadout.ts and this test.
|
|
11
|
-
*
|
|
12
|
-
* Uses HIVE_DB_PATH=":memory:".
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
process.env.HIVE_DB_PATH = ":memory:";
|
|
16
|
-
|
|
17
|
-
import { describe, test, expect, beforeAll, afterAll } from "bun:test";
|
|
18
|
-
import { closeHiveDb } from "../packages/core/src/storage/hivedb";
|
|
19
|
-
import { ensureHiveDb } from "../packages/core/src/storage/bootstrap";
|
|
20
|
-
import { createAllTools } from "../packages/core/src/tools/index";
|
|
21
|
-
import { getMinimalSkills } from "../packages/core/src/agent/skill-selector";
|
|
22
|
-
import {
|
|
23
|
-
MINIMAL_TOOLS,
|
|
24
|
-
isMinimalSkill,
|
|
25
|
-
parseSkillTools,
|
|
26
|
-
} from "../packages/core/src/agent/minimal-loadout";
|
|
27
|
-
|
|
28
|
-
beforeAll(async () => {
|
|
29
|
-
closeHiveDb();
|
|
30
|
-
await ensureHiveDb();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
afterAll(() => {
|
|
34
|
-
closeHiveDb();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
describe("minimal loadout", () => {
|
|
38
|
-
test("every minimal tool is a real executor", () => {
|
|
39
|
-
const executors = new Set(createAllTools({ tools: {} } as never).map((t) => t.name));
|
|
40
|
-
const missing = [...MINIMAL_TOOLS].filter((name) => !executors.has(name)).sort();
|
|
41
|
-
expect(missing).toEqual([]);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test("no minimal skill teaches a tool outside the loadout", async () => {
|
|
45
|
-
const offenders = (await getMinimalSkills())
|
|
46
|
-
.map((skill) => ({
|
|
47
|
-
name: skill.name,
|
|
48
|
-
outside: parseSkillTools(skill.tools).filter((t) => !MINIMAL_TOOLS.has(t)),
|
|
49
|
-
}))
|
|
50
|
-
.filter((s) => s.outside.length > 0);
|
|
51
|
-
expect(offenders).toEqual([]);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test("the derivation actually selects something", async () => {
|
|
55
|
-
const names = (await getMinimalSkills()).map((s) => s.name);
|
|
56
|
-
// capability_discovery documents search_knowledge and nothing else, so it
|
|
57
|
-
// qualifies by construction. If this ever empties out, the loadout and the
|
|
58
|
-
// bundled skills have drifted apart again.
|
|
59
|
-
expect(names).toContain("capability_discovery");
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("the rule rejects skills that reach outside the loadout", () => {
|
|
63
|
-
expect(isMinimalSkill("search_knowledge")).toBe(true);
|
|
64
|
-
expect(isMinimalSkill("task_delegate, agent_find, task_status")).toBe(true);
|
|
65
|
-
expect(isMinimalSkill("search_knowledge, memory_write")).toBe(false);
|
|
66
|
-
expect(isMinimalSkill("a2ui_create_surface")).toBe(false);
|
|
67
|
-
expect(isMinimalSkill("")).toBe(false); // no anchor to the loadout
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("skills left out still reach the agent through discovery", async () => {
|
|
71
|
-
// They are not lost, just not always-on: agent-loop injects a skill when
|
|
72
|
-
// search_knowledge surfaces any tool the skill declares.
|
|
73
|
-
const minimal = new Set((await getMinimalSkills()).map((s) => s.name));
|
|
74
|
-
expect(minimal.has("memory_manager")).toBe(false);
|
|
75
|
-
expect(minimal.has("a2ui_dashboard")).toBe(false);
|
|
76
|
-
expect(minimal.has("task_orchestrator")).toBe(false);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
// Importar el seed arrastra los módulos de storage, que resuelven la ruta de la
|
|
2
|
-
// BD al cargarse. Sin esto el archivo podía dejar apuntando la BD real.
|
|
3
|
-
process.env.HIVE_DB_PATH = ":memory:";
|
|
4
|
-
|
|
5
|
-
import { describe, expect, test } from "bun:test";
|
|
6
|
-
import { SEED_DATA } from "../packages/core/src/storage/seed";
|
|
7
|
-
import { catalogModelKey, wireModelId, isResellerProvider } from "../packages/core/src/storage/model-id";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Guardas del catálogo de modelos.
|
|
11
|
-
*
|
|
12
|
-
* La colección `models` se indexa por una sola clave, así que dos entradas que
|
|
13
|
-
* colapsen a la misma la pisan entre sí — pasó de verdad con `z-ai/glm-5.2`,
|
|
14
|
-
* sembrado a la vez en NVIDIA NIM y en OpenRouter, donde una fila desaparecía en
|
|
15
|
-
* silencio. Y un modelo LLM sin precio se reporta como $0 en el dashboard, que es
|
|
16
|
-
* indistinguible de un endpoint realmente gratuito.
|
|
17
|
-
*/
|
|
18
|
-
describe("catálogo de modelos (SEED_DATA.models)", () => {
|
|
19
|
-
test("ninguna entrada colapsa a la misma clave de BD", () => {
|
|
20
|
-
const seen = new Map<string, string>();
|
|
21
|
-
const collisions: string[] = [];
|
|
22
|
-
|
|
23
|
-
for (const model of SEED_DATA.models) {
|
|
24
|
-
const key = catalogModelKey(model.providerId, model.id);
|
|
25
|
-
const previous = seen.get(key);
|
|
26
|
-
if (previous) {
|
|
27
|
-
collisions.push(`${key} ← ${previous} y ${model.providerId}/${model.id}`);
|
|
28
|
-
}
|
|
29
|
-
seen.set(key, `${model.providerId}/${model.id}`);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
expect(collisions).toEqual([]);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("todo modelo llm trae precio de entrada y de salida", () => {
|
|
36
|
-
const unpriced = SEED_DATA.models
|
|
37
|
-
.filter((m) => m.modelType === "llm")
|
|
38
|
-
.filter((m) => m.inputPer1M === undefined || m.outputPer1M === undefined)
|
|
39
|
-
.map((m) => `${m.providerId}/${m.id}`);
|
|
40
|
-
|
|
41
|
-
expect(unpriced).toEqual([]);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test("los precios son números no negativos", () => {
|
|
45
|
-
const invalid = SEED_DATA.models
|
|
46
|
-
.filter((m) => m.modelType === "llm")
|
|
47
|
-
.filter((m) => !(typeof m.inputPer1M === "number" && m.inputPer1M >= 0)
|
|
48
|
-
|| !(typeof m.outputPer1M === "number" && m.outputPer1M >= 0))
|
|
49
|
-
.map((m) => `${m.providerId}/${m.id}`);
|
|
50
|
-
|
|
51
|
-
expect(invalid).toEqual([]);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test("todo modelo apunta a un provider sembrado", () => {
|
|
55
|
-
const providerIds = new Set(SEED_DATA.providers.map((p) => p.id));
|
|
56
|
-
const orphans = SEED_DATA.models
|
|
57
|
-
.filter((m) => !providerIds.has(m.providerId))
|
|
58
|
-
.map((m) => `${m.providerId}/${m.id}`);
|
|
59
|
-
|
|
60
|
-
expect(orphans).toEqual([]);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
describe("catalogModelKey / wireModelId", () => {
|
|
65
|
-
test("los revendedores prefijan y los propietarios no", () => {
|
|
66
|
-
expect(catalogModelKey("nvidia", "z-ai/glm-5.2")).toBe("nvidia/z-ai/glm-5.2");
|
|
67
|
-
expect(catalogModelKey("openrouter", "z-ai/glm-5.2")).toBe("openrouter/z-ai/glm-5.2");
|
|
68
|
-
expect(catalogModelKey("anthropic", "claude-opus-5")).toBe("claude-opus-5");
|
|
69
|
-
expect(catalogModelKey("z-ai", "glm-5.2")).toBe("glm-5.2");
|
|
70
|
-
// ModelScope revende modelos de terceros (Qwen/, deepseek-ai/, zai-org/),
|
|
71
|
-
// que se solapan con los de NVIDIA y OpenRouter.
|
|
72
|
-
expect(catalogModelKey("modelscope", "Qwen/Qwen3.5-397B-A17B")).toBe("modelscope/Qwen/Qwen3.5-397B-A17B");
|
|
73
|
-
expect(wireModelId("modelscope", "modelscope/Qwen-Ambassador/Qwen3.8-Max")).toBe("Qwen-Ambassador/Qwen3.8-Max");
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("`id` en el catálogo es siempre el nombre del cable, no la clave ya prefijada", () => {
|
|
77
|
-
// No es idempotente a propósito: aplicarlo dos veces prefija dos veces. Un
|
|
78
|
-
// atajo `startsWith` para evitarlo rompía los modelos cuyo namespace propio
|
|
79
|
-
// coincide con el id del provider (ver el caso de abajo).
|
|
80
|
-
expect(catalogModelKey("opencode-go", "glm-5.1")).toBe("opencode-go/glm-5.1");
|
|
81
|
-
for (const model of SEED_DATA.models) {
|
|
82
|
-
expect(model.id.startsWith(`${model.providerId}/${model.providerId}/`)).toBe(false);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test("un modelo propio del revendedor conserva su namespace tras el strip", () => {
|
|
87
|
-
// El nombre de cable de NVIDIA para su Nemotron ya empieza con "nvidia/",
|
|
88
|
-
// así que la clave lleva el prefijo dos veces a propósito.
|
|
89
|
-
const key = catalogModelKey("nvidia", "nvidia/nemotron-3-super-120b-a12b");
|
|
90
|
-
expect(key).toBe("nvidia/nvidia/nemotron-3-super-120b-a12b");
|
|
91
|
-
expect(wireModelId("nvidia", key)).toBe("nvidia/nemotron-3-super-120b-a12b");
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test("wireModelId revierte catalogModelKey para todo el catálogo", () => {
|
|
95
|
-
for (const model of SEED_DATA.models) {
|
|
96
|
-
const key = catalogModelKey(model.providerId, model.id);
|
|
97
|
-
expect(wireModelId(model.providerId, key)).toBe(model.id);
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test("wireModelId no toca a los providers propietarios", () => {
|
|
102
|
-
expect(isResellerProvider("anthropic")).toBe(false);
|
|
103
|
-
expect(wireModelId("anthropic", "claude-opus-5")).toBe("claude-opus-5");
|
|
104
|
-
});
|
|
105
|
-
});
|
package/test/preload.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Preload de la suite de tests.
|
|
3
|
-
*
|
|
4
|
-
* Cada archivo de test pone `process.env.HIVE_DB_PATH = ":memory:"` en su primera
|
|
5
|
-
* línea, pero los `import` de ESM se evalúan ANTES que cualquier sentencia del
|
|
6
|
-
* módulo: si algo de la cadena de imports resuelve la ruta de la BD al cargarse,
|
|
7
|
-
* la asignación llega tarde y la suite termina abriendo la base real del usuario
|
|
8
|
-
* (~/.hive/data/hivedb), escribiéndole contadores y filtrando estado entre
|
|
9
|
-
* archivos. Un preload corre antes que todo, así que acá sí gana.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
process.env.HIVE_DB_PATH ||= ":memory:";
|
package/test/reflector.test.ts
DELETED
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reflector tests: local trace-batch analysis, plus the G9 causalLog path
|
|
3
|
-
* where per-tool insights (failure count, avg latency) come from HiveDB's
|
|
4
|
-
* whole-history toolStats() instead of just the current batch of traces.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
process.env.HIVE_DB_PATH = ":memory:";
|
|
8
|
-
|
|
9
|
-
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
10
|
-
import { closeHiveDb, getHiveDb } from "../packages/core/src/storage/hivedb";
|
|
11
|
-
import { ensureHiveDb } from "../packages/core/src/storage/bootstrap";
|
|
12
|
-
import { resetBootId } from "../packages/core/src/storage/boot-id";
|
|
13
|
-
import { col, nextId } from "../packages/core/src/storage/hive";
|
|
14
|
-
import type { TraceDoc, ReflectionDoc } from "../packages/core/src/storage/collections";
|
|
15
|
-
import { runReflector } from "../packages/core/src/agent/reflector";
|
|
16
|
-
|
|
17
|
-
async function seedTrace(overrides: Partial<TraceDoc>) {
|
|
18
|
-
const tracesCol = await col<TraceDoc>("traces");
|
|
19
|
-
const id = await nextId("traces");
|
|
20
|
-
await tracesCol.put(id, {
|
|
21
|
-
id,
|
|
22
|
-
thread_id: "thread-1",
|
|
23
|
-
agent_id: "agent-1",
|
|
24
|
-
agent_name: "Agent 1",
|
|
25
|
-
tool_used: null,
|
|
26
|
-
input_summary: "input",
|
|
27
|
-
output_summary: "output",
|
|
28
|
-
success: true,
|
|
29
|
-
error_message: null,
|
|
30
|
-
duration_ms: null,
|
|
31
|
-
tokens_used: null,
|
|
32
|
-
created_at: Date.now(),
|
|
33
|
-
...overrides,
|
|
34
|
-
});
|
|
35
|
-
return id;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
beforeEach(async () => {
|
|
39
|
-
closeHiveDb();
|
|
40
|
-
resetBootId();
|
|
41
|
-
await ensureHiveDb();
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
afterEach(() => {
|
|
45
|
-
closeHiveDb();
|
|
46
|
-
delete process.env.HIVE_CAUSAL_LOG;
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe("reflector: local trace-batch analysis (causalLog disabled)", () => {
|
|
50
|
-
test("failure_pattern insight uses the batch-local failure count", async () => {
|
|
51
|
-
for (let i = 0; i < 4; i++) {
|
|
52
|
-
await seedTrace({ tool_used: "flaky_tool", success: false });
|
|
53
|
-
}
|
|
54
|
-
for (let i = 0; i < 6; i++) {
|
|
55
|
-
await seedTrace({ tool_used: "ok_tool", success: true });
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
await runReflector();
|
|
59
|
-
|
|
60
|
-
const reflectionsCol = await col<ReflectionDoc>("reflections");
|
|
61
|
-
const all = await reflectionsCol.scan({});
|
|
62
|
-
const failureInsight = all.find((e) => e.doc.insight_type === "failure_pattern");
|
|
63
|
-
expect(failureInsight).toBeDefined();
|
|
64
|
-
expect(failureInsight!.doc.description).toContain("flaky_tool");
|
|
65
|
-
expect(failureInsight!.doc.description).toContain("4 times");
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
describe("reflector: G9 causal event log (causalLog enabled)", () => {
|
|
70
|
-
test("failure_pattern insight uses toolStats().errors from the whole event log, not just this batch", async () => {
|
|
71
|
-
process.env.HIVE_CAUSAL_LOG = "true";
|
|
72
|
-
const db = await getHiveDb();
|
|
73
|
-
|
|
74
|
-
// 6 historical ToolCall errors for "flaky_tool" — more than the 4 failures
|
|
75
|
-
// present in this batch of traces.
|
|
76
|
-
for (let i = 0; i < 6; i++) {
|
|
77
|
-
await db.append({
|
|
78
|
-
agentId: "agent-1",
|
|
79
|
-
streamId: "stream-history",
|
|
80
|
-
kind: "ToolCall",
|
|
81
|
-
payload: JSON.stringify({ tool: "flaky_tool", outcome: { Err: `boom-${i}` } }),
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
for (let i = 0; i < 4; i++) {
|
|
86
|
-
await seedTrace({ tool_used: "flaky_tool", success: false });
|
|
87
|
-
}
|
|
88
|
-
for (let i = 0; i < 6; i++) {
|
|
89
|
-
await seedTrace({ tool_used: "ok_tool", success: true });
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
await runReflector();
|
|
93
|
-
|
|
94
|
-
const reflectionsCol = await col<ReflectionDoc>("reflections");
|
|
95
|
-
const all = await reflectionsCol.scan({});
|
|
96
|
-
const failureInsight = all.find((e) => e.doc.insight_type === "failure_pattern");
|
|
97
|
-
expect(failureInsight).toBeDefined();
|
|
98
|
-
// 6 from the event log's full history, not 4 from this batch alone
|
|
99
|
-
expect(failureInsight!.doc.description).toContain("6 times");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("optimization (slow tool) insight uses toolStats().totalLatencyMs/invocations", async () => {
|
|
103
|
-
process.env.HIVE_CAUSAL_LOG = "true";
|
|
104
|
-
const db = await getHiveDb();
|
|
105
|
-
|
|
106
|
-
// 3 historical ToolCall events for "slow_tool" with a high average latency
|
|
107
|
-
// (12000ms), distinct from the batch's own (lower) durations.
|
|
108
|
-
for (let i = 0; i < 3; i++) {
|
|
109
|
-
await db.append({
|
|
110
|
-
agentId: "agent-1",
|
|
111
|
-
streamId: "stream-history",
|
|
112
|
-
kind: "ToolCall",
|
|
113
|
-
payload: JSON.stringify({ tool: "slow_tool", latency_ms: 12000, outcome: "Ok" }),
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
for (let i = 0; i < 3; i++) {
|
|
118
|
-
await seedTrace({ tool_used: "slow_tool", success: true, duration_ms: 6000 });
|
|
119
|
-
}
|
|
120
|
-
for (let i = 0; i < 7; i++) {
|
|
121
|
-
await seedTrace({ tool_used: "ok_tool", success: true });
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
await runReflector();
|
|
125
|
-
|
|
126
|
-
const reflectionsCol = await col<ReflectionDoc>("reflections");
|
|
127
|
-
const all = await reflectionsCol.scan({});
|
|
128
|
-
const slowInsight = all.find(
|
|
129
|
-
(e) => e.doc.insight_type === "optimization" && e.doc.description.includes("slow_tool")
|
|
130
|
-
);
|
|
131
|
-
expect(slowInsight).toBeDefined();
|
|
132
|
-
// avg from toolStats (12000ms), not from the batch's own durations (6000ms)
|
|
133
|
-
expect(slowInsight!.doc.description).toContain("12000ms");
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
describe("reflector: G9 causal-thread evaluation (evaluateHarness)", () => {
|
|
138
|
-
test("a stream with a repeated tool-call failure produces root_cause and learning_proposal insights", async () => {
|
|
139
|
-
process.env.HIVE_CAUSAL_LOG = "true";
|
|
140
|
-
const db = await getHiveDb();
|
|
141
|
-
|
|
142
|
-
// Build a realistic causal thread: Intent -> Decision -> 3x ToolCall Err
|
|
143
|
-
// (same tool + message), mirroring exactly what agent-loop.ts emits and
|
|
144
|
-
// matching the 3-repetition threshold hiveBD's errorLoop detector requires.
|
|
145
|
-
const streamId = "stream-rootcause-1";
|
|
146
|
-
const intentSeq = await db.append({
|
|
147
|
-
agentId: "agent-1",
|
|
148
|
-
streamId,
|
|
149
|
-
kind: "IntentLogged",
|
|
150
|
-
payload: JSON.stringify({ actor: "agent-1", intent: "deploy the checkout service" }),
|
|
151
|
-
});
|
|
152
|
-
const decisionSeq = await db.append({
|
|
153
|
-
agentId: "agent-1",
|
|
154
|
-
streamId,
|
|
155
|
-
kind: "StateTransition",
|
|
156
|
-
payload: JSON.stringify({ description: "Calling flaky_tool" }),
|
|
157
|
-
causation: intentSeq,
|
|
158
|
-
});
|
|
159
|
-
for (let i = 0; i < 3; i++) {
|
|
160
|
-
await db.append({
|
|
161
|
-
agentId: "agent-1",
|
|
162
|
-
streamId,
|
|
163
|
-
kind: "ToolCall",
|
|
164
|
-
payload: JSON.stringify({ tool: "flaky_tool", outcome: { Err: "boom" } }),
|
|
165
|
-
causation: decisionSeq,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
await seedTrace({
|
|
170
|
-
tool_used: "flaky_tool",
|
|
171
|
-
success: false,
|
|
172
|
-
causal_stream_id: streamId,
|
|
173
|
-
input_summary: "deploy the checkout service",
|
|
174
|
-
});
|
|
175
|
-
for (let i = 0; i < 9; i++) {
|
|
176
|
-
await seedTrace({ tool_used: "ok_tool", success: true });
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
await runReflector();
|
|
180
|
-
|
|
181
|
-
const reflectionsCol = await col<ReflectionDoc>("reflections");
|
|
182
|
-
const all = await reflectionsCol.scan({});
|
|
183
|
-
|
|
184
|
-
const rootCause = all.find((e) => e.doc.insight_type === "root_cause");
|
|
185
|
-
expect(rootCause).toBeDefined();
|
|
186
|
-
expect(rootCause!.doc.description).toContain("flaky_tool");
|
|
187
|
-
|
|
188
|
-
const proposal = all.find((e) => e.doc.insight_type === "learning_proposal");
|
|
189
|
-
expect(proposal).toBeDefined();
|
|
190
|
-
expect(proposal!.doc.description.toLowerCase()).toContain("flaky_tool");
|
|
191
|
-
expect(proposal!.doc.confidence).toBeGreaterThanOrEqual(0.5);
|
|
192
|
-
|
|
193
|
-
// The mapped playbook categories from curator.ts (root_cause -> error_avoidance,
|
|
194
|
-
// learning_proposal -> response_quality) confirm the insights flowed all the
|
|
195
|
-
// way through to the playbook, not just into the reflections collection.
|
|
196
|
-
const playbookCol = await col<import("../packages/core/src/storage/collections").PlaybookDoc>("playbook");
|
|
197
|
-
const playbook = await playbookCol.scan({});
|
|
198
|
-
expect(playbook.some((e) => e.doc.source_reflection_id === rootCause!.id && e.doc.category === "error_avoidance")).toBe(true);
|
|
199
|
-
expect(playbook.some((e) => e.doc.source_reflection_id === proposal!.id && e.doc.category === "response_quality")).toBe(true);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
test("a stream with no failures produces no root_cause or learning_proposal insights", async () => {
|
|
203
|
-
process.env.HIVE_CAUSAL_LOG = "true";
|
|
204
|
-
const db = await getHiveDb();
|
|
205
|
-
|
|
206
|
-
const streamId = "stream-clean-1";
|
|
207
|
-
const intentSeq = await db.append({
|
|
208
|
-
agentId: "agent-1",
|
|
209
|
-
streamId,
|
|
210
|
-
kind: "IntentLogged",
|
|
211
|
-
payload: JSON.stringify({ actor: "agent-1", intent: "read a file" }),
|
|
212
|
-
});
|
|
213
|
-
await db.append({
|
|
214
|
-
agentId: "agent-1",
|
|
215
|
-
streamId,
|
|
216
|
-
kind: "StateTransition",
|
|
217
|
-
payload: JSON.stringify({ description: "Calling read_file" }),
|
|
218
|
-
causation: intentSeq,
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
await seedTrace({ tool_used: "read_file", success: true, causal_stream_id: streamId });
|
|
222
|
-
for (let i = 0; i < 9; i++) {
|
|
223
|
-
await seedTrace({ tool_used: "ok_tool", success: true });
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
await runReflector();
|
|
227
|
-
|
|
228
|
-
const reflectionsCol = await col<ReflectionDoc>("reflections");
|
|
229
|
-
const all = await reflectionsCol.scan({});
|
|
230
|
-
expect(all.some((e) => e.doc.insight_type === "root_cause")).toBe(false);
|
|
231
|
-
expect(all.some((e) => e.doc.insight_type === "learning_proposal")).toBe(false);
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
test("a single tool-call failure produces exactly one root_cause insight, not two", async () => {
|
|
235
|
-
// Regression guard: harness.rs's find_root_cause() emits both
|
|
236
|
-
// evaluation.rootCause AND an equivalent finding{kind:"rootCause"} from the
|
|
237
|
-
// same resolution — analyzeCausalThreads() must only turn that into one
|
|
238
|
-
// insight, not two, per failure.
|
|
239
|
-
process.env.HIVE_CAUSAL_LOG = "true";
|
|
240
|
-
const db = await getHiveDb();
|
|
241
|
-
|
|
242
|
-
const streamId = "stream-single-failure";
|
|
243
|
-
const intentSeq = await db.append({
|
|
244
|
-
agentId: "agent-1",
|
|
245
|
-
streamId,
|
|
246
|
-
kind: "IntentLogged",
|
|
247
|
-
payload: JSON.stringify({ actor: "agent-1", intent: "deploy the checkout service" }),
|
|
248
|
-
});
|
|
249
|
-
const decisionSeq = await db.append({
|
|
250
|
-
agentId: "agent-1",
|
|
251
|
-
streamId,
|
|
252
|
-
kind: "StateTransition",
|
|
253
|
-
payload: JSON.stringify({ description: "Calling deploy_service" }),
|
|
254
|
-
causation: intentSeq,
|
|
255
|
-
});
|
|
256
|
-
await db.append({
|
|
257
|
-
agentId: "agent-1",
|
|
258
|
-
streamId,
|
|
259
|
-
kind: "ToolCall",
|
|
260
|
-
payload: JSON.stringify({ tool: "deploy_service", outcome: { Err: "boom" } }),
|
|
261
|
-
causation: decisionSeq,
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
await seedTrace({ tool_used: "deploy_service", success: false, causal_stream_id: streamId });
|
|
265
|
-
for (let i = 0; i < 9; i++) {
|
|
266
|
-
await seedTrace({ tool_used: "ok_tool", success: true });
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
await runReflector();
|
|
270
|
-
|
|
271
|
-
const reflectionsCol = await col<ReflectionDoc>("reflections");
|
|
272
|
-
const all = await reflectionsCol.scan({});
|
|
273
|
-
const rootCauseInsights = all.filter((e) => e.doc.insight_type === "root_cause");
|
|
274
|
-
expect(rootCauseInsights.length).toBe(1);
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
test("the same underlying root cause across two different streams reinforces one playbook rule instead of duplicating it", async () => {
|
|
278
|
-
process.env.HIVE_CAUSAL_LOG = "true";
|
|
279
|
-
const db = await getHiveDb();
|
|
280
|
-
|
|
281
|
-
async function seedFailingStream(streamId: string) {
|
|
282
|
-
const intentSeq = await db.append({
|
|
283
|
-
agentId: "agent-1",
|
|
284
|
-
streamId,
|
|
285
|
-
kind: "IntentLogged",
|
|
286
|
-
payload: JSON.stringify({ actor: "agent-1", intent: "deploy the checkout service" }),
|
|
287
|
-
});
|
|
288
|
-
const decisionSeq = await db.append({
|
|
289
|
-
agentId: "agent-1",
|
|
290
|
-
streamId,
|
|
291
|
-
kind: "StateTransition",
|
|
292
|
-
payload: JSON.stringify({ description: "Calling deploy_service" }),
|
|
293
|
-
causation: intentSeq,
|
|
294
|
-
});
|
|
295
|
-
await db.append({
|
|
296
|
-
agentId: "agent-1",
|
|
297
|
-
streamId,
|
|
298
|
-
kind: "ToolCall",
|
|
299
|
-
payload: JSON.stringify({ tool: "deploy_service", outcome: { Err: "boom" } }),
|
|
300
|
-
causation: decisionSeq,
|
|
301
|
-
});
|
|
302
|
-
await seedTrace({ tool_used: "deploy_service", success: false, causal_stream_id: streamId });
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
await seedFailingStream("stream-reinforce-a");
|
|
306
|
-
await seedFailingStream("stream-reinforce-b");
|
|
307
|
-
for (let i = 0; i < 8; i++) {
|
|
308
|
-
await seedTrace({ tool_used: "ok_tool", success: true });
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
await runReflector();
|
|
312
|
-
|
|
313
|
-
const playbookCol = await col<import("../packages/core/src/storage/collections").PlaybookDoc>("playbook");
|
|
314
|
-
const rootCauseRules = (await playbookCol.scan({})).filter((e) =>
|
|
315
|
-
e.doc.rule.includes('decision "Calling deploy_service"')
|
|
316
|
-
);
|
|
317
|
-
expect(rootCauseRules.length).toBe(1);
|
|
318
|
-
expect(rootCauseRules[0].doc.helpful_count).toBe(2);
|
|
319
|
-
});
|
|
320
|
-
});
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from "bun:test";
|
|
2
|
-
process.env.HIVE_DB_PATH = ":memory:";
|
|
3
|
-
|
|
4
|
-
import { closeHiveDb } from "../packages/core/src/storage/hivedb";
|
|
5
|
-
import { ensureHiveDb } from "../packages/core/src/storage/bootstrap";
|
|
6
|
-
import { col } from "../packages/core/src/storage/hive";
|
|
7
|
-
import { reconcileOnBoot } from "../packages/core/src/storage/reconcile";
|
|
8
|
-
|
|
9
|
-
beforeEach(async () => {
|
|
10
|
-
await closeHiveDb();
|
|
11
|
-
await ensureHiveDb();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
describe("retention cap in reconcileOnBoot", () => {
|
|
15
|
-
it("caps agentRuns to 500 per thread", async () => {
|
|
16
|
-
const agentRunsCol = await col<any>("agentRuns");
|
|
17
|
-
// Insert 510 runs for the same thread
|
|
18
|
-
for (let i = 0; i < 510; i++) {
|
|
19
|
-
await agentRunsCol.put(`run-${i}`, {
|
|
20
|
-
agent_id: "agent-1",
|
|
21
|
-
thread_id: "thread-1",
|
|
22
|
-
kind: "chat",
|
|
23
|
-
status: "completed",
|
|
24
|
-
created_at: i, // ascending so higher = newer
|
|
25
|
-
lease_expires_at: Date.now() + 60000,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
const beforeCount = (await agentRunsCol.scan({})).length;
|
|
29
|
-
expect(beforeCount).toBe(510);
|
|
30
|
-
|
|
31
|
-
await reconcileOnBoot("test-boot");
|
|
32
|
-
|
|
33
|
-
const afterCount = (await agentRunsCol.scan({})).length;
|
|
34
|
-
expect(afterCount).toBe(500);
|
|
35
|
-
|
|
36
|
-
// Verify we kept the newest (highest created_at)
|
|
37
|
-
const remaining = await agentRunsCol.scan({});
|
|
38
|
-
const ids = remaining.map((e: any) => parseInt(e.id.split("-")[1]));
|
|
39
|
-
expect(Math.min(...ids)).toBe(10); // first 10 pruned, 10..509 kept
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("caps jobs to 500 per run_id", async () => {
|
|
43
|
-
const jobsCol = await col<any>("jobQueue");
|
|
44
|
-
for (let i = 0; i < 520; i++) {
|
|
45
|
-
await jobsCol.put(`job-${i}`, {
|
|
46
|
-
lane: "default",
|
|
47
|
-
type: "chat_turn",
|
|
48
|
-
status: "completed",
|
|
49
|
-
run_id: "run-1",
|
|
50
|
-
attempts: 1,
|
|
51
|
-
max_attempts: 3,
|
|
52
|
-
lease_expires_at: null,
|
|
53
|
-
created_at: i,
|
|
54
|
-
payload_json: "{}",
|
|
55
|
-
priority: 0,
|
|
56
|
-
not_before: 0,
|
|
57
|
-
boot_id: null,
|
|
58
|
-
result_json: null,
|
|
59
|
-
error: null,
|
|
60
|
-
started_at: null,
|
|
61
|
-
finished_at: null,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
const beforeCount = (await jobsCol.scan({})).length;
|
|
65
|
-
expect(beforeCount).toBe(520);
|
|
66
|
-
|
|
67
|
-
await reconcileOnBoot("test-boot");
|
|
68
|
-
|
|
69
|
-
const afterCount = (await jobsCol.scan({})).length;
|
|
70
|
-
expect(afterCount).toBe(500);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it("does not prune when under the cap", async () => {
|
|
74
|
-
const agentRunsCol = await col<any>("agentRuns");
|
|
75
|
-
for (let i = 0; i < 100; i++) {
|
|
76
|
-
await agentRunsCol.put(`run-${i}`, {
|
|
77
|
-
agent_id: "agent-1",
|
|
78
|
-
thread_id: "thread-1",
|
|
79
|
-
kind: "chat",
|
|
80
|
-
status: "completed",
|
|
81
|
-
created_at: i,
|
|
82
|
-
lease_expires_at: Date.now() + 60000,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
await reconcileOnBoot("test-boot");
|
|
87
|
-
|
|
88
|
-
const afterCount = (await agentRunsCol.scan({})).length;
|
|
89
|
-
expect(afterCount).toBe(100);
|
|
90
|
-
});
|
|
91
|
-
});
|