@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
@@ -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
  }