@swarmclawai/swarmclaw 0.2.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/README.md +577 -0
- package/bin/server-cmd.js +359 -0
- package/bin/swarmclaw.js +29 -0
- package/bin/swarmclaw.mjs +1504 -0
- package/next.config.ts +33 -0
- package/package.json +112 -0
- package/postcss.config.mjs +7 -0
- package/public/branding/swarmclaw-org-avatar.png +0 -0
- package/public/branding/swarmclaw-org-avatar.svg +58 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/next.svg +1 -0
- package/public/screenshots/agents.png +0 -0
- package/public/screenshots/connectors.png +0 -0
- package/public/screenshots/dashboard.png +0 -0
- package/public/screenshots/new-session-openclaw.png +0 -0
- package/public/screenshots/providers.png +0 -0
- package/public/screenshots/schedules.png +0 -0
- package/public/screenshots/tasks.png +0 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/src/app/api/agents/[id]/route.ts +30 -0
- package/src/app/api/agents/[id]/thread/route.ts +66 -0
- package/src/app/api/agents/generate/route.ts +42 -0
- package/src/app/api/agents/route.ts +33 -0
- package/src/app/api/auth/route.ts +25 -0
- package/src/app/api/claude-skills/route.ts +42 -0
- package/src/app/api/clawhub/install/route.ts +39 -0
- package/src/app/api/clawhub/search/route.ts +11 -0
- package/src/app/api/connectors/[id]/route.ts +79 -0
- package/src/app/api/connectors/route.ts +60 -0
- package/src/app/api/credentials/[id]/route.ts +14 -0
- package/src/app/api/credentials/route.ts +31 -0
- package/src/app/api/daemon/health-check/route.ts +11 -0
- package/src/app/api/daemon/route.ts +22 -0
- package/src/app/api/dirs/pick/route.ts +60 -0
- package/src/app/api/dirs/route.ts +29 -0
- package/src/app/api/documents/[id]/route.ts +47 -0
- package/src/app/api/documents/route.ts +93 -0
- package/src/app/api/files/serve/route.ts +69 -0
- package/src/app/api/generate/info/route.ts +12 -0
- package/src/app/api/generate/route.ts +106 -0
- package/src/app/api/ip/route.ts +6 -0
- package/src/app/api/knowledge/[id]/route.ts +61 -0
- package/src/app/api/knowledge/route.ts +48 -0
- package/src/app/api/knowledge/upload/route.ts +86 -0
- package/src/app/api/logs/route.ts +65 -0
- package/src/app/api/mcp-servers/[id]/route.ts +32 -0
- package/src/app/api/mcp-servers/[id]/test/route.ts +23 -0
- package/src/app/api/mcp-servers/[id]/tools/route.ts +32 -0
- package/src/app/api/mcp-servers/route.ts +27 -0
- package/src/app/api/memory/[id]/route.ts +126 -0
- package/src/app/api/memory/maintenance/route.ts +63 -0
- package/src/app/api/memory/route.ts +111 -0
- package/src/app/api/memory-images/[filename]/route.ts +36 -0
- package/src/app/api/orchestrator/run/route.ts +43 -0
- package/src/app/api/plugins/install/route.ts +58 -0
- package/src/app/api/plugins/marketplace/route.ts +33 -0
- package/src/app/api/plugins/route.ts +21 -0
- package/src/app/api/preview-server/route.ts +339 -0
- package/src/app/api/providers/[id]/models/route.ts +29 -0
- package/src/app/api/providers/[id]/route.ts +34 -0
- package/src/app/api/providers/configs/route.ts +7 -0
- package/src/app/api/providers/ollama/route.ts +30 -0
- package/src/app/api/providers/openclaw/health/route.ts +23 -0
- package/src/app/api/providers/route.ts +28 -0
- package/src/app/api/runs/[id]/route.ts +9 -0
- package/src/app/api/runs/route.ts +13 -0
- package/src/app/api/schedules/[id]/route.ts +28 -0
- package/src/app/api/schedules/[id]/run/route.ts +104 -0
- package/src/app/api/schedules/route.ts +78 -0
- package/src/app/api/secrets/[id]/route.ts +29 -0
- package/src/app/api/secrets/route.ts +42 -0
- package/src/app/api/sessions/[id]/browser/route.ts +13 -0
- package/src/app/api/sessions/[id]/chat/route.ts +96 -0
- package/src/app/api/sessions/[id]/clear/route.ts +19 -0
- package/src/app/api/sessions/[id]/deploy/route.ts +34 -0
- package/src/app/api/sessions/[id]/devserver/route.ts +69 -0
- package/src/app/api/sessions/[id]/mailbox/route.ts +70 -0
- package/src/app/api/sessions/[id]/main-loop/route.ts +94 -0
- package/src/app/api/sessions/[id]/messages/route.ts +9 -0
- package/src/app/api/sessions/[id]/retry/route.ts +28 -0
- package/src/app/api/sessions/[id]/route.ts +103 -0
- package/src/app/api/sessions/[id]/stop/route.ts +13 -0
- package/src/app/api/sessions/heartbeat/route.ts +26 -0
- package/src/app/api/sessions/route.ts +85 -0
- package/src/app/api/settings/route.ts +58 -0
- package/src/app/api/setup/check-provider/route.ts +326 -0
- package/src/app/api/setup/doctor/route.ts +250 -0
- package/src/app/api/skills/[id]/route.ts +40 -0
- package/src/app/api/skills/import/route.ts +69 -0
- package/src/app/api/skills/route.ts +28 -0
- package/src/app/api/tasks/[id]/route.ts +102 -0
- package/src/app/api/tasks/route.ts +115 -0
- package/src/app/api/tts/route.ts +40 -0
- package/src/app/api/upload/route.ts +18 -0
- package/src/app/api/uploads/[filename]/route.ts +59 -0
- package/src/app/api/usage/route.ts +35 -0
- package/src/app/api/version/route.ts +81 -0
- package/src/app/api/version/update/route.ts +95 -0
- package/src/app/api/webhooks/[id]/history/route.ts +13 -0
- package/src/app/api/webhooks/[id]/route.ts +204 -0
- package/src/app/api/webhooks/route.ts +37 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +370 -0
- package/src/app/layout.tsx +52 -0
- package/src/app/page.tsx +172 -0
- package/src/cli/index.js +1232 -0
- package/src/cli/index.test.js +281 -0
- package/src/cli/index.ts +1158 -0
- package/src/cli/spec.js +284 -0
- package/src/components/agents/agent-card.tsx +219 -0
- package/src/components/agents/agent-chat-list.tsx +165 -0
- package/src/components/agents/agent-list.tsx +110 -0
- package/src/components/agents/agent-sheet.tsx +1220 -0
- package/src/components/auth/access-key-gate.tsx +248 -0
- package/src/components/auth/setup-wizard.tsx +940 -0
- package/src/components/auth/user-picker.tsx +88 -0
- package/src/components/chat/chat-area.tsx +406 -0
- package/src/components/chat/chat-header.tsx +491 -0
- package/src/components/chat/chat-tool-toggles.tsx +161 -0
- package/src/components/chat/code-block.tsx +146 -0
- package/src/components/chat/dev-server-bar.tsx +39 -0
- package/src/components/chat/message-bubble.tsx +486 -0
- package/src/components/chat/message-list.tsx +299 -0
- package/src/components/chat/session-debug-panel.tsx +196 -0
- package/src/components/chat/streaming-bubble.tsx +85 -0
- package/src/components/chat/thinking-indicator.tsx +26 -0
- package/src/components/chat/tool-call-bubble.tsx +438 -0
- package/src/components/chat/tool-request-banner.tsx +103 -0
- package/src/components/connectors/connector-list.tsx +196 -0
- package/src/components/connectors/connector-sheet.tsx +804 -0
- package/src/components/input/chat-input.tsx +235 -0
- package/src/components/knowledge/knowledge-list.tsx +206 -0
- package/src/components/knowledge/knowledge-sheet.tsx +316 -0
- package/src/components/layout/app-layout.tsx +1016 -0
- package/src/components/layout/daemon-indicator.tsx +56 -0
- package/src/components/layout/mobile-header.tsx +31 -0
- package/src/components/layout/network-banner.tsx +17 -0
- package/src/components/layout/update-banner.tsx +130 -0
- package/src/components/logs/log-list.tsx +358 -0
- package/src/components/mcp-servers/mcp-server-list.tsx +122 -0
- package/src/components/mcp-servers/mcp-server-sheet.tsx +243 -0
- package/src/components/memory/memory-card.tsx +63 -0
- package/src/components/memory/memory-detail.tsx +339 -0
- package/src/components/memory/memory-list.tsx +198 -0
- package/src/components/memory/memory-sheet.tsx +70 -0
- package/src/components/plugins/plugin-list.tsx +60 -0
- package/src/components/plugins/plugin-sheet.tsx +311 -0
- package/src/components/providers/provider-list.tsx +96 -0
- package/src/components/providers/provider-sheet.tsx +542 -0
- package/src/components/runs/run-list.tsx +231 -0
- package/src/components/schedules/schedule-card.tsx +63 -0
- package/src/components/schedules/schedule-list.tsx +76 -0
- package/src/components/schedules/schedule-sheet.tsx +336 -0
- package/src/components/secrets/secret-sheet.tsx +180 -0
- package/src/components/secrets/secrets-list.tsx +91 -0
- package/src/components/sessions/new-session-sheet.tsx +478 -0
- package/src/components/sessions/session-card.tsx +144 -0
- package/src/components/sessions/session-list.tsx +202 -0
- package/src/components/shared/ai-gen-block.tsx +77 -0
- package/src/components/shared/avatar.tsx +48 -0
- package/src/components/shared/bottom-sheet.tsx +30 -0
- package/src/components/shared/confirm-dialog.tsx +47 -0
- package/src/components/shared/connector-platform-icon.tsx +113 -0
- package/src/components/shared/dir-browser.tsx +285 -0
- package/src/components/shared/dropdown.tsx +55 -0
- package/src/components/shared/icon-button.tsx +25 -0
- package/src/components/shared/settings/plugin-manager.tsx +207 -0
- package/src/components/shared/settings/section-capability-policy.tsx +93 -0
- package/src/components/shared/settings/section-embedding.tsx +99 -0
- package/src/components/shared/settings/section-heartbeat.tsx +168 -0
- package/src/components/shared/settings/section-memory.tsx +77 -0
- package/src/components/shared/settings/section-orchestrator.tsx +108 -0
- package/src/components/shared/settings/section-providers.tsx +181 -0
- package/src/components/shared/settings/section-runtime-loop.tsx +183 -0
- package/src/components/shared/settings/section-secrets.tsx +132 -0
- package/src/components/shared/settings/section-user-preferences.tsx +24 -0
- package/src/components/shared/settings/section-voice.tsx +53 -0
- package/src/components/shared/settings/settings-sheet.tsx +88 -0
- package/src/components/shared/settings/types.ts +7 -0
- package/src/components/shared/settings/utils.ts +13 -0
- package/src/components/shared/settings-sheet.tsx +1 -0
- package/src/components/shared/skeleton.tsx +19 -0
- package/src/components/shared/usage-badge.tsx +28 -0
- package/src/components/skills/clawhub-browser.tsx +225 -0
- package/src/components/skills/skill-list.tsx +70 -0
- package/src/components/skills/skill-sheet.tsx +254 -0
- package/src/components/tasks/task-board.tsx +96 -0
- package/src/components/tasks/task-card.tsx +179 -0
- package/src/components/tasks/task-column.tsx +73 -0
- package/src/components/tasks/task-list.tsx +118 -0
- package/src/components/tasks/task-sheet.tsx +415 -0
- package/src/components/ui/avatar.tsx +109 -0
- package/src/components/ui/badge.tsx +48 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/dialog.tsx +158 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/scroll-area.tsx +58 -0
- package/src/components/ui/select.tsx +190 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +143 -0
- package/src/components/ui/sonner.tsx +22 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +56 -0
- package/src/components/usage/usage-list.tsx +105 -0
- package/src/components/webhooks/webhook-list.tsx +166 -0
- package/src/components/webhooks/webhook-sheet.tsx +402 -0
- package/src/hooks/use-auto-resize.ts +20 -0
- package/src/hooks/use-media-query.ts +21 -0
- package/src/hooks/use-speech-recognition.ts +83 -0
- package/src/instrumentation.ts +8 -0
- package/src/lib/agents.ts +13 -0
- package/src/lib/api-client.ts +100 -0
- package/src/lib/chat.ts +60 -0
- package/src/lib/memory.ts +42 -0
- package/src/lib/openclaw-endpoint.test.ts +48 -0
- package/src/lib/openclaw-endpoint.ts +67 -0
- package/src/lib/provider-config.ts +13 -0
- package/src/lib/providers/anthropic.ts +135 -0
- package/src/lib/providers/claude-cli.ts +202 -0
- package/src/lib/providers/codex-cli.ts +260 -0
- package/src/lib/providers/index.ts +351 -0
- package/src/lib/providers/ollama.ts +131 -0
- package/src/lib/providers/openai.ts +164 -0
- package/src/lib/providers/openclaw.ts +330 -0
- package/src/lib/providers/opencode-cli.ts +164 -0
- package/src/lib/runtime-loop.ts +15 -0
- package/src/lib/schedule-dedupe.test.ts +84 -0
- package/src/lib/schedule-dedupe.ts +174 -0
- package/src/lib/schedule-name.ts +62 -0
- package/src/lib/schedules.ts +16 -0
- package/src/lib/server/agent-registry.ts +70 -0
- package/src/lib/server/api-routes.test.ts +362 -0
- package/src/lib/server/autonomy-contract.ts +200 -0
- package/src/lib/server/build-llm.ts +155 -0
- package/src/lib/server/capability-router.test.ts +21 -0
- package/src/lib/server/capability-router.ts +172 -0
- package/src/lib/server/chat-execution.ts +894 -0
- package/src/lib/server/clawhub-client.test.ts +161 -0
- package/src/lib/server/clawhub-client.ts +26 -0
- package/src/lib/server/connectors/connector-routing.test.ts +243 -0
- package/src/lib/server/connectors/discord.ts +116 -0
- package/src/lib/server/connectors/googlechat.ts +66 -0
- package/src/lib/server/connectors/manager.ts +559 -0
- package/src/lib/server/connectors/matrix.ts +78 -0
- package/src/lib/server/connectors/media.ts +149 -0
- package/src/lib/server/connectors/openclaw.test.ts +375 -0
- package/src/lib/server/connectors/openclaw.ts +1132 -0
- package/src/lib/server/connectors/signal.ts +183 -0
- package/src/lib/server/connectors/slack.ts +258 -0
- package/src/lib/server/connectors/teams.ts +94 -0
- package/src/lib/server/connectors/telegram.ts +221 -0
- package/src/lib/server/connectors/types.ts +62 -0
- package/src/lib/server/connectors/whatsapp.ts +349 -0
- package/src/lib/server/context-manager.ts +232 -0
- package/src/lib/server/cost.ts +31 -0
- package/src/lib/server/daemon-state.ts +354 -0
- package/src/lib/server/data-dir.ts +3 -0
- package/src/lib/server/embeddings.ts +111 -0
- package/src/lib/server/execution-log.ts +257 -0
- package/src/lib/server/gateway/protocol.test.ts +54 -0
- package/src/lib/server/gateway/protocol.ts +114 -0
- package/src/lib/server/heartbeat-service.ts +366 -0
- package/src/lib/server/knowledge-db.test.ts +441 -0
- package/src/lib/server/logger.ts +47 -0
- package/src/lib/server/main-agent-loop.ts +1017 -0
- package/src/lib/server/mcp-client.test.ts +342 -0
- package/src/lib/server/mcp-client.ts +130 -0
- package/src/lib/server/memory-db.ts +1078 -0
- package/src/lib/server/memory-graph.test.ts +153 -0
- package/src/lib/server/memory-graph.ts +138 -0
- package/src/lib/server/openclaw-health.ts +245 -0
- package/src/lib/server/orchestrator-lg.ts +431 -0
- package/src/lib/server/orchestrator.ts +364 -0
- package/src/lib/server/playwright-proxy.mjs +70 -0
- package/src/lib/server/plugins.ts +229 -0
- package/src/lib/server/process-manager.ts +327 -0
- package/src/lib/server/provider-health.ts +113 -0
- package/src/lib/server/queue.ts +859 -0
- package/src/lib/server/runtime-settings.ts +119 -0
- package/src/lib/server/scheduler.ts +196 -0
- package/src/lib/server/session-mailbox.ts +129 -0
- package/src/lib/server/session-run-manager.ts +512 -0
- package/src/lib/server/session-tools/connector.ts +124 -0
- package/src/lib/server/session-tools/context-mgmt.ts +103 -0
- package/src/lib/server/session-tools/context.ts +114 -0
- package/src/lib/server/session-tools/crud.ts +673 -0
- package/src/lib/server/session-tools/delegate.ts +708 -0
- package/src/lib/server/session-tools/file.ts +264 -0
- package/src/lib/server/session-tools/index.ts +164 -0
- package/src/lib/server/session-tools/memory.ts +230 -0
- package/src/lib/server/session-tools/session-info.ts +422 -0
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +166 -0
- package/src/lib/server/session-tools/shell.ts +171 -0
- package/src/lib/server/session-tools/web.ts +408 -0
- package/src/lib/server/session-tools.ts +9 -0
- package/src/lib/server/skills-normalize.ts +130 -0
- package/src/lib/server/storage-mcp.test.ts +161 -0
- package/src/lib/server/storage.ts +670 -0
- package/src/lib/server/stream-agent-chat.ts +571 -0
- package/src/lib/server/task-reports.ts +122 -0
- package/src/lib/server/task-result.ts +161 -0
- package/src/lib/server/task-validation.test.ts +27 -0
- package/src/lib/server/task-validation.ts +90 -0
- package/src/lib/server/tool-capability-policy.test.ts +58 -0
- package/src/lib/server/tool-capability-policy.ts +262 -0
- package/src/lib/sessions.ts +68 -0
- package/src/lib/tasks.ts +20 -0
- package/src/lib/tts.ts +42 -0
- package/src/lib/upload.ts +10 -0
- package/src/lib/utils.ts +6 -0
- package/src/proxy.ts +43 -0
- package/src/stores/use-app-store.ts +468 -0
- package/src/stores/use-chat-store.ts +323 -0
- package/src/types/index.ts +621 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { loadSettings, loadCredentials, decryptKey } from './storage'
|
|
2
|
+
|
|
3
|
+
let localPipeline: any = null
|
|
4
|
+
let localPipelineLoading: Promise<any> | null = null
|
|
5
|
+
|
|
6
|
+
async function getLocalPipeline() {
|
|
7
|
+
if (localPipeline) return localPipeline
|
|
8
|
+
if (localPipelineLoading) return localPipelineLoading
|
|
9
|
+
|
|
10
|
+
localPipelineLoading = (async () => {
|
|
11
|
+
const { pipeline } = await import('@huggingface/transformers')
|
|
12
|
+
localPipeline = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2', {
|
|
13
|
+
dtype: 'fp32',
|
|
14
|
+
})
|
|
15
|
+
return localPipeline
|
|
16
|
+
})()
|
|
17
|
+
|
|
18
|
+
return localPipelineLoading
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function getEmbedding(text: string): Promise<number[] | null> {
|
|
22
|
+
const settings = loadSettings()
|
|
23
|
+
const provider = settings.embeddingProvider
|
|
24
|
+
if (!provider) return null
|
|
25
|
+
|
|
26
|
+
const model = settings.embeddingModel || 'text-embedding-3-small'
|
|
27
|
+
|
|
28
|
+
let apiKey: string | null = null
|
|
29
|
+
if (settings.embeddingCredentialId) {
|
|
30
|
+
const creds = loadCredentials()
|
|
31
|
+
const cred = creds[settings.embeddingCredentialId]
|
|
32
|
+
if (cred?.encryptedKey) {
|
|
33
|
+
try { apiKey = decryptKey(cred.encryptedKey) } catch { /* ignore */ }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
if (provider === 'local') {
|
|
39
|
+
return await localEmbed(text)
|
|
40
|
+
} else if (provider === 'openai') {
|
|
41
|
+
return await openaiEmbed(text, model, apiKey)
|
|
42
|
+
} else if (provider === 'ollama') {
|
|
43
|
+
return await ollamaEmbed(text, model, settings.langGraphEndpoint)
|
|
44
|
+
}
|
|
45
|
+
} catch (err: any) {
|
|
46
|
+
console.error(`[embeddings] Error computing embedding:`, err.message)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function localEmbed(text: string): Promise<number[]> {
|
|
53
|
+
const pipe = await getLocalPipeline()
|
|
54
|
+
const output = await pipe(text.slice(0, 8000), { pooling: 'mean', normalize: true })
|
|
55
|
+
return Array.from(output.data as Float32Array)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function openaiEmbed(text: string, model: string, apiKey: string | null): Promise<number[]> {
|
|
59
|
+
if (!apiKey) throw new Error('OpenAI API key required for embeddings')
|
|
60
|
+
const res = await fetch('https://api.openai.com/v1/embeddings', {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
Authorization: `Bearer ${apiKey}`,
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
model,
|
|
68
|
+
input: text.slice(0, 8000),
|
|
69
|
+
}),
|
|
70
|
+
})
|
|
71
|
+
if (!res.ok) throw new Error(`OpenAI embeddings API error: ${res.status}`)
|
|
72
|
+
const data = await res.json()
|
|
73
|
+
return data.data[0].embedding
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function ollamaEmbed(text: string, model: string, endpoint?: string | null): Promise<number[]> {
|
|
77
|
+
const baseUrl = endpoint || 'http://localhost:11434'
|
|
78
|
+
const res = await fetch(`${baseUrl}/api/embeddings`, {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: { 'Content-Type': 'application/json' },
|
|
81
|
+
body: JSON.stringify({
|
|
82
|
+
model,
|
|
83
|
+
prompt: text.slice(0, 8000),
|
|
84
|
+
}),
|
|
85
|
+
})
|
|
86
|
+
if (!res.ok) throw new Error(`Ollama embeddings API error: ${res.status}`)
|
|
87
|
+
const data = await res.json()
|
|
88
|
+
return data.embedding
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function cosineSimilarity(a: number[], b: number[]): number {
|
|
92
|
+
if (a.length !== b.length) return 0
|
|
93
|
+
let dotProduct = 0
|
|
94
|
+
let normA = 0
|
|
95
|
+
let normB = 0
|
|
96
|
+
for (let i = 0; i < a.length; i++) {
|
|
97
|
+
dotProduct += a[i] * b[i]
|
|
98
|
+
normA += a[i] * a[i]
|
|
99
|
+
normB += b[i] * b[i]
|
|
100
|
+
}
|
|
101
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB)
|
|
102
|
+
return denom === 0 ? 0 : dotProduct / denom
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function serializeEmbedding(embedding: number[]): Buffer {
|
|
106
|
+
return Buffer.from(new Float32Array(embedding).buffer)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function deserializeEmbedding(buf: Buffer): number[] {
|
|
110
|
+
return Array.from(new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4))
|
|
111
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import crypto from 'crypto'
|
|
4
|
+
import Database from 'better-sqlite3'
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Types
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
export type LogCategory =
|
|
11
|
+
| 'trigger' // what kicked off the action
|
|
12
|
+
| 'decision' // reasoning / model choice
|
|
13
|
+
| 'tool_call' // tool invocation with input
|
|
14
|
+
| 'tool_result' // tool output
|
|
15
|
+
| 'outbound' // messages sent to users/platforms
|
|
16
|
+
| 'file_op' // file read/write/delete with checksums
|
|
17
|
+
| 'commit' // git commit activity
|
|
18
|
+
| 'error' // errors during execution
|
|
19
|
+
|
|
20
|
+
export interface ExecutionLogEntry {
|
|
21
|
+
id: string
|
|
22
|
+
sessionId: string
|
|
23
|
+
runId: string | null
|
|
24
|
+
agentId: string | null
|
|
25
|
+
category: LogCategory
|
|
26
|
+
summary: string
|
|
27
|
+
detail: Record<string, unknown> | null
|
|
28
|
+
ts: number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface LogQueryOpts {
|
|
32
|
+
sessionId?: string
|
|
33
|
+
agentId?: string
|
|
34
|
+
runId?: string
|
|
35
|
+
category?: LogCategory
|
|
36
|
+
since?: number
|
|
37
|
+
until?: number
|
|
38
|
+
limit?: number
|
|
39
|
+
offset?: number
|
|
40
|
+
search?: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Database setup
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
import { DATA_DIR } from './data-dir'
|
|
48
|
+
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR, { recursive: true })
|
|
49
|
+
|
|
50
|
+
const DB_PATH = path.join(DATA_DIR, 'logs.db')
|
|
51
|
+
|
|
52
|
+
let _db: Database.Database | null = null
|
|
53
|
+
|
|
54
|
+
function getDb(): Database.Database {
|
|
55
|
+
if (_db) return _db
|
|
56
|
+
_db = new Database(DB_PATH)
|
|
57
|
+
_db.pragma('journal_mode = WAL')
|
|
58
|
+
_db.exec(`
|
|
59
|
+
CREATE TABLE IF NOT EXISTS execution_logs (
|
|
60
|
+
id TEXT PRIMARY KEY,
|
|
61
|
+
session_id TEXT NOT NULL,
|
|
62
|
+
run_id TEXT,
|
|
63
|
+
agent_id TEXT,
|
|
64
|
+
category TEXT NOT NULL,
|
|
65
|
+
summary TEXT NOT NULL,
|
|
66
|
+
detail TEXT,
|
|
67
|
+
ts INTEGER NOT NULL
|
|
68
|
+
)
|
|
69
|
+
`)
|
|
70
|
+
_db.exec(`CREATE INDEX IF NOT EXISTS idx_logs_session ON execution_logs(session_id, ts)`)
|
|
71
|
+
_db.exec(`CREATE INDEX IF NOT EXISTS idx_logs_agent ON execution_logs(agent_id, ts)`)
|
|
72
|
+
_db.exec(`CREATE INDEX IF NOT EXISTS idx_logs_run ON execution_logs(run_id)`)
|
|
73
|
+
_db.exec(`CREATE INDEX IF NOT EXISTS idx_logs_cat ON execution_logs(category)`)
|
|
74
|
+
return _db
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Write
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
const insertStmt = () =>
|
|
82
|
+
getDb().prepare(
|
|
83
|
+
`INSERT INTO execution_logs (id, session_id, run_id, agent_id, category, summary, detail, ts)
|
|
84
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
export function logExecution(
|
|
88
|
+
sessionId: string,
|
|
89
|
+
category: LogCategory,
|
|
90
|
+
summary: string,
|
|
91
|
+
opts?: {
|
|
92
|
+
runId?: string | null
|
|
93
|
+
agentId?: string | null
|
|
94
|
+
detail?: Record<string, unknown>
|
|
95
|
+
},
|
|
96
|
+
): string {
|
|
97
|
+
const id = crypto.randomBytes(8).toString('hex')
|
|
98
|
+
const ts = Date.now()
|
|
99
|
+
try {
|
|
100
|
+
insertStmt().run(
|
|
101
|
+
id,
|
|
102
|
+
sessionId,
|
|
103
|
+
opts?.runId ?? null,
|
|
104
|
+
opts?.agentId ?? null,
|
|
105
|
+
category,
|
|
106
|
+
summary,
|
|
107
|
+
opts?.detail ? JSON.stringify(opts.detail) : null,
|
|
108
|
+
ts,
|
|
109
|
+
)
|
|
110
|
+
} catch {
|
|
111
|
+
// Non-critical — never block agent execution for logging failures
|
|
112
|
+
}
|
|
113
|
+
return id
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Batch insert for bulk writes (e.g. file ops)
|
|
117
|
+
export function logExecutionBatch(
|
|
118
|
+
entries: Array<{
|
|
119
|
+
sessionId: string
|
|
120
|
+
category: LogCategory
|
|
121
|
+
summary: string
|
|
122
|
+
runId?: string | null
|
|
123
|
+
agentId?: string | null
|
|
124
|
+
detail?: Record<string, unknown>
|
|
125
|
+
}>,
|
|
126
|
+
): void {
|
|
127
|
+
const db = getDb()
|
|
128
|
+
const stmt = db.prepare(
|
|
129
|
+
`INSERT INTO execution_logs (id, session_id, run_id, agent_id, category, summary, detail, ts)
|
|
130
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
131
|
+
)
|
|
132
|
+
const ts = Date.now()
|
|
133
|
+
const tx = db.transaction(() => {
|
|
134
|
+
for (const e of entries) {
|
|
135
|
+
stmt.run(
|
|
136
|
+
crypto.randomBytes(8).toString('hex'),
|
|
137
|
+
e.sessionId,
|
|
138
|
+
e.runId ?? null,
|
|
139
|
+
e.agentId ?? null,
|
|
140
|
+
e.category,
|
|
141
|
+
e.summary,
|
|
142
|
+
e.detail ? JSON.stringify(e.detail) : null,
|
|
143
|
+
ts,
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
try { tx() } catch { /* non-critical */ }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// Read / Query
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
|
|
154
|
+
export function queryLogs(opts: LogQueryOpts): ExecutionLogEntry[] {
|
|
155
|
+
const conditions: string[] = []
|
|
156
|
+
const params: unknown[] = []
|
|
157
|
+
|
|
158
|
+
if (opts.sessionId) {
|
|
159
|
+
conditions.push('session_id = ?')
|
|
160
|
+
params.push(opts.sessionId)
|
|
161
|
+
}
|
|
162
|
+
if (opts.agentId) {
|
|
163
|
+
conditions.push('agent_id = ?')
|
|
164
|
+
params.push(opts.agentId)
|
|
165
|
+
}
|
|
166
|
+
if (opts.runId) {
|
|
167
|
+
conditions.push('run_id = ?')
|
|
168
|
+
params.push(opts.runId)
|
|
169
|
+
}
|
|
170
|
+
if (opts.category) {
|
|
171
|
+
conditions.push('category = ?')
|
|
172
|
+
params.push(opts.category)
|
|
173
|
+
}
|
|
174
|
+
if (opts.since) {
|
|
175
|
+
conditions.push('ts >= ?')
|
|
176
|
+
params.push(opts.since)
|
|
177
|
+
}
|
|
178
|
+
if (opts.until) {
|
|
179
|
+
conditions.push('ts <= ?')
|
|
180
|
+
params.push(opts.until)
|
|
181
|
+
}
|
|
182
|
+
if (opts.search) {
|
|
183
|
+
conditions.push('(summary LIKE ? OR detail LIKE ?)')
|
|
184
|
+
const pattern = `%${opts.search}%`
|
|
185
|
+
params.push(pattern, pattern)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
|
|
189
|
+
const limit = opts.limit ?? 200
|
|
190
|
+
const offset = opts.offset ?? 0
|
|
191
|
+
|
|
192
|
+
const rows = getDb()
|
|
193
|
+
.prepare(`SELECT * FROM execution_logs ${where} ORDER BY ts DESC LIMIT ? OFFSET ?`)
|
|
194
|
+
.all(...params, limit, offset) as Array<{
|
|
195
|
+
id: string
|
|
196
|
+
session_id: string
|
|
197
|
+
run_id: string | null
|
|
198
|
+
agent_id: string | null
|
|
199
|
+
category: string
|
|
200
|
+
summary: string
|
|
201
|
+
detail: string | null
|
|
202
|
+
ts: number
|
|
203
|
+
}>
|
|
204
|
+
|
|
205
|
+
return rows.map((r) => ({
|
|
206
|
+
id: r.id,
|
|
207
|
+
sessionId: r.session_id,
|
|
208
|
+
runId: r.run_id,
|
|
209
|
+
agentId: r.agent_id,
|
|
210
|
+
category: r.category as LogCategory,
|
|
211
|
+
summary: r.summary,
|
|
212
|
+
detail: r.detail ? JSON.parse(r.detail) : null,
|
|
213
|
+
ts: r.ts,
|
|
214
|
+
}))
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function countLogs(opts: Omit<LogQueryOpts, 'limit' | 'offset'>): number {
|
|
218
|
+
const conditions: string[] = []
|
|
219
|
+
const params: unknown[] = []
|
|
220
|
+
|
|
221
|
+
if (opts.sessionId) { conditions.push('session_id = ?'); params.push(opts.sessionId) }
|
|
222
|
+
if (opts.agentId) { conditions.push('agent_id = ?'); params.push(opts.agentId) }
|
|
223
|
+
if (opts.runId) { conditions.push('run_id = ?'); params.push(opts.runId) }
|
|
224
|
+
if (opts.category) { conditions.push('category = ?'); params.push(opts.category) }
|
|
225
|
+
if (opts.since) { conditions.push('ts >= ?'); params.push(opts.since) }
|
|
226
|
+
if (opts.until) { conditions.push('ts <= ?'); params.push(opts.until) }
|
|
227
|
+
if (opts.search) {
|
|
228
|
+
conditions.push('(summary LIKE ? OR detail LIKE ?)')
|
|
229
|
+
const pattern = `%${opts.search}%`
|
|
230
|
+
params.push(pattern, pattern)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''
|
|
234
|
+
const row = getDb()
|
|
235
|
+
.prepare(`SELECT COUNT(*) as cnt FROM execution_logs ${where}`)
|
|
236
|
+
.get(...params) as { cnt: number }
|
|
237
|
+
return row.cnt
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
// Clear
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
export function clearLogs(sessionId?: string): number {
|
|
245
|
+
if (sessionId) {
|
|
246
|
+
const result = getDb().prepare('DELETE FROM execution_logs WHERE session_id = ?').run(sessionId)
|
|
247
|
+
return result.changes
|
|
248
|
+
}
|
|
249
|
+
const result = getDb().prepare('DELETE FROM execution_logs').run()
|
|
250
|
+
return result.changes
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function clearLogsByAge(maxAgeMs: number): number {
|
|
254
|
+
const cutoff = Date.now() - maxAgeMs
|
|
255
|
+
const result = getDb().prepare('DELETE FROM execution_logs WHERE ts < ?').run(cutoff)
|
|
256
|
+
return result.changes
|
|
257
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { test } from 'node:test'
|
|
3
|
+
import {
|
|
4
|
+
createGatewayRequestFrame,
|
|
5
|
+
parseGatewayFrame,
|
|
6
|
+
serializeGatewayFrame,
|
|
7
|
+
} from './protocol.ts'
|
|
8
|
+
|
|
9
|
+
test('gateway protocol parses request/response/event frames', () => {
|
|
10
|
+
const req = parseGatewayFrame('{"type":"req","id":"1","method":"connect","params":{"foo":"bar"}}')
|
|
11
|
+
assert.deepEqual(req, {
|
|
12
|
+
type: 'req',
|
|
13
|
+
id: '1',
|
|
14
|
+
method: 'connect',
|
|
15
|
+
params: { foo: 'bar' },
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const res = parseGatewayFrame('{"type":"res","id":"1","ok":true,"payload":{"ok":true}}')
|
|
19
|
+
assert.deepEqual(res, {
|
|
20
|
+
type: 'res',
|
|
21
|
+
id: '1',
|
|
22
|
+
ok: true,
|
|
23
|
+
payload: { ok: true },
|
|
24
|
+
error: null,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const event = parseGatewayFrame('{"type":"event","event":"tick","payload":{"n":1}}')
|
|
28
|
+
assert.deepEqual(event, {
|
|
29
|
+
type: 'event',
|
|
30
|
+
event: 'tick',
|
|
31
|
+
payload: { n: 1 },
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('gateway protocol rejects malformed frames', () => {
|
|
36
|
+
assert.equal(parseGatewayFrame('not-json'), null)
|
|
37
|
+
assert.equal(parseGatewayFrame('{"type":"event"}'), null)
|
|
38
|
+
assert.equal(parseGatewayFrame('{"type":"req","id":"1"}'), null)
|
|
39
|
+
assert.equal(parseGatewayFrame({ nope: true }), null)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('gateway request helper and serializer produce valid frame', () => {
|
|
43
|
+
const frame = createGatewayRequestFrame('abc', 'chat.send', { message: 'hello' })
|
|
44
|
+
assert.deepEqual(frame, {
|
|
45
|
+
type: 'req',
|
|
46
|
+
id: 'abc',
|
|
47
|
+
method: 'chat.send',
|
|
48
|
+
params: { message: 'hello' },
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const encoded = serializeGatewayFrame(frame)
|
|
52
|
+
const decoded = parseGatewayFrame(encoded)
|
|
53
|
+
assert.deepEqual(decoded, frame)
|
|
54
|
+
})
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export interface GatewayRequestFrame {
|
|
2
|
+
type: 'req'
|
|
3
|
+
id: string
|
|
4
|
+
method: string
|
|
5
|
+
params?: Record<string, unknown>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface GatewayResponseFrame {
|
|
9
|
+
type: 'res'
|
|
10
|
+
id: string
|
|
11
|
+
ok: boolean
|
|
12
|
+
payload?: unknown
|
|
13
|
+
error?: {
|
|
14
|
+
code?: string
|
|
15
|
+
message?: string
|
|
16
|
+
} | null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface GatewayEventFrame {
|
|
20
|
+
type: 'event'
|
|
21
|
+
event: string
|
|
22
|
+
payload?: unknown
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type GatewayFrame = GatewayRequestFrame | GatewayResponseFrame | GatewayEventFrame
|
|
26
|
+
|
|
27
|
+
function toJsonString(raw: unknown): string | null {
|
|
28
|
+
if (typeof raw === 'string') return raw
|
|
29
|
+
if (raw instanceof Uint8Array) return Buffer.from(raw).toString('utf8')
|
|
30
|
+
if (Buffer.isBuffer(raw)) return raw.toString('utf8')
|
|
31
|
+
if (raw && typeof raw === 'object' && 'toString' in raw) {
|
|
32
|
+
try {
|
|
33
|
+
return String((raw as { toString: () => string }).toString())
|
|
34
|
+
} catch {
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
42
|
+
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parseFrameObject(value: unknown): GatewayFrame | null {
|
|
46
|
+
if (!isRecord(value)) return null
|
|
47
|
+
const type = typeof value.type === 'string' ? value.type : ''
|
|
48
|
+
if (type === 'req') {
|
|
49
|
+
const id = typeof value.id === 'string' ? value.id : ''
|
|
50
|
+
const method = typeof value.method === 'string' ? value.method : ''
|
|
51
|
+
if (!id || !method) return null
|
|
52
|
+
const params = isRecord(value.params) ? value.params : undefined
|
|
53
|
+
return { type: 'req', id, method, params }
|
|
54
|
+
}
|
|
55
|
+
if (type === 'res') {
|
|
56
|
+
const id = typeof value.id === 'string' ? value.id : ''
|
|
57
|
+
const ok = value.ok === true
|
|
58
|
+
if (!id) return null
|
|
59
|
+
const error = isRecord(value.error)
|
|
60
|
+
? {
|
|
61
|
+
code: typeof value.error.code === 'string' ? value.error.code : undefined,
|
|
62
|
+
message: typeof value.error.message === 'string' ? value.error.message : undefined,
|
|
63
|
+
}
|
|
64
|
+
: null
|
|
65
|
+
return {
|
|
66
|
+
type: 'res',
|
|
67
|
+
id,
|
|
68
|
+
ok,
|
|
69
|
+
payload: value.payload,
|
|
70
|
+
error,
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (type === 'event') {
|
|
74
|
+
const event = typeof value.event === 'string' ? value.event : ''
|
|
75
|
+
if (!event) return null
|
|
76
|
+
return {
|
|
77
|
+
type: 'event',
|
|
78
|
+
event,
|
|
79
|
+
payload: value.payload,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function parseGatewayFrame(raw: unknown): GatewayFrame | null {
|
|
86
|
+
if (isRecord(raw) && typeof raw.type === 'string') {
|
|
87
|
+
return parseFrameObject(raw)
|
|
88
|
+
}
|
|
89
|
+
const text = toJsonString(raw)
|
|
90
|
+
if (!text) return null
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(text)
|
|
93
|
+
return parseFrameObject(parsed)
|
|
94
|
+
} catch {
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function serializeGatewayFrame(frame: GatewayFrame): string {
|
|
100
|
+
return JSON.stringify(frame)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function createGatewayRequestFrame(
|
|
104
|
+
id: string,
|
|
105
|
+
method: string,
|
|
106
|
+
params?: Record<string, unknown>,
|
|
107
|
+
): GatewayRequestFrame {
|
|
108
|
+
return {
|
|
109
|
+
type: 'req',
|
|
110
|
+
id,
|
|
111
|
+
method,
|
|
112
|
+
params,
|
|
113
|
+
}
|
|
114
|
+
}
|