@johpaz/hive-sdk 0.1.6 → 0.3.0
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.
- package/CHANGELOG.md +441 -0
- package/README.md +21 -3
- package/package.json +27 -5
- package/packages/core/src/agent/acceptance-checks.ts +9 -9
- package/packages/core/src/agent/agent-catalog.ts +82 -25
- package/packages/core/src/agent/agent-loop.ts +115 -26
- package/packages/core/src/agent/capability-search.ts +2 -2
- package/packages/core/src/agent/catalog-selector.ts +4 -4
- package/packages/core/src/agent/compaction.ts +30 -10
- package/packages/core/src/agent/context-compiler.ts +63 -36
- package/packages/core/src/agent/conversation-store.ts +167 -9
- package/packages/core/src/agent/curator.ts +16 -7
- package/packages/core/src/agent/delegation-runtime.ts +5 -5
- package/packages/core/src/agent/goal-runner.ts +9 -9
- package/packages/core/src/agent/index.ts +1 -0
- package/packages/core/src/agent/llm-client.ts +98 -37
- package/packages/core/src/agent/llm-providers/anthropic.ts +4 -4
- package/packages/core/src/agent/llm-providers/deepseek.ts +1 -1
- package/packages/core/src/agent/llm-providers/gemini.ts +4 -4
- package/packages/core/src/agent/llm-providers/groq.ts +1 -1
- package/packages/core/src/agent/llm-providers/hiveagents.ts +3 -3
- package/packages/core/src/agent/llm-providers/interface.ts +2 -2
- package/packages/core/src/agent/llm-providers/kimi.ts +1 -1
- package/packages/core/src/agent/llm-providers/minimax.ts +1 -1
- package/packages/core/src/agent/llm-providers/mistral.ts +1 -1
- package/packages/core/src/agent/llm-providers/modelscope.ts +1 -1
- package/packages/core/src/agent/llm-providers/nvidia.ts +40 -1
- package/packages/core/src/agent/llm-providers/ollama.ts +4 -4
- package/packages/core/src/agent/llm-providers/openai-compat-base.ts +47 -7
- package/packages/core/src/agent/llm-providers/openai.ts +1 -1
- package/packages/core/src/agent/llm-providers/opencode-go.ts +1 -1
- package/packages/core/src/agent/llm-providers/openrouter.ts +1 -1
- package/packages/core/src/agent/llm-providers/qwen.ts +1 -1
- package/packages/core/src/agent/llm-providers/z-ai.ts +1 -1
- package/packages/core/src/agent/mcp-result-normalizer.ts +192 -0
- package/packages/core/src/agent/playbook-selector.ts +22 -7
- package/packages/core/src/agent/prompt-builder.ts +5 -5
- package/packages/core/src/agent/proof-packet.ts +5 -5
- package/packages/core/src/agent/providers/index.ts +39 -4
- package/packages/core/src/agent/realtime-providers/gemini-live.ts +238 -0
- package/packages/core/src/agent/realtime-providers/index.ts +29 -0
- package/packages/core/src/agent/realtime-providers/interface.ts +108 -0
- package/packages/core/src/agent/reflector.ts +38 -15
- package/packages/core/src/agent/run-store.ts +8 -8
- package/packages/core/src/agent/service.ts +10 -10
- package/packages/core/src/agent/skill-selector.ts +6 -6
- package/packages/core/src/agent/thread-id.ts +71 -0
- package/packages/core/src/agent/thread-store.ts +293 -0
- package/packages/core/src/agent/tool-selector.ts +9 -5
- package/packages/core/src/agent/tracer.ts +5 -5
- package/packages/core/src/api/createAgent.ts +67 -2
- package/packages/core/src/artifacts/index.ts +15 -0
- package/packages/core/src/artifacts/store.ts +161 -5
- package/packages/core/src/canvas/emitter.ts +2 -2
- package/packages/core/src/canvas/index.ts +9 -0
- package/packages/core/src/channels/telegram.ts +1 -1
- package/packages/core/src/channels/webchat.ts +1 -1
- package/packages/core/src/config/loader.ts +14 -5
- package/packages/core/src/ethics/EthicsGuard.ts +7 -1
- package/packages/core/src/events/agent-bus.ts +3 -3
- package/packages/core/src/events/channel-narration.ts +3 -3
- package/packages/core/src/events/event-bus.ts +1 -1
- package/packages/core/src/events/index.ts +18 -0
- package/packages/core/src/events/narration.ts +3 -3
- package/packages/core/src/events/tool-narration.ts +4 -0
- package/packages/core/src/gateway/channel-notify.ts +103 -6
- package/packages/core/src/gateway/delegation-groups.ts +4 -4
- package/packages/core/src/gateway/durable-queue.ts +18 -6
- package/packages/core/src/gateway/index.ts +3 -0
- package/packages/core/src/gateway/job-store.ts +11 -5
- package/packages/core/src/gateway/notification-inbox.ts +2 -2
- package/packages/core/src/gateway/server.ts +2 -2
- package/packages/core/src/harness/executors.ts +493 -0
- package/packages/core/src/harness/index.ts +12 -2
- package/packages/core/src/hooks/index.ts +203 -0
- package/packages/core/src/images/index.ts +161 -0
- package/packages/core/src/index.ts +1 -0
- package/packages/core/src/mcp/MCPClient.ts +3 -3
- package/packages/core/src/mcp/hot-reload.ts +5 -5
- package/packages/core/src/mcp/tool-sync.ts +5 -5
- package/packages/core/src/mcp/transports/index.ts +2 -2
- package/packages/core/src/mcp/transports/sse.ts +1 -1
- package/packages/core/src/models/index.ts +36 -0
- package/packages/core/src/multimodal/index.ts +2 -2
- package/packages/core/src/multimodal/vision-service.ts +51 -19
- package/packages/core/src/plugins/loader.ts +4 -1
- package/packages/core/src/resilience/circuit-breaker.ts +16 -5
- package/packages/core/src/resilience/index.ts +13 -0
- package/packages/core/src/resilience/retry.ts +1 -1
- package/packages/core/src/scheduler/CronScheduler.ts +54 -27
- package/packages/core/src/scheduler/cron/expression.ts +165 -0
- package/packages/core/src/scheduler/cron/index.ts +10 -0
- package/packages/core/src/scheduler/cron/job.ts +339 -0
- package/packages/core/src/scheduler/cron/next-run.ts +121 -0
- package/packages/core/src/scheduler/cron/zoned-time.ts +138 -0
- package/packages/core/src/scheduler/index.ts +21 -3
- package/packages/core/src/scheduler/integration.ts +25 -14
- package/packages/core/src/scheduler/types.ts +3 -18
- package/packages/core/src/services/agents.ts +268 -0
- package/packages/core/src/services/cron.ts +257 -0
- package/packages/core/src/services/endpoints.ts +289 -0
- package/packages/core/src/services/ethics.ts +107 -0
- package/packages/core/src/services/images.ts +212 -0
- package/packages/core/src/services/index.ts +112 -0
- package/packages/core/src/services/mcp.ts +201 -0
- package/packages/core/src/services/memory.ts +133 -0
- package/packages/core/src/services/models.ts +179 -0
- package/packages/core/src/services/providers.ts +152 -0
- package/packages/core/src/services/setup.ts +222 -0
- package/packages/core/src/services/skills.ts +241 -0
- package/packages/core/src/services/swarms.ts +307 -0
- package/packages/core/src/services/tools.ts +106 -0
- package/packages/core/src/sessions/index.ts +268 -0
- package/packages/core/src/sessions/resolve.ts +108 -0
- package/packages/core/src/skills/SkillLoader.ts +8 -1
- package/packages/core/src/skills/bundled/artifacts/artifact_reader/SKILL.md +105 -0
- package/packages/core/src/skills/bundled/cron_manager/SKILL.md +21 -11
- package/packages/core/src/skills/bundled/images/image_editor/SKILL.md +120 -0
- package/packages/core/src/skills/bundled/web/browser_automate/SKILL.md +12 -3
- package/packages/core/src/skills/bundled/web/browser_scrape/SKILL.md +22 -7
- package/packages/core/src/skills/bundled-data.generated.ts +110 -12
- package/packages/core/src/storage/bootstrap.ts +107 -12
- package/packages/core/src/storage/causal-events.ts +1 -1
- package/packages/core/src/storage/collections.ts +138 -2
- package/packages/core/src/storage/crypto.ts +35 -4
- package/packages/core/src/storage/hive.ts +1 -1
- package/packages/core/src/storage/hivedb.ts +10 -1
- package/packages/core/src/storage/index.ts +2 -1
- package/packages/core/src/storage/onboarding.ts +61 -45
- package/packages/core/src/storage/reconcile.ts +11 -6
- package/packages/core/src/storage/seed.ts +191 -23
- package/packages/core/src/storage/usage.ts +3 -3
- package/packages/core/src/swarm/AgentExecutor.ts +2 -2
- package/packages/core/src/swarm/Coordinator.ts +8 -8
- package/packages/core/src/swarm/EventBridge.ts +2 -2
- package/packages/core/src/swarm/RoleSwarm.ts +234 -0
- package/packages/core/src/swarm/TaskGraph.ts +2 -2
- package/packages/core/src/swarm/index.ts +7 -0
- package/packages/core/src/swarm/presets/HiveLearnPreset.ts +2 -2
- package/packages/core/src/swarm/presets/ResearchPreset.ts +2 -2
- package/packages/core/src/swarm/strategies/ParallelStrategy.ts +1 -1
- package/packages/core/src/swarm/strategies/PriorityStrategy.ts +3 -3
- package/packages/core/src/swarm/types.ts +3 -18
- package/packages/core/src/tool-runtime/embedded-worker.generated.ts +21 -0
- package/packages/core/src/tool-runtime/index.ts +129 -14
- package/packages/core/src/tools/ToolExecutor.ts +7 -3
- package/packages/core/src/tools/agents/index.ts +18 -60
- package/packages/core/src/tools/cli/index.ts +55 -0
- package/packages/core/src/tools/core/index.ts +52 -4
- package/packages/core/src/tools/cron/index.ts +8 -8
- package/packages/core/src/tools/images/index.ts +130 -0
- package/packages/core/src/tools/index.ts +14 -1
- package/packages/core/src/tools/office/office-escribir-xlsx.ts +2 -1
- package/packages/core/src/tools/office/office-leer-xlsx.ts +2 -1
- package/packages/core/src/tools/office/xlsx-loader.ts +19 -0
- package/packages/core/src/tools/web/artifact-inspect.ts +2 -2
- package/packages/core/src/tools/web/artifact-read.ts +162 -0
- package/packages/core/src/tools/web/browser-backend.ts +141 -44
- package/packages/core/src/tools/web/browser-click.ts +2 -2
- package/packages/core/src/tools/web/browser-extract.ts +2 -2
- package/packages/core/src/tools/web/browser-navigate.ts +2 -2
- package/packages/core/src/tools/web/browser-screenshot.ts +12 -5
- package/packages/core/src/tools/web/browser-script.ts +2 -2
- package/packages/core/src/tools/web/browser-service.ts +63 -384
- package/packages/core/src/tools/web/browser-session.ts +125 -0
- package/packages/core/src/tools/web/browser-type.ts +2 -2
- package/packages/core/src/tools/web/browser-wait.ts +2 -2
- package/packages/core/src/tools/web/computer-use.ts +553 -0
- package/packages/core/src/tools/web/index.ts +8 -1
- package/packages/core/src/tools/web/webview-backend.ts +460 -21
- package/packages/core/src/utils/index.ts +1 -0
- package/packages/core/src/utils/logger.ts +12 -4
- package/packages/core/src/utils/redact-binary.ts +17 -0
- package/packages/core/src/utils/toon.ts +1 -1
- package/packages/core/src/voice/index.ts +6 -6
- package/bun.lock +0 -859
- package/bunfig.toml +0 -9
- package/docs/API-AGENTS.md +0 -367
- package/docs/API-CONTEXT-COMPILER.md +0 -249
- package/docs/API-DAG-SCHEDULER.md +0 -273
- package/docs/API-TOOLS-SKILLS-CHANNELS.md +0 -446
- package/docs/API-WORKERS-EVENTS.md +0 -299
- package/docs/HIVE-HARNESS.md +0 -113
- package/docs/INDEX.md +0 -190
- package/docs/TEMPLATE-HIVE-APP.md +0 -360
- package/packages/cli/package.json +0 -17
- package/packages/cli/src/commands/create-app.test.ts +0 -180
- package/packages/core/package.json +0 -70
- package/packages/core/src/api/createAgent.test.ts +0 -160
- package/packages/core/src/canvas/canvas.test.ts +0 -36
- package/packages/core/src/channels/channels.test.ts +0 -18
- package/packages/core/src/ethics/EthicsGuard.test.ts +0 -108
- package/packages/core/src/gateway/gateway.test.ts +0 -38
- package/packages/core/src/memory/Scratchpad.test.ts +0 -68
- package/packages/core/src/scheduler/scheduler.test.ts +0 -15
- package/packages/core/src/skills/skills.test.ts +0 -62
- package/packages/core/src/swarm/swarm.test.ts +0 -24
- package/packages/core/src/tool-runtime/tool-runtime.test.ts +0 -99
- package/packages/core/src/tools/ToolRegistry.test.ts +0 -98
- package/packages/core/src/tools/api/api-request.test.ts +0 -164
- package/packages/core/src/tools/web/browser-service.test.ts +0 -83
- package/packages/core/src/workers/workers.test.ts +0 -41
- package/scripts/bump-version.ts +0 -248
- package/scripts/generate-skill-bundle.ts +0 -108
- package/test/acceptance-checks.test.ts +0 -403
- package/test/agent-loop-terminal-synthesis.test.ts +0 -32
- package/test/browser-backend.test.ts +0 -308
- package/test/catalog-agents-stay-enabled.test.ts +0 -117
- package/test/causal-events.test.ts +0 -117
- package/test/compaction.test.ts +0 -105
- package/test/context-compiler.test.ts +0 -269
- package/test/curator.test.ts +0 -130
- package/test/durable-queue.test.ts +0 -114
- package/test/harness-barrel.test.ts +0 -64
- package/test/hive-helpers.test.ts +0 -130
- package/test/hivedb-search.test.ts +0 -189
- package/test/internal-turns.test.ts +0 -166
- package/test/job-idempotency.test.ts +0 -68
- package/test/job-retry-backoff.test.ts +0 -184
- package/test/job-store.test.ts +0 -381
- package/test/llm-retry.test.ts +0 -97
- package/test/memory-perf.test.ts +0 -774
- package/test/minimal-loadout.test.ts +0 -78
- package/test/model-catalog.test.ts +0 -105
- package/test/preload.ts +0 -12
- package/test/reflector.test.ts +0 -320
- package/test/retention-cap.test.ts +0 -91
- package/test/retired-capabilities-pruned.test.ts +0 -192
- package/test/run-store.test.ts +0 -355
- package/test/scratchpad.test.ts +0 -74
- package/test/secrets-durability.test.ts +0 -119
- package/test/seed-model-reseed.test.ts +0 -155
- package/test/setup-agent-seed.test.ts +0 -264
- package/test/tool-inventory.test.ts +0 -65
- package/test/tool-runtime.test.ts +0 -258
- package/test/tool-selector-runtime-tools.test.ts +0 -117
- package/test/toon.test.ts +0 -429
- package/tsconfig.json +0 -42
|
@@ -33,22 +33,9 @@ export const memoryWriteTool: Tool = {
|
|
|
33
33
|
required: ["title", "content"],
|
|
34
34
|
},
|
|
35
35
|
execute: async (params: Record<string, unknown>) => {
|
|
36
|
-
const title = params.title as string;
|
|
37
|
-
const content = params.content as string;
|
|
38
|
-
|
|
39
36
|
try {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
const now = Date.now();
|
|
43
|
-
await memoryCol.put(title, {
|
|
44
|
-
id: title,
|
|
45
|
-
title,
|
|
46
|
-
content,
|
|
47
|
-
created_at: existing?.doc.created_at ?? now,
|
|
48
|
-
updated_at: now,
|
|
49
|
-
}, existing ? { expectedVersion: existing.version } : { expectedVersion: 0 });
|
|
50
|
-
|
|
51
|
-
return { ok: true, title, message: "Memory saved." };
|
|
37
|
+
const entry = await writeMemory(params.title as string, params.content as string);
|
|
38
|
+
return { ok: true, title: entry.title, message: "Memory saved." };
|
|
52
39
|
} catch (error) {
|
|
53
40
|
return { ok: false, error: `Failed to save memory: ${(error as Error).message}` };
|
|
54
41
|
}
|
|
@@ -69,21 +56,15 @@ export const memoryReadTool: Tool = {
|
|
|
69
56
|
},
|
|
70
57
|
execute: async (params: Record<string, unknown>) => {
|
|
71
58
|
const title = params.title as string;
|
|
72
|
-
|
|
73
59
|
try {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (!entry) {
|
|
78
|
-
return { ok: false, error: `Memory not found: ${title}` };
|
|
79
|
-
}
|
|
80
|
-
|
|
60
|
+
const entry = await readMemory(title);
|
|
61
|
+
if (!entry) return { ok: false, error: `Memory not found: ${title}` };
|
|
81
62
|
return {
|
|
82
63
|
ok: true,
|
|
83
|
-
title: entry.
|
|
84
|
-
content: entry.
|
|
85
|
-
createdAt: new Date(entry.
|
|
86
|
-
updatedAt: new Date(entry.
|
|
64
|
+
title: entry.title,
|
|
65
|
+
content: entry.content,
|
|
66
|
+
createdAt: new Date(entry.createdAt).toISOString(),
|
|
67
|
+
updatedAt: new Date(entry.updatedAt).toISOString(),
|
|
87
68
|
};
|
|
88
69
|
} catch (error) {
|
|
89
70
|
return { ok: false, error: `Failed to read memory: ${(error as Error).message}` };
|
|
@@ -102,15 +83,11 @@ export const memoryListTool: Tool = {
|
|
|
102
83
|
},
|
|
103
84
|
execute: async () => {
|
|
104
85
|
try {
|
|
105
|
-
const
|
|
106
|
-
const notes = (await memoryCol.scan({}))
|
|
107
|
-
.map(e => e.doc)
|
|
108
|
-
.sort((a, b) => b.updated_at - a.updated_at);
|
|
109
|
-
|
|
86
|
+
const entries = await listMemories();
|
|
110
87
|
return {
|
|
111
88
|
ok: true,
|
|
112
|
-
count:
|
|
113
|
-
entries:
|
|
89
|
+
count: entries.length,
|
|
90
|
+
entries: entries.map((n) => ({ title: n.title, createdAt: new Date(n.createdAt).toISOString() })),
|
|
114
91
|
};
|
|
115
92
|
} catch (error) {
|
|
116
93
|
return { ok: false, error: `Failed to list memories: ${(error as Error).message}` };
|
|
@@ -132,23 +109,9 @@ export const memorySearchTool: Tool = {
|
|
|
132
109
|
},
|
|
133
110
|
execute: async (params: Record<string, unknown>) => {
|
|
134
111
|
const query = params.query as string;
|
|
135
|
-
const needle = query.toLowerCase();
|
|
136
|
-
|
|
137
112
|
try {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
.map(e => e.doc)
|
|
141
|
-
.filter(n => n.content.toLowerCase().includes(needle) || n.title.toLowerCase().includes(needle));
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
ok: true,
|
|
145
|
-
query,
|
|
146
|
-
count: notes.length,
|
|
147
|
-
results: notes.map((n) => ({
|
|
148
|
-
title: n.title,
|
|
149
|
-
snippet: n.content.slice(0, 200) + (n.content.length > 200 ? "..." : ""),
|
|
150
|
-
})),
|
|
151
|
-
};
|
|
113
|
+
const results = await searchMemories(query);
|
|
114
|
+
return { ok: true, query, count: results.length, results };
|
|
152
115
|
} catch (error) {
|
|
153
116
|
return { ok: false, error: `Failed to search memories: ${(error as Error).message}` };
|
|
154
117
|
}
|
|
@@ -169,17 +132,9 @@ export const memoryDeleteTool: Tool = {
|
|
|
169
132
|
},
|
|
170
133
|
execute: async (params: Record<string, unknown>) => {
|
|
171
134
|
const title = params.title as string;
|
|
172
|
-
|
|
173
135
|
try {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (!existing) {
|
|
178
|
-
return { ok: false, error: `Memory not found: ${title}` };
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
await memoryCol.delete(title);
|
|
182
|
-
|
|
136
|
+
const borrada = await deleteMemory(title);
|
|
137
|
+
if (!borrada) return { ok: false, error: `Memory not found: ${title}` };
|
|
183
138
|
return { ok: true, title, message: "Memory deleted." };
|
|
184
139
|
} catch (error) {
|
|
185
140
|
return { ok: false, error: `Failed to delete memory: ${(error as Error).message}` };
|
|
@@ -1202,6 +1157,9 @@ export const busReadTool: Tool = {
|
|
|
1202
1157
|
|
|
1203
1158
|
import crypto from "crypto";
|
|
1204
1159
|
import { getAvailableModelsTool } from "./get-available-models.ts";
|
|
1160
|
+
// Las tools de memoria son envoltorios: la implementación vive en services/memory.ts,
|
|
1161
|
+
// para que una UI pueda usarla sin pasar por el formato que espera el modelo.
|
|
1162
|
+
import { writeMemory, readMemory, listMemories, searchMemories, deleteMemory } from "../../services/memory.ts";
|
|
1205
1163
|
|
|
1206
1164
|
export function createTools(): Tool[] {
|
|
1207
1165
|
return [
|
|
@@ -10,6 +10,7 @@ import type { Tool } from "../types.ts";
|
|
|
10
10
|
import { logger } from "../../utils/logger.ts";
|
|
11
11
|
import { resolveInWorkspace, getWorkspace, expandPath } from "../filesystem/workspace-guard.ts";
|
|
12
12
|
import * as fs from "node:fs";
|
|
13
|
+
import { loadConfig } from "../../config/loader.ts";
|
|
13
14
|
|
|
14
15
|
const log = logger.child("cli-exec");
|
|
15
16
|
|
|
@@ -28,6 +29,53 @@ const BLOCKED_PATTERNS: Array<{ pattern: RegExp; reason: string }> = [
|
|
|
28
29
|
{ pattern: /format\s+[a-z]:/i, reason: "disk format (Windows)" },
|
|
29
30
|
];
|
|
30
31
|
|
|
32
|
+
/** El primer token del comando: `git status` → `git`. Es lo que se compara. */
|
|
33
|
+
function commandName(command: string): string {
|
|
34
|
+
const limpio = command.trim().replace(/^\s*(sudo|env|nohup)\s+/i, "");
|
|
35
|
+
return (limpio.split(/[\s;|&<>]/)[0] ?? "").split("/").pop() ?? "";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Aplica la política de comandos que el usuario configuró.
|
|
40
|
+
*
|
|
41
|
+
* `tools.exec.allowlist` y `.denylist` existían en el esquema de configuración y
|
|
42
|
+
* **no las leía nadie**: alguien podía escribir `denylist: ["curl", "rm"]`
|
|
43
|
+
* creyendo que restringía a sus agentes, y no restringía nada. En una opción de
|
|
44
|
+
* seguridad eso es peor que no tenerla, porque da confianza falsa.
|
|
45
|
+
*
|
|
46
|
+
* `BLOCKED_PATTERNS` sigue siendo incondicional: lo catastrófico se bloquea
|
|
47
|
+
* haya o no configuración. Esta capa es la que el usuario controla.
|
|
48
|
+
*
|
|
49
|
+
* Precedencia: si hay allowlist, sólo eso se permite (es la postura más
|
|
50
|
+
* restrictiva y la que alguien espera al escribirla). La denylist se aplica
|
|
51
|
+
* después, para poder tener una allowlist amplia con excepciones puntuales.
|
|
52
|
+
*
|
|
53
|
+
* Devuelve el motivo del rechazo, o `null` si no hay objeción.
|
|
54
|
+
*/
|
|
55
|
+
function checkExecPolicy(command: string): string | null {
|
|
56
|
+
const exec = loadConfig().tools?.exec;
|
|
57
|
+
if (!exec) return null;
|
|
58
|
+
|
|
59
|
+
if (exec.enabled === false) {
|
|
60
|
+
return "La ejecución de comandos está deshabilitada en la configuración";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const nombre = commandName(command).toLowerCase();
|
|
64
|
+
if (!nombre) return null;
|
|
65
|
+
|
|
66
|
+
const permitidos = exec.allowlist?.map((c) => c.toLowerCase());
|
|
67
|
+
if (permitidos?.length && !permitidos.includes(nombre)) {
|
|
68
|
+
return `Comando no permitido: "${nombre}". Permitidos: ${permitidos.join(", ")}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const denegados = exec.denylist?.map((c) => c.toLowerCase());
|
|
72
|
+
if (denegados?.includes(nombre)) {
|
|
73
|
+
return `Comando denegado por la configuración: "${nombre}"`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return null; // sin objeción
|
|
77
|
+
}
|
|
78
|
+
|
|
31
79
|
export const cliExecTool: Tool = {
|
|
32
80
|
name: "cli_exec",
|
|
33
81
|
// Long-running commands need a generous runtime ceiling (the tool allows
|
|
@@ -75,6 +123,13 @@ export const cliExecTool: Tool = {
|
|
|
75
123
|
return { ok: false, error: `Working directory not found: ${cwd}` };
|
|
76
124
|
}
|
|
77
125
|
|
|
126
|
+
// ── Política configurable por el usuario ──────────────────────────────────
|
|
127
|
+
const objecion = checkExecPolicy(command);
|
|
128
|
+
if (objecion) {
|
|
129
|
+
log.warn(`bloqueado por configuración: ${command}`);
|
|
130
|
+
return { ok: false, error: objecion };
|
|
131
|
+
}
|
|
132
|
+
|
|
78
133
|
// ── Dangerous pattern check ────────────────────────────────────────────────
|
|
79
134
|
for (const { pattern, reason } of BLOCKED_PATTERNS) {
|
|
80
135
|
if (pattern.test(command)) {
|
|
@@ -136,6 +136,40 @@ function translateQueryToEnglish(query: string): string {
|
|
|
136
136
|
|
|
137
137
|
// ─── search_knowledge ────────────────────────────────────────────────────────
|
|
138
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Las tools que el agente que pregunta puede realmente usar.
|
|
141
|
+
*
|
|
142
|
+
* El descubrimiento buscaba contra el índice global y devolvía cualquier
|
|
143
|
+
* coincidencia, sin mirar la lista blanca de quien preguntaba. La ejecución sí
|
|
144
|
+
* estaba protegida —`context-compiler.ts` recorta `allTools`, así que una tool
|
|
145
|
+
* fuera de la lista no se puede resolver ni llamar— pero el agente igual veía su
|
|
146
|
+
* nombre y su descripción. Filtrar acá cierra esa fuga y, de paso, deja de
|
|
147
|
+
* ofrecerle al modelo capacidades que no va a poder usar, que es una forma
|
|
148
|
+
* segura de hacerle perder un turno.
|
|
149
|
+
*
|
|
150
|
+
* Sin agente en el contexto (una llamada suelta, un test) no se filtra nada.
|
|
151
|
+
*/
|
|
152
|
+
async function allowedToolNames(agentId?: string): Promise<Set<string> | null> {
|
|
153
|
+
if (!agentId) return null;
|
|
154
|
+
try {
|
|
155
|
+
const agents = await col<AgentDoc>("agents");
|
|
156
|
+
const entry = await agents.get(agentId);
|
|
157
|
+
if (!entry) return null;
|
|
158
|
+
|
|
159
|
+
const raw = entry.doc.tool_allowlist_json ?? entry.doc.tools_json;
|
|
160
|
+
if (!raw) return null; // sin lista declarada, descubrimiento abierto
|
|
161
|
+
|
|
162
|
+
const { expandToolAllowlist } = await import("../../agent/delegation-runtime.ts");
|
|
163
|
+
const patrones = JSON.parse(raw) as string[];
|
|
164
|
+
if (!Array.isArray(patrones) || patrones.length === 0) return new Set();
|
|
165
|
+
|
|
166
|
+
const { MINIMAL_TOOLS } = await import("../../agent/minimal-loadout.ts");
|
|
167
|
+
return new Set([...MINIMAL_TOOLS, ...expandToolAllowlist(patrones)]);
|
|
168
|
+
} catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
139
173
|
export const searchKnowledgeTool: Tool = {
|
|
140
174
|
name: "search_knowledge",
|
|
141
175
|
description: "Busca en TODO el conocimiento de Hive: tools nativas, MCP, skills, agentes de catálogo y playbook.",
|
|
@@ -158,10 +192,11 @@ export const searchKnowledgeTool: Tool = {
|
|
|
158
192
|
},
|
|
159
193
|
required: ["query"],
|
|
160
194
|
},
|
|
161
|
-
execute: async (params: Record<string, unknown
|
|
195
|
+
execute: async (params: Record<string, unknown>, config?: any) => {
|
|
162
196
|
const query = params.query as string;
|
|
163
197
|
const type = (params.type as string) ?? "all";
|
|
164
198
|
const limit = (params.limit as number) ?? 10;
|
|
199
|
+
const usuarioActual = (config?.configurable?.user_id as string) ?? "";
|
|
165
200
|
const MIN_RESULTS_FOR_BILINGUAL = 2;
|
|
166
201
|
|
|
167
202
|
// Map the tool's `type` param onto capability types
|
|
@@ -187,6 +222,8 @@ export const searchKnowledgeTool: Tool = {
|
|
|
187
222
|
|
|
188
223
|
const result: any = { query, type, tools: [], skills: [], playbook: [], toolsmcp: [], agents: [] };
|
|
189
224
|
|
|
225
|
+
const permitidas = await allowedToolNames(config?.configurable?.agent_id);
|
|
226
|
+
|
|
190
227
|
// ─── Hydration from HiveDB collections (index stores only ids + search text) ──
|
|
191
228
|
|
|
192
229
|
const coreCatalog = new Map(CORE_TOOL_CATALOG.map(t => [t.name, t]));
|
|
@@ -198,6 +235,10 @@ export const searchKnowledgeTool: Tool = {
|
|
|
198
235
|
const agentsCol = await col<AgentDoc>("agents");
|
|
199
236
|
|
|
200
237
|
async function hydrateTool(hit: CapabilityHit): Promise<any | null> {
|
|
238
|
+
// Ofrecerle al modelo una tool que no puede ejecutar es hacerle perder
|
|
239
|
+
// un turno, además de contarle qué existe fuera de su alcance.
|
|
240
|
+
if (permitidas && !permitidas.has(hit.rawId)) return null;
|
|
241
|
+
|
|
201
242
|
const entry = await toolsCol.get(hit.rawId);
|
|
202
243
|
if (entry) {
|
|
203
244
|
const row = entry.doc;
|
|
@@ -232,6 +273,10 @@ export const searchKnowledgeTool: Tool = {
|
|
|
232
273
|
const entry = await playbookCol.get(hit.rawId);
|
|
233
274
|
const p = entry?.doc;
|
|
234
275
|
if (!p || !p.active) return null;
|
|
276
|
+
// Mismo alcance que la inyección en el prompt: lo global más lo propio.
|
|
277
|
+
// Sin esto, el agente puede buscar en el playbook y leerle a un usuario
|
|
278
|
+
// lo que aprendió de otro, por la puerta de al lado.
|
|
279
|
+
if (p.user_id !== "" && p.user_id !== usuarioActual) return null;
|
|
235
280
|
return {
|
|
236
281
|
id: p.id, rule: p.rule, category: p.category,
|
|
237
282
|
applicable_to: p.applicable_to ? JSON.parse(p.applicable_to) : null,
|
|
@@ -346,14 +391,17 @@ export const notifyTool: Tool = {
|
|
|
346
391
|
required: ["message"],
|
|
347
392
|
},
|
|
348
393
|
execute: async (params: Record<string, unknown>, config?: any) => {
|
|
349
|
-
const { sendToUserChannel } = await import("../../gateway/channel-notify");
|
|
394
|
+
const { sendToUserChannel } = await import("../../gateway/channel-notify.ts");
|
|
350
395
|
const message = params.message as string;
|
|
351
396
|
const channel = (config?.configurable?.channel as string) ?? "webchat";
|
|
352
397
|
const userId = (config?.configurable?.user_id as string) ?? "";
|
|
353
398
|
|
|
354
399
|
log.info(`[notify] Sending to ${channel}/${userId}: ${message.substring(0, 80)}`);
|
|
355
400
|
|
|
356
|
-
|
|
401
|
+
// El aviso tiene que volver al hilo del que salió: sin el threadId,
|
|
402
|
+
// `notifyChannel` no sabe a qué conversación del canal responder.
|
|
403
|
+
const threadId = config?.configurable?.thread_id as string | undefined;
|
|
404
|
+
const result = await sendToUserChannel(channel, userId, message, { threadId })
|
|
357
405
|
if (!result.ok) throw new Error(`Channel send failed: ${result.error}`)
|
|
358
406
|
return result
|
|
359
407
|
},
|
|
@@ -423,7 +471,7 @@ export const reportProgressTool: Tool = {
|
|
|
423
471
|
required: ["progress", "message"],
|
|
424
472
|
},
|
|
425
473
|
execute: async (params: Record<string, unknown>, config?: any) => {
|
|
426
|
-
const { sendToUserChannel } = await import("../../gateway/channel-notify");
|
|
474
|
+
const { sendToUserChannel } = await import("../../gateway/channel-notify.ts");
|
|
427
475
|
const progress = params.progress as number;
|
|
428
476
|
const message = params.message as string;
|
|
429
477
|
const taskId = (params.task_id as string) ?? null;
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
* @category cron
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type { Tool } from "../types";
|
|
12
|
-
import { col, toIndexable } from "../../storage/hive";
|
|
13
|
-
import type { UserDoc, UserIdentityDoc, ChannelDoc, CronJobDoc, TaskRunDoc } from "../../storage/collections";
|
|
14
|
-
import { logger } from "../../utils/logger";
|
|
15
|
-
import { Cron } from "
|
|
11
|
+
import type { Tool } from "../types.ts";
|
|
12
|
+
import { col, toIndexable } from "../../storage/hive.ts";
|
|
13
|
+
import type { UserDoc, UserIdentityDoc, ChannelDoc, CronJobDoc, TaskRunDoc } from "../../storage/collections.ts";
|
|
14
|
+
import { logger } from "../../utils/logger.ts";
|
|
15
|
+
import { Cron } from "../../scheduler/cron/index.ts";
|
|
16
16
|
|
|
17
17
|
const log = logger.child("CronTools");
|
|
18
18
|
|
|
@@ -114,9 +114,9 @@ export const cronCreateTool: Tool = {
|
|
|
114
114
|
tool_name: { type: "string", description: "Specific tool to execute (optional)" },
|
|
115
115
|
max_runs: { type: "number", description: "Maximum executions (optional, null = unlimited)" },
|
|
116
116
|
channel: { type: "string", description: "Notification channel (system, telegram, discord, whatsapp, cli)" },
|
|
117
|
-
start_at: { type: "string", description: "ISO 8601 datetime: start of execution window
|
|
118
|
-
stop_at: { type: "string", description: "ISO 8601 datetime: end of execution window
|
|
119
|
-
dom_and_dow: { type: "boolean", description: "If true, both day-of-month AND day-of-week must match
|
|
117
|
+
start_at: { type: "string", description: "ISO 8601 datetime: start of execution window. Optional." },
|
|
118
|
+
stop_at: { type: "string", description: "ISO 8601 datetime: end of execution window. Optional." },
|
|
119
|
+
dom_and_dow: { type: "boolean", description: "If true, both day-of-month AND day-of-week must match. Default: false (OR logic)" },
|
|
120
120
|
},
|
|
121
121
|
required: ["name", "task", "task_type"],
|
|
122
122
|
},
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tools de imagen — redimensionar, convertir y medir.
|
|
3
|
+
*
|
|
4
|
+
* Se apoyan en `Bun.Image` (ver `images/`), así que no agregan dependencias: el
|
|
5
|
+
* SDK ya exige Bun ≥ 1.4.
|
|
6
|
+
*
|
|
7
|
+
* Trabajan sobre **artefactos**, no sobre base64 suelto, y eso es deliberado.
|
|
8
|
+
* Devolver una imagen en base64 al modelo es exactamente lo que llena la
|
|
9
|
+
* ventana de contexto —el problema que `mcp-result-normalizer.ts` existe para
|
|
10
|
+
* evitar—. Acá la entrada es un `artifact_id` y la salida es otro: el modelo
|
|
11
|
+
* maneja referencias y sólo mira la imagen si de verdad la necesita.
|
|
12
|
+
*
|
|
13
|
+
* Como cualquier otra tool, se activan o desactivan desde el catálogo.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { Tool } from "../types.ts";
|
|
17
|
+
import { createArtifact, readArtifactBytes } from "../../artifacts/store.ts";
|
|
18
|
+
import { measureImage, transformImage, imagesSupported, type ImageFormat } from "../../images/index.ts";
|
|
19
|
+
import { resolveUserId } from "../../storage/onboarding.ts";
|
|
20
|
+
import { logger } from "../../utils/logger.ts";
|
|
21
|
+
|
|
22
|
+
const log = logger.child("tools/images");
|
|
23
|
+
|
|
24
|
+
const FORMATOS: ImageFormat[] = ["jpeg", "png", "webp", "avif", "heic"];
|
|
25
|
+
|
|
26
|
+
/** Mensaje común: sin Bun 1.4 estas tools no pueden hacer nada. */
|
|
27
|
+
function sinSoporte() {
|
|
28
|
+
return { ok: false, error: "El procesamiento de imágenes necesita Bun >= 1.4 (Bun.Image no está disponible)" };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function leerArtefacto(artifactId: string) {
|
|
32
|
+
const datos = await readArtifactBytes(artifactId);
|
|
33
|
+
if (!datos) return null;
|
|
34
|
+
return datos;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const imageMetadataTool: Tool = {
|
|
38
|
+
name: "image_metadata",
|
|
39
|
+
description:
|
|
40
|
+
"Lee las dimensiones y el formato de una imagen guardada, sin abrirla ni cargarla al contexto. " +
|
|
41
|
+
"Spanish: medir imagen, dimensiones de la imagen, tamaño de la foto, formato de imagen",
|
|
42
|
+
parameters: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
artifact_id: { type: "string", description: "Id del artefacto que contiene la imagen" },
|
|
46
|
+
},
|
|
47
|
+
required: ["artifact_id"],
|
|
48
|
+
},
|
|
49
|
+
execute: async (params: Record<string, unknown>) => {
|
|
50
|
+
if (!imagesSupported()) return sinSoporte();
|
|
51
|
+
const id = params.artifact_id as string;
|
|
52
|
+
try {
|
|
53
|
+
const datos = await leerArtefacto(id);
|
|
54
|
+
if (!datos) return { ok: false, error: `No encontré el artefacto ${id}` };
|
|
55
|
+
const meta = await measureImage(datos.bytes);
|
|
56
|
+
return { ok: true, artifact_id: id, ...meta, bytes: datos.bytes.length };
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return { ok: false, error: `No pude leer la imagen: ${(error as Error).message}` };
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const imageTransformTool: Tool = {
|
|
64
|
+
name: "image_transform",
|
|
65
|
+
description:
|
|
66
|
+
"Redimensiona, rota o convierte de formato una imagen guardada y devuelve un artefacto nuevo. " +
|
|
67
|
+
"El original no se toca. Spanish: redimensionar imagen, cambiar tamaño, convertir a webp, " +
|
|
68
|
+
"comprimir imagen, rotar foto, achicar imagen",
|
|
69
|
+
parameters: {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
artifact_id: { type: "string", description: "Id del artefacto de origen" },
|
|
73
|
+
width: { type: "number", description: "Ancho en píxeles. Si sólo se da uno, se mantiene la proporción" },
|
|
74
|
+
height: { type: "number", description: "Alto en píxeles" },
|
|
75
|
+
format: { type: "string", description: "Formato de salida", enum: FORMATOS },
|
|
76
|
+
quality: { type: "number", description: "1–100, sólo para formatos con pérdida", minimum: 1, maximum: 100 },
|
|
77
|
+
rotate: { type: "number", description: "Grados: 90, 180 o 270" },
|
|
78
|
+
},
|
|
79
|
+
required: ["artifact_id"],
|
|
80
|
+
},
|
|
81
|
+
execute: async (params: Record<string, unknown>) => {
|
|
82
|
+
if (!imagesSupported()) return sinSoporte();
|
|
83
|
+
const id = params.artifact_id as string;
|
|
84
|
+
|
|
85
|
+
const formato = params.format as ImageFormat | undefined;
|
|
86
|
+
if (formato && !FORMATOS.includes(formato)) {
|
|
87
|
+
return { ok: false, error: `Formato no soportado: ${formato}. Disponibles: ${FORMATOS.join(", ")}` };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const datos = await leerArtefacto(id);
|
|
92
|
+
if (!datos) return { ok: false, error: `No encontré el artefacto ${id}` };
|
|
93
|
+
|
|
94
|
+
const { bytes, metadata } = await transformImage(datos.bytes, {
|
|
95
|
+
width: params.width as number | undefined,
|
|
96
|
+
height: params.height as number | undefined,
|
|
97
|
+
format: formato,
|
|
98
|
+
quality: params.quality as number | undefined,
|
|
99
|
+
rotate: params.rotate as number | undefined,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const userId = (await resolveUserId({}).catch(() => null)) ?? "";
|
|
103
|
+
const artefacto = await createArtifact({
|
|
104
|
+
bytes,
|
|
105
|
+
mimeType: `image/${metadata.format}`,
|
|
106
|
+
kind: "image",
|
|
107
|
+
userId,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
log.info(`imagen ${id} → ${artefacto.id} (${metadata.width}x${metadata.height} ${metadata.format})`);
|
|
111
|
+
// Se devuelve la referencia, nunca los bytes: el base64 en el contexto es
|
|
112
|
+
// justo lo que se quiere evitar.
|
|
113
|
+
return {
|
|
114
|
+
ok: true,
|
|
115
|
+
artifact_id: artefacto.id,
|
|
116
|
+
width: metadata.width,
|
|
117
|
+
height: metadata.height,
|
|
118
|
+
format: metadata.format,
|
|
119
|
+
bytes: bytes.length,
|
|
120
|
+
original_bytes: datos.bytes.length,
|
|
121
|
+
};
|
|
122
|
+
} catch (error) {
|
|
123
|
+
return { ok: false, error: `No pude transformar la imagen: ${(error as Error).message}` };
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export function createTools(): Tool[] {
|
|
129
|
+
return [imageMetadataTool, imageTransformTool];
|
|
130
|
+
}
|
|
@@ -13,8 +13,9 @@ import * as filesystem from "./filesystem/index.ts";
|
|
|
13
13
|
|
|
14
14
|
// Web (10)
|
|
15
15
|
import * as web from "./web/index.ts";
|
|
16
|
+
import * as images from "./images/index.ts";
|
|
16
17
|
|
|
17
|
-
// Cron (8) -
|
|
18
|
+
// Cron (8) - scheduler tools
|
|
18
19
|
import * as cron from "./cron/index.ts";
|
|
19
20
|
|
|
20
21
|
// CLI (1)
|
|
@@ -70,6 +71,7 @@ export function createAllTools(config: Config): Tool[] {
|
|
|
70
71
|
|
|
71
72
|
// WEB (10)
|
|
72
73
|
...web.createTools(),
|
|
74
|
+
...images.createTools(),
|
|
73
75
|
|
|
74
76
|
// CRON (8)
|
|
75
77
|
...cron.createTools(),
|
|
@@ -215,3 +217,14 @@ export {
|
|
|
215
217
|
export {
|
|
216
218
|
apiRequestTool,
|
|
217
219
|
} from "./api/index.ts";
|
|
220
|
+
|
|
221
|
+
// Arranque del navegador. Las browser tools existen en el catálogo desde el
|
|
222
|
+
// seed, pero no operan hasta que alguien levanta el servicio: quien construya
|
|
223
|
+
// sobre el SDK necesita poder hacerlo sin importar rutas internas.
|
|
224
|
+
export {
|
|
225
|
+
initializeBrowserService,
|
|
226
|
+
getBrowserService,
|
|
227
|
+
shutdownBrowser,
|
|
228
|
+
} from "./web/browser-service.ts";
|
|
229
|
+
|
|
230
|
+
export * from "./images/index.ts";
|
|
@@ -10,6 +10,7 @@ import type { Tool } from "../types.ts";
|
|
|
10
10
|
import { logger } from "../../utils/logger.ts";
|
|
11
11
|
import * as path from "node:path";
|
|
12
12
|
import * as fs from "node:fs";
|
|
13
|
+
import { cargarXlsx } from "./xlsx-loader.ts";
|
|
13
14
|
|
|
14
15
|
const log = logger.child("office-escribir-xlsx");
|
|
15
16
|
|
|
@@ -55,7 +56,7 @@ export const officeEscribirXlsxTool: Tool = {
|
|
|
55
56
|
log.debug(`Generando XLSX: ${ruta}`);
|
|
56
57
|
|
|
57
58
|
try {
|
|
58
|
-
const XLSX = await
|
|
59
|
+
const XLSX = await cargarXlsx();
|
|
59
60
|
|
|
60
61
|
const workbook = XLSX.utils.book_new();
|
|
61
62
|
|
|
@@ -10,6 +10,7 @@ import type { Tool } from "../types.ts";
|
|
|
10
10
|
import { logger } from "../../utils/logger.ts";
|
|
11
11
|
import * as fs from "node:fs";
|
|
12
12
|
import * as path from "node:path";
|
|
13
|
+
import { cargarXlsx } from "./xlsx-loader.ts";
|
|
13
14
|
|
|
14
15
|
const log = logger.child("office-leer-xlsx");
|
|
15
16
|
|
|
@@ -55,7 +56,7 @@ export const officeLeerXlsxTool: Tool = {
|
|
|
55
56
|
return { ok: false, error: `Archivo no encontrado: ${rutaAbsoluta}` };
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
const XLSX = await
|
|
59
|
+
const XLSX = await cargarXlsx();
|
|
59
60
|
const buffer = fs.readFileSync(rutaAbsoluta);
|
|
60
61
|
const workbook = XLSX.read(buffer, { type: "buffer" });
|
|
61
62
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carga el lector de hojas de cálculo, o explica por qué no puede.
|
|
3
|
+
*
|
|
4
|
+
* `xlsx` se importa en caliente para no pagarlo en cada arranque, y eso deja el
|
|
5
|
+
* fallo a merced del momento: si la dependencia no llegó a instalarse —su
|
|
6
|
+
* tarball se cayó una vez en mitad de un release— el usuario recibía un
|
|
7
|
+
* "Cannot find module" crudo en medio de una respuesta.
|
|
8
|
+
*/
|
|
9
|
+
export async function cargarXlsx(): Promise<typeof import("xlsx")> {
|
|
10
|
+
try {
|
|
11
|
+
return await import("xlsx");
|
|
12
|
+
} catch (error) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
"El soporte de hojas de cálculo no está disponible en esta instalación: " +
|
|
15
|
+
`falta la dependencia «xlsx» (${(error as Error).message}). ` +
|
|
16
|
+
"Reinstala las dependencias con `bun install` para recuperarlo.",
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Tool } from "../types";
|
|
2
|
-
import { inspectArtifact } from "../../artifacts/store";
|
|
1
|
+
import type { Tool } from "../types.ts";
|
|
2
|
+
import { inspectArtifact } from "../../artifacts/store.ts";
|
|
3
3
|
|
|
4
4
|
export const artifactInspectTool: Tool = {
|
|
5
5
|
name: "artifact_inspect",
|