@johpaz/hive-sdk 0.1.3 → 0.1.5

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 (272) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +78 -23
  3. package/bunfig.toml +4 -2
  4. package/docs/API-AGENTS.md +78 -27
  5. package/docs/API-CONTEXT-COMPILER.md +31 -34
  6. package/docs/API-TOOLS-SKILLS-CHANNELS.md +58 -22
  7. package/docs/HIVE-HARNESS.md +1 -1
  8. package/docs/INDEX.md +4 -4
  9. package/docs/TEMPLATE-HIVE-APP.md +10 -10
  10. package/package.json +9 -4
  11. package/packages/cli/package.json +2 -2
  12. package/packages/cli/src/commands/create-app.test.ts +36 -7
  13. package/packages/cli/src/commands/init.ts +3 -3
  14. package/packages/cli/src/commands/run.ts +1 -1
  15. package/packages/cli/src/commands/test.ts +37 -25
  16. package/packages/cli/src/commands/trace.ts +30 -28
  17. package/packages/cli/templates/hive-app/.env.example +10 -2
  18. package/packages/cli/templates/hive-app/README.md +103 -0
  19. package/packages/cli/templates/hive-app/hive.config.ts +9 -3
  20. package/packages/cli/templates/hive-app/src/agents/coordinator.ts +8 -1
  21. package/packages/cli/templates/hive-app/src/main.ts +12 -19
  22. package/packages/core/package.json +5 -4
  23. package/packages/core/src/agent/acceptance-checks.ts +166 -0
  24. package/packages/core/src/agent/agent-catalog.ts +348 -0
  25. package/packages/core/src/agent/agent-loop.ts +1373 -0
  26. package/packages/core/src/agent/capability-search.ts +186 -0
  27. package/packages/core/src/agent/catalog-selector.ts +103 -0
  28. package/packages/core/src/agent/{Compaction.ts → compaction.ts} +86 -63
  29. package/packages/core/src/agent/context-compiler.ts +689 -0
  30. package/packages/core/src/agent/conversation-store.ts +381 -0
  31. package/packages/core/src/agent/curator.ts +276 -0
  32. package/packages/core/src/agent/delegation-runtime.ts +241 -0
  33. package/packages/core/src/agent/goal-runner.ts +323 -0
  34. package/packages/core/src/agent/index.ts +17 -12
  35. package/packages/core/src/agent/llm-client.ts +266 -0
  36. package/packages/core/src/agent/llm-providers/anthropic.ts +264 -0
  37. package/packages/core/src/agent/llm-providers/deepseek.ts +8 -0
  38. package/packages/core/src/agent/{providers → llm-providers}/gemini.ts +98 -60
  39. package/packages/core/src/agent/llm-providers/groq.ts +5 -0
  40. package/packages/core/src/agent/llm-providers/hiveagents.ts +253 -0
  41. package/packages/core/src/agent/{providers → llm-providers}/interface.ts +73 -13
  42. package/packages/core/src/agent/llm-providers/kimi.ts +8 -0
  43. package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
  44. package/packages/core/src/agent/llm-providers/mistral.ts +5 -0
  45. package/packages/core/src/agent/llm-providers/modelscope.ts +5 -0
  46. package/packages/core/src/agent/llm-providers/nvidia.ts +5 -0
  47. package/packages/core/src/agent/{providers → llm-providers}/ollama.ts +31 -5
  48. package/packages/core/src/agent/llm-providers/openai-compat-base.ts +418 -0
  49. package/packages/core/src/agent/llm-providers/openai.ts +5 -0
  50. package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
  51. package/packages/core/src/agent/llm-providers/openrouter.ts +5 -0
  52. package/packages/core/src/agent/llm-providers/qwen.ts +5 -0
  53. package/packages/core/src/agent/llm-providers/z-ai.ts +5 -0
  54. package/packages/core/src/agent/minimal-loadout.ts +47 -0
  55. package/packages/core/src/agent/playbook-selector.ts +119 -0
  56. package/packages/core/src/agent/{PromptBuilder.ts → prompt-builder.ts} +21 -22
  57. package/packages/core/src/{harness → agent}/proof-packet.ts +16 -21
  58. package/packages/core/src/agent/providers/index.ts +35 -16
  59. package/packages/core/src/agent/reflector.ts +320 -0
  60. package/packages/core/src/agent/routing-intent.ts +22 -0
  61. package/packages/core/src/{harness → agent}/run-epoch.ts +4 -3
  62. package/packages/core/src/{harness → agent}/run-store.ts +142 -81
  63. package/packages/core/src/agent/{Service.ts → service.ts} +37 -26
  64. package/packages/core/src/agent/skill-selector.ts +374 -0
  65. package/packages/core/src/agent/stuck-loop.ts +209 -0
  66. package/packages/core/src/agent/{selectors/ToolSelector.ts → tool-selector.ts} +188 -178
  67. package/packages/core/src/{ace/Tracer.ts → agent/tracer.ts} +37 -27
  68. package/packages/core/src/api/createAgent.test.ts +139 -27
  69. package/packages/core/src/api/createAgent.ts +232 -44
  70. package/packages/core/src/artifacts/store.ts +162 -0
  71. package/packages/core/src/canvas/canvas-manager.ts +161 -0
  72. package/packages/core/src/canvas/canvas.test.ts +8 -4
  73. package/packages/core/src/canvas/emitter.ts +131 -80
  74. package/packages/core/src/canvas/index.ts +1 -3
  75. package/packages/core/src/channels/base.ts +9 -1
  76. package/packages/core/src/channels/discord.ts +5 -4
  77. package/packages/core/src/channels/manager.ts +122 -30
  78. package/packages/core/src/channels/slack.ts +5 -4
  79. package/packages/core/src/channels/telegram.ts +36 -6
  80. package/packages/core/src/channels/webchat.ts +11 -10
  81. package/packages/core/src/channels/whatsapp.ts +23 -7
  82. package/packages/core/src/config/index.ts +13 -2
  83. package/packages/core/src/config/loader.ts +71 -29
  84. package/packages/core/src/ethics/EthicsGuard.test.ts +90 -36
  85. package/packages/core/src/ethics/EthicsGuard.ts +51 -47
  86. package/packages/core/src/events/agent-bus.ts +44 -68
  87. package/packages/core/src/events/channel-narration.ts +150 -0
  88. package/packages/core/src/events/narration.ts +82 -0
  89. package/packages/core/src/events/tool-narration.ts +62 -0
  90. package/packages/core/src/gateway/delegation-groups.ts +258 -0
  91. package/packages/core/src/{harness → gateway}/durable-queue.ts +102 -42
  92. package/packages/core/src/{harness → gateway}/job-store.ts +85 -48
  93. package/packages/core/src/gateway/lane-queue.ts +173 -0
  94. package/packages/core/src/gateway/notification-inbox.ts +57 -0
  95. package/packages/core/src/gateway/server.ts +1 -1
  96. package/packages/core/src/harness/index.ts +46 -27
  97. package/packages/core/src/index.ts +33 -20
  98. package/packages/core/src/mcp/hot-reload.ts +32 -23
  99. package/packages/core/src/mcp/index.ts +6 -3
  100. package/packages/core/src/mcp/singleton.ts +1 -4
  101. package/packages/core/src/mcp/tool-sync.ts +138 -0
  102. package/packages/core/src/memory/Scratchpad.test.ts +39 -20
  103. package/packages/core/src/memory/Scratchpad.ts +27 -34
  104. package/packages/core/src/multimodal/vision-service.ts +44 -38
  105. package/packages/core/src/resilience/retry.ts +95 -0
  106. package/packages/core/src/scheduler/CronScheduler.ts +334 -287
  107. package/packages/core/src/scheduler/index.ts +9 -7
  108. package/packages/core/src/scheduler/integration.ts +46 -26
  109. package/packages/core/src/scheduler/scheduler.test.ts +9 -13
  110. package/packages/core/src/scheduler/types.ts +7 -2
  111. package/packages/core/src/security/Pairing.ts +1 -1
  112. package/packages/core/src/skills/bundled/a2ui/a2ui_dashboard/SKILL.md +176 -0
  113. package/packages/core/src/skills/bundled/a2ui/a2ui_form/SKILL.md +202 -0
  114. package/packages/core/src/skills/bundled/a2ui/a2ui_interactive/SKILL.md +206 -0
  115. package/packages/core/src/skills/bundled/agents/agent_spawner/SKILL.md +173 -0
  116. package/packages/core/src/skills/bundled/agents/memory_manager/SKILL.md +143 -0
  117. package/packages/core/src/skills/bundled/agents/research_and_remember/SKILL.md +139 -0
  118. package/packages/core/src/skills/bundled/agents/task_orchestrator/SKILL.md +98 -0
  119. package/packages/core/src/skills/bundled/api/api_client/SKILL.md +132 -0
  120. package/packages/core/src/skills/bundled/cli/cli_pipeline/SKILL.md +135 -0
  121. package/packages/core/src/skills/bundled/cli/cli_safe_exec/SKILL.md +125 -0
  122. package/packages/core/src/skills/bundled/cli/software_engineering/SKILL.md +23 -0
  123. package/packages/core/src/skills/bundled/cron_manager/SKILL.md +188 -0
  124. package/packages/core/src/skills/bundled/cron_reminder/SKILL.md +112 -0
  125. package/packages/core/src/skills/bundled/filesystem/file_manager/SKILL.md +118 -0
  126. package/packages/core/src/skills/bundled/filesystem/file_read_and_summarize/SKILL.md +109 -0
  127. package/packages/core/src/skills/bundled/filesystem/file_writer/SKILL.md +129 -0
  128. package/packages/core/src/skills/bundled/filesystem/workspace_file_operator/SKILL.md +22 -0
  129. package/packages/core/src/skills/bundled/office/office_document_manager/SKILL.md +262 -0
  130. package/packages/core/src/skills/bundled/search_knowledge/capability_discovery/SKILL.md +75 -0
  131. package/packages/core/src/skills/bundled/web/browser_automate/SKILL.md +120 -0
  132. package/packages/core/src/skills/bundled/web/browser_scrape/SKILL.md +109 -0
  133. package/packages/core/src/skills/bundled/web/web_monitor/SKILL.md +127 -0
  134. package/packages/core/src/skills/bundled/web/web_research/SKILL.md +119 -0
  135. package/packages/core/src/skills/bundled-data.generated.ts +731 -2678
  136. package/packages/core/src/skills/skills.test.ts +52 -11
  137. package/packages/core/src/{harness → storage}/boot-id.ts +5 -2
  138. package/packages/core/src/storage/bootstrap.ts +151 -0
  139. package/packages/core/src/storage/causal-events.ts +84 -0
  140. package/packages/core/src/storage/collections.ts +680 -0
  141. package/packages/core/src/storage/crypto.ts +205 -74
  142. package/packages/core/src/{harness/db-helpers.ts → storage/hive.ts} +63 -7
  143. package/packages/core/src/storage/hivedb.ts +61 -0
  144. package/packages/core/src/storage/index.ts +111 -17
  145. package/packages/core/src/storage/model-id.ts +53 -0
  146. package/packages/core/src/storage/onboarding.ts +540 -972
  147. package/packages/core/src/storage/reconcile.ts +238 -0
  148. package/packages/core/src/storage/seed.ts +572 -406
  149. package/packages/core/src/storage/usage.ts +285 -225
  150. package/packages/core/src/storage/user-email.ts +11 -0
  151. package/packages/core/src/swarm/AgentExecutor.ts +1 -1
  152. package/packages/core/src/swarm/EventBridge.ts +1 -1
  153. package/packages/core/src/swarm/index.ts +12 -9
  154. package/packages/core/src/tool-runtime/index.ts +146 -23
  155. package/packages/core/src/tool-runtime/tool-worker.ts +2 -2
  156. package/packages/core/src/tool-runtime/worker-tools.ts +27 -0
  157. package/packages/core/src/{canvas/a2ui-tools.ts → tools/a2ui/index.ts} +17 -8
  158. package/packages/core/src/tools/agents/get-available-models.ts +36 -54
  159. package/packages/core/src/tools/agents/index.ts +784 -292
  160. package/packages/core/src/tools/api/api-request.test.ts +164 -0
  161. package/packages/core/src/tools/api/api-request.ts +174 -0
  162. package/packages/core/src/tools/api/index.ts +16 -0
  163. package/packages/core/src/tools/cli/index.ts +4 -0
  164. package/packages/core/src/tools/core/index.ts +281 -112
  165. package/packages/core/src/tools/cron/index.ts +121 -124
  166. package/packages/core/src/tools/index.ts +63 -78
  167. package/packages/core/src/tools/office/office-escribir-xlsx.ts +3 -1
  168. package/packages/core/src/tools/types.ts +3 -1
  169. package/packages/core/src/tools/web/artifact-inspect.ts +23 -0
  170. package/packages/core/src/tools/web/browser-screenshot.ts +26 -5
  171. package/packages/core/src/tools/web/browser-service.ts +5 -0
  172. package/packages/core/src/tools/web/browser-type.ts +3 -8
  173. package/packages/core/src/tools/web/index.ts +4 -4
  174. package/packages/core/src/voice/index.ts +89 -63
  175. package/packages/core/src/workers/agent.worker.ts +2 -2
  176. package/packages/core/src/workers/workers.test.ts +3 -10
  177. package/scripts/bump-version.ts +248 -0
  178. package/scripts/generate-skill-bundle.ts +108 -0
  179. package/test/agent-loop-terminal-synthesis.test.ts +32 -0
  180. package/test/catalog-agents-stay-enabled.test.ts +117 -0
  181. package/test/causal-events.test.ts +117 -0
  182. package/test/compaction.test.ts +105 -0
  183. package/test/context-compiler.test.ts +269 -0
  184. package/test/curator.test.ts +130 -0
  185. package/test/durable-queue.test.ts +114 -0
  186. package/test/harness-barrel.test.ts +64 -0
  187. package/test/hive-helpers.test.ts +130 -0
  188. package/test/hivedb-search.test.ts +189 -0
  189. package/test/internal-turns.test.ts +166 -0
  190. package/test/job-idempotency.test.ts +68 -0
  191. package/test/job-retry-backoff.test.ts +184 -0
  192. package/test/job-store.test.ts +381 -0
  193. package/test/llm-retry.test.ts +97 -0
  194. package/test/memory-perf.test.ts +774 -0
  195. package/test/minimal-loadout.test.ts +78 -0
  196. package/test/model-catalog.test.ts +105 -0
  197. package/test/preload.ts +12 -0
  198. package/test/reflector.test.ts +320 -0
  199. package/test/retention-cap.test.ts +91 -0
  200. package/test/retired-capabilities-pruned.test.ts +192 -0
  201. package/test/run-store.test.ts +355 -0
  202. package/test/scratchpad.test.ts +74 -0
  203. package/test/secrets-durability.test.ts +119 -0
  204. package/test/seed-model-reseed.test.ts +155 -0
  205. package/test/setup-agent-seed.test.ts +264 -0
  206. package/test/tool-inventory.test.ts +65 -0
  207. package/test/tool-runtime.test.ts +258 -0
  208. package/test/toon.test.ts +429 -0
  209. package/tsconfig.json +2 -0
  210. package/packages/core/src/ace/Curator.ts +0 -158
  211. package/packages/core/src/ace/Reflector.ts +0 -200
  212. package/packages/core/src/ace/index.ts +0 -4
  213. package/packages/core/src/agent/AgentRunner.ts +0 -711
  214. package/packages/core/src/agent/ContextCompiler.ts +0 -567
  215. package/packages/core/src/agent/ContextGuard.ts +0 -91
  216. package/packages/core/src/agent/ConversationStore.ts +0 -254
  217. package/packages/core/src/agent/Hooks.ts +0 -166
  218. package/packages/core/src/agent/StuckLoop.ts +0 -133
  219. package/packages/core/src/agent/providers/LLMClient.ts +0 -149
  220. package/packages/core/src/agent/providers/anthropic.ts +0 -212
  221. package/packages/core/src/agent/providers/openai-compat.ts +0 -231
  222. package/packages/core/src/agent/selectors/PlaybookSelector.ts +0 -121
  223. package/packages/core/src/agent/selectors/SkillSelector.ts +0 -322
  224. package/packages/core/src/agent/selectors/index.ts +0 -6
  225. package/packages/core/src/auth/auth.ts +0 -121
  226. package/packages/core/src/auth/index.ts +0 -1
  227. package/packages/core/src/canvas/CanvasManager.ts +0 -390
  228. package/packages/core/src/canvas/canvas-tools.ts +0 -448
  229. package/packages/core/src/harness/collections.ts +0 -98
  230. package/packages/core/src/harness/goal-verifier.ts +0 -141
  231. package/packages/core/src/harness/harness.test.ts +0 -236
  232. package/packages/core/src/harness/reconcile.ts +0 -149
  233. package/packages/core/src/mcp/MCPToolAdapter.ts +0 -176
  234. package/packages/core/src/multimodal/VisionService.ts +0 -293
  235. package/packages/core/src/scheduler/dag/AgentExecutor.ts +0 -53
  236. package/packages/core/src/scheduler/dag/DAGScheduler.ts +0 -250
  237. package/packages/core/src/scheduler/dag/EventBridge.ts +0 -122
  238. package/packages/core/src/scheduler/dag/TaskGraph.ts +0 -192
  239. package/packages/core/src/scheduler/dag/TaskNode.ts +0 -97
  240. package/packages/core/src/scheduler/dag/TaskResult.ts +0 -22
  241. package/packages/core/src/scheduler/dag/errors.ts +0 -37
  242. package/packages/core/src/scheduler/dag/index.ts +0 -26
  243. package/packages/core/src/scheduler/dag/presets/ResearchPreset.ts +0 -97
  244. package/packages/core/src/scheduler/dag/strategies/ParallelStrategy.ts +0 -21
  245. package/packages/core/src/scheduler/dag/strategies/PriorityStrategy.ts +0 -46
  246. package/packages/core/src/storage/HiveDBStorage.ts +0 -64
  247. package/packages/core/src/storage/SQLiteStorage.ts +0 -414
  248. package/packages/core/src/storage/hiveSeed.ts +0 -308
  249. package/packages/core/src/storage/hiveStorage.test.ts +0 -38
  250. package/packages/core/src/storage/schema.ts +0 -689
  251. package/packages/core/src/storage/storage.test.ts +0 -37
  252. package/packages/core/src/swarm/AgentBus.ts +0 -460
  253. package/packages/core/src/swarm/EventBus.ts +0 -169
  254. package/packages/core/src/swarm/WorkerPool.ts +0 -236
  255. package/packages/core/src/tools/bridge-events.ts +0 -26
  256. package/packages/core/src/tools/canvas/index.ts +0 -375
  257. package/packages/core/src/tools/codebridge/index.ts +0 -342
  258. package/packages/core/src/tools/meeting/index.ts +0 -353
  259. package/packages/core/src/tools/projects/index.ts +0 -37
  260. package/packages/core/src/tools/projects/project-create.ts +0 -94
  261. package/packages/core/src/tools/projects/project-done.ts +0 -66
  262. package/packages/core/src/tools/projects/project-fail.ts +0 -66
  263. package/packages/core/src/tools/projects/project-list.ts +0 -96
  264. package/packages/core/src/tools/projects/project-update.ts +0 -72
  265. package/packages/core/src/tools/projects/task-create.ts +0 -68
  266. package/packages/core/src/tools/projects/task-evaluate.ts +0 -93
  267. package/packages/core/src/tools/projects/task-update.ts +0 -93
  268. package/packages/core/src/tools/voice/index.ts +0 -104
  269. package/packages/core/src/tools/web/api-request.test.ts +0 -170
  270. package/packages/core/src/tools/web/api-request.ts +0 -239
  271. package/test/setup-db.ts +0 -216
  272. /package/packages/core/src/agent/{NativeTools.ts → native-tools.ts} +0 -0
