@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,164 @@
1
+ /**
2
+ * `api_request` cambió de contrato en 0.1.5.
3
+ *
4
+ * El tool anterior (`tools/web/api-request.ts`) tenía helpers de autenticación
5
+ * (`auth: { type: "bearer" | "basic" | "api_key" }`), aceptaba `body` como
6
+ * objeto y devolvía `{ ok, status, data }`. El de hive es más chato: la
7
+ * autenticación va como un header más, `body` es string, y la respuesta expone
8
+ * `body` + `contentType` en vez de `data`.
9
+ */
10
+
11
+ import { describe, it, expect, beforeEach, afterEach } from "bun:test";
12
+ import { apiRequestTool } from "./api-request.ts";
13
+
14
+ describe("apiRequestTool", () => {
15
+ let originalFetch: typeof fetch;
16
+
17
+ beforeEach(() => {
18
+ originalFetch = globalThis.fetch;
19
+ });
20
+
21
+ afterEach(() => {
22
+ globalThis.fetch = originalFetch;
23
+ });
24
+
25
+ function stubFetch(handler: (url: string, init: RequestInit) => Response) {
26
+ globalThis.fetch = Object.assign(
27
+ async (url: any, init?: any) => handler(url.toString(), init ?? {}),
28
+ { preconnect: async () => undefined }
29
+ ) as typeof fetch;
30
+ }
31
+
32
+ function jsonResponse(body: unknown, init: ResponseInit = {}) {
33
+ return new Response(JSON.stringify(body), {
34
+ status: init.status ?? 200,
35
+ statusText: init.statusText ?? "OK",
36
+ headers: { "content-type": "application/json" },
37
+ });
38
+ }
39
+
40
+ it("ejecuta un GET simple y parsea el JSON", async () => {
41
+ let capturedUrl = "";
42
+ let capturedInit: RequestInit = {};
43
+ stubFetch((url, init) => {
44
+ capturedUrl = url;
45
+ capturedInit = init;
46
+ return jsonResponse({ hello: "world" });
47
+ });
48
+
49
+ const result = await apiRequestTool.execute({
50
+ method: "GET",
51
+ url: "https://api.example.com/data",
52
+ }) as any;
53
+
54
+ expect(capturedUrl).toBe("https://api.example.com/data");
55
+ expect(capturedInit.method).toBe("GET");
56
+ expect(result.ok).toBe(true);
57
+ expect(result.status).toBe(200);
58
+ expect(result.body).toEqual({ hello: "world" });
59
+ });
60
+
61
+ it("manda POST con el body tal cual y los headers dados", async () => {
62
+ let capturedInit: RequestInit = {};
63
+ stubFetch((_url, init) => {
64
+ capturedInit = init;
65
+ return jsonResponse({ id: 1 }, { status: 201, statusText: "Created" });
66
+ });
67
+
68
+ const result = await apiRequestTool.execute({
69
+ method: "POST",
70
+ url: "https://api.example.com/items",
71
+ body: JSON.stringify({ name: "test" }),
72
+ headers: { "Content-Type": "application/json", Authorization: "Bearer tok" },
73
+ }) as any;
74
+
75
+ expect(capturedInit.method).toBe("POST");
76
+ expect(capturedInit.body).toBe('{"name":"test"}');
77
+ expect((capturedInit.headers as Record<string, string>).Authorization).toBe("Bearer tok");
78
+ expect(result.status).toBe(201);
79
+ expect(result.body).toEqual({ id: 1 });
80
+ });
81
+
82
+ it("codifica query_params en la URL", async () => {
83
+ let capturedUrl = "";
84
+ stubFetch((url) => {
85
+ capturedUrl = url;
86
+ return jsonResponse({});
87
+ });
88
+
89
+ await apiRequestTool.execute({
90
+ method: "GET",
91
+ url: "https://api.example.com/search",
92
+ query_params: { q: "hola mundo", limit: "10" },
93
+ });
94
+
95
+ expect(capturedUrl).toContain("q=hola+mundo");
96
+ expect(capturedUrl).toContain("limit=10");
97
+ });
98
+
99
+ it("devuelve texto plano sin intentar parsearlo", async () => {
100
+ stubFetch(() => new Response("hello world", {
101
+ status: 200,
102
+ statusText: "OK",
103
+ headers: { "content-type": "text/plain" },
104
+ }));
105
+
106
+ const result = await apiRequestTool.execute({
107
+ method: "GET",
108
+ url: "https://api.example.com/text",
109
+ }) as any;
110
+
111
+ expect(result.ok).toBe(true);
112
+ expect(result.body).toBe("hello world");
113
+ expect(result.contentType).toContain("text/plain");
114
+ });
115
+
116
+ it("marca ok:false en un HTTP no exitoso sin lanzar", async () => {
117
+ stubFetch(() => jsonResponse({ error: "not found" }, { status: 404, statusText: "Not Found" }));
118
+
119
+ const result = await apiRequestTool.execute({
120
+ method: "GET",
121
+ url: "https://api.example.com/missing",
122
+ }) as any;
123
+
124
+ expect(result.ok).toBe(false);
125
+ expect(result.status).toBe(404);
126
+ expect(result.statusText).toBe("Not Found");
127
+ });
128
+
129
+ it("rechaza un método no permitido antes de tocar la red", async () => {
130
+ let called = false;
131
+ stubFetch(() => { called = true; return jsonResponse({}); });
132
+
133
+ const result = await apiRequestTool.execute({
134
+ method: "TRACE",
135
+ url: "https://api.example.com/data",
136
+ }) as any;
137
+
138
+ expect(called).toBe(false);
139
+ expect(result.ok).toBe(false);
140
+ expect(result.error).toContain("Invalid HTTP method");
141
+ });
142
+
143
+ it("exige url", async () => {
144
+ const result = await apiRequestTool.execute({ method: "GET" }) as any;
145
+
146
+ expect(result.ok).toBe(false);
147
+ expect(result.error).toContain("url");
148
+ });
149
+
150
+ it("devuelve el error de red como resultado, no como excepción", async () => {
151
+ globalThis.fetch = Object.assign(
152
+ async () => { throw new Error("ECONNREFUSED"); },
153
+ { preconnect: async () => undefined }
154
+ ) as typeof fetch;
155
+
156
+ const result = await apiRequestTool.execute({
157
+ method: "GET",
158
+ url: "https://api.example.com/down",
159
+ }) as any;
160
+
161
+ expect(result.ok).toBe(false);
162
+ expect(result.error).toContain("ECONNREFUSED");
163
+ });
164
+ });
@@ -0,0 +1,174 @@
1
+ /**
2
+ * api_request - Make HTTP requests to REST APIs (curl-like)
3
+ *
4
+ * Supports: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
5
+ * Features: custom headers, JSON/form body, query params, timeout
6
+ *
7
+ * @category api
8
+ * @seedId api_request
9
+ * @spanish llamar api, petición http, curl, post a api, put api, delete api
10
+ */
11
+
12
+ import type { Tool } from "../types.ts";
13
+ import { logger } from "../../utils/logger.ts";
14
+
15
+ const log = logger.child("api-request");
16
+
17
+ const ALLOWED_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
18
+
19
+ export const apiRequestTool: Tool = {
20
+ name: "api_request",
21
+ description:
22
+ "Make an HTTP request to a REST API endpoint. Supports GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS with custom headers, body, and query parameters. " +
23
+ "Spanish: llamar api, petición http, curl, post a api, put api, delete api, consumir servicio rest",
24
+ parameters: {
25
+ type: "object",
26
+ properties: {
27
+ method: {
28
+ type: "string",
29
+ description: "HTTP method: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS",
30
+ enum: ALLOWED_METHODS,
31
+ },
32
+ url: {
33
+ type: "string",
34
+ description: "Full URL of the API endpoint (including query string, or use query_params)",
35
+ },
36
+ headers: {
37
+ type: "object",
38
+ description: "Optional HTTP headers as key-value pairs. Example: {\"Content-Type\": \"application/json\", \"Authorization\": \"Bearer token\"}",
39
+ additionalProperties: { type: "string" },
40
+ },
41
+ body: {
42
+ type: "string",
43
+ description: "Optional request body as a string. For JSON APIs, pass a JSON string. For form data, pass URL-encoded string.",
44
+ },
45
+ query_params: {
46
+ type: "object",
47
+ description: "Optional query parameters as key-value pairs. Will be URL-encoded and appended to the URL.",
48
+ additionalProperties: { type: "string" },
49
+ },
50
+ timeout_ms: {
51
+ type: "number",
52
+ description: "Request timeout in milliseconds. Default: 30000 (30s). Max: 120000 (2 min).",
53
+ minimum: 1000,
54
+ maximum: 120000,
55
+ },
56
+ },
57
+ required: ["method", "url"],
58
+ },
59
+ execute: async (params: Record<string, unknown>) => {
60
+ const method = (params.method as string)?.toUpperCase().trim() || "GET";
61
+ let url = params.url as string;
62
+ const headers = (params.headers as Record<string, string>) || {};
63
+ const body = params.body as string | undefined;
64
+ const queryParams = (params.query_params as Record<string, string>) || {};
65
+ const timeoutMs = (params.timeout_ms as number) || 30000;
66
+
67
+ if (!ALLOWED_METHODS.includes(method)) {
68
+ return {
69
+ ok: false,
70
+ error: `Invalid HTTP method: ${method}. Allowed: ${ALLOWED_METHODS.join(", ")}`,
71
+ };
72
+ }
73
+
74
+ if (!url || typeof url !== "string") {
75
+ return {
76
+ ok: false,
77
+ error: "Missing required parameter: url",
78
+ };
79
+ }
80
+
81
+ // Append query params to URL
82
+ if (Object.keys(queryParams).length > 0) {
83
+ const urlObj = new URL(url);
84
+ for (const [key, value] of Object.entries(queryParams)) {
85
+ urlObj.searchParams.append(key, value);
86
+ }
87
+ url = urlObj.toString();
88
+ }
89
+
90
+ log.info(`[api_request] ${method} ${url}`);
91
+
92
+ const fetchOptions: RequestInit = {
93
+ method,
94
+ headers: {
95
+ "User-Agent": "HiveAgent/1.0",
96
+ ...headers,
97
+ },
98
+ // @ts-ignore — Bun supports timeout
99
+ timeout: timeoutMs,
100
+ };
101
+
102
+ if (body !== undefined && body !== null && body !== "") {
103
+ // Auto-set Content-Type to application/json if body looks like JSON
104
+ if (
105
+ !headers["Content-Type"] &&
106
+ !headers["content-type"] &&
107
+ typeof body === "string" &&
108
+ (body.trim().startsWith("{") || body.trim().startsWith("["))
109
+ ) {
110
+ (fetchOptions.headers as Record<string, string>)["Content-Type"] = "application/json";
111
+ }
112
+ fetchOptions.body = body;
113
+ }
114
+
115
+ try {
116
+ const response = await fetch(url, fetchOptions);
117
+
118
+ // For HEAD requests, don't read body
119
+ if (method === "HEAD") {
120
+ const responseHeaders: Record<string, string> = {};
121
+ response.headers.forEach((value, key) => {
122
+ responseHeaders[key] = value;
123
+ });
124
+ return {
125
+ ok: response.ok,
126
+ status: response.status,
127
+ statusText: response.statusText,
128
+ headers: responseHeaders,
129
+ url: response.url,
130
+ };
131
+ }
132
+
133
+ const contentType = response.headers.get("content-type") || "";
134
+ let responseBody: string | object;
135
+ const rawText = await response.text();
136
+
137
+ if (contentType.includes("application/json")) {
138
+ try {
139
+ responseBody = JSON.parse(rawText);
140
+ } catch {
141
+ responseBody = rawText;
142
+ }
143
+ } else {
144
+ responseBody = rawText;
145
+ }
146
+
147
+ const responseHeaders: Record<string, string> = {};
148
+ response.headers.forEach((value, key) => {
149
+ responseHeaders[key] = value;
150
+ });
151
+
152
+ log.info(`[api_request] ${method} ${url} → ${response.status} ${response.statusText}`);
153
+
154
+ return {
155
+ ok: response.ok,
156
+ status: response.status,
157
+ statusText: response.statusText,
158
+ headers: responseHeaders,
159
+ body: responseBody,
160
+ contentType,
161
+ url: response.url,
162
+ };
163
+ } catch (error) {
164
+ const msg = (error as Error).message;
165
+ log.error(`[api_request] ${method} ${url} failed: ${msg}`);
166
+ return {
167
+ ok: false,
168
+ error: `HTTP request failed: ${msg}`,
169
+ url,
170
+ method,
171
+ };
172
+ }
173
+ },
174
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * API Tools - HTTP client for REST APIs (curl-like)
3
+ *
4
+ * Makes requests to external APIs with full control over method, headers, body.
5
+ */
6
+
7
+ import type { Tool } from "../types.ts";
8
+ import { apiRequestTool } from "./api-request.ts";
9
+
10
+ export function createTools(): Tool[] {
11
+ return [
12
+ apiRequestTool,
13
+ ];
14
+ }
15
+
16
+ export { apiRequestTool } from "./api-request.ts";
@@ -30,6 +30,10 @@ const BLOCKED_PATTERNS: Array<{ pattern: RegExp; reason: string }> = [
30
30
 
31
31
  export const cliExecTool: Tool = {
32
32
  name: "cli_exec",
33
+ // Long-running commands need a generous runtime ceiling (the tool allows
34
+ // its own per-call `timeout` up to 300s, so the harness timeout must be
35
+ // above that to avoid the worker killing a still-running command).
36
+ timeoutMs: 330000,
33
37
  description: "Execute shell/bash commands in the agent workspace. NOTE: do NOT use for scheduling tasks, use cron.create instead. Spanish: ejecutar comando, terminal, bash, script, consola",
34
38
  parameters: {
35
39
  type: "object",