@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,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",