@johpaz/hive-sdk 0.0.14 → 0.0.16
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 +64 -0
- package/README.md +280 -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 +350 -0
- package/docs/API-WORKERS-EVENTS.md +299 -0
- package/docs/INDEX.md +190 -0
- package/docs/README.md +161 -0
- package/docs/TEMPLATE-HIVE-APP.md +360 -0
- package/package.json +60 -104
- package/packages/cli/bin/hive +2 -0
- package/packages/cli/package.json +17 -0
- package/packages/cli/src/commands/add-skill.ts +42 -0
- package/packages/cli/src/commands/add-tool.ts +45 -0
- package/packages/cli/src/commands/add-worker.ts +49 -0
- package/packages/cli/src/commands/create-app-utils.ts +32 -0
- package/packages/cli/src/commands/create-app.test.ts +151 -0
- package/packages/cli/src/commands/create-app.ts +35 -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 +59 -0
- package/packages/cli/templates/hive-app/.env.example +17 -0
- package/packages/cli/templates/hive-app/docker-compose.yml +20 -0
- package/packages/cli/templates/hive-app/hive.config.ts +19 -0
- package/packages/cli/templates/hive-app/package.json +16 -0
- package/packages/cli/templates/hive-app/src/agents/coordinator.ts +9 -0
- package/packages/cli/templates/hive-app/src/main.ts +56 -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/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/auth/auth.ts +108 -0
- package/packages/core/src/auth/index.ts +1 -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/canvas.test.ts +32 -0
- package/packages/core/src/canvas/emitter.ts +149 -0
- package/packages/core/src/canvas/index.ts +3 -0
- package/packages/core/src/channels/base.ts +154 -0
- package/packages/core/src/channels/channels.test.ts +18 -0
- package/packages/core/src/channels/discord.ts +273 -0
- package/packages/core/src/channels/index.ts +7 -0
- package/packages/core/src/channels/manager.ts +450 -0
- package/packages/core/src/channels/slack.ts +323 -0
- package/packages/core/src/channels/telegram.ts +612 -0
- package/packages/core/src/channels/webchat.ts +139 -0
- package/packages/core/src/channels/whatsapp.ts +548 -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/events/agent-bus.ts +460 -0
- package/packages/core/src/events/event-bus.ts +169 -0
- package/packages/core/src/gateway/channel-notify.ts +37 -0
- package/packages/core/src/gateway/gateway.test.ts +38 -0
- package/packages/core/src/gateway/index.ts +2 -0
- package/packages/core/src/gateway/server.ts +139 -0
- package/packages/core/src/heartbeat/index.ts +157 -0
- package/packages/core/src/index.ts +81 -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/multimodal/vision-service.ts +283 -0
- package/packages/core/src/plugins/api.ts +128 -0
- package/packages/core/src/plugins/index.ts +2 -0
- package/packages/core/src/plugins/loader.ts +365 -0
- package/packages/core/src/resilience/circuit-breaker.ts +225 -0
- package/packages/core/src/scheduler/CronScheduler.ts +699 -0
- package/packages/core/src/scheduler/dag/AgentExecutor.ts +53 -0
- package/packages/core/src/scheduler/dag/DAGScheduler.ts +250 -0
- package/packages/core/src/scheduler/dag/EventBridge.ts +122 -0
- package/packages/core/src/scheduler/dag/TaskGraph.ts +192 -0
- package/packages/core/src/scheduler/dag/TaskNode.ts +97 -0
- package/packages/core/src/scheduler/dag/TaskResult.ts +22 -0
- package/packages/core/src/scheduler/dag/errors.ts +37 -0
- package/packages/core/src/scheduler/dag/index.ts +26 -0
- package/packages/core/src/scheduler/dag/presets/ResearchPreset.ts +97 -0
- package/packages/core/src/scheduler/dag/strategies/ParallelStrategy.ts +21 -0
- package/packages/core/src/scheduler/dag/strategies/PriorityStrategy.ts +46 -0
- package/packages/core/src/scheduler/index.ts +22 -0
- package/packages/core/src/scheduler/integration.ts +237 -0
- package/packages/core/src/scheduler/scheduler.test.ts +19 -0
- package/packages/core/src/scheduler/types.ts +164 -0
- package/packages/core/src/security/Pairing.ts +250 -0
- package/packages/core/src/security/RateLimit.ts +270 -0
- package/packages/core/src/security/google-chat.ts +269 -0
- package/packages/core/src/security/index.ts +192 -0
- package/packages/core/src/security/rate-limit.ts +270 -0
- package/packages/core/src/security/signal.ts +321 -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 +233 -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/storage.test.ts +37 -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/swarm.test.ts +24 -0
- package/packages/core/src/swarm/types.ts +164 -0
- package/packages/core/src/tool-runtime/index.ts +522 -0
- package/packages/core/src/tool-runtime/tool-runtime.test.ts +91 -0
- package/packages/core/src/tool-runtime/tool-worker.ts +125 -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 +643 -0
- package/packages/core/src/workers/WorkerPool.ts +167 -0
- package/packages/core/src/workers/agent.worker.ts +68 -0
- package/packages/core/src/workers/createWorker.ts +144 -0
- package/packages/core/src/workers/index.ts +5 -0
- package/packages/core/src/workers/workers.test.ts +48 -0
- package/test/setup-db.ts +216 -0
- package/tsconfig.json +40 -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,460 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Bus - Sistema de mensajería pub/sub para comunicación entre workers
|
|
3
|
+
*
|
|
4
|
+
* Permite que los workers se comuniquen entre sí sin pasar por el coordinador.
|
|
5
|
+
* Útil para:
|
|
6
|
+
* - Notificar completado de tareas con dependencias
|
|
7
|
+
* - Solicitar ayuda entre workers
|
|
8
|
+
* - Compartir resultados intermedios
|
|
9
|
+
* - Coordinar ejecución en paralelo
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { EventEmitter } from "events";
|
|
13
|
+
import { logger } from "../utils/logger";
|
|
14
|
+
import { getDb } from "../storage/SQLiteStorage";
|
|
15
|
+
|
|
16
|
+
const log = logger.child("agent-bus");
|
|
17
|
+
|
|
18
|
+
// ─── Tipos de eventos ────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
export interface AgentBusEventMap {
|
|
21
|
+
"worker:task_started": {
|
|
22
|
+
workerId: string;
|
|
23
|
+
workerName: string;
|
|
24
|
+
taskId: number;
|
|
25
|
+
taskName: string;
|
|
26
|
+
projectId: string;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
};
|
|
29
|
+
"worker:task_completed": {
|
|
30
|
+
workerId: string;
|
|
31
|
+
workerName: string;
|
|
32
|
+
taskId: number;
|
|
33
|
+
taskName: string;
|
|
34
|
+
projectId: string;
|
|
35
|
+
result: string;
|
|
36
|
+
timestamp: number;
|
|
37
|
+
};
|
|
38
|
+
"worker:task_failed": {
|
|
39
|
+
workerId: string;
|
|
40
|
+
workerName: string;
|
|
41
|
+
taskId: number;
|
|
42
|
+
taskName: string;
|
|
43
|
+
projectId: string;
|
|
44
|
+
error: string;
|
|
45
|
+
timestamp: number;
|
|
46
|
+
};
|
|
47
|
+
"worker:help_request": {
|
|
48
|
+
fromWorkerId: string;
|
|
49
|
+
fromWorkerName: string;
|
|
50
|
+
taskId: number;
|
|
51
|
+
request: string;
|
|
52
|
+
requiredSkill?: string;
|
|
53
|
+
timestamp: number;
|
|
54
|
+
};
|
|
55
|
+
"worker:help_response": {
|
|
56
|
+
toWorkerId: string;
|
|
57
|
+
fromWorkerId: string;
|
|
58
|
+
fromWorkerName: string;
|
|
59
|
+
taskId: number;
|
|
60
|
+
response: string;
|
|
61
|
+
timestamp: number;
|
|
62
|
+
};
|
|
63
|
+
"worker:blocked": {
|
|
64
|
+
workerId: string;
|
|
65
|
+
workerName: string;
|
|
66
|
+
taskId: number;
|
|
67
|
+
blockedBy: string;
|
|
68
|
+
reason: string;
|
|
69
|
+
timestamp: number;
|
|
70
|
+
};
|
|
71
|
+
"worker:unblocked": {
|
|
72
|
+
workerId: string;
|
|
73
|
+
workerName: string;
|
|
74
|
+
taskId: number;
|
|
75
|
+
unblockedBy: string;
|
|
76
|
+
timestamp: number;
|
|
77
|
+
};
|
|
78
|
+
"project:started": {
|
|
79
|
+
projectId: string;
|
|
80
|
+
projectName: string;
|
|
81
|
+
coordinatorId: string;
|
|
82
|
+
timestamp: number;
|
|
83
|
+
};
|
|
84
|
+
"project:completed": {
|
|
85
|
+
projectId: string;
|
|
86
|
+
projectName: string;
|
|
87
|
+
coordinatorId: string;
|
|
88
|
+
summary: string;
|
|
89
|
+
timestamp: number;
|
|
90
|
+
};
|
|
91
|
+
"message:custom": {
|
|
92
|
+
fromWorkerId: string;
|
|
93
|
+
fromWorkerName: string;
|
|
94
|
+
toWorkerId?: string;
|
|
95
|
+
topic: string;
|
|
96
|
+
content: string;
|
|
97
|
+
timestamp: number;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type AgentBusEventKey = keyof AgentBusEventMap;
|
|
102
|
+
|
|
103
|
+
export interface AgentBusEventHandler<K extends AgentBusEventKey> {
|
|
104
|
+
(data: AgentBusEventMap[K]): void | Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ─── Message Store - Persistencia de mensajes en BD ─────────────────────────
|
|
108
|
+
|
|
109
|
+
export interface AgentBusMessage {
|
|
110
|
+
id: number;
|
|
111
|
+
event_type: string;
|
|
112
|
+
from_worker_id: string | null;
|
|
113
|
+
to_worker_id: string | null;
|
|
114
|
+
topic: string | null;
|
|
115
|
+
content: string;
|
|
116
|
+
metadata: string | null;
|
|
117
|
+
created_at: number;
|
|
118
|
+
read: number;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Guarda un mensaje en la base de datos para persistencia
|
|
123
|
+
*/
|
|
124
|
+
function persistMessage(event: AgentBusEventKey, data: any, metadata?: Record<string, unknown>): void {
|
|
125
|
+
const db = getDb();
|
|
126
|
+
|
|
127
|
+
// Extraer IDs de worker según el tipo de evento
|
|
128
|
+
let fromWorkerId: string | null = null;
|
|
129
|
+
let toWorkerId: string | null = null;
|
|
130
|
+
let topic: string | null = null;
|
|
131
|
+
let content: string;
|
|
132
|
+
|
|
133
|
+
switch (event) {
|
|
134
|
+
case "worker:task_started":
|
|
135
|
+
case "worker:task_completed":
|
|
136
|
+
case "worker:task_failed":
|
|
137
|
+
fromWorkerId = (data as any).workerId || null;
|
|
138
|
+
topic = event;
|
|
139
|
+
content = JSON.stringify(data);
|
|
140
|
+
break;
|
|
141
|
+
case "worker:help_request":
|
|
142
|
+
fromWorkerId = (data as any).fromWorkerId || null;
|
|
143
|
+
topic = "help_request";
|
|
144
|
+
content = (data as any).request || "";
|
|
145
|
+
break;
|
|
146
|
+
case "worker:help_response":
|
|
147
|
+
fromWorkerId = (data as any).fromWorkerId || null;
|
|
148
|
+
toWorkerId = (data as any).toWorkerId || null;
|
|
149
|
+
topic = "help_response";
|
|
150
|
+
content = (data as any).response || "";
|
|
151
|
+
break;
|
|
152
|
+
case "worker:blocked":
|
|
153
|
+
case "worker:unblocked":
|
|
154
|
+
fromWorkerId = (data as any).workerId || null;
|
|
155
|
+
topic = event;
|
|
156
|
+
content = JSON.stringify(data);
|
|
157
|
+
break;
|
|
158
|
+
case "message:custom":
|
|
159
|
+
fromWorkerId = (data as any).fromWorkerId || null;
|
|
160
|
+
toWorkerId = (data as any).toWorkerId || null;
|
|
161
|
+
topic = (data as any).topic || null;
|
|
162
|
+
content = (data as any).content || "";
|
|
163
|
+
break;
|
|
164
|
+
default:
|
|
165
|
+
fromWorkerId = (data as any).workerId || (data as any).fromWorkerId || null;
|
|
166
|
+
topic = event;
|
|
167
|
+
content = JSON.stringify(data);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
db.query(`
|
|
172
|
+
INSERT OR IGNORE INTO agent_bus_messages
|
|
173
|
+
(event_type, from_worker_id, to_worker_id, topic, content, metadata, created_at, read)
|
|
174
|
+
VALUES (?, ?, ?, ?, ?, ?, unixepoch(), 0)
|
|
175
|
+
`).run(
|
|
176
|
+
event,
|
|
177
|
+
fromWorkerId,
|
|
178
|
+
toWorkerId,
|
|
179
|
+
topic,
|
|
180
|
+
content,
|
|
181
|
+
metadata ? JSON.stringify(metadata) : null
|
|
182
|
+
);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
log.warn(`Failed to persist message (non-critical): ${(err as Error).message}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Obtiene mensajes no leídos para un worker específico
|
|
190
|
+
*/
|
|
191
|
+
export function getUnreadMessagesForWorker(workerId: string, limit: number = 50): AgentBusMessage[] {
|
|
192
|
+
const db = getDb();
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const messages = db.query<any, [string, number]>(`
|
|
196
|
+
SELECT * FROM agent_bus_messages
|
|
197
|
+
WHERE (to_worker_id = ? OR to_worker_id IS NULL) AND read = 0
|
|
198
|
+
ORDER BY created_at ASC
|
|
199
|
+
LIMIT ?
|
|
200
|
+
`).all(workerId, limit);
|
|
201
|
+
|
|
202
|
+
// Marcar como leídos
|
|
203
|
+
if (messages.length > 0) {
|
|
204
|
+
const ids = messages.map((m: AgentBusMessage) => m.id).join(",");
|
|
205
|
+
db.query(`UPDATE agent_bus_messages SET read = 1 WHERE id IN (${ids})`).run();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return messages;
|
|
209
|
+
} catch (err) {
|
|
210
|
+
log.error(`Failed to get unread messages: ${(err as Error).message}`);
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Obtiene el historial de mensajes de un proyecto
|
|
217
|
+
*/
|
|
218
|
+
export function getProjectMessageHistory(projectId: string, limit: number = 100): AgentBusMessage[] {
|
|
219
|
+
const db = getDb();
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
// Primero obtenemos los task_ids del proyecto
|
|
223
|
+
const tasks = db.query<any, [string]>(
|
|
224
|
+
"SELECT id FROM tasks WHERE project_id = ?"
|
|
225
|
+
).all(projectId);
|
|
226
|
+
|
|
227
|
+
if (tasks.length === 0) return [];
|
|
228
|
+
|
|
229
|
+
// Obtenemos los agent_ids de las tareas
|
|
230
|
+
const agentIds = tasks
|
|
231
|
+
.map((t: any) => t.agent_id)
|
|
232
|
+
.filter((id: string | null) => id !== null);
|
|
233
|
+
|
|
234
|
+
if (agentIds.length === 0) return [];
|
|
235
|
+
|
|
236
|
+
// Obtenemos mensajes relacionados a estos agents
|
|
237
|
+
const placeholders = agentIds.map(() => "?").join(",");
|
|
238
|
+
const messages = db.query<any, any[]>(`
|
|
239
|
+
SELECT * FROM agent_bus_messages
|
|
240
|
+
WHERE from_worker_id IN (${placeholders})
|
|
241
|
+
ORDER BY created_at DESC
|
|
242
|
+
LIMIT ?
|
|
243
|
+
`).all([...agentIds, limit]);
|
|
244
|
+
|
|
245
|
+
return messages;
|
|
246
|
+
} catch (err) {
|
|
247
|
+
log.error(`Failed to get project message history: ${(err as Error).message}`);
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ─── Agent Bus Implementation ────────────────────────────────────────────────
|
|
253
|
+
|
|
254
|
+
class AgentBusImpl {
|
|
255
|
+
private emitter = new EventEmitter();
|
|
256
|
+
private logPrefix = "[agent-bus]";
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Publica un evento en el bus
|
|
260
|
+
*/
|
|
261
|
+
publish<K extends AgentBusEventKey>(event: K, data: AgentBusEventMap[K], metadata?: Record<string, unknown>): void {
|
|
262
|
+
const enrichedData = {
|
|
263
|
+
...data,
|
|
264
|
+
_eventId: crypto.randomUUID(),
|
|
265
|
+
_timestamp: Date.now(),
|
|
266
|
+
_event: event,
|
|
267
|
+
} as AgentBusEventMap[K] & { _eventId: string; _timestamp: number; _event: string };
|
|
268
|
+
|
|
269
|
+
// Emitir evento en memoria
|
|
270
|
+
this.emitter.emit(event, enrichedData);
|
|
271
|
+
|
|
272
|
+
// Persistir en BD
|
|
273
|
+
persistMessage(event, enrichedData, metadata);
|
|
274
|
+
|
|
275
|
+
log.info(`${this.logPrefix} published: ${event}`, {
|
|
276
|
+
event,
|
|
277
|
+
fromWorkerId: (data as any).workerId || (data as any).fromWorkerId,
|
|
278
|
+
toWorkerId: (data as any).toWorkerId
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Se suscribe a un tipo de evento
|
|
284
|
+
*/
|
|
285
|
+
subscribe<K extends AgentBusEventKey>(
|
|
286
|
+
event: K,
|
|
287
|
+
handler: AgentBusEventHandler<K>
|
|
288
|
+
): () => void {
|
|
289
|
+
this.emitter.on(event, handler);
|
|
290
|
+
log.debug(`${this.logPrefix} subscribed to: ${event}`);
|
|
291
|
+
|
|
292
|
+
return () => this.unsubscribe(event, handler);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Se suscribe una vez a un evento
|
|
297
|
+
*/
|
|
298
|
+
subscribeOnce<K extends AgentBusEventKey>(
|
|
299
|
+
event: K,
|
|
300
|
+
handler: AgentBusEventHandler<K>
|
|
301
|
+
): void {
|
|
302
|
+
this.emitter.once(event, handler);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Cancela suscripción
|
|
307
|
+
*/
|
|
308
|
+
unsubscribe<K extends AgentBusEventKey>(
|
|
309
|
+
event: K,
|
|
310
|
+
handler: AgentBusEventHandler<K>
|
|
311
|
+
): void {
|
|
312
|
+
this.emitter.off(event, handler);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Elimina todos los listeners
|
|
317
|
+
*/
|
|
318
|
+
removeAllListeners<K extends AgentBusEventKey>(event?: K): void {
|
|
319
|
+
if (event) {
|
|
320
|
+
this.emitter.removeAllListeners(event);
|
|
321
|
+
} else {
|
|
322
|
+
this.emitter.removeAllListeners();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Obtiene cantidad de listeners para un evento
|
|
328
|
+
*/
|
|
329
|
+
listenerCount<K extends AgentBusEventKey>(event: K): number {
|
|
330
|
+
return this.emitter.listenerCount(event);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Publica un mensaje personalizado de worker a worker
|
|
335
|
+
*/
|
|
336
|
+
sendMessage(
|
|
337
|
+
fromWorkerId: string,
|
|
338
|
+
fromWorkerName: string,
|
|
339
|
+
content: string,
|
|
340
|
+
options?: { toWorkerId?: string; topic?: string }
|
|
341
|
+
): void {
|
|
342
|
+
this.publish("message:custom", {
|
|
343
|
+
fromWorkerId,
|
|
344
|
+
fromWorkerName,
|
|
345
|
+
toWorkerId: options?.toWorkerId,
|
|
346
|
+
topic: options?.topic || "general",
|
|
347
|
+
content,
|
|
348
|
+
timestamp: Date.now(),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Notifica que una tarea comenzó
|
|
354
|
+
*/
|
|
355
|
+
notifyTaskStarted(
|
|
356
|
+
workerId: string,
|
|
357
|
+
workerName: string,
|
|
358
|
+
taskId: number,
|
|
359
|
+
taskName: string,
|
|
360
|
+
projectId: string
|
|
361
|
+
): void {
|
|
362
|
+
this.publish("worker:task_started", {
|
|
363
|
+
workerId,
|
|
364
|
+
workerName,
|
|
365
|
+
taskId,
|
|
366
|
+
taskName,
|
|
367
|
+
projectId,
|
|
368
|
+
timestamp: Date.now(),
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Notifica que una tarea completó
|
|
374
|
+
*/
|
|
375
|
+
notifyTaskCompleted(
|
|
376
|
+
workerId: string,
|
|
377
|
+
workerName: string,
|
|
378
|
+
taskId: number,
|
|
379
|
+
taskName: string,
|
|
380
|
+
projectId: string,
|
|
381
|
+
result: string
|
|
382
|
+
): void {
|
|
383
|
+
this.publish("worker:task_completed", {
|
|
384
|
+
workerId,
|
|
385
|
+
workerName,
|
|
386
|
+
taskId,
|
|
387
|
+
taskName,
|
|
388
|
+
projectId,
|
|
389
|
+
result,
|
|
390
|
+
timestamp: Date.now(),
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Notifica que una tarea falló
|
|
396
|
+
*/
|
|
397
|
+
notifyTaskFailed(
|
|
398
|
+
workerId: string,
|
|
399
|
+
workerName: string,
|
|
400
|
+
taskId: number,
|
|
401
|
+
taskName: string,
|
|
402
|
+
projectId: string,
|
|
403
|
+
error: string
|
|
404
|
+
): void {
|
|
405
|
+
this.publish("worker:task_failed", {
|
|
406
|
+
workerId,
|
|
407
|
+
workerName,
|
|
408
|
+
taskId,
|
|
409
|
+
taskName,
|
|
410
|
+
projectId,
|
|
411
|
+
error,
|
|
412
|
+
timestamp: Date.now(),
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Solicita ayuda a otros workers
|
|
418
|
+
*/
|
|
419
|
+
requestHelp(
|
|
420
|
+
fromWorkerId: string,
|
|
421
|
+
fromWorkerName: string,
|
|
422
|
+
taskId: number,
|
|
423
|
+
request: string,
|
|
424
|
+
requiredSkill?: string
|
|
425
|
+
): void {
|
|
426
|
+
this.publish("worker:help_request", {
|
|
427
|
+
fromWorkerId,
|
|
428
|
+
fromWorkerName,
|
|
429
|
+
taskId,
|
|
430
|
+
request,
|
|
431
|
+
requiredSkill,
|
|
432
|
+
timestamp: Date.now(),
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Responde a una solicitud de ayuda
|
|
438
|
+
*/
|
|
439
|
+
respondToHelp(
|
|
440
|
+
toWorkerId: string,
|
|
441
|
+
fromWorkerId: string,
|
|
442
|
+
fromWorkerName: string,
|
|
443
|
+
taskId: number,
|
|
444
|
+
response: string
|
|
445
|
+
): void {
|
|
446
|
+
this.publish("worker:help_response", {
|
|
447
|
+
toWorkerId,
|
|
448
|
+
fromWorkerId,
|
|
449
|
+
fromWorkerName,
|
|
450
|
+
taskId,
|
|
451
|
+
response,
|
|
452
|
+
timestamp: Date.now(),
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Singleton
|
|
458
|
+
export const agentBus = new AgentBusImpl();
|
|
459
|
+
|
|
460
|
+
export type AgentBus = typeof agentBus;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import { logger } from "../utils/logger";
|
|
3
|
+
|
|
4
|
+
export interface EventMap {
|
|
5
|
+
"message:received": {
|
|
6
|
+
channel: string;
|
|
7
|
+
userId: string;
|
|
8
|
+
content: string;
|
|
9
|
+
timestamp: number;
|
|
10
|
+
sessionId: string;
|
|
11
|
+
};
|
|
12
|
+
"message:sent": {
|
|
13
|
+
channel: string;
|
|
14
|
+
userId: string;
|
|
15
|
+
content: string;
|
|
16
|
+
messageId: string;
|
|
17
|
+
sessionId: string;
|
|
18
|
+
};
|
|
19
|
+
"agent:thinking": {
|
|
20
|
+
agentId: string;
|
|
21
|
+
sessionId: string;
|
|
22
|
+
stage: "planning" | "executing" | "responding";
|
|
23
|
+
};
|
|
24
|
+
"agent:response": {
|
|
25
|
+
agentId: string;
|
|
26
|
+
sessionId: string;
|
|
27
|
+
content: string;
|
|
28
|
+
toolsUsed: string[];
|
|
29
|
+
duration: number;
|
|
30
|
+
};
|
|
31
|
+
"tool:executing": {
|
|
32
|
+
toolName: string;
|
|
33
|
+
args: Record<string, unknown>;
|
|
34
|
+
sessionId: string;
|
|
35
|
+
};
|
|
36
|
+
"tool:completed": {
|
|
37
|
+
toolName: string;
|
|
38
|
+
result: unknown;
|
|
39
|
+
duration: number;
|
|
40
|
+
success: boolean;
|
|
41
|
+
};
|
|
42
|
+
"tool:error": {
|
|
43
|
+
toolName: string;
|
|
44
|
+
error: Error;
|
|
45
|
+
args: Record<string, unknown>;
|
|
46
|
+
};
|
|
47
|
+
"error": {
|
|
48
|
+
source: string;
|
|
49
|
+
error: Error;
|
|
50
|
+
context: Record<string, unknown>;
|
|
51
|
+
recoverable: boolean;
|
|
52
|
+
};
|
|
53
|
+
"session:started": {
|
|
54
|
+
sessionId: string;
|
|
55
|
+
agentId: string;
|
|
56
|
+
channel: string;
|
|
57
|
+
userId: string;
|
|
58
|
+
};
|
|
59
|
+
"session:ended": {
|
|
60
|
+
sessionId: string;
|
|
61
|
+
duration: number;
|
|
62
|
+
messageCount: number;
|
|
63
|
+
reason: "completed" | "cancelled" | "error" | "timeout";
|
|
64
|
+
};
|
|
65
|
+
"mcp:connected": {
|
|
66
|
+
serverName: string;
|
|
67
|
+
toolsCount: number;
|
|
68
|
+
resourcesCount: number;
|
|
69
|
+
};
|
|
70
|
+
"mcp:disconnected": {
|
|
71
|
+
serverName: string;
|
|
72
|
+
reason: string;
|
|
73
|
+
};
|
|
74
|
+
"mcp:error": {
|
|
75
|
+
serverName: string;
|
|
76
|
+
error: Error;
|
|
77
|
+
};
|
|
78
|
+
"channel:started": {
|
|
79
|
+
channel: string;
|
|
80
|
+
accountId: string;
|
|
81
|
+
};
|
|
82
|
+
"channel:stopped": {
|
|
83
|
+
channel: string;
|
|
84
|
+
accountId: string;
|
|
85
|
+
reason: string;
|
|
86
|
+
};
|
|
87
|
+
"gateway:started": {
|
|
88
|
+
host: string;
|
|
89
|
+
port: number;
|
|
90
|
+
};
|
|
91
|
+
"gateway:stopped": {
|
|
92
|
+
reason: string;
|
|
93
|
+
};
|
|
94
|
+
"pairing:requested": {
|
|
95
|
+
channel: string;
|
|
96
|
+
userId: string;
|
|
97
|
+
code: string;
|
|
98
|
+
expiresAt: number;
|
|
99
|
+
};
|
|
100
|
+
"pairing:approved": {
|
|
101
|
+
channel: string;
|
|
102
|
+
userId: string;
|
|
103
|
+
};
|
|
104
|
+
"pairing:rejected": {
|
|
105
|
+
channel: string;
|
|
106
|
+
userId: string;
|
|
107
|
+
reason: string;
|
|
108
|
+
};
|
|
109
|
+
"pairing:expired": {
|
|
110
|
+
code: string;
|
|
111
|
+
channel: string;
|
|
112
|
+
userId: string;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export type EventKey = keyof EventMap;
|
|
117
|
+
|
|
118
|
+
export interface EventHandler<K extends EventKey> {
|
|
119
|
+
(data: EventMap[K]): void | Promise<void>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
class TypedEventBusImpl {
|
|
123
|
+
private emitter = new EventEmitter();
|
|
124
|
+
private logPrefix = "[events]";
|
|
125
|
+
|
|
126
|
+
emit<K extends EventKey>(event: K, data: EventMap[K]): void {
|
|
127
|
+
const enrichedData = {
|
|
128
|
+
...data,
|
|
129
|
+
_eventId: crypto.randomUUID(),
|
|
130
|
+
_timestamp: Date.now(),
|
|
131
|
+
_event: event,
|
|
132
|
+
} as EventMap[K] & { _eventId: string; _timestamp: number; _event: string };
|
|
133
|
+
|
|
134
|
+
this.emitter.emit(event, enrichedData);
|
|
135
|
+
|
|
136
|
+
if (process.env.DEBUG_EVENTS === "true") {
|
|
137
|
+
logger.debug(`${this.logPrefix} emitted: ${event}`, { data });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
on<K extends EventKey>(event: K, handler: EventHandler<K>): () => void {
|
|
142
|
+
this.emitter.on(event, handler);
|
|
143
|
+
return () => this.off(event, handler);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
once<K extends EventKey>(event: K, handler: EventHandler<K>): void {
|
|
147
|
+
this.emitter.once(event, handler);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
off<K extends EventKey>(event: K, handler: EventHandler<K>): void {
|
|
151
|
+
this.emitter.off(event, handler);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
removeAllListeners<K extends EventKey>(event?: K): void {
|
|
155
|
+
if (event) {
|
|
156
|
+
this.emitter.removeAllListeners(event);
|
|
157
|
+
} else {
|
|
158
|
+
this.emitter.removeAllListeners();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
listenerCount<K extends EventKey>(event: K): number {
|
|
163
|
+
return this.emitter.listenerCount(event);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export const eventBus = new TypedEventBusImpl();
|
|
168
|
+
|
|
169
|
+
export type TypedEventBus = typeof eventBus;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel Notify — stub for SDK compatibility.
|
|
3
|
+
* In the full harness this sends messages back to channels.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export async function notifyChannel(
|
|
7
|
+
channel: string,
|
|
8
|
+
userId: string,
|
|
9
|
+
message: string,
|
|
10
|
+
opts?: { threadId?: string; metadata?: Record<string, unknown> }
|
|
11
|
+
): Promise<void> {
|
|
12
|
+
// TODO: integrate with ChannelManager for full functionality
|
|
13
|
+
console.log(`[channel-notify] ${channel}: ${message}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function sendToUserChannel(
|
|
17
|
+
channel: string,
|
|
18
|
+
userId: string,
|
|
19
|
+
message: string,
|
|
20
|
+
opts?: { threadId?: string; metadata?: Record<string, unknown> }
|
|
21
|
+
): Promise<{ ok: boolean; error?: string }> {
|
|
22
|
+
try {
|
|
23
|
+
await notifyChannel(channel, userId, message, opts);
|
|
24
|
+
return { ok: true };
|
|
25
|
+
} catch (err) {
|
|
26
|
+
return { ok: false, error: (err as Error).message };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function broadcastNotification(
|
|
31
|
+
channels: string[],
|
|
32
|
+
message: string
|
|
33
|
+
): Promise<void> {
|
|
34
|
+
for (const channel of channels) {
|
|
35
|
+
await notifyChannel(channel, "", message);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, expect, it, beforeAll, afterAll } from "bun:test";
|
|
2
|
+
import { startGateway } from "./index.ts";
|
|
3
|
+
|
|
4
|
+
describe("gateway", () => {
|
|
5
|
+
let server: ReturnType<typeof startGateway> extends Promise<infer T> ? T : never;
|
|
6
|
+
|
|
7
|
+
beforeAll(async () => {
|
|
8
|
+
server = await startGateway({ host: "127.0.0.1", port: 18791, agentId: "test" });
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterAll(() => {
|
|
12
|
+
server?.stop(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("returns health status", async () => {
|
|
16
|
+
const res = await fetch("http://127.0.0.1:18791/status");
|
|
17
|
+
expect(res.status).toBe(200);
|
|
18
|
+
const body = await res.json();
|
|
19
|
+
expect(body.status).toBe("ok");
|
|
20
|
+
expect(body.gateway).toBe(true);
|
|
21
|
+
expect(body.agentId).toBe("test");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("returns 404 for unknown routes", async () => {
|
|
25
|
+
const res = await fetch("http://127.0.0.1:18791/unknown");
|
|
26
|
+
expect(res.status).toBe(404);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("chat endpoint requires database setup", async () => {
|
|
30
|
+
const res = await fetch("http://127.0.0.1:18791/chat", {
|
|
31
|
+
method: "POST",
|
|
32
|
+
headers: { "Content-Type": "application/json" },
|
|
33
|
+
body: JSON.stringify({ message: "hello" }),
|
|
34
|
+
});
|
|
35
|
+
// Will fail because no DB/agent configured, but endpoint exists
|
|
36
|
+
expect(res.status).toBe(500);
|
|
37
|
+
});
|
|
38
|
+
});
|