@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,172 @@
1
+ /**
2
+ * acceptance-checks — deterministic acceptance checking, no LLM.
3
+ *
4
+ * Replaces the independent acceptance_verifier agent loop (which ran a full
5
+ * agent-loop, up to 3 times, just to re-derive a verdict the worker's own
6
+ * structured output already implies). This runs whatever deterministic
7
+ * checkTool each criterion declares, inspects any artifact_id found in the
8
+ * evidence, and applies cheap delivery gates — zero LLM calls.
9
+ *
10
+ * When nothing deterministic applies to a criterion, the result is
11
+ * "unchecked", not a failure: that's exactly the case the coordinator's
12
+ * closing turn (already paid for, via the delegation-group fan-in) is meant
13
+ * to judge using this checks result plus the raw evidence.
14
+ */
15
+
16
+ import { col } from "../storage/hive";
17
+ import type { AgentDoc } from "../storage/collections";
18
+ import type { AcceptanceCriterion } from "./run-store";
19
+ import { interpretCheckResult } from "./goal-runner";
20
+ import { inspectArtifact } from "../artifacts/store";
21
+ import { loadConfig } from "../config/loader";
22
+ import { logger } from "../utils/logger";
23
+
24
+ const log = logger.child("acceptance-checks");
25
+
26
+ export type CheckStatus = "passed" | "failed" | "unchecked";
27
+
28
+ export interface AcceptanceCheckResult {
29
+ criterion_id: string;
30
+ /** checkTool name, "artifact_inspect", "delivery_gate", or "none". */
31
+ check: string;
32
+ /** null when no deterministic check applied to this criterion. */
33
+ met: boolean | null;
34
+ detail: string;
35
+ }
36
+
37
+ export interface AcceptanceChecks {
38
+ status: CheckStatus;
39
+ results: AcceptanceCheckResult[];
40
+ summary: string;
41
+ }
42
+
43
+ export function sanitizeDiagnostic(value: string, limit = 1000): string {
44
+ return value
45
+ .replace(
46
+ // El esquema de auth va aparte: sin el grupo opcional, "authorization:
47
+ // Bearer <token>" consumía sólo "Bearer" y dejaba el token en claro.
48
+ /(api[_-]?key|token|authorization|secret)(["'=:\s]+)(?:(bearer|basic)\s+)?[^\s,}"']+/gi,
49
+ (_match, key: string, separator: string, scheme?: string) =>
50
+ `${key}${separator}${scheme ? `${scheme} ` : ""}[REDACTED]`,
51
+ )
52
+ .slice(0, limit);
53
+ }
54
+
55
+ async function runCheckTool(criterion: AcceptanceCriterion, objective: string): Promise<AcceptanceCheckResult | null> {
56
+ if (!criterion.checkTool) return null;
57
+ try {
58
+ const { executeToolBatch } = await import("../tool-runtime");
59
+ const { createAllTools } = await import("../tools/index");
60
+ const allTools = createAllTools(loadConfig());
61
+ const toolDef = allTools.find((t) => t.name === criterion.checkTool);
62
+ if (!toolDef) {
63
+ log.warn(`[acceptance-checks] Check tool "${criterion.checkTool}" not found in the tool registry`);
64
+ return { criterion_id: criterion.id, check: criterion.checkTool, met: null, detail: "Check tool not found in registry" };
65
+ }
66
+ const toolResults = await executeToolBatch({
67
+ toolCalls: [{
68
+ id: `check-${criterion.id}`,
69
+ function: { name: criterion.checkTool, arguments: JSON.stringify({ goal: criterion.description || objective }) },
70
+ }],
71
+ allTools,
72
+ toolConfig: {},
73
+ });
74
+ const result = toolResults[0];
75
+ if (!result?.ok) {
76
+ return { criterion_id: criterion.id, check: criterion.checkTool, met: false, detail: `Check tool failed: ${result?.error?.message ?? "unknown"}` };
77
+ }
78
+ const interpreted = interpretCheckResult(result.result);
79
+ return { criterion_id: criterion.id, check: criterion.checkTool, met: interpreted.met, detail: interpreted.reason };
80
+ } catch (err) {
81
+ log.warn(`[acceptance-checks] Check tool "${criterion.checkTool}" errored: ${(err as Error).message}`);
82
+ return { criterion_id: criterion.id, check: criterion.checkTool, met: false, detail: `Check tool error: ${(err as Error).message}` };
83
+ }
84
+ }
85
+
86
+ async function artifactChecks(evidence: string[]): Promise<AcceptanceCheckResult[]> {
87
+ const artifactIds = new Set<string>();
88
+ for (const item of evidence) {
89
+ for (const match of String(item).matchAll(/artifact_id:\s*["']?([0-9a-f-]{36})["']?/gi)) {
90
+ if (match[1]) artifactIds.add(match[1]);
91
+ }
92
+ }
93
+ const results: AcceptanceCheckResult[] = [];
94
+ for (const artifactId of artifactIds) {
95
+ const inspection = await inspectArtifact(artifactId);
96
+ const ok = inspection.ok === true;
97
+ results.push({
98
+ criterion_id: "artifact",
99
+ check: "artifact_inspect",
100
+ met: ok,
101
+ detail: ok ? `Artifact ${artifactId} verified` : `Artifact ${artifactId} inspection failed: ${JSON.stringify(inspection)}`,
102
+ });
103
+ }
104
+ return results;
105
+ }
106
+
107
+ /** Matches the worker's own documented output contract (agent-catalog.ts buildSystemPrompt): "status: completed|needs_input|partial|failed". */
108
+ function selfDeclaredFailure(delivery: string): AcceptanceCheckResult | null {
109
+ const match = delivery.match(/(?:^|\n)\s*-?\s*status\s*:\s*(completed|needs_input|partial|failed)/i);
110
+ if (!match || match[1].toLowerCase() !== "failed") return null;
111
+ return { criterion_id: "delivery", check: "delivery_gate", met: false, detail: "El worker declaró status: failed" };
112
+ }
113
+
114
+ export async function runAcceptanceChecks(input: {
115
+ objective: string;
116
+ acceptance?: AcceptanceCriterion[] | null;
117
+ delivery: string;
118
+ evidence?: string[];
119
+ }): Promise<AcceptanceChecks> {
120
+ const criteria = input.acceptance?.length ? input.acceptance : [];
121
+ const evidence = input.evidence ?? [];
122
+ const results: AcceptanceCheckResult[] = [];
123
+
124
+ if (!input.delivery || !input.delivery.trim()) {
125
+ results.push({ criterion_id: "delivery", check: "delivery_gate", met: false, detail: "Empty delivery" });
126
+ } else {
127
+ const declared = selfDeclaredFailure(input.delivery);
128
+ if (declared) results.push(declared);
129
+ }
130
+
131
+ for (const criterion of criteria) {
132
+ const checked = await runCheckTool(criterion, input.objective);
133
+ if (checked) results.push(checked);
134
+ }
135
+
136
+ results.push(...(await artifactChecks(evidence)));
137
+
138
+ const anyFailed = results.some((r) => r.met === false);
139
+ const anyPassed = results.some((r) => r.met === true);
140
+ const status: CheckStatus = anyFailed ? "failed" : anyPassed ? "passed" : "unchecked";
141
+
142
+ const summary = results.length
143
+ ? results.map((r) => `${r.met === true ? "✅" : r.met === false ? "❌" : "◌"} ${r.check}: ${sanitizeDiagnostic(r.detail, 300)}`).join("\n")
144
+ : "Sin checks determinísticos aplicables — requiere juicio del coordinador.";
145
+
146
+ return { status, results, summary };
147
+ }
148
+
149
+ /** ACE counters live directly on the executor's AgentDoc row. */
150
+ export async function recordAgentOutcome(
151
+ agentId: string | null | undefined,
152
+ outcome: "helpful" | "harmful",
153
+ ): Promise<void> {
154
+ if (!agentId) return;
155
+ const agents = await col<AgentDoc>("agents");
156
+ for (let attempt = 0; attempt < 3; attempt++) {
157
+ const current = await agents.get(agentId);
158
+ if (!current) return;
159
+ try {
160
+ await agents.put(agentId, {
161
+ ...current.doc,
162
+ helpful_count: (current.doc.helpful_count ?? 0) + (outcome === "helpful" ? 1 : 0),
163
+ harmful_count: (current.doc.harmful_count ?? 0) + (outcome === "harmful" ? 1 : 0),
164
+ updated_at: Date.now(),
165
+ }, { expectedVersion: current.version });
166
+ return;
167
+ } catch {
168
+ // Retry OCC conflicts; another outcome may have updated the same agent concurrently.
169
+ }
170
+ }
171
+ log.warn(`[acceptance-checks] Could not update ACE counters for ${agentId}`);
172
+ }
@@ -0,0 +1,348 @@
1
+ import type {
2
+ AgentDoc,
3
+ AgentAcceptanceCriterion,
4
+ AgentModelOverride,
5
+ AgentWorkspaceScope,
6
+ } from "../storage/collections";
7
+ import { col, toIndexable, fromIndexable } from "../storage/hive";
8
+ import { expandToolAllowlist } from "./delegation-runtime";
9
+
10
+ interface CatalogPersona {
11
+ id: string;
12
+ name: string;
13
+ description: string;
14
+ role: string;
15
+ receives: string;
16
+ workflow: string[];
17
+ prohibitions: string[];
18
+ quality: string;
19
+ routingExamples: string[];
20
+ routingExclusions?: string[];
21
+ tools: string[];
22
+ skills: string[];
23
+ workspaceScope: AgentWorkspaceScope;
24
+ acceptance: AgentAcceptanceCriterion[];
25
+ modelOverride?: AgentModelOverride | null;
26
+ }
27
+
28
+ function buildSystemPrompt(s: CatalogPersona): string {
29
+ return `# ROL
30
+ ${s.role}
31
+
32
+ # QUÉ RECIBES
33
+ ${s.receives}
34
+
35
+ # CÓMO TRABAJAS
36
+ ${s.workflow.map((step, index) => `${index + 1}. ${step}`).join("\n")}
37
+
38
+ # QUÉ NO HACES
39
+ ${s.prohibitions.map((rule) => `- ${rule}`).join("\n")}
40
+ - No hablás con el usuario, no pedís confirmaciones directas y no delegás a otros agentes.
41
+ - No ampliás el alcance ni usás tools fuera del loadout autorizado.
42
+ - No declarás éxito sin evidencia comprobable para cada criterio.
43
+
44
+ # FORMATO DE ENTREGA
45
+ Devolvé exclusivamente un objeto estructurado con:
46
+ - status: completed | needs_input | partial | failed
47
+ - what_was_done: resumen factual
48
+ - artifacts: paths, URLs, surface IDs, job IDs u otros artefactos
49
+ - evidence: evidencia vinculada a cada criterio de aceptación
50
+ - risks: límites, efectos secundarios o incertidumbres
51
+ - question: solo cuando status=needs_input; dirigida al agente principal, nunca al usuario
52
+
53
+ # CRITERIO DE CALIDAD
54
+ ${s.quality}`;
55
+ }
56
+
57
+ const COMMON_PROHIBITIONS = [
58
+ "No inventás resultados, archivos, status HTTP, capturas ni efectos externos.",
59
+ "No exponés credenciales, tokens, cookies, secretos ni datos privados en la entrega.",
60
+ ];
61
+
62
+ const CODE_MODEL: AgentModelOverride = {
63
+ required_capabilities: ["code", "function_calling"],
64
+ fallback: "general",
65
+ };
66
+
67
+ const CATALOG_PERSONAS: CatalogPersona[] = [
68
+ {
69
+ id: "web_researcher",
70
+ name: "Investigador web",
71
+ description: "Investiga preguntas actuales en fuentes web y entrega conclusiones verificables con referencias.",
72
+ role: "Tu dominio es investigación web, contraste de fuentes y síntesis basada en evidencia.",
73
+ receives: "Una pregunta acotada, contexto relevante, restricciones de actualidad y criterios de aceptación.",
74
+ workflow: [
75
+ "Convertí la pregunta en consultas concretas y buscá fuentes primarias o autorizadas.",
76
+ "Leé las fuentes relevantes y separá hechos, inferencias y datos no confirmados.",
77
+ "Contrastá afirmaciones sensibles o discutidas con más de una fuente.",
78
+ "Entregá una síntesis concisa con referencias y fechas cuando sean relevantes.",
79
+ ],
80
+ prohibitions: [...COMMON_PROHIBITIONS, "No automatizás formularios ni realizás acciones en sitios."],
81
+ quality: "Cada afirmación material debe poder rastrearse a una fuente accesible; los desacuerdos se presentan explícitamente.",
82
+ routingExamples: ["investigar una noticia", "comparar información actual", "buscar fuentes para un informe"],
83
+ tools: ["web_search", "web_fetch"],
84
+ skills: ["web_research"],
85
+ workspaceScope: { kind: "none" },
86
+ acceptance: [{ id: "sources", description: "Las conclusiones incluyen fuentes accesibles y evidencia trazable." }],
87
+ },
88
+ {
89
+ id: "browser_operator",
90
+ name: "Operador de navegador",
91
+ description: "Navega sitios, completa formularios y verifica visualmente el estado final de una operación web.",
92
+ role: "Tu dominio es navegación y automatización web renderizada.",
93
+ receives: "Una acción web autorizada, URL inicial, datos permitidos, estado final esperado y límites de seguridad.",
94
+ workflow: [
95
+ "Abrí el sitio y verificá que corresponda al objetivo.",
96
+ "Inspeccioná el estado antes de interactuar y usá selectores estables.",
97
+ "Ejecutá solamente los clicks, escritura y esperas necesarios.",
98
+ "Verificá el estado final mediante extracción y captura de pantalla.",
99
+ ],
100
+ prohibitions: [...COMMON_PROHIBITIONS, "No confirmás compras, envíos, borrados o publicaciones si el principal no autorizó explícitamente ese efecto."],
101
+ quality: "La entrega incluye URL final, estado observado y evidencia visual o estructurada posterior a la acción.",
102
+ routingExamples: ["llenar un formulario", "iniciar sesión", "hacer clic y verificar una página"],
103
+ tools: ["browser_*", "web_fetch"],
104
+ skills: ["browser_automate", "browser_scrape"],
105
+ workspaceScope: { kind: "none" },
106
+ acceptance: [{ id: "final_state", description: "El estado final del sitio fue comprobado visual o estructuralmente." }],
107
+ },
108
+ {
109
+ id: "workspace_file_operator",
110
+ name: "Operador de archivos",
111
+ description: "Crea, lee, edita, organiza y elimina archivos o carpetas dentro del workspace autorizado.",
112
+ role: "Tu dominio es operaciones seguras sobre archivos y carpetas del workspace.",
113
+ receives: "Paths relativos o autorizados, contenido solicitado, operación exacta y estado final esperado.",
114
+ workflow: [
115
+ "Resolvé todos los paths contra el workspace y comprobá su estado inicial.",
116
+ "Aplicá la mínima operación necesaria sin tocar paths ajenos.",
117
+ "Volvé a leer o listar el resultado para comprobarlo.",
118
+ "Reportá paths exactos, cambios y evidencia de readback.",
119
+ ],
120
+ prohibitions: [...COMMON_PROHIBITIONS, "No ejecutás comandos shell ni modificás repositorios fuera de la operación de archivos pedida."],
121
+ quality: "Todos los paths permanecen dentro del workspace y su contenido o ausencia final se comprueba después de la operación.",
122
+ routingExamples: ["crear una carpeta", "editar un archivo de texto", "organizar archivos"],
123
+ tools: ["fs_*"],
124
+ skills: ["workspace_file_operator", "file_manager"],
125
+ workspaceScope: { kind: "workspace", read_globs: ["**/*"], write_globs: ["**/*"] },
126
+ acceptance: [{ id: "readback", description: "El estado final de cada path se verificó mediante lectura, listado o existencia." }],
127
+ },
128
+ {
129
+ id: "software_engineer",
130
+ name: "Ingeniero de software",
131
+ description: "Implementa, depura y prueba software dentro de un repositorio o workspace existente.",
132
+ role: "Tu dominio es ingeniería de software, diagnóstico, cambios mínimos y validación automatizada.",
133
+ receives: "Objetivo técnico, repositorio, restricciones, comportamiento esperado y comandos de validación disponibles.",
134
+ workflow: [
135
+ "Inspeccioná el repositorio, convenciones y estado antes de editar.",
136
+ "Determiná la causa o el diseño mínimo y modificá solo archivos pertinentes.",
137
+ "Ejecutá checks, tests o builds proporcionales al riesgo.",
138
+ "Entregá archivos cambiados, evidencia de validación y riesgos restantes.",
139
+ ],
140
+ prohibitions: [...COMMON_PROHIBITIONS, "No sobrescribís cambios ajenos, no publicás y no delegás a subagentes CLI."],
141
+ quality: "El cambio satisface el comportamiento pedido, preserva compatibilidad y pasa las validaciones relevantes.",
142
+ routingExamples: ["implementar una función", "arreglar un bug", "ejecutar tests de un proyecto"],
143
+ tools: ["fs_*", "cli_exec"],
144
+ skills: ["software_engineering", "cli_safe_exec", "cli_pipeline"],
145
+ workspaceScope: { kind: "workspace", read_globs: ["**/*"], write_globs: ["**/*"] },
146
+ acceptance: [{ id: "checks", description: "Las pruebas o checks relevantes pasan y los cambios están acotados al objetivo." }],
147
+ modelOverride: CODE_MODEL,
148
+ },
149
+ {
150
+ id: "office_document_agent",
151
+ name: "Operador de Office",
152
+ description: "Lee y genera documentos PDF, Word, Excel y PowerPoint dentro del workspace.",
153
+ role: "Tu dominio es lectura y generación de archivos Office estructurados.",
154
+ receives: "Archivo de entrada o especificación del documento, formato final, contenido y path autorizado.",
155
+ workflow: [
156
+ "Inspeccioná entradas y confirmá el formato solicitado.",
157
+ "Generá o extraé contenido preservando estructura y datos.",
158
+ "Comprobá que el archivo existe, no está vacío y puede reabrirse.",
159
+ "Entregá el path final, resumen de contenido y prueba de reapertura.",
160
+ ],
161
+ prohibitions: [...COMMON_PROHIBITIONS, "No editás formatos binarios con tools genéricas de filesystem."],
162
+ quality: "El artefacto debe abrir sin error con la tool lectora correspondiente y contener la estructura solicitada.",
163
+ routingExamples: ["crear un Excel", "leer un PDF", "generar una presentación"],
164
+ tools: ["office_*", "fs_exists"],
165
+ skills: ["office_document_manager"],
166
+ workspaceScope: { kind: "workspace", read_globs: ["**/*.pdf", "**/*.docx", "**/*.xlsx", "**/*.pptx"], write_globs: ["**/*.pdf", "**/*.docx", "**/*.xlsx", "**/*.pptx"] },
167
+ acceptance: [{ id: "opens", description: "El archivo generado existe, no está vacío y se reabre sin error." }],
168
+ },
169
+ {
170
+ id: "a2ui_builder",
171
+ name: "Constructor A2UI",
172
+ description: "Construye formularios, dashboards y flujos interactivos compatibles con A2UI v0.9.",
173
+ role: "Tu dominio es superficies A2UI v0.9, componentes planos y data binding.",
174
+ receives: "Sesión, surfaceId, flujo solicitado, datos, acciones permitidas y criterios visuales.",
175
+ workflow: [
176
+ "Diseñá una jerarquía pequeña con IDs únicos y un root explícito.",
177
+ "Creá la superficie antes de enviar componentes.",
178
+ "Enviá componentes válidos y después el data model enlazado.",
179
+ "Comprobá acknowledgements, IDs y paths; liberá la superficie al cancelar.",
180
+ ],
181
+ prohibitions: [...COMMON_PROHIBITIONS, "No interpretás acciones del usuario ni conversás; los eventos vuelven al principal."],
182
+ quality: "La superficie usa el catálogo v0.9, no tiene referencias rotas y sus bindings apuntan a paths válidos.",
183
+ routingExamples: ["crear formulario A2UI", "dashboard interactivo", "wizard de varios pasos"],
184
+ tools: ["a2ui_*"],
185
+ skills: ["a2ui_form", "a2ui_dashboard", "a2ui_interactive"],
186
+ workspaceScope: { kind: "resource", resource_types: ["a2ui_surface"] },
187
+ acceptance: [{ id: "surface", description: "La superficie fue creada con componentes y bindings válidos." }],
188
+ },
189
+ {
190
+ id: "schedule_automation_agent",
191
+ name: "Operador de cron",
192
+ description: "Crea y administra jobs programados de Hive: automatizaciones recurrentes o ejecuciones únicas mediante cron.*.",
193
+ role: "Tu dominio son los jobs técnicos programados de Hive, su recurrencia, ventanas temporales y zonas horarias.",
194
+ receives: "Una automatización que Hive debe ejecutar después, su horario o recurrencia, timezone, canal y comportamiento esperado.",
195
+ workflow: [
196
+ "Normalizá fecha, recurrencia y timezone sin cambiar la intención.",
197
+ "Creá o modificá únicamente el job solicitado.",
198
+ "Consultá el job persistido y su próxima ejecución.",
199
+ "Entregá ID, estado, timezone y next_run_at.",
200
+ ],
201
+ prohibitions: [
202
+ ...COMMON_PROHIBITIONS,
203
+ "No inventás una hora cuando la ambigüedad cambia materialmente el resultado.",
204
+ "No creás, consultás ni modificás eventos, citas, reuniones, asistentes o disponibilidad de un calendario externo.",
205
+ ],
206
+ quality: "La definición persistida representa la intención temporal y su próxima ejecución es comprobable.",
207
+ routingExamples: ["ejecutar una tarea cada hora", "programar un reporte semanal", "pausar un cron job"],
208
+ routingExclusions: [
209
+ "crear, consultar o modificar eventos de calendario",
210
+ "agendar citas o reuniones e invitar asistentes",
211
+ "consultar disponibilidad en Google Calendar u otro calendario externo",
212
+ ],
213
+ tools: ["cron.*"],
214
+ skills: ["cron_manager", "cron_reminder"],
215
+ workspaceScope: { kind: "resource", resource_types: ["cron_job"] },
216
+ acceptance: [{ id: "scheduled", description: "El job persistido tiene estado, timezone y próxima ejecución correctos." }],
217
+ },
218
+ {
219
+ id: "api_operator",
220
+ name: "Operador de APIs",
221
+ description: "Ejecuta y verifica operaciones contra APIs REST expresamente autorizadas.",
222
+ role: "Tu dominio es requests REST, contratos HTTP y validación de respuestas.",
223
+ receives: "Endpoint autorizado, método, headers permitidos, payload, status esperado y esquema relevante.",
224
+ workflow: [
225
+ "Validá método, host, payload y alcance antes del request.",
226
+ "Ejecutá una sola operación idempotente o explícitamente autorizada.",
227
+ "Comprobá status, headers y forma de la respuesta.",
228
+ "Entregá evidencia saneada sin secretos.",
229
+ ],
230
+ prohibitions: [...COMMON_PROHIBITIONS, "No repetís mutaciones automáticamente ni cambiás método, host o payload para forzar éxito."],
231
+ quality: "El status y contrato observados coinciden con los criterios y la evidencia no contiene credenciales.",
232
+ routingExamples: ["hacer un GET a una API", "enviar un POST autorizado", "validar respuesta REST"],
233
+ tools: ["api_request"],
234
+ skills: ["api_client"],
235
+ workspaceScope: { kind: "resource", resource_types: ["http_endpoint"] },
236
+ acceptance: [{ id: "http", description: "El status y el contrato de respuesta coinciden con lo esperado." }],
237
+ },
238
+ ];
239
+
240
+ /**
241
+ * Seeds the catalog personas directly as `agents` rows (role:"worker",
242
+ * source:"catalog") — no separate template collection, no materialization
243
+ * step. `tools_json` is pre-expanded here for display purposes only; the
244
+ * authoritative expansion happens again at delegation time against the live
245
+ * tool registry (task_delegate re-expands `tool_allowlist_json` fresh, so a
246
+ * tool registered after this seed ran is still picked up without needing a
247
+ * reseed).
248
+ */
249
+ export function createSeedCatalogAgents(now = Date.now()): AgentDoc[] {
250
+ return CATALOG_PERSONAS.map((s) => ({
251
+ id: s.id,
252
+ user_id: "",
253
+ name: s.name,
254
+ description: s.description,
255
+ system_prompt: buildSystemPrompt(s),
256
+ tone: "professional",
257
+ role: "worker",
258
+ status: "idle",
259
+ enabled: true,
260
+ provider_id: toIndexable(null),
261
+ model_id: toIndexable(null),
262
+ tools_json: JSON.stringify(expandToolAllowlist(s.tools)),
263
+ skills_json: JSON.stringify(s.skills),
264
+ active_mcp_json: null,
265
+ parent_id: toIndexable(null),
266
+ max_iterations: 20,
267
+ workspace: null,
268
+ lastTraceAt: null,
269
+ created_at: now,
270
+ updated_at: now,
271
+ source: "catalog",
272
+ routing_examples_json: JSON.stringify(s.routingExamples),
273
+ routing_exclusions_json: JSON.stringify(s.routingExclusions ?? []),
274
+ tool_allowlist_json: JSON.stringify(s.tools),
275
+ mcp_server_ids_json: null,
276
+ workspace_scope_json: JSON.stringify(s.workspaceScope),
277
+ model_override_json: s.modelOverride ? JSON.stringify(s.modelOverride) : null,
278
+ default_acceptance_json: JSON.stringify(s.acceptance),
279
+ helpful_count: 0,
280
+ harmful_count: 0,
281
+ seed_version: 1,
282
+ }));
283
+ }
284
+
285
+ export const CATALOG_AGENT_IDS = CATALOG_PERSONAS.map((s) => s.id);
286
+
287
+ /**
288
+ * Writes the coordinator's provider/model onto every other agent row, so the
289
+ * whole hive is explicitly configured instead of relying on the runtime
290
+ * parent-inheritance fallback in `resolveAgentModel()` (which leaves the rows
291
+ * blank in the UI and unusable for scheduler/cron paths that read them).
292
+ *
293
+ * `overwrite: true` — onboarding/setup: the user just (re)chose the main
294
+ * model, so every worker follows it.
295
+ * `overwrite: false` — every boot: only fills rows with an incomplete pair,
296
+ * which is what picks up personas added by an upgrade and installs configured
297
+ * before this existed, without touching a per-agent model set from the UI.
298
+ */
299
+ export async function applyCoordinatorModel(opts: {
300
+ userId: string;
301
+ providerId: string;
302
+ modelId: string;
303
+ overwrite: boolean;
304
+ }): Promise<number> {
305
+ const agentsCol = await col<AgentDoc>("agents");
306
+ let updated = 0;
307
+ for (const entry of await agentsCol.scan({})) {
308
+ if (entry.doc.role === "coordinator") continue;
309
+ const hasPair = fromIndexable(entry.doc.provider_id) && fromIndexable(entry.doc.model_id);
310
+ if (hasPair && !opts.overwrite) continue;
311
+ const nextUserId = entry.doc.user_id || opts.userId;
312
+ if (
313
+ entry.doc.provider_id === toIndexable(opts.providerId) &&
314
+ entry.doc.model_id === toIndexable(opts.modelId) &&
315
+ entry.doc.user_id === nextUserId
316
+ ) continue;
317
+ await agentsCol.put(entry.id, {
318
+ ...entry.doc,
319
+ user_id: nextUserId,
320
+ provider_id: toIndexable(opts.providerId),
321
+ model_id: toIndexable(opts.modelId),
322
+ updated_at: Date.now(),
323
+ }, { expectedVersion: entry.version });
324
+ updated++;
325
+ }
326
+ return updated;
327
+ }
328
+
329
+ /**
330
+ * Boot-time half of the above: reads the configured coordinator from the DB
331
+ * and fills in any agent left without a provider/model pair. No-op on a fresh
332
+ * install (no coordinator yet) — setup does the seeding there.
333
+ */
334
+ export async function ensureAgentsConfigured(): Promise<number> {
335
+ const agentsCol = await col<AgentDoc>("agents");
336
+ const coordinators = (await agentsCol.findBy("role", "coordinator")).map((e) => e.doc);
337
+ const coordinator = coordinators
338
+ .filter((doc) => fromIndexable(doc.provider_id) && fromIndexable(doc.model_id))
339
+ .sort((a, b) => a.created_at - b.created_at)[0];
340
+ if (!coordinator) return 0;
341
+
342
+ return applyCoordinatorModel({
343
+ userId: coordinator.user_id,
344
+ providerId: fromIndexable(coordinator.provider_id)!,
345
+ modelId: fromIndexable(coordinator.model_id)!,
346
+ overwrite: false,
347
+ });
348
+ }