@johpaz/hive-sdk 0.1.4 → 0.1.6

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 (278) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/README.md +78 -23
  3. package/bun.lock +55 -29
  4. package/bunfig.toml +4 -2
  5. package/docs/API-AGENTS.md +78 -27
  6. package/docs/API-CONTEXT-COMPILER.md +31 -34
  7. package/docs/API-TOOLS-SKILLS-CHANNELS.md +58 -22
  8. package/docs/HIVE-HARNESS.md +1 -1
  9. package/docs/INDEX.md +4 -4
  10. package/docs/TEMPLATE-HIVE-APP.md +10 -10
  11. package/package.json +17 -12
  12. package/packages/cli/package.json +2 -2
  13. package/packages/cli/src/commands/create-app.test.ts +36 -7
  14. package/packages/cli/src/commands/init.ts +3 -3
  15. package/packages/cli/src/commands/run.ts +1 -1
  16. package/packages/cli/src/commands/test.ts +37 -25
  17. package/packages/cli/src/commands/trace.ts +30 -28
  18. package/packages/cli/templates/hive-app/.env.example +10 -2
  19. package/packages/cli/templates/hive-app/README.md +103 -0
  20. package/packages/cli/templates/hive-app/hive.config.ts +9 -3
  21. package/packages/cli/templates/hive-app/src/agents/coordinator.ts +8 -1
  22. package/packages/cli/templates/hive-app/src/main.ts +12 -19
  23. package/packages/core/package.json +13 -12
  24. package/packages/core/src/agent/acceptance-checks.ts +172 -0
  25. package/packages/core/src/agent/agent-catalog.ts +348 -0
  26. package/packages/core/src/agent/agent-loop.ts +1373 -0
  27. package/packages/core/src/agent/capability-search.ts +186 -0
  28. package/packages/core/src/agent/catalog-selector.ts +103 -0
  29. package/packages/core/src/agent/{Compaction.ts → compaction.ts} +86 -63
  30. package/packages/core/src/agent/context-compiler.ts +689 -0
  31. package/packages/core/src/agent/conversation-store.ts +381 -0
  32. package/packages/core/src/agent/curator.ts +276 -0
  33. package/packages/core/src/agent/delegation-runtime.ts +241 -0
  34. package/packages/core/src/agent/goal-runner.ts +323 -0
  35. package/packages/core/src/agent/index.ts +17 -12
  36. package/packages/core/src/agent/llm-client.ts +266 -0
  37. package/packages/core/src/agent/llm-providers/anthropic.ts +264 -0
  38. package/packages/core/src/agent/llm-providers/deepseek.ts +8 -0
  39. package/packages/core/src/agent/{providers → llm-providers}/gemini.ts +98 -60
  40. package/packages/core/src/agent/llm-providers/groq.ts +5 -0
  41. package/packages/core/src/agent/llm-providers/hiveagents.ts +253 -0
  42. package/packages/core/src/agent/{providers → llm-providers}/interface.ts +73 -13
  43. package/packages/core/src/agent/llm-providers/kimi.ts +8 -0
  44. package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
  45. package/packages/core/src/agent/llm-providers/mistral.ts +5 -0
  46. package/packages/core/src/agent/llm-providers/modelscope.ts +5 -0
  47. package/packages/core/src/agent/llm-providers/nvidia.ts +5 -0
  48. package/packages/core/src/agent/{providers → llm-providers}/ollama.ts +31 -5
  49. package/packages/core/src/agent/llm-providers/openai-compat-base.ts +418 -0
  50. package/packages/core/src/agent/llm-providers/openai.ts +5 -0
  51. package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
  52. package/packages/core/src/agent/llm-providers/openrouter.ts +5 -0
  53. package/packages/core/src/agent/llm-providers/qwen.ts +5 -0
  54. package/packages/core/src/agent/llm-providers/z-ai.ts +5 -0
  55. package/packages/core/src/agent/minimal-loadout.ts +47 -0
  56. package/packages/core/src/agent/playbook-selector.ts +119 -0
  57. package/packages/core/src/agent/{PromptBuilder.ts → prompt-builder.ts} +21 -22
  58. package/packages/core/src/{harness → agent}/proof-packet.ts +16 -21
  59. package/packages/core/src/agent/providers/index.ts +35 -16
  60. package/packages/core/src/agent/reflector.ts +320 -0
  61. package/packages/core/src/agent/routing-intent.ts +22 -0
  62. package/packages/core/src/{harness → agent}/run-epoch.ts +4 -3
  63. package/packages/core/src/{harness → agent}/run-store.ts +142 -81
  64. package/packages/core/src/agent/{Service.ts → service.ts} +37 -26
  65. package/packages/core/src/agent/skill-selector.ts +374 -0
  66. package/packages/core/src/agent/stuck-loop.ts +209 -0
  67. package/packages/core/src/agent/{selectors/ToolSelector.ts → tool-selector.ts} +188 -178
  68. package/packages/core/src/{ace/Tracer.ts → agent/tracer.ts} +37 -27
  69. package/packages/core/src/api/createAgent.test.ts +139 -27
  70. package/packages/core/src/api/createAgent.ts +232 -44
  71. package/packages/core/src/artifacts/store.ts +162 -0
  72. package/packages/core/src/canvas/canvas-manager.ts +161 -0
  73. package/packages/core/src/canvas/canvas.test.ts +8 -4
  74. package/packages/core/src/canvas/emitter.ts +131 -80
  75. package/packages/core/src/canvas/index.ts +1 -3
  76. package/packages/core/src/channels/base.ts +9 -1
  77. package/packages/core/src/channels/discord.ts +5 -4
  78. package/packages/core/src/channels/manager.ts +122 -30
  79. package/packages/core/src/channels/slack.ts +5 -4
  80. package/packages/core/src/channels/telegram.ts +36 -6
  81. package/packages/core/src/channels/webchat.ts +11 -10
  82. package/packages/core/src/channels/whatsapp.ts +23 -7
  83. package/packages/core/src/config/index.ts +13 -2
  84. package/packages/core/src/config/loader.ts +76 -29
  85. package/packages/core/src/ethics/EthicsGuard.test.ts +90 -36
  86. package/packages/core/src/ethics/EthicsGuard.ts +51 -47
  87. package/packages/core/src/events/agent-bus.ts +44 -68
  88. package/packages/core/src/events/channel-narration.ts +150 -0
  89. package/packages/core/src/events/narration.ts +82 -0
  90. package/packages/core/src/events/tool-narration.ts +62 -0
  91. package/packages/core/src/gateway/delegation-groups.ts +258 -0
  92. package/packages/core/src/{harness → gateway}/durable-queue.ts +102 -42
  93. package/packages/core/src/{harness → gateway}/job-store.ts +85 -48
  94. package/packages/core/src/gateway/lane-queue.ts +173 -0
  95. package/packages/core/src/gateway/notification-inbox.ts +57 -0
  96. package/packages/core/src/gateway/server.ts +1 -1
  97. package/packages/core/src/harness/index.ts +46 -27
  98. package/packages/core/src/index.ts +33 -27
  99. package/packages/core/src/mcp/hot-reload.ts +32 -23
  100. package/packages/core/src/mcp/index.ts +6 -3
  101. package/packages/core/src/mcp/singleton.ts +1 -4
  102. package/packages/core/src/mcp/tool-sync.ts +138 -0
  103. package/packages/core/src/memory/Scratchpad.test.ts +39 -20
  104. package/packages/core/src/memory/Scratchpad.ts +27 -34
  105. package/packages/core/src/multimodal/vision-service.ts +44 -38
  106. package/packages/core/src/resilience/retry.ts +95 -0
  107. package/packages/core/src/scheduler/CronScheduler.ts +334 -287
  108. package/packages/core/src/scheduler/index.ts +9 -7
  109. package/packages/core/src/scheduler/integration.ts +46 -26
  110. package/packages/core/src/scheduler/scheduler.test.ts +9 -13
  111. package/packages/core/src/scheduler/types.ts +7 -2
  112. package/packages/core/src/security/Pairing.ts +1 -1
  113. package/packages/core/src/skills/bundled/a2ui/a2ui_dashboard/SKILL.md +176 -0
  114. package/packages/core/src/skills/bundled/a2ui/a2ui_form/SKILL.md +202 -0
  115. package/packages/core/src/skills/bundled/a2ui/a2ui_interactive/SKILL.md +206 -0
  116. package/packages/core/src/skills/bundled/agents/agent_spawner/SKILL.md +173 -0
  117. package/packages/core/src/skills/bundled/agents/memory_manager/SKILL.md +143 -0
  118. package/packages/core/src/skills/bundled/agents/research_and_remember/SKILL.md +139 -0
  119. package/packages/core/src/skills/bundled/agents/task_orchestrator/SKILL.md +98 -0
  120. package/packages/core/src/skills/bundled/api/api_client/SKILL.md +132 -0
  121. package/packages/core/src/skills/bundled/cli/cli_pipeline/SKILL.md +135 -0
  122. package/packages/core/src/skills/bundled/cli/cli_safe_exec/SKILL.md +125 -0
  123. package/packages/core/src/skills/bundled/cli/software_engineering/SKILL.md +23 -0
  124. package/packages/core/src/skills/bundled/cron_manager/SKILL.md +188 -0
  125. package/packages/core/src/skills/bundled/cron_reminder/SKILL.md +112 -0
  126. package/packages/core/src/skills/bundled/filesystem/file_manager/SKILL.md +118 -0
  127. package/packages/core/src/skills/bundled/filesystem/file_read_and_summarize/SKILL.md +109 -0
  128. package/packages/core/src/skills/bundled/filesystem/file_writer/SKILL.md +129 -0
  129. package/packages/core/src/skills/bundled/filesystem/workspace_file_operator/SKILL.md +22 -0
  130. package/packages/core/src/skills/bundled/office/office_document_manager/SKILL.md +262 -0
  131. package/packages/core/src/skills/bundled/search_knowledge/capability_discovery/SKILL.md +75 -0
  132. package/packages/core/src/skills/bundled/web/browser_automate/SKILL.md +120 -0
  133. package/packages/core/src/skills/bundled/web/browser_scrape/SKILL.md +109 -0
  134. package/packages/core/src/skills/bundled/web/web_monitor/SKILL.md +127 -0
  135. package/packages/core/src/skills/bundled/web/web_research/SKILL.md +119 -0
  136. package/packages/core/src/skills/bundled-data.generated.ts +731 -2678
  137. package/packages/core/src/skills/skills.test.ts +52 -11
  138. package/packages/core/src/{harness → storage}/boot-id.ts +5 -2
  139. package/packages/core/src/storage/bootstrap.ts +151 -0
  140. package/packages/core/src/storage/causal-events.ts +84 -0
  141. package/packages/core/src/storage/collections.ts +680 -0
  142. package/packages/core/src/storage/crypto.ts +205 -74
  143. package/packages/core/src/{harness/db-helpers.ts → storage/hive.ts} +63 -7
  144. package/packages/core/src/storage/hivedb.ts +61 -0
  145. package/packages/core/src/storage/index.ts +111 -18
  146. package/packages/core/src/storage/model-id.ts +53 -0
  147. package/packages/core/src/storage/onboarding.ts +540 -972
  148. package/packages/core/src/storage/reconcile.ts +238 -0
  149. package/packages/core/src/storage/seed.ts +572 -406
  150. package/packages/core/src/storage/usage.ts +285 -225
  151. package/packages/core/src/storage/user-email.ts +11 -0
  152. package/packages/core/src/swarm/AgentExecutor.ts +1 -1
  153. package/packages/core/src/swarm/EventBridge.ts +1 -1
  154. package/packages/core/src/swarm/index.ts +12 -9
  155. package/packages/core/src/tool-runtime/index.ts +146 -23
  156. package/packages/core/src/tool-runtime/tool-worker.ts +2 -2
  157. package/packages/core/src/tool-runtime/worker-tools.ts +27 -0
  158. package/packages/core/src/{canvas/a2ui-tools.ts → tools/a2ui/index.ts} +17 -8
  159. package/packages/core/src/tools/agents/get-available-models.ts +36 -54
  160. package/packages/core/src/tools/agents/index.ts +784 -292
  161. package/packages/core/src/tools/api/api-request.test.ts +164 -0
  162. package/packages/core/src/tools/api/api-request.ts +174 -0
  163. package/packages/core/src/tools/api/index.ts +16 -0
  164. package/packages/core/src/tools/cli/index.ts +4 -0
  165. package/packages/core/src/tools/core/index.ts +281 -112
  166. package/packages/core/src/tools/cron/index.ts +121 -124
  167. package/packages/core/src/tools/index.ts +63 -78
  168. package/packages/core/src/tools/office/office-escribir-xlsx.ts +3 -1
  169. package/packages/core/src/tools/types.ts +3 -1
  170. package/packages/core/src/tools/web/artifact-inspect.ts +23 -0
  171. package/packages/core/src/tools/web/browser-backend.ts +129 -0
  172. package/packages/core/src/tools/web/browser-screenshot.ts +26 -5
  173. package/packages/core/src/tools/web/browser-service.ts +80 -35
  174. package/packages/core/src/tools/web/browser-type.ts +3 -8
  175. package/packages/core/src/tools/web/index.ts +4 -4
  176. package/packages/core/src/tools/web/webview-backend.ts +412 -0
  177. package/packages/core/src/voice/index.ts +89 -63
  178. package/packages/core/src/workers/agent.worker.ts +2 -2
  179. package/packages/core/src/workers/workers.test.ts +3 -10
  180. package/scripts/bump-version.ts +248 -0
  181. package/scripts/generate-skill-bundle.ts +108 -0
  182. package/test/acceptance-checks.test.ts +403 -0
  183. package/test/agent-loop-terminal-synthesis.test.ts +32 -0
  184. package/test/browser-backend.test.ts +308 -0
  185. package/test/catalog-agents-stay-enabled.test.ts +117 -0
  186. package/test/causal-events.test.ts +117 -0
  187. package/test/compaction.test.ts +105 -0
  188. package/test/context-compiler.test.ts +269 -0
  189. package/test/curator.test.ts +130 -0
  190. package/test/durable-queue.test.ts +114 -0
  191. package/test/harness-barrel.test.ts +64 -0
  192. package/test/hive-helpers.test.ts +130 -0
  193. package/test/hivedb-search.test.ts +189 -0
  194. package/test/internal-turns.test.ts +166 -0
  195. package/test/job-idempotency.test.ts +68 -0
  196. package/test/job-retry-backoff.test.ts +184 -0
  197. package/test/job-store.test.ts +381 -0
  198. package/test/llm-retry.test.ts +97 -0
  199. package/test/memory-perf.test.ts +774 -0
  200. package/test/minimal-loadout.test.ts +78 -0
  201. package/test/model-catalog.test.ts +105 -0
  202. package/test/preload.ts +12 -0
  203. package/test/reflector.test.ts +320 -0
  204. package/test/retention-cap.test.ts +91 -0
  205. package/test/retired-capabilities-pruned.test.ts +192 -0
  206. package/test/run-store.test.ts +355 -0
  207. package/test/scratchpad.test.ts +74 -0
  208. package/test/secrets-durability.test.ts +119 -0
  209. package/test/seed-model-reseed.test.ts +155 -0
  210. package/test/setup-agent-seed.test.ts +264 -0
  211. package/test/tool-inventory.test.ts +65 -0
  212. package/test/tool-runtime.test.ts +258 -0
  213. package/test/tool-selector-runtime-tools.test.ts +117 -0
  214. package/test/toon.test.ts +429 -0
  215. package/tsconfig.json +2 -0
  216. package/packages/core/src/ace/Curator.ts +0 -158
  217. package/packages/core/src/ace/Reflector.ts +0 -200
  218. package/packages/core/src/ace/index.ts +0 -4
  219. package/packages/core/src/agent/AgentRunner.ts +0 -711
  220. package/packages/core/src/agent/ContextCompiler.ts +0 -567
  221. package/packages/core/src/agent/ContextGuard.ts +0 -91
  222. package/packages/core/src/agent/ConversationStore.ts +0 -254
  223. package/packages/core/src/agent/Hooks.ts +0 -166
  224. package/packages/core/src/agent/StuckLoop.ts +0 -133
  225. package/packages/core/src/agent/providers/LLMClient.ts +0 -149
  226. package/packages/core/src/agent/providers/anthropic.ts +0 -212
  227. package/packages/core/src/agent/providers/openai-compat.ts +0 -231
  228. package/packages/core/src/agent/selectors/PlaybookSelector.ts +0 -121
  229. package/packages/core/src/agent/selectors/SkillSelector.ts +0 -322
  230. package/packages/core/src/agent/selectors/index.ts +0 -6
  231. package/packages/core/src/auth/auth.ts +0 -121
  232. package/packages/core/src/auth/index.ts +0 -1
  233. package/packages/core/src/canvas/CanvasManager.ts +0 -390
  234. package/packages/core/src/canvas/canvas-tools.ts +0 -448
  235. package/packages/core/src/harness/collections.ts +0 -98
  236. package/packages/core/src/harness/goal-verifier.ts +0 -141
  237. package/packages/core/src/harness/harness.test.ts +0 -236
  238. package/packages/core/src/harness/reconcile.ts +0 -149
  239. package/packages/core/src/mcp/MCPToolAdapter.ts +0 -176
  240. package/packages/core/src/multimodal/VisionService.ts +0 -293
  241. package/packages/core/src/scheduler/dag/AgentExecutor.ts +0 -53
  242. package/packages/core/src/scheduler/dag/DAGScheduler.ts +0 -250
  243. package/packages/core/src/scheduler/dag/EventBridge.ts +0 -122
  244. package/packages/core/src/scheduler/dag/TaskGraph.ts +0 -192
  245. package/packages/core/src/scheduler/dag/TaskNode.ts +0 -97
  246. package/packages/core/src/scheduler/dag/TaskResult.ts +0 -22
  247. package/packages/core/src/scheduler/dag/errors.ts +0 -37
  248. package/packages/core/src/scheduler/dag/index.ts +0 -26
  249. package/packages/core/src/scheduler/dag/presets/ResearchPreset.ts +0 -97
  250. package/packages/core/src/scheduler/dag/strategies/ParallelStrategy.ts +0 -21
  251. package/packages/core/src/scheduler/dag/strategies/PriorityStrategy.ts +0 -46
  252. package/packages/core/src/storage/HiveDBStorage.ts +0 -64
  253. package/packages/core/src/storage/SQLiteStorage.ts +0 -414
  254. package/packages/core/src/storage/hiveSeed.ts +0 -308
  255. package/packages/core/src/storage/hiveStorage.test.ts +0 -38
  256. package/packages/core/src/storage/schema.ts +0 -689
  257. package/packages/core/src/storage/storage.test.ts +0 -37
  258. package/packages/core/src/swarm/AgentBus.ts +0 -460
  259. package/packages/core/src/swarm/EventBus.ts +0 -169
  260. package/packages/core/src/swarm/WorkerPool.ts +0 -236
  261. package/packages/core/src/tools/bridge-events.ts +0 -26
  262. package/packages/core/src/tools/canvas/index.ts +0 -375
  263. package/packages/core/src/tools/codebridge/index.ts +0 -342
  264. package/packages/core/src/tools/meeting/index.ts +0 -353
  265. package/packages/core/src/tools/projects/index.ts +0 -37
  266. package/packages/core/src/tools/projects/project-create.ts +0 -94
  267. package/packages/core/src/tools/projects/project-done.ts +0 -66
  268. package/packages/core/src/tools/projects/project-fail.ts +0 -66
  269. package/packages/core/src/tools/projects/project-list.ts +0 -96
  270. package/packages/core/src/tools/projects/project-update.ts +0 -72
  271. package/packages/core/src/tools/projects/task-create.ts +0 -68
  272. package/packages/core/src/tools/projects/task-evaluate.ts +0 -93
  273. package/packages/core/src/tools/projects/task-update.ts +0 -93
  274. package/packages/core/src/tools/voice/index.ts +0 -104
  275. package/packages/core/src/tools/web/api-request.test.ts +0 -170
  276. package/packages/core/src/tools/web/api-request.ts +0 -239
  277. package/test/setup-db.ts +0 -216
  278. /package/packages/core/src/agent/{NativeTools.ts → native-tools.ts} +0 -0
