freddie 0.0.47 → 0.0.49
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/AGENTS.md +12 -0
- package/CHANGELOG.md +26 -2
- package/package.json +3 -2
- package/plugins/ansi_strip/handler.js +7 -0
- package/plugins/ansi_strip/plugin.js +2 -0
- package/plugins/approval/handler.js +13 -0
- package/plugins/approval/plugin.js +2 -0
- package/plugins/bash/handler.js +33 -0
- package/plugins/bash/plugin.js +2 -0
- package/plugins/binary_extensions/handler.js +20 -0
- package/plugins/binary_extensions/plugin.js +2 -0
- package/plugins/browser/handler.js +46 -0
- package/plugins/browser/plugin.js +2 -0
- package/plugins/budget_config/handler.js +12 -0
- package/plugins/budget_config/plugin.js +2 -0
- package/plugins/checkpoint/handler.js +27 -0
- package/plugins/checkpoint/plugin.js +2 -0
- package/plugins/clarify/handler.js +13 -0
- package/plugins/clarify/plugin.js +2 -0
- package/plugins/code_execution/handler.js +25 -0
- package/plugins/code_execution/plugin.js +2 -0
- package/plugins/core-agent-machine/plugin.js +8 -0
- package/plugins/core-cli/plugin.js +83 -0
- package/plugins/core-commands/plugin.js +7 -0
- package/plugins/core-compressor/plugin.js +15 -0
- package/plugins/core-context-engine/plugin.js +7 -0
- package/plugins/core-cron/plugin.js +7 -0
- package/plugins/core-skills/plugin.js +7 -0
- package/plugins/credential_files/handler.js +14 -0
- package/plugins/credential_files/plugin.js +2 -0
- package/plugins/cronjob/handler.js +14 -0
- package/plugins/cronjob/plugin.js +2 -0
- package/plugins/debug_helpers/handler.js +8 -0
- package/plugins/debug_helpers/plugin.js +2 -0
- package/plugins/delegate/handler.js +27 -0
- package/plugins/delegate/plugin.js +2 -0
- package/plugins/discord_tool/handler.js +12 -0
- package/plugins/discord_tool/plugin.js +2 -0
- package/plugins/edit/handler.js +29 -0
- package/plugins/edit/plugin.js +2 -0
- package/plugins/env_passthrough/handler.js +14 -0
- package/plugins/env_passthrough/plugin.js +2 -0
- package/plugins/feishu_doc/handler.js +14 -0
- package/plugins/feishu_doc/plugin.js +2 -0
- package/plugins/feishu_drive/handler.js +13 -0
- package/plugins/feishu_drive/plugin.js +2 -0
- package/plugins/file_operations/handler.js +15 -0
- package/plugins/file_operations/plugin.js +2 -0
- package/plugins/file_state/handler.js +14 -0
- package/plugins/file_state/plugin.js +2 -0
- package/plugins/file_tools/handler.js +21 -0
- package/plugins/file_tools/plugin.js +2 -0
- package/plugins/fuzzy_match/handler.js +7 -0
- package/plugins/fuzzy_match/plugin.js +2 -0
- package/plugins/gm-cc/plugin.js +28 -0
- package/plugins/grep/handler.js +49 -0
- package/plugins/grep/plugin.js +2 -0
- package/plugins/gui-agents/plugin.js +26 -0
- package/plugins/gui-batch/plugin.js +11 -0
- package/plugins/gui-chat/plugin.js +21 -0
- package/plugins/gui-config/plugin.js +12 -0
- package/plugins/gui-cron/plugin.js +13 -0
- package/plugins/gui-debug/plugin.js +24 -0
- package/plugins/gui-env/plugin.js +7 -0
- package/plugins/gui-gateway/plugin.js +9 -0
- package/plugins/gui-profiles-commands-health/plugin.js +11 -0
- package/plugins/gui-sessions/plugin.js +9 -0
- package/plugins/gui-skills/plugin.js +8 -0
- package/plugins/gui-tools/plugin.js +7 -0
- package/plugins/homeassistant_tool/handler.js +14 -0
- package/plugins/homeassistant_tool/plugin.js +2 -0
- package/plugins/image_gen/handler.js +31 -0
- package/plugins/image_gen/plugin.js +2 -0
- package/plugins/interrupt/handler.js +16 -0
- package/plugins/interrupt/plugin.js +2 -0
- package/plugins/managed_tool_gateway/handler.js +9 -0
- package/plugins/managed_tool_gateway/plugin.js +2 -0
- package/plugins/mcp_oauth/handler.js +20 -0
- package/plugins/mcp_oauth/plugin.js +2 -0
- package/plugins/mcp_oauth_manager/handler.js +18 -0
- package/plugins/mcp_oauth_manager/plugin.js +2 -0
- package/plugins/mcp_tool/handler.js +34 -0
- package/plugins/mcp_tool/plugin.js +2 -0
- package/plugins/memory/handler.js +66 -0
- package/plugins/memory/plugin.js +2 -0
- package/plugins/memory-byterover/handler.js +25 -0
- package/plugins/memory-byterover/plugin.js +2 -0
- package/plugins/memory-hindsight/handler.js +25 -0
- package/plugins/memory-hindsight/plugin.js +2 -0
- package/plugins/memory-holographic/handler.js +31 -0
- package/plugins/memory-holographic/plugin.js +2 -0
- package/plugins/memory-honcho/handler.js +25 -0
- package/plugins/memory-honcho/plugin.js +2 -0
- package/plugins/memory-mem0/handler.js +25 -0
- package/plugins/memory-mem0/plugin.js +2 -0
- package/plugins/memory-openviking/handler.js +25 -0
- package/plugins/memory-openviking/plugin.js +2 -0
- package/plugins/memory-retaindb/handler.js +25 -0
- package/plugins/memory-retaindb/plugin.js +2 -0
- package/plugins/memory-supermemory/handler.js +25 -0
- package/plugins/memory-supermemory/plugin.js +2 -0
- package/plugins/mixture_of_agents/handler.js +13 -0
- package/plugins/mixture_of_agents/plugin.js +2 -0
- package/plugins/neutts_synth/handler.js +12 -0
- package/plugins/neutts_synth/plugin.js +2 -0
- package/plugins/openrouter_client/handler.js +12 -0
- package/plugins/openrouter_client/plugin.js +2 -0
- package/plugins/osv_check/handler.js +10 -0
- package/plugins/osv_check/plugin.js +2 -0
- package/plugins/patch_parser/handler.js +40 -0
- package/plugins/patch_parser/plugin.js +2 -0
- package/plugins/path_security/handler.js +14 -0
- package/plugins/path_security/plugin.js +2 -0
- package/plugins/platform-api_server/handler.js +21 -0
- package/plugins/platform-api_server/plugin.js +2 -0
- package/plugins/platform-bluebubbles/handler.js +32 -0
- package/plugins/platform-bluebubbles/plugin.js +2 -0
- package/plugins/platform-dingtalk/handler.js +32 -0
- package/plugins/platform-dingtalk/plugin.js +2 -0
- package/plugins/platform-discord/handler.js +24 -0
- package/plugins/platform-discord/plugin.js +2 -0
- package/plugins/platform-email/handler.js +51 -0
- package/plugins/platform-email/plugin.js +2 -0
- package/plugins/platform-feishu/handler.js +32 -0
- package/plugins/platform-feishu/plugin.js +2 -0
- package/plugins/platform-feishu_comment/handler.js +12 -0
- package/plugins/platform-feishu_comment/plugin.js +2 -0
- package/plugins/platform-feishu_comment_rules/handler.js +11 -0
- package/plugins/platform-feishu_comment_rules/plugin.js +2 -0
- package/plugins/platform-homeassistant/handler.js +32 -0
- package/plugins/platform-homeassistant/plugin.js +2 -0
- package/plugins/platform-matrix/handler.js +40 -0
- package/plugins/platform-matrix/plugin.js +2 -0
- package/plugins/platform-mattermost/handler.js +29 -0
- package/plugins/platform-mattermost/plugin.js +2 -0
- package/plugins/platform-qqbot/handler.js +32 -0
- package/plugins/platform-qqbot/plugin.js +2 -0
- package/plugins/platform-signal/handler.js +33 -0
- package/plugins/platform-signal/plugin.js +2 -0
- package/plugins/platform-slack/handler.js +34 -0
- package/plugins/platform-slack/plugin.js +2 -0
- package/plugins/platform-sms/handler.js +34 -0
- package/plugins/platform-sms/plugin.js +2 -0
- package/plugins/platform-telegram/handler.js +38 -0
- package/plugins/platform-telegram/plugin.js +2 -0
- package/plugins/platform-telegram_network/handler.js +17 -0
- package/plugins/platform-telegram_network/plugin.js +2 -0
- package/plugins/platform-webhook/handler.js +19 -0
- package/plugins/platform-webhook/plugin.js +2 -0
- package/plugins/platform-wecom/handler.js +32 -0
- package/plugins/platform-wecom/plugin.js +2 -0
- package/plugins/platform-wecom_callback/handler.js +15 -0
- package/plugins/platform-wecom_callback/plugin.js +2 -0
- package/plugins/platform-wecom_crypto/handler.js +16 -0
- package/plugins/platform-wecom_crypto/plugin.js +2 -0
- package/plugins/platform-weixin/handler.js +32 -0
- package/plugins/platform-weixin/plugin.js +2 -0
- package/plugins/platform-whatsapp/handler.js +40 -0
- package/plugins/platform-whatsapp/plugin.js +2 -0
- package/plugins/platform-yuanbao/handler.js +9 -0
- package/plugins/platform-yuanbao/plugin.js +2 -0
- package/plugins/platform-yuanbao_media/handler.js +5 -0
- package/plugins/platform-yuanbao_media/plugin.js +2 -0
- package/plugins/platform-yuanbao_proto/handler.js +9 -0
- package/plugins/platform-yuanbao_proto/plugin.js +2 -0
- package/plugins/platform-yuanbao_sticker/handler.js +6 -0
- package/plugins/platform-yuanbao_sticker/plugin.js +2 -0
- package/plugins/process_registry/handler.js +15 -0
- package/plugins/process_registry/plugin.js +2 -0
- package/plugins/read/handler.js +24 -0
- package/plugins/read/plugin.js +2 -0
- package/plugins/rl_training/handler.js +12 -0
- package/plugins/rl_training/plugin.js +2 -0
- package/plugins/schema_sanitizer/handler.js +17 -0
- package/plugins/schema_sanitizer/plugin.js +2 -0
- package/plugins/send_message/handler.js +30 -0
- package/plugins/send_message/plugin.js +2 -0
- package/plugins/session_search/handler.js +21 -0
- package/plugins/session_search/plugin.js +2 -0
- package/plugins/skill_manager/handler.js +16 -0
- package/plugins/skill_manager/plugin.js +2 -0
- package/plugins/skill_usage/handler.js +18 -0
- package/plugins/skill_usage/plugin.js +2 -0
- package/plugins/skills_guard/handler.js +16 -0
- package/plugins/skills_guard/plugin.js +2 -0
- package/plugins/skills_hub/handler.js +29 -0
- package/plugins/skills_hub/plugin.js +2 -0
- package/plugins/skills_index/handler.js +12 -0
- package/plugins/skills_index/plugin.js +2 -0
- package/plugins/skills_sync/handler.js +17 -0
- package/plugins/skills_sync/plugin.js +2 -0
- package/plugins/skills_tool/handler.js +9 -0
- package/plugins/skills_tool/plugin.js +2 -0
- package/plugins/slash_confirm/handler.js +14 -0
- package/plugins/slash_confirm/plugin.js +2 -0
- package/plugins/terminal/handler.js +27 -0
- package/plugins/terminal/plugin.js +2 -0
- package/plugins/tirith_security/handler.js +23 -0
- package/plugins/tirith_security/plugin.js +2 -0
- package/plugins/todo/handler.js +52 -0
- package/plugins/todo/plugin.js +2 -0
- package/plugins/tool_backend_helpers/handler.js +24 -0
- package/plugins/tool_backend_helpers/plugin.js +2 -0
- package/plugins/tool_output_limits/handler.js +14 -0
- package/plugins/tool_output_limits/plugin.js +2 -0
- package/plugins/tool_result_storage/handler.js +18 -0
- package/plugins/tool_result_storage/plugin.js +2 -0
- package/plugins/transcription/handler.js +18 -0
- package/plugins/transcription/plugin.js +2 -0
- package/plugins/tts/handler.js +18 -0
- package/plugins/tts/plugin.js +2 -0
- package/plugins/url_safety/handler.js +14 -0
- package/plugins/url_safety/plugin.js +2 -0
- package/plugins/vision/handler.js +17 -0
- package/plugins/vision/plugin.js +2 -0
- package/plugins/voice_mode/handler.js +9 -0
- package/plugins/voice_mode/plugin.js +2 -0
- package/plugins/web_search/handler.js +35 -0
- package/plugins/web_search/plugin.js +2 -0
- package/plugins/web_tools/handler.js +17 -0
- package/plugins/web_tools/plugin.js +2 -0
- package/plugins/website_policy/handler.js +13 -0
- package/plugins/website_policy/plugin.js +2 -0
- package/plugins/write/handler.js +23 -0
- package/plugins/write/plugin.js +2 -0
- package/plugins/xai_http/handler.js +12 -0
- package/plugins/xai_http/plugin.js +2 -0
- package/plugins/yuanbao_tools/handler.js +12 -0
- package/plugins/yuanbao_tools/plugin.js +2 -0
- package/src/sessions.js +2 -1
- package/src/web/app.js +17 -0
- package/src/web/index.html +7 -3
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { db } from '../../src/db.js'
|
|
2
|
+
import { getConfigValue } from '../../src/config.js'
|
|
3
|
+
import { createMemoryProvider } from '../../src/plugins/memory/provider.js'
|
|
4
|
+
|
|
5
|
+
async function init() {
|
|
6
|
+
const d = await db()
|
|
7
|
+
await d.exec(`CREATE TABLE IF NOT EXISTS memory_local (id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT NOT NULL, ts INTEGER NOT NULL)`)
|
|
8
|
+
if (!d._fts5_unavailable) {
|
|
9
|
+
try { await d.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS memory_local_fts USING fts5(content, content='memory_local', content_rowid='id')`) } catch (e) { d._fts5_unavailable = true }
|
|
10
|
+
try { await d.exec(`CREATE TRIGGER IF NOT EXISTS memory_local_ai AFTER INSERT ON memory_local BEGIN INSERT INTO memory_local_fts(rowid, content) VALUES (new.id, new.content); END`) } catch (e) { d._fts5_unavailable = true }
|
|
11
|
+
}
|
|
12
|
+
return d
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function provider() {
|
|
16
|
+
const name = getConfigValue('memory.provider')
|
|
17
|
+
if (!name) return null
|
|
18
|
+
try { return createMemoryProvider(name, {}) } catch { return null }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const ACTIONS = {
|
|
22
|
+
add: async ({ content }) => {
|
|
23
|
+
if (!content) return { error: 'content required' }
|
|
24
|
+
const p = provider()
|
|
25
|
+
if (p) { await p.syncTurn([{ role: 'note', content }]); return { stored: 'remote' } }
|
|
26
|
+
const d = await init()
|
|
27
|
+
const info = await d.prepare(`INSERT INTO memory_local (content, ts) VALUES (?, ?)`).run(content, Date.now())
|
|
28
|
+
return { id: Number(info.lastInsertRowid), stored: 'local' }
|
|
29
|
+
},
|
|
30
|
+
search: async ({ query, limit = 10 }) => {
|
|
31
|
+
const p = provider()
|
|
32
|
+
if (p) return await p.prefetch(query)
|
|
33
|
+
const d = await init()
|
|
34
|
+
const likePattern = `%${query}%`
|
|
35
|
+
if (d._fts5_unavailable) {
|
|
36
|
+
const rows = await d.prepare(`SELECT id, content, ts FROM memory_local WHERE content LIKE ? ORDER BY ts DESC LIMIT ?`).all(likePattern, limit)
|
|
37
|
+
return { items: rows }
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const rows = await d.prepare(`SELECT m.id, m.content, m.ts FROM memory_local_fts f JOIN memory_local m ON m.id = f.rowid WHERE memory_local_fts MATCH ? ORDER BY rank LIMIT ?`).all(query, limit)
|
|
41
|
+
return { items: rows }
|
|
42
|
+
} catch {
|
|
43
|
+
const rows = await d.prepare(`SELECT id, content, ts FROM memory_local WHERE content LIKE ? ORDER BY ts DESC LIMIT ?`).all(likePattern, limit)
|
|
44
|
+
return { items: rows }
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
list: async () => {
|
|
48
|
+
const d = await init()
|
|
49
|
+
return { items: await d.prepare(`SELECT id, content, ts FROM memory_local ORDER BY id DESC LIMIT 50`).all() }
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const _tool = ({
|
|
54
|
+
name: 'memory',
|
|
55
|
+
toolset: 'core',
|
|
56
|
+
schema: {
|
|
57
|
+
name: 'memory',
|
|
58
|
+
description: 'Add/search/list memory. Routes to configured provider or local SQLite fallback.',
|
|
59
|
+
parameters: { type: 'object', properties: { action: { type: 'string', enum: Object.keys(ACTIONS) }, content: { type: 'string' }, query: { type: 'string' }, limit: { type: 'number' } }, required: ['action'] },
|
|
60
|
+
},
|
|
61
|
+
handler: async (args) => {
|
|
62
|
+
const fn = ACTIONS[args.action]
|
|
63
|
+
if (!fn) return { error: 'unknown action: ' + args.action }
|
|
64
|
+
return await fn(args)
|
|
65
|
+
},
|
|
66
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class ByteroverMemory {
|
|
2
|
+
constructor(opts = {}) {
|
|
3
|
+
this.name = 'byterover'
|
|
4
|
+
this.apiKey = opts.apiKey || process.env.BYTEROVER_API_KEY
|
|
5
|
+
this.base = opts.base || "https://api.byterover.com"
|
|
6
|
+
this.userId = opts.userId || 'default'
|
|
7
|
+
}
|
|
8
|
+
getRequiredEnv() { return ["BYTEROVER_API_KEY"] }
|
|
9
|
+
_headers() {
|
|
10
|
+
if (!this.apiKey) throw new Error('ByteroverMemory: BYTEROVER_API_KEY required')
|
|
11
|
+
return { authorization: `Bearer ${this.apiKey}`, 'content-type': 'application/json' }
|
|
12
|
+
}
|
|
13
|
+
async syncTurn(messages) {
|
|
14
|
+
const res = await fetch(`${this.base}/memories`, { method: 'POST', headers: this._headers(), body: JSON.stringify({ user_id: this.userId, messages }) })
|
|
15
|
+
return { status: res.status, ok: res.ok }
|
|
16
|
+
}
|
|
17
|
+
async prefetch(query) {
|
|
18
|
+
const url = `${this.base}/memories/search?query=${encodeURIComponent(query || '')}&user_id=${encodeURIComponent(this.userId)}`
|
|
19
|
+
const res = await fetch(url, { headers: this._headers() })
|
|
20
|
+
if (!res.ok) return { items: [], status: res.status }
|
|
21
|
+
return { items: await res.json() }
|
|
22
|
+
}
|
|
23
|
+
async shutdown() {}
|
|
24
|
+
async postSetup() {}
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class HindsightMemory {
|
|
2
|
+
constructor(opts = {}) {
|
|
3
|
+
this.name = 'hindsight'
|
|
4
|
+
this.apiKey = opts.apiKey || process.env.HINDSIGHT_API_KEY
|
|
5
|
+
this.base = opts.base || "https://api.hindsightai.com"
|
|
6
|
+
this.userId = opts.userId || 'default'
|
|
7
|
+
}
|
|
8
|
+
getRequiredEnv() { return ["HINDSIGHT_API_KEY"] }
|
|
9
|
+
_headers() {
|
|
10
|
+
if (!this.apiKey) throw new Error('HindsightMemory: HINDSIGHT_API_KEY required')
|
|
11
|
+
return { authorization: `Bearer ${this.apiKey}`, 'content-type': 'application/json' }
|
|
12
|
+
}
|
|
13
|
+
async syncTurn(messages) {
|
|
14
|
+
const res = await fetch(`${this.base}/memories`, { method: 'POST', headers: this._headers(), body: JSON.stringify({ user_id: this.userId, messages }) })
|
|
15
|
+
return { status: res.status, ok: res.ok }
|
|
16
|
+
}
|
|
17
|
+
async prefetch(query) {
|
|
18
|
+
const url = `${this.base}/memories/search?query=${encodeURIComponent(query || '')}&user_id=${encodeURIComponent(this.userId)}`
|
|
19
|
+
const res = await fetch(url, { headers: this._headers() })
|
|
20
|
+
if (!res.ok) return { items: [], status: res.status }
|
|
21
|
+
return { items: await res.json() }
|
|
22
|
+
}
|
|
23
|
+
async shutdown() {}
|
|
24
|
+
async postSetup() {}
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { getFreddieHome } from '../../src/plugins/../home.js'
|
|
4
|
+
|
|
5
|
+
export class HolographicMemory {
|
|
6
|
+
constructor(opts = {}) {
|
|
7
|
+
this.name = 'holographic'
|
|
8
|
+
this.dir = opts.dir || path.join(getFreddieHome(), 'memory', 'holographic')
|
|
9
|
+
fs.mkdirSync(this.dir, { recursive: true })
|
|
10
|
+
}
|
|
11
|
+
getRequiredEnv() { return [] }
|
|
12
|
+
async syncTurn(messages) {
|
|
13
|
+
const file = path.join(this.dir, Date.now() + '.json')
|
|
14
|
+
fs.writeFileSync(file, JSON.stringify({ ts: Date.now(), messages }), 'utf8')
|
|
15
|
+
return { stored: file }
|
|
16
|
+
}
|
|
17
|
+
async prefetch(query) {
|
|
18
|
+
const files = fs.readdirSync(this.dir).filter(f => f.endsWith('.json')).sort().slice(-20)
|
|
19
|
+
const items = []
|
|
20
|
+
for (const f of files) {
|
|
21
|
+
try {
|
|
22
|
+
const data = JSON.parse(fs.readFileSync(path.join(this.dir, f), 'utf8'))
|
|
23
|
+
const last = data.messages?.slice(-2).map(m => m.content).join(' ') || ''
|
|
24
|
+
if (!query || last.toLowerCase().includes(String(query).toLowerCase())) items.push({ file: f, summary: last.slice(0, 200) })
|
|
25
|
+
} catch {}
|
|
26
|
+
}
|
|
27
|
+
return { items }
|
|
28
|
+
}
|
|
29
|
+
async shutdown() {}
|
|
30
|
+
async postSetup() {}
|
|
31
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class HonchoMemory {
|
|
2
|
+
constructor(opts = {}) {
|
|
3
|
+
this.name = 'honcho'
|
|
4
|
+
this.apiKey = opts.apiKey || process.env.HONCHO_API_KEY
|
|
5
|
+
this.base = opts.base || "https://api.honcho.dev"
|
|
6
|
+
this.userId = opts.userId || 'default'
|
|
7
|
+
}
|
|
8
|
+
getRequiredEnv() { return ["HONCHO_API_KEY"] }
|
|
9
|
+
_headers() {
|
|
10
|
+
if (!this.apiKey) throw new Error('HonchoMemory: HONCHO_API_KEY required')
|
|
11
|
+
return { authorization: `Bearer ${this.apiKey}`, 'content-type': 'application/json' }
|
|
12
|
+
}
|
|
13
|
+
async syncTurn(messages) {
|
|
14
|
+
const res = await fetch(`${this.base}/memories`, { method: 'POST', headers: this._headers(), body: JSON.stringify({ user_id: this.userId, messages }) })
|
|
15
|
+
return { status: res.status, ok: res.ok }
|
|
16
|
+
}
|
|
17
|
+
async prefetch(query) {
|
|
18
|
+
const url = `${this.base}/memories/search?query=${encodeURIComponent(query || '')}&user_id=${encodeURIComponent(this.userId)}`
|
|
19
|
+
const res = await fetch(url, { headers: this._headers() })
|
|
20
|
+
if (!res.ok) return { items: [], status: res.status }
|
|
21
|
+
return { items: await res.json() }
|
|
22
|
+
}
|
|
23
|
+
async shutdown() {}
|
|
24
|
+
async postSetup() {}
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class Mem0Memory {
|
|
2
|
+
constructor(opts = {}) {
|
|
3
|
+
this.name = 'mem0'
|
|
4
|
+
this.apiKey = opts.apiKey || process.env.MEM0_API_KEY
|
|
5
|
+
this.base = opts.base || "https://api.mem0.ai/v1"
|
|
6
|
+
this.userId = opts.userId || 'default'
|
|
7
|
+
}
|
|
8
|
+
getRequiredEnv() { return ["MEM0_API_KEY"] }
|
|
9
|
+
_headers() {
|
|
10
|
+
if (!this.apiKey) throw new Error('Mem0Memory: MEM0_API_KEY required')
|
|
11
|
+
return { authorization: `Bearer ${this.apiKey}`, 'content-type': 'application/json' }
|
|
12
|
+
}
|
|
13
|
+
async syncTurn(messages) {
|
|
14
|
+
const res = await fetch(`${this.base}/memories`, { method: 'POST', headers: this._headers(), body: JSON.stringify({ user_id: this.userId, messages }) })
|
|
15
|
+
return { status: res.status, ok: res.ok }
|
|
16
|
+
}
|
|
17
|
+
async prefetch(query) {
|
|
18
|
+
const url = `${this.base}/memories/search?query=${encodeURIComponent(query || '')}&user_id=${encodeURIComponent(this.userId)}`
|
|
19
|
+
const res = await fetch(url, { headers: this._headers() })
|
|
20
|
+
if (!res.ok) return { items: [], status: res.status }
|
|
21
|
+
return { items: await res.json() }
|
|
22
|
+
}
|
|
23
|
+
async shutdown() {}
|
|
24
|
+
async postSetup() {}
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class OpenvikingMemory {
|
|
2
|
+
constructor(opts = {}) {
|
|
3
|
+
this.name = 'openviking'
|
|
4
|
+
this.apiKey = opts.apiKey || process.env.OPENVIKING_API_KEY
|
|
5
|
+
this.base = opts.base || "https://api.openviking.com"
|
|
6
|
+
this.userId = opts.userId || 'default'
|
|
7
|
+
}
|
|
8
|
+
getRequiredEnv() { return ["OPENVIKING_API_KEY"] }
|
|
9
|
+
_headers() {
|
|
10
|
+
if (!this.apiKey) throw new Error('OpenvikingMemory: OPENVIKING_API_KEY required')
|
|
11
|
+
return { authorization: `Bearer ${this.apiKey}`, 'content-type': 'application/json' }
|
|
12
|
+
}
|
|
13
|
+
async syncTurn(messages) {
|
|
14
|
+
const res = await fetch(`${this.base}/memories`, { method: 'POST', headers: this._headers(), body: JSON.stringify({ user_id: this.userId, messages }) })
|
|
15
|
+
return { status: res.status, ok: res.ok }
|
|
16
|
+
}
|
|
17
|
+
async prefetch(query) {
|
|
18
|
+
const url = `${this.base}/memories/search?query=${encodeURIComponent(query || '')}&user_id=${encodeURIComponent(this.userId)}`
|
|
19
|
+
const res = await fetch(url, { headers: this._headers() })
|
|
20
|
+
if (!res.ok) return { items: [], status: res.status }
|
|
21
|
+
return { items: await res.json() }
|
|
22
|
+
}
|
|
23
|
+
async shutdown() {}
|
|
24
|
+
async postSetup() {}
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class RetaindbMemory {
|
|
2
|
+
constructor(opts = {}) {
|
|
3
|
+
this.name = 'retaindb'
|
|
4
|
+
this.apiKey = opts.apiKey || process.env.RETAINDB_API_KEY
|
|
5
|
+
this.base = opts.base || "https://api.retaindb.com"
|
|
6
|
+
this.userId = opts.userId || 'default'
|
|
7
|
+
}
|
|
8
|
+
getRequiredEnv() { return ["RETAINDB_API_KEY"] }
|
|
9
|
+
_headers() {
|
|
10
|
+
if (!this.apiKey) throw new Error('RetaindbMemory: RETAINDB_API_KEY required')
|
|
11
|
+
return { authorization: `Bearer ${this.apiKey}`, 'content-type': 'application/json' }
|
|
12
|
+
}
|
|
13
|
+
async syncTurn(messages) {
|
|
14
|
+
const res = await fetch(`${this.base}/memories`, { method: 'POST', headers: this._headers(), body: JSON.stringify({ user_id: this.userId, messages }) })
|
|
15
|
+
return { status: res.status, ok: res.ok }
|
|
16
|
+
}
|
|
17
|
+
async prefetch(query) {
|
|
18
|
+
const url = `${this.base}/memories/search?query=${encodeURIComponent(query || '')}&user_id=${encodeURIComponent(this.userId)}`
|
|
19
|
+
const res = await fetch(url, { headers: this._headers() })
|
|
20
|
+
if (!res.ok) return { items: [], status: res.status }
|
|
21
|
+
return { items: await res.json() }
|
|
22
|
+
}
|
|
23
|
+
async shutdown() {}
|
|
24
|
+
async postSetup() {}
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class SupermemoryMemory {
|
|
2
|
+
constructor(opts = {}) {
|
|
3
|
+
this.name = 'supermemory'
|
|
4
|
+
this.apiKey = opts.apiKey || process.env.SUPERMEMORY_API_KEY
|
|
5
|
+
this.base = opts.base || "https://api.supermemory.ai/v3"
|
|
6
|
+
this.userId = opts.userId || 'default'
|
|
7
|
+
}
|
|
8
|
+
getRequiredEnv() { return ["SUPERMEMORY_API_KEY"] }
|
|
9
|
+
_headers() {
|
|
10
|
+
if (!this.apiKey) throw new Error('SupermemoryMemory: SUPERMEMORY_API_KEY required')
|
|
11
|
+
return { authorization: `Bearer ${this.apiKey}`, 'content-type': 'application/json' }
|
|
12
|
+
}
|
|
13
|
+
async syncTurn(messages) {
|
|
14
|
+
const res = await fetch(`${this.base}/memories`, { method: 'POST', headers: this._headers(), body: JSON.stringify({ user_id: this.userId, messages }) })
|
|
15
|
+
return { status: res.status, ok: res.ok }
|
|
16
|
+
}
|
|
17
|
+
async prefetch(query) {
|
|
18
|
+
const url = `${this.base}/memories/search?query=${encodeURIComponent(query || '')}&user_id=${encodeURIComponent(this.userId)}`
|
|
19
|
+
const res = await fetch(url, { headers: this._headers() })
|
|
20
|
+
if (!res.ok) return { items: [], status: res.status }
|
|
21
|
+
return { items: await res.json() }
|
|
22
|
+
}
|
|
23
|
+
async shutdown() {}
|
|
24
|
+
async postSetup() {}
|
|
25
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { runTurn } from '../../src/agent/machine.js'
|
|
2
|
+
|
|
3
|
+
export const _tool = ({
|
|
4
|
+
name: 'mixture_of_agents',
|
|
5
|
+
toolset: 'core',
|
|
6
|
+
schema: { name: 'mixture_of_agents', description: 'Run the same prompt through N sub-agents (different models or seeds), then synthesize the results. Reduces variance.', parameters: { type: 'object', properties: { prompt: { type: 'string' }, models: { type: 'array', items: { type: 'string' } }, callLLM: {} }, required: ['prompt'] } },
|
|
7
|
+
handler: async ({ prompt, models = ['default'] }, ctx = {}) => {
|
|
8
|
+
const llm = ctx.callLLM || null
|
|
9
|
+
const runs = await Promise.all(models.map(m => runTurn({ prompt, model: m, callLLM: llm, timeoutMs: 30000 }).catch(e => ({ error: String(e.message || e) }))))
|
|
10
|
+
const synthesized = runs.map(r => r.result || r.error || '').join('\n---\n')
|
|
11
|
+
return { runs: runs.length, synthesized }
|
|
12
|
+
},
|
|
13
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const _tool = ({
|
|
2
|
+
name: 'neutts_synth',
|
|
3
|
+
toolset: 'creative',
|
|
4
|
+
schema: { name: 'neutts_synth', description: 'Local NeuTTS synth (alternate TTS backend).', parameters: { type: 'object', properties: { text: { type: 'string' }, voice: { type: 'string', default: 'default' } }, required: ['text'] } },
|
|
5
|
+
requiresEnv: ['NEUTTS_URL'],
|
|
6
|
+
checkFn: () => Boolean(process.env.NEUTTS_URL),
|
|
7
|
+
handler: async ({ text, voice = 'default' }) => {
|
|
8
|
+
if (!process.env.NEUTTS_URL) return { error: 'NEUTTS_URL required' }
|
|
9
|
+
const r = await fetch(process.env.NEUTTS_URL + '/synthesize', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ text, voice }) })
|
|
10
|
+
return { status: r.status, bytes: (await r.arrayBuffer()).byteLength }
|
|
11
|
+
},
|
|
12
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const _tool = ({
|
|
2
|
+
name: 'openrouter',
|
|
3
|
+
toolset: 'core',
|
|
4
|
+
schema: { name: 'openrouter', description: 'Chat completion via OpenRouter (any model).', parameters: { type: 'object', properties: { prompt: { type: 'string' }, model: { type: 'string', default: 'anthropic/claude-sonnet-4' } }, required: ['prompt'] } },
|
|
5
|
+
requiresEnv: ['OPENROUTER_API_KEY'],
|
|
6
|
+
checkFn: () => Boolean(process.env.OPENROUTER_API_KEY),
|
|
7
|
+
handler: async ({ prompt, model = 'anthropic/claude-sonnet-4' }) => {
|
|
8
|
+
if (!process.env.OPENROUTER_API_KEY) return { error: 'OPENROUTER_API_KEY required' }
|
|
9
|
+
const r = await fetch('https://openrouter.ai/api/v1/chat/completions', { method: 'POST', headers: { authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`, 'content-type': 'application/json' }, body: JSON.stringify({ model, messages: [{ role: 'user', content: prompt }] }) })
|
|
10
|
+
return await r.json()
|
|
11
|
+
},
|
|
12
|
+
})
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const _tool = ({
|
|
2
|
+
name: 'osv_check',
|
|
3
|
+
toolset: 'core',
|
|
4
|
+
schema: { name: 'osv_check', description: 'Query osv.dev for known vulnerabilities. Pass either {package, version, ecosystem} or {commit_sha, repo}.', parameters: { type: 'object', properties: { package: { type: 'string' }, version: { type: 'string' }, ecosystem: { type: 'string' }, commit_sha: { type: 'string' } } } },
|
|
5
|
+
handler: async (args) => {
|
|
6
|
+
const body = args.commit_sha ? { commit: args.commit_sha } : { package: { name: args.package, ecosystem: args.ecosystem || 'npm' }, version: args.version }
|
|
7
|
+
const r = await fetch('https://api.osv.dev/v1/query', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) })
|
|
8
|
+
return await r.json()
|
|
9
|
+
},
|
|
10
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
export function applyUnifiedDiff(diff, { cwd = process.cwd() } = {}) {
|
|
4
|
+
const lines = diff.split('\n')
|
|
5
|
+
const results = []
|
|
6
|
+
let curFile = null, curHunks = [], curHunk = null
|
|
7
|
+
const flush = () => {
|
|
8
|
+
if (!curFile) return
|
|
9
|
+
const file = path.join(cwd, curFile)
|
|
10
|
+
if (!fs.existsSync(file)) { results.push({ file: curFile, error: 'not found' }); curFile = null; curHunks = []; return }
|
|
11
|
+
let src = fs.readFileSync(file, 'utf8').split('\n')
|
|
12
|
+
for (const h of curHunks) {
|
|
13
|
+
const before = src.slice(0, h.start)
|
|
14
|
+
const after = src.slice(h.start + h.removed)
|
|
15
|
+
src = [...before, ...h.added, ...after]
|
|
16
|
+
}
|
|
17
|
+
fs.writeFileSync(file, src.join('\n'), 'utf8')
|
|
18
|
+
results.push({ file: curFile, applied: curHunks.length })
|
|
19
|
+
curFile = null; curHunks = []
|
|
20
|
+
}
|
|
21
|
+
for (const l of lines) {
|
|
22
|
+
if (l.startsWith('--- ')) { flush(); curFile = l.slice(6).trim() }
|
|
23
|
+
else if (l.startsWith('+++ ')) {}
|
|
24
|
+
else if (l.startsWith('@@ ')) {
|
|
25
|
+
const m = l.match(/@@ -(\d+),(\d+) \+(\d+),(\d+) @@/)
|
|
26
|
+
if (m) { curHunk = { start: Number(m[1]) - 1, removed: Number(m[2]), added: [] }; curHunks.push(curHunk) }
|
|
27
|
+
} else if (curHunk) {
|
|
28
|
+
if (l.startsWith('+')) curHunk.added.push(l.slice(1))
|
|
29
|
+
else if (l.startsWith(' ')) curHunk.added.push(l.slice(1))
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
flush()
|
|
33
|
+
return results
|
|
34
|
+
}
|
|
35
|
+
export const _tool = ({
|
|
36
|
+
name: 'patch_parser',
|
|
37
|
+
toolset: 'core',
|
|
38
|
+
schema: { name: 'patch_parser', description: 'Apply a unified diff to files in cwd. Returns per-file results.', parameters: { type: 'object', properties: { diff: { type: 'string' }, cwd: { type: 'string' } }, required: ['diff'] } },
|
|
39
|
+
handler: async ({ diff, cwd }) => ({ results: applyUnifiedDiff(diff, { cwd }) }),
|
|
40
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
const FORBIDDEN = ['/etc/passwd', '/etc/shadow', '/.ssh/', '/.aws/', 'C:\\Windows\\System32']
|
|
3
|
+
export function isPathSafe(p, { cwd = process.cwd() } = {}) {
|
|
4
|
+
const abs = path.resolve(cwd, p)
|
|
5
|
+
for (const bad of FORBIDDEN) if (abs.includes(bad)) return { safe: false, reason: 'forbidden: ' + bad }
|
|
6
|
+
if (abs.includes('..')) return { safe: false, reason: 'parent reference in resolved path' }
|
|
7
|
+
return { safe: true, abs }
|
|
8
|
+
}
|
|
9
|
+
export const _tool = ({
|
|
10
|
+
name: 'path_security',
|
|
11
|
+
toolset: 'core',
|
|
12
|
+
schema: { name: 'path_security', description: 'Check whether a path is allowed (no /etc/passwd, no .ssh/, etc).', parameters: { type: 'object', properties: { path: { type: 'string' }, cwd: { type: 'string' } }, required: ['path'] } },
|
|
13
|
+
handler: async ({ path: p, cwd }) => isPathSafe(p, { cwd }),
|
|
14
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import { EventEmitter } from 'node:events'
|
|
3
|
+
|
|
4
|
+
export class ApiServerAdapter extends EventEmitter {
|
|
5
|
+
constructor({ port = 0 } = {}) { super(); this.port = port; this._server = null; this._messages = [] }
|
|
6
|
+
async start() {
|
|
7
|
+
const app = express()
|
|
8
|
+
app.use(express.json())
|
|
9
|
+
app.post('/messages', (req, res) => {
|
|
10
|
+
const m = { from: req.body?.from || 'api', text: req.body?.text || '', raw: req.body }
|
|
11
|
+
this.emit('message', m)
|
|
12
|
+
res.json({ ok: true })
|
|
13
|
+
})
|
|
14
|
+
app.get('/messages', (_, res) => res.json(this._messages))
|
|
15
|
+
await new Promise(resolve => { this._server = app.listen(this.port, () => resolve()) })
|
|
16
|
+
this.port = this._server.address().port
|
|
17
|
+
}
|
|
18
|
+
async stop() { if (this._server) await new Promise(r => this._server.close(() => r())) }
|
|
19
|
+
async send(reply) { this._messages.push(reply) }
|
|
20
|
+
drain() { const out = [...this._messages]; this._messages.length = 0; return out }
|
|
21
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import { EventEmitter } from 'node:events'
|
|
3
|
+
|
|
4
|
+
export class BluebubblesAdapter extends EventEmitter {
|
|
5
|
+
constructor(opts = {}) {
|
|
6
|
+
super()
|
|
7
|
+
this.platform = 'bluebubbles'
|
|
8
|
+
this.token = opts.token || process.env.BLUEBUBBLES_PASSWORD
|
|
9
|
+
this.port = opts.port || 0
|
|
10
|
+
this.api = opts.api || "http://localhost:1234/api/v1/message/text"
|
|
11
|
+
this._server = null
|
|
12
|
+
}
|
|
13
|
+
getRequiredEnv() { return ["BLUEBUBBLES_PASSWORD"] }
|
|
14
|
+
async start() {
|
|
15
|
+
if (!this.token) throw new Error('BluebubblesAdapter: ' + this.getRequiredEnv().join(', ') + ' required')
|
|
16
|
+
const app = express()
|
|
17
|
+
app.use(express.json())
|
|
18
|
+
app.post('/webhook', (req, res) => {
|
|
19
|
+
const text = req.body?.text || req.body?.message?.text || req.body?.content || ''
|
|
20
|
+
const from = req.body?.from || req.body?.user_id || req.body?.sender_id || ''
|
|
21
|
+
this.emit('message', { from: String(from), text, raw: req.body })
|
|
22
|
+
res.json({ ok: true })
|
|
23
|
+
})
|
|
24
|
+
await new Promise(r => { this._server = app.listen(this.port, () => r()) })
|
|
25
|
+
this.port = this._server.address().port
|
|
26
|
+
}
|
|
27
|
+
async stop() { if (this._server) await new Promise(r => this._server.close(() => r())) }
|
|
28
|
+
async send(reply) {
|
|
29
|
+
if (!this.token) throw new Error('BluebubblesAdapter: token required')
|
|
30
|
+
return fetch(this.api, { method: 'POST', headers: { authorization: `Bearer ${this.token}`, 'content-type': 'application/json' }, body: JSON.stringify({ to: reply.to, text: reply.text }) }).then(r => r.json())
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import express from 'express'
|
|
2
|
+
import { EventEmitter } from 'node:events'
|
|
3
|
+
|
|
4
|
+
export class DingtalkAdapter extends EventEmitter {
|
|
5
|
+
constructor(opts = {}) {
|
|
6
|
+
super()
|
|
7
|
+
this.platform = 'dingtalk'
|
|
8
|
+
this.token = opts.token || process.env.DINGTALK_ACCESS_TOKEN
|
|
9
|
+
this.port = opts.port || 0
|
|
10
|
+
this.api = opts.api || "https://oapi.dingtalk.com/robot/send"
|
|
11
|
+
this._server = null
|
|
12
|
+
}
|
|
13
|
+
getRequiredEnv() { return ["DINGTALK_ACCESS_TOKEN"] }
|
|
14
|
+
async start() {
|
|
15
|
+
if (!this.token) throw new Error('DingtalkAdapter: ' + this.getRequiredEnv().join(', ') + ' required')
|
|
16
|
+
const app = express()
|
|
17
|
+
app.use(express.json())
|
|
18
|
+
app.post('/webhook', (req, res) => {
|
|
19
|
+
const text = req.body?.text || req.body?.message?.text || req.body?.content || ''
|
|
20
|
+
const from = req.body?.from || req.body?.user_id || req.body?.sender_id || ''
|
|
21
|
+
this.emit('message', { from: String(from), text, raw: req.body })
|
|
22
|
+
res.json({ ok: true })
|
|
23
|
+
})
|
|
24
|
+
await new Promise(r => { this._server = app.listen(this.port, () => r()) })
|
|
25
|
+
this.port = this._server.address().port
|
|
26
|
+
}
|
|
27
|
+
async stop() { if (this._server) await new Promise(r => this._server.close(() => r())) }
|
|
28
|
+
async send(reply) {
|
|
29
|
+
if (!this.token) throw new Error('DingtalkAdapter: token required')
|
|
30
|
+
return fetch(this.api, { method: 'POST', headers: { authorization: `Bearer ${this.token}`, 'content-type': 'application/json' }, body: JSON.stringify({ to: reply.to, text: reply.text }) }).then(r => r.json())
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events'
|
|
2
|
+
|
|
3
|
+
export class DiscordAdapter extends EventEmitter {
|
|
4
|
+
constructor(opts = {}) {
|
|
5
|
+
super()
|
|
6
|
+
this.platform = 'discord'
|
|
7
|
+
this.token = opts.token || process.env.DISCORD_BOT_TOKEN
|
|
8
|
+
this.api = opts.api || 'https://discord.com/api/v10'
|
|
9
|
+
this._ws = null
|
|
10
|
+
}
|
|
11
|
+
getRequiredEnv() { return ['DISCORD_BOT_TOKEN'] }
|
|
12
|
+
async start() {
|
|
13
|
+
if (!this.token) throw new Error('DiscordAdapter: DISCORD_BOT_TOKEN required')
|
|
14
|
+
const gw = await fetch(`${this.api}/gateway/bot`, { headers: { authorization: `Bot ${this.token}` } }).then(r => r.json())
|
|
15
|
+
if (!gw.url) throw new Error('DiscordAdapter: gateway lookup failed: ' + JSON.stringify(gw))
|
|
16
|
+
this.gatewayUrl = gw.url + '/?v=10&encoding=json'
|
|
17
|
+
}
|
|
18
|
+
async stop() { try { this._ws?.close?.() } catch {} }
|
|
19
|
+
async send(reply) {
|
|
20
|
+
if (!this.token) throw new Error('DiscordAdapter: token required')
|
|
21
|
+
const url = `${this.api}/channels/${reply.to}/messages`
|
|
22
|
+
return fetch(url, { method: 'POST', headers: { authorization: `Bot ${this.token}`, 'content-type': 'application/json' }, body: JSON.stringify({ content: reply.text }) }).then(r => r.json())
|
|
23
|
+
}
|
|
24
|
+
}
|