@@ -1,49 +1,48 @@
1
1
  /**
2
- * run-store — persistent checkpoint + lease for harness runs. Ported from
3
- * `hive`'s agent/run-store.ts.
2
+ * run-store — persistent checkpoint + lease for agentRuns.
4
3
  *
5
- * A HarnessRunDoc tracks the lifecycle of a single durable agent invocation
4
+ * An AgentRun tracks the lifecycle of a single agent-loop invocation
6
5
  * (chat turn, worker task, goal run). Its checkpoint (state_json) allows
7
6
  * resuming after a crash: messages, iteration count, token totals and
8
- * pending tool calls are persisted after every round-trip — when the host
9
- * app chooses to call `checkpoint()` from its own agent loop (this module
10
- * does not wire itself into `AgentRunner` automatically).
7
+ * pending tool calls are persisted after every round-trip.
11
8
  *
12
9
  * All write operations use OCC (expectedVersion). Only the owning loop
13
10
  * should write to a run; single-writer pattern keeps contention minimal.
14
11
  */
15
12
 
16
- import { col, updateDoc, nextId } from "./db-helpers";
17
- import type { HarnessRunDoc } from "./collections";
18
- import { getBootId } from "./boot-id";
13
+ import { col, updateDoc, nextId, toIndexable } from "../storage/hive";
14
+ import type { AgentRunDoc } from "../storage/collections";
15
+ import { getBootId } from "../storage/boot-id";
19
16
  import { logger } from "../utils/logger";
