@johpaz/hive-sdk 0.0.14 → 0.0.15
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/.github/CODEOWNERS +9 -0
- package/.github/workflows/publish.yml +89 -0
- package/.github/workflows/version-bump.yml +102 -0
- package/CHANGELOG.md +38 -0
- package/README.md +158 -0
- package/bun.lock +543 -0
- package/bunfig.toml +7 -0
- package/docs/API-AGENTS.md +316 -0
- package/docs/API-CONTEXT-COMPILER.md +252 -0
- package/docs/API-DAG-SCHEDULER.md +273 -0
- package/docs/API-TOOLS-SKILLS-CHANNELS.md +293 -0
- package/docs/API-WORKERS-EVENTS.md +152 -0
- package/docs/INDEX.md +141 -0
- package/docs/README.md +68 -0
- package/package.json +54 -105
- package/packages/cli/package.json +17 -0
- package/packages/cli/src/commands/init.ts +56 -0
- package/packages/cli/src/commands/run.ts +45 -0
- package/packages/cli/src/commands/test.ts +42 -0
- package/packages/cli/src/commands/trace.ts +55 -0
- package/packages/cli/src/index.ts +43 -0
- package/packages/core/package.json +58 -0
- package/packages/core/src/ace/Curator.ts +158 -0
- package/packages/core/src/ace/Reflector.ts +200 -0
- package/packages/core/src/ace/Tracer.ts +100 -0
- package/packages/core/src/ace/index.ts +4 -0
- package/packages/core/src/agent/AgentRunner.ts +699 -0
- package/packages/core/src/agent/Compaction.ts +221 -0
- package/packages/core/src/agent/ContextCompiler.ts +567 -0
- package/packages/core/src/agent/ContextGuard.ts +91 -0
- package/packages/core/src/agent/ConversationStore.ts +244 -0
- package/packages/core/src/agent/Hooks.ts +166 -0
- package/packages/core/src/agent/NativeTools.ts +31 -0
- package/packages/core/src/agent/PromptBuilder.ts +169 -0
- package/packages/core/src/agent/Service.ts +267 -0
- package/packages/core/src/agent/StuckLoop.ts +133 -0
- package/packages/core/src/agent/index.ts +12 -0
- package/packages/core/src/agent/providers/LLMClient.ts +149 -0
- package/packages/core/src/agent/providers/anthropic.ts +212 -0
- package/packages/core/src/agent/providers/gemini.ts +215 -0
- package/packages/core/src/agent/providers/index.ts +199 -0
- package/packages/core/src/agent/providers/interface.ts +195 -0
- package/packages/core/src/agent/providers/ollama.ts +175 -0
- package/packages/core/src/agent/providers/openai-compat.ts +231 -0
- package/packages/core/src/agent/providers.ts +1 -0
- package/packages/core/src/agent/selectors/PlaybookSelector.ts +147 -0
- package/packages/core/src/agent/selectors/SkillSelector.ts +478 -0
- package/packages/core/src/agent/selectors/ToolSelector.ts +577 -0
- package/packages/core/src/agent/selectors/index.ts +6 -0
- package/packages/core/src/api/createAgent.test.ts +48 -0
- package/packages/core/src/api/createAgent.ts +122 -0
- package/packages/core/src/api/index.ts +2 -0
- package/packages/core/src/canvas/CanvasManager.ts +390 -0
- package/packages/core/src/canvas/a2ui-tools.ts +255 -0
- package/packages/core/src/canvas/canvas-tools.ts +448 -0
- package/packages/core/src/canvas/emitter.ts +149 -0
- package/packages/core/src/canvas/index.ts +6 -0
- package/packages/core/src/config/index.ts +2 -0
- package/packages/core/src/config/loader.ts +554 -0
- package/packages/core/src/ethics/EthicsGuard.test.ts +54 -0
- package/packages/core/src/ethics/EthicsGuard.ts +66 -0
- package/packages/core/src/ethics/index.ts +2 -0
- package/packages/core/src/gateway/channel-notify.test.ts +14 -0
- package/packages/core/src/gateway/channel-notify.ts +12 -0
- package/packages/core/src/gateway/index.ts +1 -0
- package/packages/core/src/index.ts +37 -0
- package/packages/core/src/mcp/MCPClient.ts +439 -0
- package/packages/core/src/mcp/MCPToolAdapter.ts +176 -0
- package/packages/core/src/mcp/config.ts +13 -0
- package/packages/core/src/mcp/hot-reload.ts +147 -0
- package/packages/core/src/mcp/index.ts +11 -0
- package/packages/core/src/mcp/logger.ts +42 -0
- package/packages/core/src/mcp/singleton.ts +21 -0
- package/packages/core/src/mcp/transports/index.ts +67 -0
- package/packages/core/src/mcp/transports/sse.ts +241 -0
- package/packages/core/src/mcp/transports/websocket.ts +159 -0
- package/packages/core/src/memory/Scratchpad.test.ts +47 -0
- package/packages/core/src/memory/Scratchpad.ts +37 -0
- package/packages/core/src/memory/Storage.ts +6 -0
- package/packages/core/src/memory/index.ts +2 -0
- package/packages/core/src/multimodal/VisionService.ts +293 -0
- package/packages/core/src/multimodal/index.ts +2 -0
- package/packages/core/src/multimodal/types.ts +28 -0
- package/packages/core/src/security/Pairing.ts +250 -0
- package/packages/core/src/security/RateLimit.ts +270 -0
- package/packages/core/src/security/index.ts +4 -0
- package/packages/core/src/skills/SkillLoader.ts +388 -0
- package/packages/core/src/skills/bundled-data.generated.ts +3332 -0
- package/packages/core/src/skills/defineSkill.ts +18 -0
- package/packages/core/src/skills/index.ts +4 -0
- package/packages/core/src/state/index.ts +2 -0
- package/packages/core/src/state/store.ts +312 -0
- package/packages/core/src/storage/SQLiteStorage.ts +407 -0
- package/packages/core/src/storage/crypto.ts +101 -0
- package/packages/core/src/storage/index.ts +10 -0
- package/packages/core/src/storage/onboarding.ts +1603 -0
- package/packages/core/src/storage/schema.ts +689 -0
- package/packages/core/src/storage/seed.ts +740 -0
- package/packages/core/src/storage/usage.ts +374 -0
- package/packages/core/src/swarm/AgentBus.ts +460 -0
- package/packages/core/src/swarm/AgentExecutor.ts +53 -0
- package/packages/core/src/swarm/Coordinator.ts +251 -0
- package/packages/core/src/swarm/EventBridge.ts +122 -0
- package/packages/core/src/swarm/EventBus.ts +169 -0
- package/packages/core/src/swarm/TaskGraph.ts +192 -0
- package/packages/core/src/swarm/TaskNode.ts +97 -0
- package/packages/core/src/swarm/TaskResult.ts +22 -0
- package/packages/core/src/swarm/WorkerPool.ts +236 -0
- package/packages/core/src/swarm/errors.ts +37 -0
- package/packages/core/src/swarm/index.ts +30 -0
- package/packages/core/src/swarm/presets/HiveLearnPreset.ts +99 -0
- package/packages/core/src/swarm/presets/ResearchPreset.ts +97 -0
- package/packages/core/src/swarm/presets/index.ts +4 -0
- package/packages/core/src/swarm/strategies/ParallelStrategy.ts +21 -0
- package/packages/core/src/swarm/strategies/PriorityStrategy.ts +46 -0
- package/packages/core/src/swarm/strategies/index.ts +3 -0
- package/packages/core/src/swarm/types.ts +164 -0
- package/packages/core/src/tools/ToolExecutor.ts +58 -0
- package/packages/core/src/tools/ToolRegistry.test.ts +98 -0
- package/packages/core/src/tools/ToolRegistry.ts +61 -0
- package/packages/core/src/tools/agents/get-available-models.ts +118 -0
- package/packages/core/src/tools/agents/index.ts +715 -0
- package/packages/core/src/tools/bridge-events.ts +26 -0
- package/packages/core/src/tools/canvas/index.ts +375 -0
- package/packages/core/src/tools/cli/index.ts +142 -0
- package/packages/core/src/tools/codebridge/index.ts +342 -0
- package/packages/core/src/tools/core/index.ts +476 -0
- package/packages/core/src/tools/cron/index.ts +626 -0
- package/packages/core/src/tools/filesystem/fs-delete.ts +78 -0
- package/packages/core/src/tools/filesystem/fs-edit.ts +106 -0
- package/packages/core/src/tools/filesystem/fs-exists.ts +63 -0
- package/packages/core/src/tools/filesystem/fs-glob.ts +108 -0
- package/packages/core/src/tools/filesystem/fs-list.ts +129 -0
- package/packages/core/src/tools/filesystem/fs-read.ts +72 -0
- package/packages/core/src/tools/filesystem/fs-write.ts +67 -0
- package/packages/core/src/tools/filesystem/index.ts +34 -0
- package/packages/core/src/tools/filesystem/workspace-guard.ts +62 -0
- package/packages/core/src/tools/index.ts +231 -0
- package/packages/core/src/tools/meeting/index.ts +363 -0
- package/packages/core/src/tools/office/index.ts +47 -0
- package/packages/core/src/tools/office/office-escribir-docx.ts +192 -0
- package/packages/core/src/tools/office/office-escribir-pdf.ts +172 -0
- package/packages/core/src/tools/office/office-escribir-pptx.ts +174 -0
- package/packages/core/src/tools/office/office-escribir-xlsx.ts +116 -0
- package/packages/core/src/tools/office/office-leer-docx.ts +93 -0
- package/packages/core/src/tools/office/office-leer-pdf.ts +114 -0
- package/packages/core/src/tools/office/office-leer-pptx.ts +136 -0
- package/packages/core/src/tools/office/office-leer-xlsx.ts +124 -0
- package/packages/core/src/tools/projects/index.ts +37 -0
- package/packages/core/src/tools/projects/project-create.ts +94 -0
- package/packages/core/src/tools/projects/project-done.ts +66 -0
- package/packages/core/src/tools/projects/project-fail.ts +66 -0
- package/packages/core/src/tools/projects/project-list.ts +96 -0
- package/packages/core/src/tools/projects/project-update.ts +72 -0
- package/packages/core/src/tools/projects/task-create.ts +68 -0
- package/packages/core/src/tools/projects/task-evaluate.ts +93 -0
- package/packages/core/src/tools/projects/task-update.ts +93 -0
- package/packages/core/src/tools/types.ts +39 -0
- package/packages/core/src/tools/voice/index.ts +104 -0
- package/packages/core/src/tools/web/browser-click.ts +78 -0
- package/packages/core/src/tools/web/browser-extract.ts +139 -0
- package/packages/core/src/tools/web/browser-navigate.ts +106 -0
- package/packages/core/src/tools/web/browser-screenshot.ts +87 -0
- package/packages/core/src/tools/web/browser-script.ts +88 -0
- package/packages/core/src/tools/web/browser-service.ts +554 -0
- package/packages/core/src/tools/web/browser-type.ts +101 -0
- package/packages/core/src/tools/web/browser-wait.ts +136 -0
- package/packages/core/src/tools/web/index.ts +41 -0
- package/packages/core/src/tools/web/web-fetch.ts +78 -0
- package/packages/core/src/tools/web/web-search.ts +123 -0
- package/packages/core/src/utils/benchmark.ts +80 -0
- package/packages/core/src/utils/crypto.ts +73 -0
- package/packages/core/src/utils/date.ts +42 -0
- package/packages/core/src/utils/index.ts +10 -0
- package/packages/core/src/utils/logger.ts +389 -0
- package/packages/core/src/utils/retry.ts +70 -0
- package/packages/core/src/utils/toon.ts +253 -0
- package/packages/core/src/voice/index.ts +656 -0
- package/test/setup-db.ts +216 -0
- package/tsconfig.json +39 -0
- package/src/agents.ts +0 -1
- package/src/canvas.ts +0 -1
- package/src/channels.ts +0 -1
- package/src/config.ts +0 -1
- package/src/events.ts +0 -1
- package/src/gateway.ts +0 -1
- package/src/index.ts +0 -304
- package/src/mcp.ts +0 -1
- package/src/multimodal.ts +0 -1
- package/src/scheduler.ts +0 -1
- package/src/security.ts +0 -1
- package/src/skills.ts +0 -1
- package/src/state.ts +0 -1
- package/src/storage.ts +0 -1
- package/src/tools.ts +0 -1
- package/src/tts.ts +0 -1
- package/src/types.ts +0 -82
- package/src/utils.ts +0 -1
- package/src/voice.ts +0 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge Events - WebSocket event subscription for CodeBridge
|
|
3
|
+
*
|
|
4
|
+
* Manages WebSocket subscriptions for real-time CodeBridge events
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const bridgeSubscribers = new Set<{ send: (data: string) => void }>()
|
|
8
|
+
|
|
9
|
+
export function subscribeBridge(ws: { send: (data: string) => void }) {
|
|
10
|
+
bridgeSubscribers.add(ws)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function unsubscribeBridge(ws: { send: (data: string) => void }) {
|
|
14
|
+
bridgeSubscribers.delete(ws)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function emitBridgeEvent(event: { type: string; data: any }) {
|
|
18
|
+
const payload = JSON.stringify(event)
|
|
19
|
+
for (const ws of bridgeSubscribers) {
|
|
20
|
+
try {
|
|
21
|
+
ws.send(payload)
|
|
22
|
+
} catch {
|
|
23
|
+
bridgeSubscribers.delete(ws)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas Tools - 7 tools + A2UI v0.9 tools
|
|
3
|
+
*
|
|
4
|
+
* @category canvas
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Tool, ToolResult } from "../types.ts";
|
|
8
|
+
import { emitCanvas, removeCanvasComponent, type CanvasEventType } from "../../canvas/emitter.ts";
|
|
9
|
+
import { logger } from "../../utils/logger.ts";
|
|
10
|
+
import { canvasManager } from "../../canvas/CanvasManager.ts";
|
|
11
|
+
import { createA2UISurfaceTool, createA2UIUpdateComponentsTool, createA2UIUpdateDataModelTool, createA2UIDeleteSurfaceTool } from "../../canvas/a2ui-tools.ts";
|
|
12
|
+
import type { Config } from "../../config/loader.ts";
|
|
13
|
+
|
|
14
|
+
const log = logger.child("canvas");
|
|
15
|
+
|
|
16
|
+
// ─── Pending canvas interactions ─────────────────────────────────────────────
|
|
17
|
+
// Simple map indexed by componentId — no session ID required.
|
|
18
|
+
// Server calls resolveCanvasInteraction() when it receives canvas:interact.
|
|
19
|
+
|
|
20
|
+
interface PendingInteraction {
|
|
21
|
+
resolve: (data: unknown) => void;
|
|
22
|
+
reject: (err: Error) => void;
|
|
23
|
+
timeout: ReturnType<typeof setTimeout>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const pendingInteractions = new Map<string, PendingInteraction>();
|
|
27
|
+
|
|
28
|
+
export function resolveCanvasInteraction(componentId: string, data: unknown): boolean {
|
|
29
|
+
const pending = pendingInteractions.get(componentId);
|
|
30
|
+
if (!pending) return false;
|
|
31
|
+
clearTimeout(pending.timeout);
|
|
32
|
+
pendingInteractions.delete(componentId);
|
|
33
|
+
pending.resolve(data);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function waitForCanvasInteraction(componentId: string, timeoutMs = 300000): Promise<unknown> {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
const timeout = setTimeout(() => {
|
|
40
|
+
pendingInteractions.delete(componentId);
|
|
41
|
+
reject(new Error(`Interaction timeout for ${componentId}`));
|
|
42
|
+
}, timeoutMs);
|
|
43
|
+
pendingInteractions.set(componentId, { resolve, reject, timeout });
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ─── canvas_render ───────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
export const canvasRenderTool: Tool = {
|
|
50
|
+
name: "canvas_render",
|
|
51
|
+
description: "Render a component or visualization on the canvas. Use specific types instead of always using card+markdown. Key types: chart (bar/line/area/pie graphs), table (tabular data), markdown (rich text), form (interactive form - waits for submit), button (interactive button), alert-dialog (confirm/cancel dialog), progress (progress bars), accordion, tabs, badge, card, bee-loader. Spanish: renderizar, visualizar, gráfico, diagrama, tabla, formulario",
|
|
52
|
+
parameters: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
component: { type: "string", description: "Component type. Visualization: chart, table, markdown, card, progress, accordion, tabs, badge, separator, bee-loader. Interactive: form, button, alert-dialog. Layout: carousel, collapsible, resizable, scroll-area, tabs. Other: alert, avatar, breadcrumb, calendar, checkbox, dialog, drawer, dropdown-menu, hover-card, input, input-otp, label, menubar, navigation-menu, pagination, popover, radio-group, select, sheet, skeleton, slider, switch, textarea, toggle, toggle-group, tooltip, aspect-ratio, command, context-menu, custom" },
|
|
56
|
+
data: { type: "object", description: "Props for the component. chart: {type:'bar'|'line'|'area'|'pie', data:[{name,...}], xKey:'name', keys:['value'], title}. table: {title, columns:[{header,key}], data:[{}]}. form: {title, fields:[{name,label,type:'text'|'email'|'number'|'textarea'|'select'|'checkbox',placeholder,options:[{value,label}]}], submitLabel}. alert-dialog: {title, description, confirmLabel, cancelLabel}. button: {label, variant:'default'|'outline'|'secondary'|'destructive'}. markdown: {content}. progress: {value:0-100}." },
|
|
57
|
+
},
|
|
58
|
+
required: ["component", "data"],
|
|
59
|
+
},
|
|
60
|
+
execute: async (params: Record<string, unknown>) => {
|
|
61
|
+
const componentType = params.component as string;
|
|
62
|
+
const data = params.data as Record<string, unknown>;
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const id = `render_${componentType}_${Date.now()}`;
|
|
66
|
+
emitCanvas("canvas:render", {
|
|
67
|
+
component: {
|
|
68
|
+
id,
|
|
69
|
+
type: componentType,
|
|
70
|
+
props: data ?? {},
|
|
71
|
+
position: { x: 0, y: 0 },
|
|
72
|
+
size: { width: 400, height: 300 },
|
|
73
|
+
agentId: "agent",
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
return { ok: true, message: `Rendered ${componentType}.` };
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return { ok: false, error: `Failed to render: ${(error as Error).message }` };
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// ─── canvas_ask ──────────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
export const canvasAskTool: Tool = {
|
|
86
|
+
name: "canvas_ask",
|
|
87
|
+
description: "Show interactive form and wait for user input. Spanish: formulario interactivo, preguntar usuario, input",
|
|
88
|
+
parameters: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
questions: {
|
|
92
|
+
type: "array",
|
|
93
|
+
description: "List of questions to ask",
|
|
94
|
+
items: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: {
|
|
97
|
+
question: { type: "string" },
|
|
98
|
+
type: { type: "string", enum: ["text", "select", "confirm"] },
|
|
99
|
+
options: { type: "array", items: { type: "string" } },
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
required: ["questions"],
|
|
105
|
+
},
|
|
106
|
+
execute: async (params: Record<string, unknown>, config?: any) => {
|
|
107
|
+
const questions = params.questions as any[];
|
|
108
|
+
const userId = config?.configurable?.user_id;
|
|
109
|
+
const threadId = config?.configurable?.thread_id;
|
|
110
|
+
// Use threadId (session) if available, then userId, then default
|
|
111
|
+
// This must match the WebSocket sessionId used by the frontend
|
|
112
|
+
const sessionId = threadId ? `canvas:${threadId}` : userId ? `canvas:${userId}` : "canvas:default";
|
|
113
|
+
|
|
114
|
+
// Convert questions to form fields
|
|
115
|
+
const fields = questions.map((q, idx) => ({
|
|
116
|
+
name: `field_${idx}`,
|
|
117
|
+
label: q.question,
|
|
118
|
+
type: q.type === "select" ? "select" : q.type === "confirm" ? "text" : "text",
|
|
119
|
+
required: true,
|
|
120
|
+
options: q.options?.map((opt: string) => ({ label: opt, value: opt })),
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
const formId = `form-${Date.now()}`;
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
// Render form via canvasManager
|
|
127
|
+
await canvasManager.render(sessionId, {
|
|
128
|
+
id: formId,
|
|
129
|
+
type: "form",
|
|
130
|
+
props: {
|
|
131
|
+
title: "Input Required",
|
|
132
|
+
fields,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Wait for user interaction
|
|
137
|
+
const response = await canvasManager.waitForInteraction(sessionId, formId, 300000);
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
ok: true,
|
|
141
|
+
message: "Form submitted by user",
|
|
142
|
+
data: response,
|
|
143
|
+
formId,
|
|
144
|
+
};
|
|
145
|
+
} catch (error) {
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
error: `Form interaction failed: ${(error as Error).message}`,
|
|
149
|
+
formId,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// ─── canvas_confirm ──────────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
export const canvasConfirmTool: Tool = {
|
|
158
|
+
name: "canvas_confirm",
|
|
159
|
+
description: "Show a confirmation dialog before executing an action. Spanish: confirmar acción, diálogo, aprobar",
|
|
160
|
+
parameters: {
|
|
161
|
+
type: "object",
|
|
162
|
+
properties: {
|
|
163
|
+
message: { type: "string", description: "Confirmation message" },
|
|
164
|
+
action: { type: "string", description: "Action to confirm" },
|
|
165
|
+
},
|
|
166
|
+
required: ["message", "action"],
|
|
167
|
+
},
|
|
168
|
+
execute: async (params: Record<string, unknown>, config?: any) => {
|
|
169
|
+
const message = params.message as string;
|
|
170
|
+
const action = params.action as string;
|
|
171
|
+
|
|
172
|
+
const confirmId = `confirm-${Date.now()}`;
|
|
173
|
+
|
|
174
|
+
emitCanvas("canvas:render", {
|
|
175
|
+
component: {
|
|
176
|
+
id: confirmId,
|
|
177
|
+
type: "alert-dialog",
|
|
178
|
+
props: { title: action, description: message, confirmLabel: "Confirmar", cancelLabel: "Cancelar" },
|
|
179
|
+
position: { x: 0, y: 0 },
|
|
180
|
+
size: { width: 400, height: 200 },
|
|
181
|
+
agentId: "agent",
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const interactionData = await waitForCanvasInteraction(confirmId, 300000) as any;
|
|
187
|
+
removeCanvasComponent(confirmId);
|
|
188
|
+
const confirmed = interactionData?.confirmed === true;
|
|
189
|
+
return { ok: true, confirmed, action, message };
|
|
190
|
+
} catch (error) {
|
|
191
|
+
removeCanvasComponent(confirmId);
|
|
192
|
+
return { ok: false, confirmed: false, error: (error as Error).message };
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// ─── canvas_show_card ────────────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
export const canvasShowCardTool: Tool = {
|
|
200
|
+
name: "canvas_show_card",
|
|
201
|
+
description: "Display structured information in card format. Spanish: mostrar tarjeta, card, información estructurada",
|
|
202
|
+
parameters: {
|
|
203
|
+
type: "object",
|
|
204
|
+
properties: {
|
|
205
|
+
title: { type: "string", description: "Card title" },
|
|
206
|
+
content: { type: "string", description: "Card content (Markdown supported)" },
|
|
207
|
+
items: {
|
|
208
|
+
type: "array",
|
|
209
|
+
description: "List of key-value items",
|
|
210
|
+
items: {
|
|
211
|
+
type: "object",
|
|
212
|
+
properties: {
|
|
213
|
+
label: { type: "string" },
|
|
214
|
+
value: { type: "string" },
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
required: ["title"],
|
|
220
|
+
},
|
|
221
|
+
execute: async (params: Record<string, unknown>) => {
|
|
222
|
+
const title = params.title as string;
|
|
223
|
+
const content = params.content as string | undefined;
|
|
224
|
+
const items = (params.items as Array<{ label: string; value: string }>) || [];
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
const id = `card_${Date.now()}`;
|
|
228
|
+
emitCanvas("canvas:render", {
|
|
229
|
+
component: {
|
|
230
|
+
id,
|
|
231
|
+
type: "card",
|
|
232
|
+
props: {
|
|
233
|
+
title,
|
|
234
|
+
children: content ?? (items.length > 0 ? items.map((item) => `**${item.label}:** ${item.value}`).join("\n") : ""),
|
|
235
|
+
// Pass items as table rows for richer rendering
|
|
236
|
+
items,
|
|
237
|
+
},
|
|
238
|
+
position: { x: 0, y: 0 },
|
|
239
|
+
size: { width: 320, height: 200 },
|
|
240
|
+
agentId: "agent",
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
return { ok: true, message: `Card "${title}" displayed.` };
|
|
244
|
+
} catch (error) {
|
|
245
|
+
return { ok: false, error: `Failed to display card: ${(error as Error).message }` };
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// ─── canvas_show_progress ────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
export const canvasShowProgressTool: Tool = {
|
|
253
|
+
name: "canvas_show_progress",
|
|
254
|
+
description: "Show progress bar or status indicator. Spanish: barra de progreso, indicador, progreso visual",
|
|
255
|
+
parameters: {
|
|
256
|
+
type: "object",
|
|
257
|
+
properties: {
|
|
258
|
+
bars: {
|
|
259
|
+
type: "array",
|
|
260
|
+
description: "List of progress bars",
|
|
261
|
+
items: {
|
|
262
|
+
type: "object",
|
|
263
|
+
properties: {
|
|
264
|
+
label: { type: "string" },
|
|
265
|
+
value: { type: "number", minimum: 0, maximum: 100 },
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
required: ["bars"],
|
|
271
|
+
},
|
|
272
|
+
execute: async (params: Record<string, unknown>) => {
|
|
273
|
+
const bars = params.bars as Array<{ label: string; value: number }>;
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
// Render each bar as a separate progress component
|
|
277
|
+
for (const bar of bars) {
|
|
278
|
+
const id = `progress_${bar.label.replace(/\s+/g, "_")}_${Date.now()}`;
|
|
279
|
+
emitCanvas("canvas:render", {
|
|
280
|
+
component: {
|
|
281
|
+
id,
|
|
282
|
+
type: "progress",
|
|
283
|
+
props: { value: bar.value, label: bar.label },
|
|
284
|
+
position: { x: 0, y: 0 },
|
|
285
|
+
size: { width: 320, height: 60 },
|
|
286
|
+
agentId: "agent",
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
return { ok: true, message: "Progress displayed." };
|
|
291
|
+
} catch (error) {
|
|
292
|
+
return { ok: false, error: `Failed to display progress: ${(error as Error).message }` };
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// ─── canvas_show_list ────────────────────────────────────────────────────────
|
|
298
|
+
|
|
299
|
+
export const canvasShowListTool: Tool = {
|
|
300
|
+
name: "canvas_show_list",
|
|
301
|
+
description: "Display key-value list information. Spanish: lista clave-valor, mostrar lista, información en lista",
|
|
302
|
+
parameters: {
|
|
303
|
+
type: "object",
|
|
304
|
+
properties: {
|
|
305
|
+
title: { type: "string", description: "List title" },
|
|
306
|
+
items: {
|
|
307
|
+
type: "object",
|
|
308
|
+
description: "Key-value pairs",
|
|
309
|
+
additionalProperties: { type: "string" },
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
required: ["title", "items"],
|
|
313
|
+
},
|
|
314
|
+
execute: async (params: Record<string, unknown>) => {
|
|
315
|
+
const title = params.title as string;
|
|
316
|
+
const items = params.items as Record<string, string>;
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
const id = `list_${Date.now()}`;
|
|
320
|
+
// Render as a table with key/value columns
|
|
321
|
+
const columns = [{ header: "Campo", key: "key" }, { header: "Valor", key: "value" }];
|
|
322
|
+
const data = Object.entries(items).map(([key, value]) => ({ key, value }));
|
|
323
|
+
|
|
324
|
+
emitCanvas("canvas:render", {
|
|
325
|
+
component: {
|
|
326
|
+
id,
|
|
327
|
+
type: "table",
|
|
328
|
+
props: { title, columns, data },
|
|
329
|
+
position: { x: 0, y: 0 },
|
|
330
|
+
size: { width: 400, height: 300 },
|
|
331
|
+
agentId: "agent",
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
return { ok: true, message: `List "${title}" displayed.` };
|
|
335
|
+
} catch (error) {
|
|
336
|
+
return { ok: false, error: `Failed to display list: ${(error as Error).message }` };
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// ─── canvas_clear ────────────────────────────────────────────────────────────
|
|
342
|
+
|
|
343
|
+
export const canvasClearTool: Tool = {
|
|
344
|
+
name: "canvas_clear",
|
|
345
|
+
description: "Clear current canvas content. Spanish: limpiar canvas, borrar visualización, resetear",
|
|
346
|
+
parameters: {
|
|
347
|
+
type: "object",
|
|
348
|
+
properties: {},
|
|
349
|
+
},
|
|
350
|
+
execute: async () => {
|
|
351
|
+
try {
|
|
352
|
+
emitCanvas("canvas:clear", {});
|
|
353
|
+
return { ok: true, message: "Canvas cleared." };
|
|
354
|
+
} catch (error) {
|
|
355
|
+
return { ok: false, error: `Failed to clear canvas: ${(error as Error).message }` };
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
export function createTools(config?: Config): Tool[] {
|
|
361
|
+
const a2uiConfig = config ?? {} as Config;
|
|
362
|
+
return [
|
|
363
|
+
canvasRenderTool,
|
|
364
|
+
canvasAskTool,
|
|
365
|
+
canvasConfirmTool,
|
|
366
|
+
canvasShowCardTool,
|
|
367
|
+
canvasShowProgressTool,
|
|
368
|
+
canvasShowListTool,
|
|
369
|
+
canvasClearTool,
|
|
370
|
+
createA2UISurfaceTool(a2uiConfig),
|
|
371
|
+
createA2UIUpdateComponentsTool(a2uiConfig),
|
|
372
|
+
createA2UIUpdateDataModelTool(a2uiConfig),
|
|
373
|
+
createA2UIDeleteSurfaceTool(a2uiConfig),
|
|
374
|
+
];
|
|
375
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli_exec - Execute shell commands
|
|
3
|
+
*
|
|
4
|
+
* @category cli
|
|
5
|
+
* @seedId cli_exec
|
|
6
|
+
* @spanish ejecutar comando, terminal, bash, script, consola
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Tool } from "../types.ts";
|
|
10
|
+
import { logger } from "../../utils/logger.ts";
|
|
11
|
+
import { resolveInWorkspace, getWorkspace, expandPath } from "../filesystem/workspace-guard.ts";
|
|
12
|
+
import * as fs from "node:fs";
|
|
13
|
+
|
|
14
|
+
const log = logger.child("cli-exec");
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Patterns that are unconditionally blocked regardless of workspace config.
|
|
18
|
+
* Checked against the full command string (lowercased).
|
|
19
|
+
*/
|
|
20
|
+
const BLOCKED_PATTERNS: Array<{ pattern: RegExp; reason: string }> = [
|
|
21
|
+
{ pattern: /rm\s+-rf\s+\/[^\s]*/, reason: "recursive delete from root" },
|
|
22
|
+
{ pattern: /rm\s+-rf\s+~/, reason: "recursive delete from home" },
|
|
23
|
+
{ pattern: />\s*\/dev\//, reason: "write to device file" },
|
|
24
|
+
{ pattern: /mkfs/, reason: "filesystem format" },
|
|
25
|
+
{ pattern: /dd\s+if=/, reason: "raw disk write" },
|
|
26
|
+
{ pattern: /:\(\)\s*\{/, reason: "fork bomb pattern" },
|
|
27
|
+
{ pattern: /del\s+\/f\s+\/s/, reason: "recursive force delete (Windows)" },
|
|
28
|
+
{ pattern: /format\s+[a-z]:/i, reason: "disk format (Windows)" },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export const cliExecTool: Tool = {
|
|
32
|
+
name: "cli_exec",
|
|
33
|
+
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
|
+
parameters: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {
|
|
37
|
+
command: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "The shell command to execute (supports pipes, redirections, variables)",
|
|
40
|
+
},
|
|
41
|
+
timeout: {
|
|
42
|
+
type: "number",
|
|
43
|
+
description: "Timeout in seconds (default: 30, max: 300)",
|
|
44
|
+
},
|
|
45
|
+
cwd: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: "Working directory (default: agent workspace)",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
required: ["command"],
|
|
51
|
+
},
|
|
52
|
+
execute: async (params: Record<string, unknown>, config?: any) => {
|
|
53
|
+
const command = params.command as string;
|
|
54
|
+
const timeoutSecs = Math.min((params.timeout as number) ?? 30, 300);
|
|
55
|
+
const timeoutMs = timeoutSecs * 1000;
|
|
56
|
+
|
|
57
|
+
// ── Workspace enforcement ──────────────────────────────────────────────────
|
|
58
|
+
const workspace = getWorkspace(config);
|
|
59
|
+
const defaultCwd = workspace ? expandPath(workspace) : process.cwd();
|
|
60
|
+
|
|
61
|
+
let cwd: string;
|
|
62
|
+
try {
|
|
63
|
+
const rawCwd = (params.cwd as string) ?? defaultCwd;
|
|
64
|
+
cwd = resolveInWorkspace(rawCwd, workspace);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
return { ok: false, error: (e as Error).message };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Ensure cwd exists
|
|
70
|
+
if (!fs.existsSync(cwd)) {
|
|
71
|
+
return { ok: false, error: `Working directory not found: ${cwd}` };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── Dangerous pattern check ────────────────────────────────────────────────
|
|
75
|
+
for (const { pattern, reason } of BLOCKED_PATTERNS) {
|
|
76
|
+
if (pattern.test(command)) {
|
|
77
|
+
return {
|
|
78
|
+
ok: false,
|
|
79
|
+
error: `Command not allowed: ${reason}`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
log.info(`Executing: ${command} (cwd=${cwd})`);
|
|
85
|
+
|
|
86
|
+
const t0 = performance.now();
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const controller = new AbortController();
|
|
90
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
91
|
+
|
|
92
|
+
// Use sh -c so pipes, redirections, variables, and quoted args all work
|
|
93
|
+
const proc = Bun.spawn(["/bin/sh", "-c", command], {
|
|
94
|
+
cwd,
|
|
95
|
+
signal: controller.signal,
|
|
96
|
+
stdout: "pipe",
|
|
97
|
+
stderr: "pipe",
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
let stdout = "";
|
|
101
|
+
let stderr = "";
|
|
102
|
+
let exitCode: number;
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
[stdout, stderr] = await Promise.all([
|
|
106
|
+
new Response(proc.stdout).text(),
|
|
107
|
+
new Response(proc.stderr).text(),
|
|
108
|
+
]);
|
|
109
|
+
exitCode = await proc.exited;
|
|
110
|
+
} catch {
|
|
111
|
+
// AbortController fired — process was killed due to timeout
|
|
112
|
+
exitCode = -1;
|
|
113
|
+
stdout = stdout || "";
|
|
114
|
+
stderr = stderr || `Process killed after ${timeoutSecs}s timeout`;
|
|
115
|
+
} finally {
|
|
116
|
+
clearTimeout(timeoutId);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const elapsedMs = Math.round(performance.now() - t0);
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
ok: exitCode === 0,
|
|
123
|
+
command,
|
|
124
|
+
exitCode,
|
|
125
|
+
stdout: stdout.trim(),
|
|
126
|
+
stderr: stderr.trim(),
|
|
127
|
+
executionTimeMs: elapsedMs,
|
|
128
|
+
cwd,
|
|
129
|
+
};
|
|
130
|
+
} catch (error) {
|
|
131
|
+
log.error(`Command failed: ${(error as Error).message}`);
|
|
132
|
+
return {
|
|
133
|
+
ok: false,
|
|
134
|
+
error: `Command execution failed: ${(error as Error).message}`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export function createTools(): Tool[] {
|
|
141
|
+
return [cliExecTool];
|
|
142
|
+
}
|