@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.
Files changed (185) hide show
  1. package/CHANGELOG.md +135 -0
  2. package/README.md +11 -1
  3. package/package.json +18 -2
  4. package/packages/core/src/agent/acceptance-checks.ts +9 -9
  5. package/packages/core/src/agent/agent-catalog.ts +3 -3
  6. package/packages/core/src/agent/agent-loop.ts +115 -26
  7. package/packages/core/src/agent/capability-search.ts +2 -2
  8. package/packages/core/src/agent/catalog-selector.ts +4 -4
  9. package/packages/core/src/agent/compaction.ts +10 -9
  10. package/packages/core/src/agent/context-compiler.ts +58 -34
  11. package/packages/core/src/agent/conversation-store.ts +31 -7
  12. package/packages/core/src/agent/curator.ts +4 -4
  13. package/packages/core/src/agent/delegation-runtime.ts +5 -5
  14. package/packages/core/src/agent/goal-runner.ts +9 -9
  15. package/packages/core/src/agent/index.ts +1 -0
  16. package/packages/core/src/agent/llm-client.ts +98 -37
  17. package/packages/core/src/agent/llm-providers/anthropic.ts +4 -4
  18. package/packages/core/src/agent/llm-providers/deepseek.ts +1 -1
  19. package/packages/core/src/agent/llm-providers/gemini.ts +4 -4
  20. package/packages/core/src/agent/llm-providers/groq.ts +1 -1
  21. package/packages/core/src/agent/llm-providers/hiveagents.ts +3 -3
  22. package/packages/core/src/agent/llm-providers/interface.ts +2 -2
  23. package/packages/core/src/agent/llm-providers/kimi.ts +1 -1
  24. package/packages/core/src/agent/llm-providers/minimax.ts +1 -1
  25. package/packages/core/src/agent/llm-providers/mistral.ts +1 -1
  26. package/packages/core/src/agent/llm-providers/modelscope.ts +1 -1
  27. package/packages/core/src/agent/llm-providers/nvidia.ts +1 -1
  28. package/packages/core/src/agent/llm-providers/ollama.ts +4 -4
  29. package/packages/core/src/agent/llm-providers/openai-compat-base.ts +9 -5
  30. package/packages/core/src/agent/llm-providers/openai.ts +1 -1
  31. package/packages/core/src/agent/llm-providers/opencode-go.ts +1 -1
  32. package/packages/core/src/agent/llm-providers/openrouter.ts +1 -1
  33. package/packages/core/src/agent/llm-providers/qwen.ts +1 -1
  34. package/packages/core/src/agent/llm-providers/z-ai.ts +1 -1
  35. package/packages/core/src/agent/mcp-result-normalizer.ts +192 -0
  36. package/packages/core/src/agent/playbook-selector.ts +4 -4
  37. package/packages/core/src/agent/prompt-builder.ts +5 -5
  38. package/packages/core/src/agent/proof-packet.ts +5 -5
  39. package/packages/core/src/agent/providers/index.ts +4 -4
  40. package/packages/core/src/agent/realtime-providers/gemini-live.ts +238 -0
  41. package/packages/core/src/agent/realtime-providers/index.ts +29 -0
  42. package/packages/core/src/agent/realtime-providers/interface.ts +108 -0
  43. package/packages/core/src/agent/reflector.ts +6 -6
  44. package/packages/core/src/agent/run-store.ts +8 -8
  45. package/packages/core/src/agent/service.ts +10 -10
  46. package/packages/core/src/agent/skill-selector.ts +6 -6
  47. package/packages/core/src/agent/thread-id.ts +71 -0
  48. package/packages/core/src/agent/thread-store.ts +250 -0
  49. package/packages/core/src/agent/tool-selector.ts +7 -5
  50. package/packages/core/src/agent/tracer.ts +5 -5
  51. package/packages/core/src/api/createAgent.ts +1 -1
  52. package/packages/core/src/artifacts/store.ts +84 -3
  53. package/packages/core/src/canvas/emitter.ts +2 -2
  54. package/packages/core/src/channels/telegram.ts +1 -1
  55. package/packages/core/src/channels/webchat.ts +1 -1
  56. package/packages/core/src/config/loader.ts +14 -5
  57. package/packages/core/src/events/agent-bus.ts +3 -3
  58. package/packages/core/src/events/channel-narration.ts +3 -3
  59. package/packages/core/src/events/event-bus.ts +1 -1
  60. package/packages/core/src/events/narration.ts +3 -3
  61. package/packages/core/src/gateway/delegation-groups.ts +4 -4
  62. package/packages/core/src/gateway/durable-queue.ts +5 -5
  63. package/packages/core/src/gateway/job-store.ts +5 -5
  64. package/packages/core/src/gateway/notification-inbox.ts +2 -2
  65. package/packages/core/src/gateway/server.ts +2 -2
  66. package/packages/core/src/mcp/MCPClient.ts +3 -3
  67. package/packages/core/src/mcp/hot-reload.ts +5 -5
  68. package/packages/core/src/mcp/tool-sync.ts +5 -5
  69. package/packages/core/src/mcp/transports/index.ts +2 -2
  70. package/packages/core/src/mcp/transports/sse.ts +1 -1
  71. package/packages/core/src/models/index.ts +36 -0
  72. package/packages/core/src/multimodal/index.ts +2 -2
  73. package/packages/core/src/multimodal/vision-service.ts +6 -6
  74. package/packages/core/src/plugins/loader.ts +4 -1
  75. package/packages/core/src/resilience/circuit-breaker.ts +16 -5
  76. package/packages/core/src/resilience/retry.ts +1 -1
  77. package/packages/core/src/scheduler/CronScheduler.ts +6 -6
  78. package/packages/core/src/scheduler/integration.ts +9 -9
  79. package/packages/core/src/sessions/index.ts +266 -0
  80. package/packages/core/src/storage/bootstrap.ts +34 -8
  81. package/packages/core/src/storage/causal-events.ts +1 -1
  82. package/packages/core/src/storage/collections.ts +32 -1
  83. package/packages/core/src/storage/crypto.ts +16 -2
  84. package/packages/core/src/storage/hive.ts +1 -1
  85. package/packages/core/src/storage/hivedb.ts +10 -1
  86. package/packages/core/src/storage/onboarding.ts +6 -6
  87. package/packages/core/src/storage/reconcile.ts +5 -5
  88. package/packages/core/src/storage/seed.ts +103 -13
  89. package/packages/core/src/storage/usage.ts +3 -3
  90. package/packages/core/src/swarm/AgentExecutor.ts +2 -2
  91. package/packages/core/src/swarm/Coordinator.ts +8 -8
  92. package/packages/core/src/swarm/EventBridge.ts +2 -2
  93. package/packages/core/src/swarm/RoleSwarm.ts +234 -0
  94. package/packages/core/src/swarm/TaskGraph.ts +2 -2
  95. package/packages/core/src/swarm/index.ts +7 -0
  96. package/packages/core/src/swarm/presets/HiveLearnPreset.ts +2 -2
  97. package/packages/core/src/swarm/presets/ResearchPreset.ts +2 -2
  98. package/packages/core/src/swarm/strategies/ParallelStrategy.ts +1 -1
  99. package/packages/core/src/swarm/strategies/PriorityStrategy.ts +3 -3
  100. package/packages/core/src/tools/ToolExecutor.ts +7 -3
  101. package/packages/core/src/tools/core/index.ts +2 -2
  102. package/packages/core/src/tools/cron/index.ts +4 -4
  103. package/packages/core/src/tools/web/artifact-inspect.ts +2 -2
  104. package/packages/core/src/tools/web/artifact-read.ts +162 -0
  105. package/packages/core/src/tools/web/browser-backend.ts +141 -44
  106. package/packages/core/src/tools/web/browser-click.ts +2 -2
  107. package/packages/core/src/tools/web/browser-extract.ts +2 -2
  108. package/packages/core/src/tools/web/browser-navigate.ts +2 -2
  109. package/packages/core/src/tools/web/browser-screenshot.ts +12 -5
  110. package/packages/core/src/tools/web/browser-script.ts +2 -2
  111. package/packages/core/src/tools/web/browser-service.ts +63 -384
  112. package/packages/core/src/tools/web/browser-session.ts +125 -0
  113. package/packages/core/src/tools/web/browser-type.ts +2 -2
  114. package/packages/core/src/tools/web/browser-wait.ts +2 -2
  115. package/packages/core/src/tools/web/computer-use.ts +553 -0
  116. package/packages/core/src/tools/web/index.ts +8 -1
  117. package/packages/core/src/tools/web/webview-backend.ts +460 -21
  118. package/packages/core/src/utils/index.ts +1 -0
  119. package/packages/core/src/utils/logger.ts +12 -4
  120. package/packages/core/src/utils/redact-binary.ts +17 -0
  121. package/packages/core/src/utils/toon.ts +1 -1
  122. package/packages/core/src/voice/index.ts +6 -6
  123. package/bun.lock +0 -859
  124. package/bunfig.toml +0 -9
  125. package/docs/API-AGENTS.md +0 -367
  126. package/docs/API-CONTEXT-COMPILER.md +0 -249
  127. package/docs/API-DAG-SCHEDULER.md +0 -273
  128. package/docs/API-TOOLS-SKILLS-CHANNELS.md +0 -446
  129. package/docs/API-WORKERS-EVENTS.md +0 -299
  130. package/docs/HIVE-HARNESS.md +0 -113
  131. package/docs/INDEX.md +0 -190
  132. package/docs/TEMPLATE-HIVE-APP.md +0 -360
  133. package/packages/cli/package.json +0 -17
  134. package/packages/cli/src/commands/create-app.test.ts +0 -180
  135. package/packages/core/package.json +0 -70
  136. package/packages/core/src/api/createAgent.test.ts +0 -160
  137. package/packages/core/src/canvas/canvas.test.ts +0 -36
  138. package/packages/core/src/channels/channels.test.ts +0 -18
  139. package/packages/core/src/ethics/EthicsGuard.test.ts +0 -108
  140. package/packages/core/src/gateway/gateway.test.ts +0 -38
  141. package/packages/core/src/memory/Scratchpad.test.ts +0 -68
  142. package/packages/core/src/scheduler/scheduler.test.ts +0 -15
  143. package/packages/core/src/skills/skills.test.ts +0 -62
  144. package/packages/core/src/swarm/swarm.test.ts +0 -24
  145. package/packages/core/src/tool-runtime/tool-runtime.test.ts +0 -99
  146. package/packages/core/src/tools/ToolRegistry.test.ts +0 -98
  147. package/packages/core/src/tools/api/api-request.test.ts +0 -164
  148. package/packages/core/src/tools/web/browser-service.test.ts +0 -83
  149. package/packages/core/src/workers/workers.test.ts +0 -41
  150. package/scripts/bump-version.ts +0 -248
  151. package/scripts/generate-skill-bundle.ts +0 -108
  152. package/test/acceptance-checks.test.ts +0 -403
  153. package/test/agent-loop-terminal-synthesis.test.ts +0 -32
  154. package/test/browser-backend.test.ts +0 -308
  155. package/test/catalog-agents-stay-enabled.test.ts +0 -117
  156. package/test/causal-events.test.ts +0 -117
  157. package/test/compaction.test.ts +0 -105
  158. package/test/context-compiler.test.ts +0 -269
  159. package/test/curator.test.ts +0 -130
  160. package/test/durable-queue.test.ts +0 -114
  161. package/test/harness-barrel.test.ts +0 -64
  162. package/test/hive-helpers.test.ts +0 -130
  163. package/test/hivedb-search.test.ts +0 -189
  164. package/test/internal-turns.test.ts +0 -166
  165. package/test/job-idempotency.test.ts +0 -68
  166. package/test/job-retry-backoff.test.ts +0 -184
  167. package/test/job-store.test.ts +0 -381
  168. package/test/llm-retry.test.ts +0 -97
  169. package/test/memory-perf.test.ts +0 -774
  170. package/test/minimal-loadout.test.ts +0 -78
  171. package/test/model-catalog.test.ts +0 -105
  172. package/test/preload.ts +0 -12
  173. package/test/reflector.test.ts +0 -320
  174. package/test/retention-cap.test.ts +0 -91
  175. package/test/retired-capabilities-pruned.test.ts +0 -192
  176. package/test/run-store.test.ts +0 -355
  177. package/test/scratchpad.test.ts +0 -74
  178. package/test/secrets-durability.test.ts +0 -119
  179. package/test/seed-model-reseed.test.ts +0 -155
  180. package/test/setup-agent-seed.test.ts +0 -264
  181. package/test/tool-inventory.test.ts +0 -65
  182. package/test/tool-runtime.test.ts +0 -258
  183. package/test/tool-selector-runtime-tools.test.ts +0 -117
  184. package/test/toon.test.ts +0 -429
  185. package/tsconfig.json +0 -42