@@ -0,0 +1,320 @@
1
+ /**
2
+ * ACE Reflector — analyzes recent traces and produces insights.
3
+ *
4
+ * Runs in the background (never blocks the main agent loop).
5
+ * Triggered by the tracer after N new traces.
6
+ *
7
+ * Output → `reflections` collection → picked up by Curator.
8
+ *
9
+ * "Last processed trace" is tracked via a `cursors` collection doc
10
+ * (id="reflector:lastTrace") instead of the SQL json_each/MAX(CAST(...))
11
+ * trick the SQLite version used — HiveDB has no equivalent primitive.
12
+ */
13
+
14
+ import { logger } from "../utils/logger"
15
+ import { col, nextId } from "../storage/hive"
16
+ import { getHiveDb } from "../storage/hivedb"
17
+ import { loadConfig } from "../config/loader"
18
+ import type { HiveDB, ToolStats } from "@johpaz/hive-db"
19
+ import type { TraceDoc, ReflectionDoc, CursorDoc } from "../storage/collections"
20
+
21
+ const log = logger.child("reflector")
22
+
23
+ const MAX_TRACES_TO_ANALYZE = 30
24
+ const MIN_TRACES_TO_RUN = 10
25
+ const CURSOR_ID = "reflector:lastTrace"
26
+
27
+ /** Main entry point — called from tracer.ts */
28
+ export async function runReflector(): Promise<void> {
29
+ try {
30
+ log.info(`[reflector] Starting reflection cycle...`)
31
+
32
+ const cursorsCol = await col<CursorDoc>("cursors")
33
+ const cursorEntry = await cursorsCol.get(CURSOR_ID)
34
+ const lastProcessedId = cursorEntry?.doc.value ?? null
35
+ log.debug(`[reflector] Last processed trace ID: ${lastProcessedId ?? "(none)"}`)
36
+
37
+ const tracesCol = await col<TraceDoc>("traces")
38
+ let candidates = lastProcessedId
39
+ ? await tracesCol.scan({ start: lastProcessedId })
40
+ : await tracesCol.scan({})
41
+ if (lastProcessedId && candidates[0]?.id === lastProcessedId) candidates = candidates.slice(1)
42
+
43
+ const traceEntries = candidates.slice(0, MAX_TRACES_TO_ANALYZE)
44
+ const traces = traceEntries.map(e => e.doc)
45
+ log.debug(`[reflector] Fetched ${traces.length} traces from DB`)
46
+
47
+ if (traces.length < MIN_TRACES_TO_RUN) {
48
+ log.debug(`[reflector] Not enough traces (${traces.length}/${MIN_TRACES_TO_RUN}), skipping`)
49
+ return
50
+ }
51
+
52
+ log.info(`[reflector] Analyzing ${traces.length} traces...`)
53
+
54
+ // G9: when enabled, per-tool insights use whole-history stats from HiveDB's
55
+ // event log (toolStats) instead of counters built from just this batch, and
56
+ // causal threads add root-cause/learning-proposal insights on top.
57
+ const causalDb = loadConfig().causalLog?.enabled ? await getHiveDb() : null
58
+ const localInsights = await analyzeTracesLocally(traces, causalDb)
59
+ const causalInsights = await analyzeCausalThreads(traces, causalDb)
60
+ const insights = [...localInsights, ...causalInsights]
61
+
62
+ if (insights.length === 0) {
63
+ log.debug("[reflector] No insights generated")
64
+ } else {
65
+ const traceIds = JSON.stringify(traceEntries.map(e => e.id))
66
+ const reflectionsCol = await col<ReflectionDoc>("reflections")
67
+
68
+ for (const insight of insights) {
69
+ const id = await nextId("reflections")
70
+ await reflectionsCol.put(id, {
71
+ id,
72
+ trace_ids: traceIds,
73
+ insight_type: insight.type,
74
+ description: insight.description,
75
+ affected_tools: insight.affectedTools ? JSON.stringify(insight.affectedTools) : null,
76
+ affected_agents: insight.affectedAgents ? JSON.stringify(insight.affectedAgents) : null,
77
+ confidence: insight.confidence,
78
+ created_at: Date.now(),
79
+ })
80
+ }
81
+
82
+ log.info(`[reflector] Generated ${insights.length} insights`)
83
+ }
84
+
85
+ // Advance the cursor regardless of whether insights were generated —
86
+ // these traces have been considered either way.
87
+ const newCursor = traceEntries[traceEntries.length - 1].id
88
+ await cursorsCol.put(CURSOR_ID, { value: newCursor }, cursorEntry ? { expectedVersion: cursorEntry.version } : { expectedVersion: 0 })
89
+
90
+ // Trigger curator
91
+ const { runCurator } = await import("./curator")
92
+ await runCurator()
93
+
94
+ log.info(`[reflector] Reflection cycle completed successfully`)
95
+ } catch (err) {
96
+ log.error(`[reflector] Error during reflection:`, {
97
+ message: (err as Error).message,
98
+ stack: (err as Error).stack,
99
+ })
100
+ }
101
+ }
102
+
103
+ // ─── Local analysis (heuristic, no LLM call needed for basic patterns) ────────
104
+
105
+ interface Insight {
106
+ type: "success_pattern" | "failure_pattern" | "optimization" | "ethics_violation" | "root_cause" | "learning_proposal"
107
+ description: string
108
+ affectedTools?: string[]
109
+ affectedAgents?: string[]
110
+ confidence: number
111
+ }
112
+
113
+ // ─── G9 causal-thread analysis (evaluateHarness) ──────────────────────────────
114
+
115
+ interface DecisionNode {
116
+ seq: number
117
+ agent: string
118
+ description: string
119
+ }
120
+
121
+ interface CausalThreadShape {
122
+ decisions: DecisionNode[]
123
+ toolCalls: Array<{ seq: number; agent: string; tool: string }>
124
+ anomalies: unknown[]
125
+ }
126
+
127
+ interface HarnessFinding {
128
+ kind: "inefficientLoop" | "objectiveDrift" | "rootCause" | "insufficientEvidence"
129
+ seq?: number
130
+ description: string
131
+ }
132
+
133
+ interface HarnessProposal {
134
+ description: string
135
+ confidence: number
136
+ }
137
+
138
+ interface HarnessEvaluationShape {
139
+ processQuality: number
140
+ outputQuality: number
141
+ rootCause?: { seq: number; agent: string }
142
+ findings: HarnessFinding[]
143
+ proposals: HarnessProposal[]
144
+ }
145
+
146
+ /**
147
+ * Evaluate the causal thread of every distinct G9 stream touched by this
148
+ * batch of traces (one thread per agent-loop invocation — see
149
+ * agent-loop.ts's causalStreamId) with HiveDB's harness loop, and turn its
150
+ * root cause / findings / learning proposals into reflector insights.
151
+ *
152
+ * Runs alongside, not instead of, analyzeTracesLocally(): it needs a full
153
+ * causal thread per stream, so it operates per-run rather than per-batch.
154
+ */
155
+ async function analyzeCausalThreads(traces: TraceDoc[], causalDb: HiveDB | null): Promise<Insight[]> {
156
+ if (!causalDb) return []
157
+
158
+ const insights: Insight[] = []
159
+ const streamIds = new Set(traces.map((t) => t.causal_stream_id).filter((s): s is string => !!s))
160
+
161
+ for (const streamId of streamIds) {
162
+ try {
163
+ const thread = (await causalDb.causalThread(streamId)) as CausalThreadShape
164
+ if (!thread.decisions?.length && !thread.toolCalls?.length) continue
165
+
166
+ const subset = traces.filter((t) => t.causal_stream_id === streamId)
167
+ const originalIntent = subset[0]?.input_summary ?? ""
168
+ const success = subset.every((t) => t.success)
169
+
170
+ const evaluation = (await causalDb.evaluateHarness({
171
+ causalThread: thread,
172
+ similarEpisodes: [],
173
+ originalIntent,
174
+ currentState: { success },
175
+ minConfidence: 0.5,
176
+ })) as HarnessEvaluationShape
177
+
178
+ // Description deliberately omits streamId/seq: curator.ts only reinforces
179
+ // an existing rule when its first-60-chars prefix matches exactly, so any
180
+ // per-run identifier in the text would make every occurrence of the same
181
+ // underlying root cause mint a brand new playbook rule instead of
182
+ // reinforcing one (confirmed via a local before/after canary run).
183
+ if (evaluation.rootCause) {
184
+ const decision = thread.decisions?.find((d) => d.seq === evaluation.rootCause!.seq)
185
+ insights.push({
186
+ type: "root_cause",
187
+ description: decision
188
+ ? `Root cause: decision "${decision.description}" (agent ${evaluation.rootCause.agent}) preceded a tool failure.`
189
+ : `Root cause: a decision by agent ${evaluation.rootCause.agent} preceded a tool failure.`,
190
+ affectedAgents: [evaluation.rootCause.agent],
191
+ confidence: 0.6,
192
+ })
193
+ }
194
+
195
+ // Only inefficientLoop findings here: harness.rs's find_root_cause() always
196
+ // emits evaluation.rootCause and an equivalent finding{kind:"rootCause"}
197
+ // together from the same resolution — including both would double-insight
198
+ // every single failure. The block above already covers it, with a richer
199
+ // description (the actual decision text, not just its seq).
200
+ for (const finding of evaluation.findings ?? []) {
201
+ if (finding.kind === "inefficientLoop") {
202
+ insights.push({
203
+ type: "root_cause",
204
+ description: finding.description,
205
+ confidence: 0.6,
206
+ })
207
+ }
208
+ }
209
+
210
+ for (const proposal of evaluation.proposals ?? []) {
211
+ insights.push({
212
+ type: "learning_proposal",
213
+ description: proposal.description,
214
+ confidence: proposal.confidence,
215
+ })
216
+ }
217
+ } catch (err) {
218
+ log.warn(`[reflector] Causal-thread analysis failed for stream ${streamId}: ${(err as Error).message}`)
219
+ }
220
+ }
221
+
222
+ return insights
223
+ }
224
+
225
+ async function analyzeTracesLocally(traces: TraceDoc[], causalDb: HiveDB | null): Promise<Insight[]> {
226
+ const insights: Insight[] = []
227
+
228
+ // G9: whole-history stats per tool touched by this batch (undefined when
229
+ // disabled, or when the tool has no events in the log yet).
230
+ const statsByTool = new Map<string, ToolStats>()
231
+ if (causalDb) {
232
+ const distinctTools = new Set(traces.map((t) => t.tool_used).filter((t): t is string => !!t))
233
+ for (const tool of distinctTools) {
234
+ try {
235
+ const stats = await causalDb.toolStats(tool)
236
+ if (stats) statsByTool.set(tool, stats)
237
+ } catch (err) {
238
+ log.warn(`[reflector] toolStats(${tool}) failed: ${(err as Error).message}`)
239
+ }
240
+ }
241
+ }
242
+
243
+ // ── Failure patterns ─────────────────────────────────────────────────────
244
+ const failures = traces.filter((t) => !t.success)
245
+ if (failures.length > 3) {
246
+ // Group by tool
247
+ const toolFailures: Record<string, number> = {}
248
+ for (const f of failures) {
249
+ if (f.tool_used) {
250
+ toolFailures[f.tool_used] = (toolFailures[f.tool_used] || 0) + 1
251
+ }
252
+ }
253
+ for (const [tool, batchCount] of Object.entries(toolFailures)) {
254
+ const count = statsByTool.get(tool)?.errors ?? batchCount
255
+ if (count >= 3) {
256
+ insights.push({
257
+ type: "failure_pattern",
258
+ description: `Tool '${tool}' failed ${count} times recently. Consider verifying its configuration or avoiding it for this type of task.`,
259
+ affectedTools: [tool],
260
+ confidence: Math.min(0.9, count / 10),
261
+ })
262
+ }
263
+ }
264
+ }
265
+
266
+ // ── Slow tools ───────────────────────────────────────────────────────────
267
+ const slowThresholdMs = 5000
268
+ const slowTools: Record<string, number[]> = {}
269
+ for (const t of traces) {
270
+ if (t.tool_used && (t.duration_ms ?? 0) > slowThresholdMs) {
271
+ if (!slowTools[t.tool_used]) slowTools[t.tool_used] = []
272
+ slowTools[t.tool_used].push(t.duration_ms!)
273
+ }
274
+ }
275
+ for (const [tool, durations] of Object.entries(slowTools)) {
276
+ if (durations.length >= 3) {
277
+ const stats = statsByTool.get(tool)
278
+ const avg = stats && stats.invocations > 0
279
+ ? Math.round(stats.totalLatencyMs / stats.invocations)
280
+ : Math.round(durations.reduce((a, b) => a + b, 0) / durations.length)
281
+ insights.push({
282
+ type: "optimization",
283
+ description: `Tool '${tool}' is consistently slow (avg ${avg}ms). Cache results when possible or use a faster alternative.`,
284
+ affectedTools: [tool],
285
+ confidence: 0.6,
286
+ })
287
+ }
288
+ }
289
+
290
+ // ── High success pattern ─────────────────────────────────────────────────
291
+ const successByTool: Record<string, { ok: number; total: number }> = {}
292
+ for (const t of traces) {
293
+ if (!t.tool_used) continue
294
+ if (!successByTool[t.tool_used]) successByTool[t.tool_used] = { ok: 0, total: 0 }
295
+ successByTool[t.tool_used].total++
296
+ if (t.success) successByTool[t.tool_used].ok++
297
+ }
298
+ for (const [tool, stats] of Object.entries(successByTool)) {
299
+ if (stats.total >= 5 && stats.ok / stats.total >= 0.9) {
300
+ insights.push({
301
+ type: "success_pattern",
302
+ description: `Tool '${tool}' has a high success rate (${stats.ok}/${stats.total}). Prefer it for related tasks.`,
303
+ affectedTools: [tool],
304
+ confidence: stats.ok / stats.total,
305
+ })
306
+ }
307
+ }
308
+
309
+ // ── High token usage ─────────────────────────────────────────────────────
310
+ const highTokenTraces = traces.filter((t) => (t.tokens_used ?? 0) > 4000)
311
+ if (highTokenTraces.length > 3) {
312
+ insights.push({
313
+ type: "optimization",
314
+ description: `${highTokenTraces.length} recent calls used >4000 tokens. Be more concise and use tool results as summaries, not raw dumps.`,
315
+ confidence: 0.7,
316
+ })
317
+ }
318
+
319
+ return insights
320
+ }
@@ -0,0 +1,22 @@
1
+ function normalizeIntent(value: string): string {
2
+ return value
3
+ .normalize("NFD")
4
+ .replace(/\p{Diacritic}/gu, "")
5
+ .toLowerCase();
6
+ }
7
+
8
+ /**
9
+ * Calendar operations are external data mutations/lookups, not future Hive
10
+ * executions. Keep them out of the native cron tool and skill selectors so
11
+ * the coordinator can discover the configured calendar MCP server instead.
12
+ */
13
+ export function isCalendarOperation(value: string): boolean {
14
+ const text = normalizeIntent(value);
15
+ const namesCalendar = /\b(calendario|calendar|agenda)\b/.test(text);
16
+ const calendarAction = /\b(crear?|agend\w*|anad\w*|agreg\w*|consult\w*|list\w*|modific\w*|mov\w*|cancel\w*|elimin\w*|invit\w*|reserv\w*|disponibilidad|availability)\b/.test(text);
17
+ if (namesCalendar && calendarAction) return true;
18
+
19
+ const directlySchedulesMeeting = /\b(crea(?:r)?|agenda(?:r)?|programa(?:r)?|reserva(?:r)?)\s+(?:un|una|la|el|nueva|nuevo)?\s*(reunion|cita|meeting)\b/.test(text);
20
+ const managesAttendees = /\b(invit\w*|asistente\w*|attendee\w*)\b.*\b(reunion|cita|meeting)\b|\b(reunion|cita|meeting)\b.*\b(invit\w*|asistente\w*|attendee\w*)\b/.test(text);
21
+ return directlySchedulesMeeting || managesAttendees;
22
+ }
@@ -6,10 +6,11 @@
6
6
  * were unchanged.
7
7
  */
8
8
 
9
+ import rootPackage from "../../../../package.json";
10
+
9
11
  export interface RunEpoch {
10
12
  provider: string;
11
13
  model: string;
12
- /** Caller-supplied version identifier (e.g. the host app's package version) — the harness doesn't assume its own version is what matters. */
13
14
  app_version: string;
14
15
  tool_catalog_hash: string;
15
16
  }
@@ -22,11 +23,11 @@ function hashToolNames(names: string[]): string {
22
23
  return hash.toString(16);
23
24
  }
24
25
 
25
- export function buildRunEpoch(opts: { provider: string; model: string; appVersion: string; toolNames: string[] }): RunEpoch {
26
+ export function buildRunEpoch(opts: { provider: string; model: string; toolNames: string[] }): RunEpoch {
26
27
  return {
27
28
  provider: opts.provider,
28
29
  model: opts.model,
29
- app_version: opts.appVersion,
30
+ app_version: (rootPackage as { version: string }).version,
30
31
  tool_catalog_hash: hashToolNames(opts.toolNames),
31
32
  };
32
33
  }