17
+ import { loadConfig } from "../config/loader";
18
+ import type { LLMMessage } from "./llm-client";
20
19
  import type { RunEpoch } from "./run-epoch";
20
+ import { formatInternalEvent } from "./conversation-store";
21
21
 
22
- const log = logger.child("harness:run-store");
22
+ const log = logger.child("run-store");
23
23
 
24
- const COLLECTION = "harness_agentRuns";
25
24
  const MAX_STATE_BYTES = 1_500_000;
25
+ const MAX_RETRIES = 5;
26
26
 
27
- let leaseRenewIntervalMs = 30_000;
28
- let leaseDurationMs = 2 * 60 * 1000;
27
+ function runLeaseDurationMs(): number {
28
+ return loadConfig().harness?.runLeaseMs ?? 2 * 60 * 1000;
29
+ }
29
30
 
30
- /** Override the run lease duration / renewal interval (defaults: 2min / 30s). */
31
- export function setRunLeaseConfig(opts: { leaseDurationMs?: number; leaseRenewIntervalMs?: number }): void {
32
- if (opts.leaseDurationMs !== undefined) leaseDurationMs = opts.leaseDurationMs;
33
- if (opts.leaseRenewIntervalMs !== undefined) leaseRenewIntervalMs = opts.leaseRenewIntervalMs;
31
+ function leaseRenewIntervalMs(): number {
32
+ return loadConfig().harness?.leaseRenewMs ?? 30_000;
34
33
  }
35
34
 
36
35
  export interface RunCheckpointState {
37
36
  version: 1
38
- messages: unknown[]
37
+ messages: LLMMessage[]
39
38
  iterations: number
40
39
  totalInputTokens: number
41
40
  totalOutputTokens: number
42
- lastToolSignature?: string
43
- consecutiveRepeat?: number
44
- idleIterations?: number
45
- injectedToolNames?: string[]
46
- systemPromptSkillSections?: string[]
41
+ lastToolSignature: string
42
+ consecutiveRepeat: number
43
+ idleIterations: number
44
+ injectedToolNames: string[]
45
+ systemPromptSkillSections: string[]
47
46
  }
48
47
 
49
48
  /** Whole-job acceptance criterion (harness-engineering "proof" concept). */
@@ -59,22 +58,23 @@ export interface CreateRunInput {
59
58
  agent_id: string
60
59
  user_id: string
61
60
  channel: string | null
62
- kind: string
61
+ kind: AgentRunDoc["kind"]
63
62
  max_iterations: number
64
63
  max_turns?: number | null
65
64
  max_tokens?: number | null
66
65
  goal?: string | null
67
66
  goal_check_tool?: string | null
68
- resume_policy?: HarnessRunDoc["resume_policy"]
67
+ resume_policy?: AgentRunDoc["resume_policy"]
69
68
  acceptance?: AcceptanceCriterion[]
70
69
  epoch?: RunEpoch
70
+ catalog_agent_id?: string | null
71
71
  }
72
72
 
73
- export async function createRun(input: CreateRunInput): Promise<HarnessRunDoc> {
73
+ export async function createRun(input: CreateRunInput): Promise<AgentRunDoc> {
74
74
  const id = crypto.randomUUID().replace(/-/g, "").slice(0, 16);
75
75
  const now = Date.now();
76
76
  const bootId = getBootId();
77
- const doc: HarnessRunDoc = {
77
+ const doc: AgentRunDoc = {
78
78
  id,
79
79
  thread_id: input.thread_id,
80
80
  agent_id: input.agent_id,
@@ -96,66 +96,72 @@ export async function createRun(input: CreateRunInput): Promise<HarnessRunDoc> {
96
96
  pending_tool_calls_json: null,
97
97
  checkpointed_at: now,
98
98
  boot_id: bootId,
99
- lease_expires_at: now + leaseDurationMs,
99
+ lease_expires_at: now + runLeaseDurationMs(),
100
100
  resume_policy: input.resume_policy ?? "resume",
101
101
  acceptance_json: input.acceptance ? JSON.stringify(input.acceptance) : null,
102
102
  epoch_json: input.epoch ? JSON.stringify(input.epoch) : null,
103
+ catalog_agent_id: toIndexable(input.catalog_agent_id),
103
104
  error: null,
104
105
  created_at: now,
105
106
  updated_at: now,
106
107
  finished_at: null,
107
108
  };
108
- const c = await col<HarnessRunDoc>(COLLECTION);
109
+ const c = await col<AgentRunDoc>("agentRuns");
109
110
  await c.put(id, doc, { expectedVersion: 0 });
110
111
  log.info(`[createRun] Run ${id} created (agent=${input.agent_id} kind=${input.kind})`);
111
112
  return doc;
112
113
  }
113
114
 
114
- /** Save a checkpoint to the run: serialize messages, trim state if too big. */
115
+ /**
116
+ * Save a checkpoint to the run: serialize messages, trim state if too big.
117
+ * Uses updateDoc (OCC retry x5).
118
+ */
115
119
  export async function checkpoint(
116
120
  runId: string,
117
121
  state: RunCheckpointState,
118
122
  pendingToolCalls?: unknown[] | null
119
- ): Promise<HarnessRunDoc> {
120
- const serialized = JSON.stringify(state);
121
- let stateJson = serialized;
122
- let stateBytes = new TextEncoder().encode(serialized).length;
123
+ ): Promise<AgentRunDoc> {
124
+ const serialized = serializeCheckpoint(state);
125
+ let stateJson = serialized.json;
126
+ let stateBytes = serialized.bytes;
123
127
 
124
128
  if (stateBytes > MAX_STATE_BYTES) {
125
129
  stateJson = truncateState(state);
126
130
  stateBytes = new TextEncoder().encode(stateJson).length;
127
131
  }
128
132
 
129
- const patch: Partial<HarnessRunDoc> = {
133
+ const patch: Partial<AgentRunDoc> = {
130
134
  state_json: stateJson,
131
135
  state_bytes: stateBytes,
132
136
  pending_tool_calls_json: pendingToolCalls ? JSON.stringify(pendingToolCalls) : null,
133
137
  checkpointed_at: Date.now(),
134
138
  iterations_used: state.iterations,
135
139
  tokens_used: state.totalInputTokens + state.totalOutputTokens,
136
- lease_expires_at: Date.now() + leaseDurationMs,
140
+ lease_expires_at: Date.now() + runLeaseDurationMs(),
137
141
  boot_id: getBootId(),
138
142
  updated_at: Date.now(),
139
143
  };
140
144
 
141
- return updateDoc<HarnessRunDoc>(COLLECTION, runId, patch);
145
+ return updateDoc<AgentRunDoc>("agentRuns", runId, patch);
142
146
  }
143
147
 
144
- export async function bumpTurn(runId: string, tokensDelta: number): Promise<HarnessRunDoc> {
148
+ /**
149
+ * Bump turns_used and update lease.
150
+ */
151
+ export async function bumpTurn(runId: string, tokensDelta: number): Promise<AgentRunDoc> {
145
152
  const existing = await getRun(runId);
146
153
  if (!existing) throw new Error(`Run ${runId} not found`);
147
- return updateDoc<HarnessRunDoc>(COLLECTION, runId, {
154
+ return updateDoc<AgentRunDoc>("agentRuns", runId, {
148
155
  turns_used: existing.turns_used + 1,
149
156
  tokens_used: existing.tokens_used + tokensDelta,
150
- lease_expires_at: Date.now() + leaseDurationMs,
157
+ lease_expires_at: Date.now() + runLeaseDurationMs(),
151
158
  updated_at: Date.now(),
152
159
  });
153
160
  }
154
161
 
155
162
  export async function completeRun(runId: string, finalContent?: string): Promise<void> {
156
163
  const now = Date.now();
157
- void finalContent;
158
- await updateDoc<HarnessRunDoc>(COLLECTION, runId, {
164
+ await updateDoc<AgentRunDoc>("agentRuns", runId, {
159
165
  status: "completed",
160
166
  state_json: "",
161
167
  state_bytes: 0,
@@ -163,13 +169,13 @@ export async function completeRun(runId: string, finalContent?: string): Promise
163
169
  lease_expires_at: now,
164
170
  finished_at: now,
165
171
  updated_at: now,
166
- } as Partial<HarnessRunDoc>);
172
+ } as Partial<AgentRunDoc>);
167
173
  log.info(`[completeRun] Run ${runId} completed`);
168
174
  }
169
175
 
170
176
  export async function failRun(runId: string, error: string): Promise<void> {
171
177
  const now = Date.now();
172
- await updateDoc<HarnessRunDoc>(COLLECTION, runId, {
178
+ await updateDoc<AgentRunDoc>("agentRuns", runId, {
173
179
  status: "failed",
174
180
  error,
175
181
  lease_expires_at: now,
@@ -184,7 +190,7 @@ export async function failRun(runId: string, error: string): Promise<void> {
184
190
 
185
191
  export async function interruptRun(runId: string, reason: string): Promise<void> {
186
192
  const now = Date.now();
187
- await updateDoc<HarnessRunDoc>(COLLECTION, runId, {
193
+ await updateDoc<AgentRunDoc>("agentRuns", runId, {
188
194
  status: "interrupted",
189
195
  error: reason,
190
196
  lease_expires_at: now,
@@ -196,46 +202,50 @@ export async function interruptRun(runId: string, reason: string): Promise<void>
196
202
 
197
203
  /**
198
204
  * Take ownership of an existing run before (re-)executing it. After a crash,
199
- * reconcile leaves the row "interrupted" with the dead process's boot_id;
200
- * both must be reset before resuming.
205
+ * reconcile leaves the row "interrupted" with the dead process's boot_id; the
206
+ * lease renewer self-stops unless status is "running", so both must be reset.
201
207
  */
202
208
  export async function reclaimRun(runId: string): Promise<void> {
203
209
  const now = Date.now();
204
- await updateDoc<HarnessRunDoc>(COLLECTION, runId, {
210
+ await updateDoc<AgentRunDoc>("agentRuns", runId, {
205
211
  status: "running",
206
212
  boot_id: getBootId(),
207
- lease_expires_at: now + leaseDurationMs,
213
+ lease_expires_at: now + runLeaseDurationMs(),
208
214
  error: null,
209
215
  finished_at: null,
210
216
  updated_at: now,
211
217
  });
212
218
  }
213
219
 
214
- export async function getRun(runId: string): Promise<HarnessRunDoc | null> {
215
- const c = await col<HarnessRunDoc>(COLLECTION);
220
+ export async function getRun(runId: string): Promise<AgentRunDoc | null> {
221
+ const c = await col<AgentRunDoc>("agentRuns");
216
222
  const entry = await c.get(runId);
217
223
  return entry ? entry.doc : null;
218
224
  }
219
225
 
220
- export async function findRunsByStatus(status: HarnessRunDoc["status"]): Promise<HarnessRunDoc[]> {
221
- const c = await col<HarnessRunDoc>(COLLECTION);
226
+ export async function findRunsByStatus(status: AgentRunDoc["status"]): Promise<AgentRunDoc[]> {
227
+ const c = await col<AgentRunDoc>("agentRuns");
222
228
  const entries = await c.findBy("status", status);
223
229
  return entries.map((e) => e.doc);
224
230
  }
225
231
 
226
- export async function findRunsByThread(threadId: string): Promise<HarnessRunDoc[]> {
227
- const c = await col<HarnessRunDoc>(COLLECTION);
232
+ export async function findRunsByThread(threadId: string): Promise<AgentRunDoc[]> {
233
+ const c = await col<AgentRunDoc>("agentRuns");
228
234
  const entries = await c.findBy("thread_id", threadId);
229
235
  return entries.map((e) => e.doc);
230
236
  }
231
237
 
232
- export async function findExpiredRuns(): Promise<HarnessRunDoc[]> {
238
+ /**
239
+ * Find runs whose lease has expired (status=running + lease_expires_at < now).
240
+ */
241
+ export async function findExpiredRuns(): Promise<AgentRunDoc[]> {
233
242
  const running = await findRunsByStatus("running");
234
243
  const now = Date.now();
235
244
  return running.filter((r) => r.lease_expires_at < now);
236
245
  }
237
246
 
238
- export function deserializeAcceptance(run: HarnessRunDoc): AcceptanceCriterion[] | null {
247
+ /** Deserialize acceptance criteria back from AgentRunDoc.acceptance_json, or null if none were set. */
248
+ export function deserializeAcceptance(run: AgentRunDoc): AcceptanceCriterion[] | null {
239
249
  if (!run.acceptance_json) return null;
240
250
  try {
241
251
  return JSON.parse(run.acceptance_json) as AcceptanceCriterion[];
@@ -245,7 +255,8 @@ export function deserializeAcceptance(run: HarnessRunDoc): AcceptanceCriterion[]
245
255
  }
246
256
  }
247
257
 
248
- export function deserializeEpoch(run: HarnessRunDoc): RunEpoch | null {
258
+ /** Deserialize the fixed-worker epoch back from AgentRunDoc.epoch_json, or null if unset. */
259
+ export function deserializeEpoch(run: AgentRunDoc): RunEpoch | null {
249
260
  if (!run.epoch_json) return null;
250
261
  try {
251
262
  return JSON.parse(run.epoch_json) as RunEpoch;
@@ -255,35 +266,95 @@ export function deserializeEpoch(run: HarnessRunDoc): RunEpoch | null {
255
266
  }
256
267
  }
257
268
 
258
- /** Deserialize a checkpoint, or null if the run has no checkpoint. */
259
- export function deserializeCheckpoint(run: HarnessRunDoc): RunCheckpointState | null {
269
+ /**
270
+ * Deserialize a checkpoint back into RunCheckpointState, or null if the
271
+ * run has no checkpoint (empty state_json — chat runs that never promoted
272
+ * to durable).
273
+ */
274
+ /**
275
+ * Checkpoints written before `source`-based internal events existed may still
276
+ * carry a stray `role:"system"` message after index 0 — either the
277
+ * compaction summary (formerly prepended to `ctx.messages`) or a delegation
278
+ * fan-in notice. `messages[0]` is always the real system prompt and stays;
279
+ * anything after it must not reach the provider as a second system message
280
+ * (see gemini.ts/anthropic.ts, which hoist ALL role:"system" messages into a
281
+ * single system instruction). Rewrite those as wrapped user turns in place —
282
+ * this is a read-time fixup, not a migration, so no version bump is needed.
283
+ */
284
+ function normalizeStraySystemMessages(messages: LLMMessage[]): LLMMessage[] {
285
+ return messages.map((m, i) => {
286
+ if (i === 0 || m.role !== "system" || typeof m.content !== "string") return m;
287
+ return { ...m, role: "user" as const, content: formatInternalEvent("legacy_internal", m.content) };
288
+ });
289
+ }
290
+
291
+ export function deserializeCheckpoint(run: AgentRunDoc): RunCheckpointState | null {
260
292
  if (!run.state_json) return null;
261
293
  try {
262
294
  const raw = JSON.parse(run.state_json);
263
295
  if (raw.version !== 1) return null;
264
- return raw as RunCheckpointState;
296
+ const state = raw as RunCheckpointState;
297
+ return { ...state, messages: normalizeStraySystemMessages(state.messages) };
265
298
  } catch {
266
299
  log.warn(`[deserializeCheckpoint] Failed to parse state_json for run ${run.id}`);
267
300
  return null;
268
301
  }
269
302
  }
270
303
 
304
+ // ─── internals ─────────────────────────────────────────────────────────────
305
+
306
+ function serializeCheckpoint(state: RunCheckpointState): { json: string; bytes: number } {
307
+ const json = JSON.stringify(state);
308
+ const bytes = new TextEncoder().encode(json).length;
309
+ return { json, bytes };
310
+ }
311
+
312
+ /**
313
+ * Truncate state to fit under MAX_STATE_BYTES by replacing old tool
314
+ * messages with a placeholder, keeping the system prompt and the last
315
+ * N messages intact.
316
+ */
271
317
  function truncateState(state: RunCheckpointState): string {
272
- const messages = [...state.messages] as Array<Record<string, unknown>>;
318
+ const messages = [...state.messages];
273
319
  const keepLastN = 8;
274
320
  const cutoff = messages.length - keepLastN;
275
321
 
276
322
  for (let i = 0; i < cutoff; i++) {
277
323
  const msg = messages[i];
278
- if (msg.role === "tool" && typeof msg.content === "string" && msg.content.length > 200) {
279
- messages[i] = { ...msg, content: `[Truncated: ${(msg.content as string).substring(0, 200)}...]` };
324
+ if (msg.role === "tool" && typeof msg.content === "string") {
325
+ messages[i] = {
326
+ ...msg,
327
+ content: msg.content.length > 200
328
+ ? `[Truncated: ${msg.content.substring(0, 200)}...]`
329
+ : msg.content,
330
+ };
280
331
  }
281
- if (msg.role === "assistant" && typeof msg.content === "string" && (msg.content as string).length > 500) {
282
- messages[i] = { ...msg, content: (msg.content as string).substring(0, 500) + "[...]" };
332
+ if (msg.role === "assistant" && typeof msg.content === "string" && msg.content.length > 500) {
333
+ messages[i] = {
334
+ ...msg,
335
+ content: msg.content.substring(0, 500) + "[...]",
336
+ };
283
337
  }
284
338
  }
285
339
 
286
- return JSON.stringify({ ...state, messages });
340
+ // Replace base64 images with placeholder
341
+ for (let i = 0; i < messages.length; i++) {
342
+ const msg = messages[i];
343
+ if (Array.isArray(msg.content)) {
344
+ messages[i] = {
345
+ ...msg,
346
+ content: (msg.content as any[]).map((part) =>
347
+ part.type === "image_url" || part.type === "image" ? "[imagen omitida]" : part
348
+ ),
349
+ };
350
+ }
351
+ }
352
+
353
+ const truncated: RunCheckpointState = {
354
+ ...state,
355
+ messages,
356
+ };
357
+ return JSON.stringify(truncated);
287
358
  }
288
359
 
289
360
  // ─── Lease renewal timer ────────────────────────────────────────────────────
@@ -299,14 +370,14 @@ export function startLeaseRenewal(runId: string): void {
299
370
  stopLeaseRenewal(runId);
300
371
  return;
301
372
  }
302
- await updateDoc<HarnessRunDoc>(COLLECTION, runId, {
303
- lease_expires_at: Date.now() + leaseDurationMs,
373
+ await updateDoc<AgentRunDoc>("agentRuns", runId, {
374
+ lease_expires_at: Date.now() + runLeaseDurationMs(),
304
375
  updated_at: Date.now(),
305
- } as Partial<HarnessRunDoc>);
376
+ } as Partial<AgentRunDoc>);
306
377
  } catch (err) {
307
378
  log.warn(`[startLeaseRenewal] Failed to renew lease for ${runId}: ${(err as Error).message}`);
308
379
  }
309
- }, leaseRenewIntervalMs);
380
+ }, leaseRenewIntervalMs());
310
381
  leaseTimers.set(runId, timer);
311
382
  }
312
383
 
@@ -322,13 +393,3 @@ export function stopAllLeaseRenewals(): void {
322
393
  for (const [, timer] of leaseTimers) clearInterval(timer);
323
394
  leaseTimers.clear();
324
395
  }
325
-
326
- export async function ensureRunStoreIndexes(): Promise<void> {
327
- const c = await col<HarnessRunDoc>(COLLECTION);
328
- await c.createIndex("status");
329
- await c.createIndex("thread_id");
330
- await c.createIndex("agent_id");
331
- await c.createIndex("kind");
332
- }
333
-
334
- export { COLLECTION as AGENT_RUNS_COLLECTION };
@@ -12,14 +12,15 @@
12
12
  * - Eventos (cron, etc.)
13
13
  */
14
14
 
15
- import { getDb } from "../storage/SQLiteStorage.ts"
16
- import { logger } from "../utils/logger.ts"
17
- import { buildSystemPromptWithProjects } from "./PromptBuilder"
18
- import { getAgentLoop, rebuildAgentLoop } from "./AgentRunner"
15
+ import { logger } from "../utils/logger"
16
+ import { buildSystemPromptWithProjects } from "./prompt-builder"
17
+ import { getAgentLoop, rebuildAgentLoop } from "./agent-loop"
19
18
  import type { MCPClientManager } from "../mcp/index.ts"
20
- import { resolveAgentId, resolveUserId } from "../storage/onboarding.ts"
21
- import { getMCPManager as getSingletonMCPManager } from "../mcp/singleton.ts"
22
- import type { ContentPart } from "./providers/LLMClient"
19
+ import { resolveAgentId, resolveUserId } from "../storage/onboarding"
20
+ import { getMCPManager as getSingletonMCPManager } from "../mcp/singleton"
21
+ import type { ContentPart } from "./llm-client"
22
+ import { col, fromIndexable } from "../storage/hive"
23
+ import type { AgentDoc, EthicsDoc } from "../storage/collections"
23
24
 
24
25
  const log = logger.child("agent-service")
25
26
 
@@ -55,14 +56,16 @@ export interface AgentDBRecord {
55
56
 
56
57
  export class AgentService {
57
58
  private agentId: string
59
+ private explicitAgentId?: string
58
60
  private workspacePath: string
59
61
  private mcpManager: MCPClientManager | null = null
60
62
  private cronHandlers: CronHandler[] = []
61
63
  private initialized: boolean = false
62
64
 
63
65
  constructor(config?: AgentServiceConfig) {
64
- // Resolve agentId from database if not provided
65
- this.agentId = config?.agentId || resolveAgentId(null) || "main"
66
+ // Resolved against the database in initialize() if not provided explicitly.
67
+ this.explicitAgentId = config?.agentId
68
+ this.agentId = config?.agentId || "main"
66
69
  this.workspacePath = config?.workspacePath || ""
67
70
  }
68
71
 
@@ -78,6 +81,11 @@ export class AgentService {
78
81
  }
79
82
 
80
83
  try {
84
+ // Resolve agentId from database if not provided explicitly
85
+ if (!this.explicitAgentId) {
86
+ this.agentId = (await resolveAgentId(null)) || "main"
87
+ }
88
+
81
89
  // Obtener MCP Manager del agent loop
82
90
  const agentLoop = getAgentLoop()
83
91
  if (agentLoop) {
@@ -97,26 +105,29 @@ export class AgentService {
97
105
  * Obtiene el registro del agente desde la DB
98
106
  */
99
107
  async getAgent(agentId?: string): Promise<AgentDBRecord | null> {
100
- const db = getDb()
101
108
  const id = agentId || this.agentId
102
-
103
- const agent = db.query<any, [string]>(
104
- "SELECT * FROM agents WHERE id = ? LIMIT 1"
105
- ).get(id) as AgentDBRecord | undefined
106
-
107
- return agent || null
109
+ const agentsCol = await col<AgentDoc>("agents")
110
+ const entry = await agentsCol.get(id)
111
+ if (!entry) return null
112
+ return {
113
+ id: entry.doc.id, user_id: entry.doc.user_id, name: entry.doc.name,
114
+ description: entry.doc.description, system_prompt: entry.doc.system_prompt, tone: entry.doc.tone,
115
+ role: entry.doc.role, status: entry.doc.status, enabled: entry.doc.enabled ? 1 : 0,
116
+ provider_id: entry.doc.provider_id, model_id: entry.doc.model_id,
117
+ tools_json: entry.doc.tools_json, skills_json: entry.doc.skills_json,
118
+ parent_id: fromIndexable(entry.doc.parent_id), max_iterations: entry.doc.max_iterations,
119
+ headers_encrypted: null, headers_iv: null,
120
+ created_at: entry.doc.created_at, updated_at: entry.doc.updated_at,
121
+ }
108
122
  }
109
123
 
110
124
  /**
111
125
  * Obtiene la ética desde la DB
112
126
  */
113
127
  async getEthics(): Promise<string> {
114
- const db = getDb()
115
- const ethics = db.query<any, []>(
116
- "SELECT content FROM ethics WHERE active = 1 LIMIT 1"
117
- ).get() as { content: string } | undefined
118
-
119
- return ethics?.content || ""
128
+ const ethicsCol = await col<EthicsDoc>("ethics")
129
+ const entries = await ethicsCol.scan({})
130
+ return entries.find((e) => e.doc.active)?.doc.content || ""
120
131
  }
121
132
 
122
133
  /**
@@ -150,8 +161,8 @@ export class AgentService {
150
161
  */
151
162
  async reloadSkills(): Promise<void> {
152
163
  log.info("Reloading skills...")
153
- const { syncSkillsToFTS } = await import("./selectors/index.ts")
154
- await syncSkillsToFTS()
164
+ const { syncSkillsToIndex } = await import("./context-compiler")
165
+ await syncSkillsToIndex()
155
166
  log.info("Skills reloaded")
156
167
  }
157
168
 
@@ -233,7 +244,7 @@ export class AgentService {
233
244
  */
234
245
  async getSystemPrompt(agentId?: string, userId?: string): Promise<string> {
235
246
  const id = agentId || this.agentId
236
- const uid = userId || resolveUserId({}) || "default"
247
+ const uid = userId || (await resolveUserId({})) || "default"
237
248
  return buildSystemPromptWithProjects({ agentId: id, userId: uid })
238
249
  }
239
250
 
@@ -241,7 +252,7 @@ export class AgentService {
241
252
  * Ejecuta un agente con un mensaje
242
253
  */
243
254
  async runAgent(message: string | ContentPart[], threadId: string, userId?: string): Promise<string> {
244
- const { runAgentIsolated } = await import("./AgentRunner.ts")
255
+ const { runAgentIsolated } = await import("./agent-loop")
245
256
  const result = await runAgentIsolated({
246
257
  agentId: this.agentId,
247
258
  taskDescription: message,