@@ -1,192 +0,0 @@
1
- /**
2
- * Seeding is put-in-place with natural ids, so dropping a tool from SEED_DATA
3
- * or deleting a bundled SKILL.md does NOT remove the row an older install
4
- * already wrote — and a stale row stays discoverable through search_knowledge
5
- * while its executor no longer exists. `pruneRetired()` in seed.ts closes that
6
- * gap; this test simulates the upgrade path it exists for.
7
- *
8
- * Uses HIVE_DB_PATH=":memory:".
9
- */
10
-
11
- process.env.HIVE_DB_PATH = ":memory:";
12
-
13
- import { describe, test, expect, beforeEach, afterEach } from "bun:test";
14
- import { closeHiveDb } from "../packages/core/src/storage/hivedb";
15
- import { ensureHiveDb } from "../packages/core/src/storage/bootstrap";
16
- import { seedAllData } from "../packages/core/src/storage/seed";
17
- import { col } from "../packages/core/src/storage/hive";
18
- import { createAllTools } from "../packages/core/src/tools/index";
19
- import { syncToolsToIndex, syncSkillsToIndex } from "../packages/core/src/agent/context-compiler";
20
- import { searchCapabilities } from "../packages/core/src/agent/capability-search";
21
- import type { AgentDoc, ToolDoc, SkillDoc } from "../packages/core/src/storage/collections";
22
-
23
- const RETIRED_TOOL = "task_delegate_code";
24
- const RETIRED_SKILL = "code_delegator";
25
- /** Projects/DAG: withdrawn when this instance stopped managing projects. */
26
- const RETIRED_PROJECT_TOOLS = ["project_create", "project_status", "task_create", "task_complete"];
27
- const RETIRED_CANVAS_TOOLS = [
28
- "canvas_render",
29
- "canvas_ask",
30
- "canvas_confirm",
31
- "canvas_show_card",
32
- "canvas_show_progress",
33
- "canvas_show_list",
34
- "canvas_clear",
35
- ];
36
- const RETIRED_CANVAS_SKILLS = ["canvas_report", "canvas_dashboard", "canvas_interact"];
37
- const RETIRED_CANVAS_AGENT = "canvas_presenter";
38
-
39
- beforeEach(async () => {
40
- closeHiveDb();
41
- await ensureHiveDb();
42
- });
43
-
44
- afterEach(() => {
45
- closeHiveDb();
46
- });
47
-
48
- /** Recreates what a pre-upgrade install has sitting in its DB. */
49
- async function writeLegacyRows(): Promise<void> {
50
- const now = Date.now();
51
- const toolsCol = await col<ToolDoc>("tools");
52
- for (const id of [RETIRED_TOOL, ...RETIRED_CANVAS_TOOLS]) {
53
- await toolsCol.put(id, {
54
- id,
55
- name: id,
56
- description: `Legacy capability ${id}`,
57
- category: id.startsWith("canvas_") ? "canvas" : "agents",
58
- enabled: true,
59
- active: true,
60
- created_at: now,
61
- updated_at: now,
62
- });
63
- }
64
-
65
- const skillsCol = await col<SkillDoc>("skills");
66
- for (const id of [RETIRED_SKILL, ...RETIRED_CANVAS_SKILLS]) {
67
- await skillsCol.put(id, {
68
- id,
69
- name: id,
70
- description: `Legacy skill ${id}`,
71
- version: "1.0.0",
72
- author: "Hive Team",
73
- icon: "🧹",
74
- category: id.startsWith("canvas_") ? "canvas" : "agents",
75
- permissions: "[]",
76
- dependencies: "[]",
77
- tools: id.startsWith("canvas_") ? "canvas_render" : "task_delegate_code",
78
- triggers: id,
79
- preferred_agents: "[]",
80
- body: `# ${id}`,
81
- version_num: 1,
82
- active: true,
83
- created_at: now,
84
- updated_at: now,
85
- });
86
- }
87
-
88
- const agentsCol = await col<AgentDoc>("agents");
89
- const template = (await agentsCol.get("workspace_file_operator"))!.doc;
90
- await agentsCol.put(RETIRED_CANVAS_AGENT, {
91
- ...template,
92
- id: RETIRED_CANVAS_AGENT,
93
- name: "Presentador Canvas",
94
- source: "catalog",
95
- });
96
- }
97
-
98
- describe("retired Code Bridge capabilities", () => {
99
- test("a fresh seed never creates them", async () => {
100
- expect(await (await col<ToolDoc>("tools")).get(RETIRED_TOOL)).toBeUndefined();
101
- expect(await (await col<SkillDoc>("skills")).get(RETIRED_SKILL)).toBeUndefined();
102
- for (const id of RETIRED_CANVAS_TOOLS) {
103
- expect(await (await col<ToolDoc>("tools")).get(id)).toBeUndefined();
104
- }
105
- for (const id of RETIRED_CANVAS_SKILLS) {
106
- expect(await (await col<SkillDoc>("skills")).get(id)).toBeUndefined();
107
- }
108
- expect(await (await col<AgentDoc>("agents")).get(RETIRED_CANVAS_AGENT)).toBeUndefined();
109
- });
110
-
111
- test("rows left by an older install are deleted on the next boot", async () => {
112
- await writeLegacyRows();
113
- expect(await (await col<ToolDoc>("tools")).get(RETIRED_TOOL)).toBeTruthy();
114
- expect(await (await col<SkillDoc>("skills")).get(RETIRED_SKILL)).toBeTruthy();
115
- expect(await (await col<AgentDoc>("agents")).get(RETIRED_CANVAS_AGENT)).toBeTruthy();
116
-
117
- await seedAllData(); // what ensureHiveDb() runs on every boot
118
-
119
- expect(await (await col<ToolDoc>("tools")).get(RETIRED_TOOL)).toBeUndefined();
120
- expect(await (await col<SkillDoc>("skills")).get(RETIRED_SKILL)).toBeUndefined();
121
- for (const id of RETIRED_CANVAS_TOOLS) {
122
- expect(await (await col<ToolDoc>("tools")).get(id)).toBeUndefined();
123
- }
124
- for (const id of RETIRED_CANVAS_SKILLS) {
125
- expect(await (await col<SkillDoc>("skills")).get(id)).toBeUndefined();
126
- }
127
- expect(await (await col<AgentDoc>("agents")).get(RETIRED_CANVAS_AGENT)).toBeUndefined();
128
- });
129
-
130
- test("pruning leaves the surviving catalog intact", async () => {
131
- await writeLegacyRows();
132
- await seedAllData();
133
-
134
- const tools = (await (await col<ToolDoc>("tools")).scan({})).map((e) => e.doc.name);
135
- expect(tools).toContain("task_delegate");
136
- expect(tools).toContain("task_status");
137
-
138
- const skills = (await (await col<SkillDoc>("skills")).scan({})).map((e) => e.doc.name);
139
- expect(skills).toContain("task_orchestrator");
140
- expect(skills).toContain("agent_spawner");
141
- });
142
-
143
- test("project/DAG rows from an older install are deleted too", async () => {
144
- const toolsCol = await col<ToolDoc>("tools");
145
- const now = Date.now();
146
- for (const id of RETIRED_PROJECT_TOOLS) {
147
- await toolsCol.put(id, {
148
- id, name: id, description: `Legacy project tool ${id}`, category: "projects",
149
- enabled: true, active: true, created_at: now, updated_at: now,
150
- });
151
- }
152
- for (const id of RETIRED_PROJECT_TOOLS) expect(await toolsCol.get(id)).toBeTruthy();
153
-
154
- await seedAllData();
155
-
156
- for (const id of RETIRED_PROJECT_TOOLS) expect(await toolsCol.get(id)).toBeUndefined();
157
- });
158
-
159
- test("no executor is registered for the retired tool", () => {
160
- const names = createAllTools({ tools: {} } as never).map((t) => t.name);
161
- expect(names).not.toContain(RETIRED_TOOL);
162
- for (const id of RETIRED_PROJECT_TOOLS) expect(names).not.toContain(id);
163
- for (const id of RETIRED_CANVAS_TOOLS) expect(names).not.toContain(id);
164
- expect(names).toContain("task_delegate");
165
- expect(names).toContain("a2ui_create_surface");
166
- });
167
-
168
- // Deleting the row is only half the job: search_knowledge queries the
169
- // capability index, so an entry indexed before the upgrade must not survive
170
- // the re-sync — otherwise the agent still discovers a tool with no executor.
171
- test("search_knowledge can no longer discover them after the upgrade", async () => {
172
- await writeLegacyRows();
173
- await syncToolsToIndex();
174
- await syncSkillsToIndex();
175
-
176
- const stale = await searchCapabilities("code bridge subagente CLI", { types: ["tool", "skill"], k: 25 });
177
- expect(stale.map((hit) => hit.rawId)).toContain(RETIRED_TOOL);
178
-
179
- await seedAllData(); // prunes the rows
180
- await syncToolsToIndex(); // full replace per type
181
- await syncSkillsToIndex();
182
-
183
- const fresh = await searchCapabilities("code bridge subagente CLI", { types: ["tool", "skill"], k: 25 });
184
- const ids = fresh.map((hit) => hit.rawId);
185
- expect(ids).not.toContain(RETIRED_TOOL);
186
- expect(ids).not.toContain(RETIRED_SKILL);
187
-
188
- // The index is still populated — the replace didn't just empty it.
189
- const alive = await searchCapabilities("delegar tarea agente", { types: ["tool"], k: 25 });
190
- expect(alive.map((hit) => hit.rawId)).toContain("task_delegate");
191
- });
192
- });
@@ -1,355 +0,0 @@
1
- /**
2
- * Tests for agent/run-store.ts — AgentRun lifecycle, checkpoint, lease.
3
- * Uses HIVE_DB_PATH=":memory:" so no state persists between runs.
4
- */
5
-
6
- process.env.HIVE_DB_PATH = ":memory:";
7
-
8
- import { describe, test, expect, beforeEach, afterEach } from "bun:test";
9
- import { closeHiveDb } from "../packages/core/src/storage/hivedb";
10
- import { ensureHiveDb } from "../packages/core/src/storage/bootstrap";
11
- import { col } from "../packages/core/src/storage/hive";
12
- import type { AgentRunDoc } from "../packages/core/src/storage/collections";
13
- import {
14
- createRun,
15
- checkpoint,
16
- completeRun,
17
- failRun,
18
- interruptRun,
19
- getRun,
20
- findRunsByStatus,
21
- findExpiredRuns,
22
- deserializeCheckpoint,
23
- type RunCheckpointState,
24
- } from "../packages/core/src/agent/run-store";
25
- import { getBootId, resetBootId } from "../packages/core/src/storage/boot-id";
26
-
27
- beforeEach(async () => {
28
- closeHiveDb();
29
- resetBootId();
30
- await ensureHiveDb();
31
- });
32
-
33
- afterEach(() => {
34
- closeHiveDb();
35
- });
36
-
37
- describe("run-store: createRun", () => {
38
- test("creates a running run with correct defaults", async () => {
39
- const run = await createRun({
40
- thread_id: "thread1",
41
- agent_id: "agent1",
42
- user_id: "user1",
43
- channel: "webchat",
44
- kind: "chat",
45
- max_iterations: 10,
46
- });
47
-
48
- expect(run.thread_id).toBe("thread1");
49
- expect(run.agent_id).toBe("agent1");
50
- expect(run.kind).toBe("chat");
51
- expect(run.status).toBe("running");
52
- expect(run.iterations_used).toBe(0);
53
- expect(run.max_iterations).toBe(10);
54
- expect(run.max_turns).toBeNull();
55
- expect(run.max_tokens).toBeNull();
56
- expect(run.goal).toBeNull();
57
- expect(run.resume_policy).toBe("resume");
58
- expect(run.error).toBeNull();
59
- expect(run.lease_expires_at).toBeGreaterThan(Date.now());
60
- expect(run.boot_id).toBe(getBootId());
61
- });
62
-
63
- test("creates with goal and budget overrides", async () => {
64
- const run = await createRun({
65
- thread_id: "thread1",
66
- agent_id: "agent1",
67
- user_id: "user1",
68
- channel: null,
69
- kind: "goal",
70
- max_iterations: 50,
71
- max_turns: 5,
72
- max_tokens: 100_000,
73
- goal: "build feature X",
74
- goal_check_tool: "verify_feature",
75
- resume_policy: "discard",
76
- });
77
-
78
- expect(run.kind).toBe("goal");
79
- expect(run.max_turns).toBe(5);
80
- expect(run.max_tokens).toBe(100_000);
81
- expect(run.goal).toBe("build feature X");
82
- expect(run.goal_check_tool).toBe("verify_feature");
83
- expect(run.resume_policy).toBe("discard");
84
- });
85
- });
86
-
87
- describe("run-store: checkpoint", () => {
88
- test("saves and deserializes checkpoint state", async () => {
89
- const run = await createRun({
90
- thread_id: "thread1",
91
- agent_id: "agent1",
92
- user_id: "user1",
93
- channel: "webchat",
94
- kind: "chat",
95
- max_iterations: 10,
96
- });
97
-
98
- const state: RunCheckpointState = {
99
- version: 1,
100
- messages: [
101
- { role: "system", content: "You are an agent" },
102
- { role: "user", content: "Hello" },
103
- { role: "assistant", content: "Hi there" },
104
- ],
105
- iterations: 3,
106
- totalInputTokens: 500,
107
- totalOutputTokens: 200,
108
- lastToolSignature: "",
109
- consecutiveRepeat: 0,
110
- idleIterations: 0,
111
- injectedToolNames: ["search_knowledge"],
112
- systemPromptSkillSections: ["## Skill: coding"],
113
- };
114
-
115
- await checkpoint(run.id, state);
116
-
117
- const saved = await getRun(run.id);
118
- expect(saved).not.toBeNull();
119
- expect(saved!.iterations_used).toBe(3);
120
- expect(saved!.tokens_used).toBe(700);
121
-
122
- const deserialized = deserializeCheckpoint(saved!);
123
- expect(deserialized).not.toBeNull();
124
- expect(deserialized!.iterations).toBe(3);
125
- expect(deserialized!.totalInputTokens).toBe(500);
126
- expect(deserialized!.totalOutputTokens).toBe(200);
127
- expect(deserialized!.messages.length).toBe(3);
128
- expect(deserialized!.injectedToolNames).toEqual(["search_knowledge"]);
129
- });
130
-
131
- test("normalizes a stray trailing system message (pre-source checkpoint) to a wrapped user turn", async () => {
132
- const run = await createRun({
133
- thread_id: "thread-stray-system",
134
- agent_id: "agent1",
135
- user_id: "user1",
136
- channel: "webchat",
137
- kind: "chat",
138
- max_iterations: 10,
139
- });
140
-
141
- // Checkpoints written before `source` existed could carry a second
142
- // system message — the compaction summary or a delegation notice —
143
- // after the real system prompt at index 0.
144
- const state: RunCheckpointState = {
145
- version: 1,
146
- messages: [
147
- { role: "system", content: "You are an agent" },
148
- { role: "user", content: "Hello" },
149
- { role: "system", content: "[Conversation Summary]: resumen previo" },
150
- ],
151
- iterations: 1,
152
- totalInputTokens: 10,
153
- totalOutputTokens: 10,
154
- lastToolSignature: "",
155
- consecutiveRepeat: 0,
156
- idleIterations: 0,
157
- injectedToolNames: [],
158
- systemPromptSkillSections: [],
159
- };
160
-
161
- await checkpoint(run.id, state);
162
- const saved = await getRun(run.id);
163
- const deserialized = deserializeCheckpoint(saved!);
164
-
165
- expect(deserialized).not.toBeNull();
166
- // index 0 stays the real system prompt.
167
- expect(deserialized!.messages[0].role).toBe("system");
168
- expect(deserialized!.messages[0].content).toBe("You are an agent");
169
- // the stray one is rewritten as a wrapped user turn, not dropped.
170
- expect(deserialized!.messages[2].role).toBe("user");
171
- expect(deserialized!.messages[2].content).toContain('<hive:internal_event source="legacy_internal">');
172
- expect(deserialized!.messages[2].content).toContain("resumen previo");
173
- // exactly one system message survives.
174
- expect(deserialized!.messages.filter((m) => m.role === "system")).toHaveLength(1);
175
- });
176
-
177
- test("truncates large tool results to stay under MAX_STATE_BYTES", async () => {
178
- const run = await createRun({
179
- thread_id: "thread1",
180
- agent_id: "agent1",
181
- user_id: "user1",
182
- channel: "webchat",
183
- kind: "chat",
184
- max_iterations: 10,
185
- });
186
-
187
- const bigContent = "x".repeat(200_000);
188
- const messages = [];
189
- for (let i = 0; i < 20; i++) {
190
- messages.push({ role: "user" as const, content: `msg ${i}` });
191
- messages.push({ role: "tool" as const, content: bigContent, tool_call_id: `tc_${i}` });
192
- }
193
- const state: RunCheckpointState = {
194
- version: 1,
195
- messages,
196
- iterations: 5,
197
- totalInputTokens: 0,
198
- totalOutputTokens: 0,
199
- lastToolSignature: "",
200
- consecutiveRepeat: 0,
201
- idleIterations: 0,
202
- injectedToolNames: [],
203
- systemPromptSkillSections: [],
204
- };
205
-
206
- await checkpoint(run.id, state);
207
-
208
- const saved = await getRun(run.id);
209
- expect(saved!.state_bytes).toBeLessThanOrEqual(1_500_000);
210
- });
211
-
212
- test("saves pending_tool_calls_json", async () => {
213
- const run = await createRun({
214
- thread_id: "thread1",
215
- agent_id: "agent1",
216
- user_id: "user1",
217
- channel: "webchat",
218
- kind: "chat",
219
- max_iterations: 10,
220
- });
221
-
222
- const state: RunCheckpointState = {
223
- version: 1,
224
- messages: [{ role: "user", content: "test" }],
225
- iterations: 2,
226
- totalInputTokens: 0,
227
- totalOutputTokens: 0,
228
- lastToolSignature: "",
229
- consecutiveRepeat: 0,
230
- idleIterations: 0,
231
- injectedToolNames: [],
232
- systemPromptSkillSections: [],
233
- };
234
-
235
- const pending = [
236
- { id: "tc1", function: { name: "memory_write", arguments: "{}" } },
237
- ];
238
-
239
- await checkpoint(run.id, state, pending);
240
-
241
- const saved = await getRun(run.id);
242
- expect(saved!.pending_tool_calls_json).not.toBeNull();
243
- const parsed = JSON.parse(saved!.pending_tool_calls_json!);
244
- expect(parsed[0].function.name).toBe("memory_write");
245
- });
246
- });
247
-
248
- describe("run-store: lifecycle transitions", () => {
249
- test("completeRun sets status to completed and clears checkpoint", async () => {
250
- const run = await createRun({
251
- thread_id: "thread1",
252
- agent_id: "agent1",
253
- user_id: "user1",
254
- channel: "webchat",
255
- kind: "chat",
256
- max_iterations: 10,
257
- });
258
-
259
- await completeRun(run.id);
260
-
261
- const saved = await getRun(run.id);
262
- expect(saved!.status).toBe("completed");
263
- expect(saved!.state_json).toBe("");
264
- expect(saved!.state_bytes).toBe(0);
265
- expect(saved!.pending_tool_calls_json).toBeNull();
266
- expect(saved!.finished_at).not.toBeNull();
267
- });
268
-
269
- test("failRun sets error message", async () => {
270
- const run = await createRun({
271
- thread_id: "thread1",
272
- agent_id: "agent1",
273
- user_id: "user1",
274
- channel: "webchat",
275
- kind: "worker",
276
- max_iterations: 10,
277
- });
278
-
279
- await failRun(run.id, "Something went wrong");
280
-
281
- const saved = await getRun(run.id);
282
- expect(saved!.status).toBe("failed");
283
- expect(saved!.error).toBe("Something went wrong");
284
- });
285
-
286
- test("interruptRun sets interrupted status", async () => {
287
- const run = await createRun({
288
- thread_id: "thread1",
289
- agent_id: "agent1",
290
- user_id: "user1",
291
- channel: "webchat",
292
- kind: "chat",
293
- max_iterations: 10,
294
- });
295
-
296
- await interruptRun(run.id, "Process restarted");
297
-
298
- const saved = await getRun(run.id);
299
- expect(saved!.status).toBe("interrupted");
300
- expect(saved!.error).toContain("Process restarted");
301
- });
302
- });
303
-
304
- describe("run-store: lease expiry", () => {
305
- test("findExpiredRuns returns runs with expired lease", async () => {
306
- const run = await createRun({
307
- thread_id: "thread1",
308
- agent_id: "agent1",
309
- user_id: "user1",
310
- channel: "webchat",
311
- kind: "chat",
312
- max_iterations: 10,
313
- });
314
-
315
- // Force lease expiry by setting lease_expires_at to past
316
- const c = await col<AgentRunDoc>("agentRuns");
317
- await c.put(run.id, { ...run, lease_expires_at: Date.now() - 1000 }, { expectedVersion: 1 });
318
-
319
- const expired = await findExpiredRuns();
320
- expect(expired.length).toBe(1);
321
- expect(expired[0].id).toBe(run.id);
322
- });
323
-
324
- test("findExpiredRuns skips runs with active lease", async () => {
325
- await createRun({
326
- thread_id: "thread1",
327
- agent_id: "agent1",
328
- user_id: "user1",
329
- channel: "webchat",
330
- kind: "chat",
331
- max_iterations: 10,
332
- });
333
-
334
- const expired = await findExpiredRuns();
335
- expect(expired.length).toBe(0);
336
- });
337
- });
338
-
339
- describe("run-store: findRunsByStatus", () => {
340
- test("finds runs by status index", async () => {
341
- const run1 = await createRun({ thread_id: "t1", agent_id: "a1", user_id: "u1", channel: null, kind: "chat", max_iterations: 10 });
342
- const run2 = await createRun({ thread_id: "t2", agent_id: "a2", user_id: "u1", channel: null, kind: "worker", max_iterations: 10 });
343
-
344
- await completeRun(run1.id);
345
- await failRun(run2.id, "err");
346
-
347
- const completed = await findRunsByStatus("completed");
348
- const failed = await findRunsByStatus("failed");
349
- const running = await findRunsByStatus("running");
350
-
351
- expect(completed.length).toBe(1);
352
- expect(failed.length).toBe(1);
353
- expect(running.length).toBe(0);
354
- });
355
- });
@@ -1,74 +0,0 @@
1
- /**
2
- * Scratchpad — integration tests
3
- *
4
- * Covers the first table migrated off SQLite onto a HiveDB document
5
- * collection: packages/core/src/agent/conversation-store.ts's scratchpad
6
- * functions on top of @johpaz/hive-db.
7
- *
8
- * Uses an in-memory index (HIVE_DB_PATH=":memory:") so no state
9
- * touches ~/.hive.
10
- */
11
-
12
- process.env.HIVE_DB_PATH = ":memory:";
13
-
14
- import { describe, test, expect, afterAll } from "bun:test";
15
- import {
16
- saveScratchpadNote,
17
- getScratchpad,
18
- deleteScratchpadNote,
19
- listAllScratchpadNotes,
20
- } from "../packages/core/src/agent/conversation-store";
21
- import { closeHiveDb } from "../packages/core/src/storage/hivedb";
22
-
23
- afterAll(() => {
24
- closeHiveDb();
25
- });
26
-
27
- describe("scratchpad (HiveDB collection)", () => {
28
- test("save then get round-trips a note", async () => {
29
- await saveScratchpadNote("t1", "greeting", "hola mundo", "agent");
30
- const notes = await getScratchpad("t1");
31
- expect(notes).toEqual([{ key: "greeting", value: "hola mundo" }]);
32
- });
33
-
34
- test("saving the same key again updates the value, not appends", async () => {
35
- await saveScratchpadNote("t2", "k", "v1");
36
- await saveScratchpadNote("t2", "k", "v2");
37
- const notes = await getScratchpad("t2");
38
- expect(notes).toEqual([{ key: "k", value: "v2" }]);
39
- });
40
-
41
- test("notes are scoped per thread", async () => {
42
- await saveScratchpadNote("t3", "a", "1");
43
- await saveScratchpadNote("t4", "b", "2");
44
- expect(await getScratchpad("t3")).toEqual([{ key: "a", value: "1" }]);
45
- expect(await getScratchpad("t4")).toEqual([{ key: "b", value: "2" }]);
46
- });
47
-
48
- test("most recently updated note comes first", async () => {
49
- await saveScratchpadNote("t5", "first", "1");
50
- await saveScratchpadNote("t5", "second", "2");
51
- const notes = await getScratchpad("t5");
52
- expect(notes.map((n) => n.key)).toEqual(["second", "first"]);
53
- });
54
-
55
- test("delete removes only the targeted note", async () => {
56
- await saveScratchpadNote("t6", "keep", "x");
57
- await saveScratchpadNote("t6", "gone", "y");
58
- await deleteScratchpadNote("t6", "gone");
59
- expect(await getScratchpad("t6")).toEqual([{ key: "keep", value: "x" }]);
60
- });
61
-
62
- test("unknown thread returns an empty list", async () => {
63
- expect(await getScratchpad("does-not-exist")).toEqual([]);
64
- });
65
-
66
- test("listAllScratchpadNotes spans every thread, most recent first, respecting limit", async () => {
67
- await saveScratchpadNote("global-a", "k", "v");
68
- await saveScratchpadNote("global-b", "k", "v");
69
- const all = await listAllScratchpadNotes(1);
70
- expect(all.length).toBe(1);
71
- expect(all[0].thread_id).toBe("global-b");
72
- expect(all[0].id).toBe("global-b:k");
73
- });
74
- });