clementine-agent 1.0.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/.env.example +44 -0
- package/LICENSE +21 -0
- package/README.md +795 -0
- package/dist/agent/agent-manager.d.ts +69 -0
- package/dist/agent/agent-manager.js +441 -0
- package/dist/agent/assistant.d.ts +225 -0
- package/dist/agent/assistant.js +3888 -0
- package/dist/agent/auto-update.d.ts +32 -0
- package/dist/agent/auto-update.js +186 -0
- package/dist/agent/daily-planner.d.ts +24 -0
- package/dist/agent/daily-planner.js +379 -0
- package/dist/agent/execution-advisor.d.ts +10 -0
- package/dist/agent/execution-advisor.js +272 -0
- package/dist/agent/hooks.d.ts +45 -0
- package/dist/agent/hooks.js +564 -0
- package/dist/agent/insight-engine.d.ts +66 -0
- package/dist/agent/insight-engine.js +225 -0
- package/dist/agent/intent-classifier.d.ts +48 -0
- package/dist/agent/intent-classifier.js +214 -0
- package/dist/agent/link-extractor.d.ts +19 -0
- package/dist/agent/link-extractor.js +90 -0
- package/dist/agent/mcp-bridge.d.ts +62 -0
- package/dist/agent/mcp-bridge.js +435 -0
- package/dist/agent/metacognition.d.ts +66 -0
- package/dist/agent/metacognition.js +221 -0
- package/dist/agent/orchestrator.d.ts +81 -0
- package/dist/agent/orchestrator.js +790 -0
- package/dist/agent/profiles.d.ts +22 -0
- package/dist/agent/profiles.js +91 -0
- package/dist/agent/prompt-cache.d.ts +24 -0
- package/dist/agent/prompt-cache.js +68 -0
- package/dist/agent/prompt-evolver.d.ts +28 -0
- package/dist/agent/prompt-evolver.js +279 -0
- package/dist/agent/role-scaffolds.d.ts +28 -0
- package/dist/agent/role-scaffolds.js +433 -0
- package/dist/agent/safe-restart.d.ts +41 -0
- package/dist/agent/safe-restart.js +150 -0
- package/dist/agent/self-improve.d.ts +66 -0
- package/dist/agent/self-improve.js +1706 -0
- package/dist/agent/session-event-log.d.ts +114 -0
- package/dist/agent/session-event-log.js +233 -0
- package/dist/agent/skill-extractor.d.ts +72 -0
- package/dist/agent/skill-extractor.js +435 -0
- package/dist/agent/source-mods.d.ts +61 -0
- package/dist/agent/source-mods.js +230 -0
- package/dist/agent/source-preflight.d.ts +25 -0
- package/dist/agent/source-preflight.js +100 -0
- package/dist/agent/stall-guard.d.ts +62 -0
- package/dist/agent/stall-guard.js +109 -0
- package/dist/agent/strategic-planner.d.ts +60 -0
- package/dist/agent/strategic-planner.js +352 -0
- package/dist/agent/team-bus.d.ts +89 -0
- package/dist/agent/team-bus.js +556 -0
- package/dist/agent/team-router.d.ts +26 -0
- package/dist/agent/team-router.js +37 -0
- package/dist/agent/tool-loop-detector.d.ts +59 -0
- package/dist/agent/tool-loop-detector.js +242 -0
- package/dist/agent/workflow-runner.d.ts +36 -0
- package/dist/agent/workflow-runner.js +317 -0
- package/dist/agent/workflow-variables.d.ts +16 -0
- package/dist/agent/workflow-variables.js +62 -0
- package/dist/channels/discord-agent-bot.d.ts +101 -0
- package/dist/channels/discord-agent-bot.js +881 -0
- package/dist/channels/discord-bot-manager.d.ts +80 -0
- package/dist/channels/discord-bot-manager.js +262 -0
- package/dist/channels/discord-utils.d.ts +51 -0
- package/dist/channels/discord-utils.js +293 -0
- package/dist/channels/discord.d.ts +12 -0
- package/dist/channels/discord.js +1832 -0
- package/dist/channels/slack-agent-bot.d.ts +73 -0
- package/dist/channels/slack-agent-bot.js +320 -0
- package/dist/channels/slack-bot-manager.d.ts +66 -0
- package/dist/channels/slack-bot-manager.js +236 -0
- package/dist/channels/slack-utils.d.ts +39 -0
- package/dist/channels/slack-utils.js +189 -0
- package/dist/channels/slack.d.ts +11 -0
- package/dist/channels/slack.js +196 -0
- package/dist/channels/telegram.d.ts +10 -0
- package/dist/channels/telegram.js +235 -0
- package/dist/channels/webhook.d.ts +9 -0
- package/dist/channels/webhook.js +78 -0
- package/dist/channels/whatsapp.d.ts +11 -0
- package/dist/channels/whatsapp.js +181 -0
- package/dist/cli/chat.d.ts +14 -0
- package/dist/cli/chat.js +220 -0
- package/dist/cli/cron.d.ts +17 -0
- package/dist/cli/cron.js +552 -0
- package/dist/cli/dashboard.d.ts +15 -0
- package/dist/cli/dashboard.js +17677 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +2474 -0
- package/dist/cli/routes/delegations.d.ts +19 -0
- package/dist/cli/routes/delegations.js +154 -0
- package/dist/cli/routes/digest.d.ts +17 -0
- package/dist/cli/routes/digest.js +375 -0
- package/dist/cli/routes/goals.d.ts +14 -0
- package/dist/cli/routes/goals.js +258 -0
- package/dist/cli/routes/workflows.d.ts +18 -0
- package/dist/cli/routes/workflows.js +97 -0
- package/dist/cli/setup.d.ts +8 -0
- package/dist/cli/setup.js +619 -0
- package/dist/cli/tunnel.d.ts +35 -0
- package/dist/cli/tunnel.js +141 -0
- package/dist/config.d.ts +145 -0
- package/dist/config.js +278 -0
- package/dist/events/bus.d.ts +43 -0
- package/dist/events/bus.js +136 -0
- package/dist/gateway/cron-scheduler.d.ts +166 -0
- package/dist/gateway/cron-scheduler.js +1767 -0
- package/dist/gateway/delivery-queue.d.ts +30 -0
- package/dist/gateway/delivery-queue.js +110 -0
- package/dist/gateway/heartbeat-scheduler.d.ts +99 -0
- package/dist/gateway/heartbeat-scheduler.js +1298 -0
- package/dist/gateway/heartbeat.d.ts +3 -0
- package/dist/gateway/heartbeat.js +3 -0
- package/dist/gateway/lanes.d.ts +24 -0
- package/dist/gateway/lanes.js +76 -0
- package/dist/gateway/notifications.d.ts +29 -0
- package/dist/gateway/notifications.js +75 -0
- package/dist/gateway/router.d.ts +210 -0
- package/dist/gateway/router.js +1330 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +1015 -0
- package/dist/memory/chunker.d.ts +28 -0
- package/dist/memory/chunker.js +226 -0
- package/dist/memory/consolidation.d.ts +44 -0
- package/dist/memory/consolidation.js +171 -0
- package/dist/memory/context-assembler.d.ts +50 -0
- package/dist/memory/context-assembler.js +149 -0
- package/dist/memory/embeddings.d.ts +38 -0
- package/dist/memory/embeddings.js +180 -0
- package/dist/memory/graph-store.d.ts +66 -0
- package/dist/memory/graph-store.js +613 -0
- package/dist/memory/mmr.d.ts +21 -0
- package/dist/memory/mmr.js +75 -0
- package/dist/memory/search.d.ts +26 -0
- package/dist/memory/search.js +67 -0
- package/dist/memory/store.d.ts +530 -0
- package/dist/memory/store.js +2022 -0
- package/dist/security/integrity.d.ts +24 -0
- package/dist/security/integrity.js +58 -0
- package/dist/security/patterns.d.ts +34 -0
- package/dist/security/patterns.js +110 -0
- package/dist/security/scanner.d.ts +32 -0
- package/dist/security/scanner.js +263 -0
- package/dist/tools/admin-tools.d.ts +12 -0
- package/dist/tools/admin-tools.js +1278 -0
- package/dist/tools/external-tools.d.ts +11 -0
- package/dist/tools/external-tools.js +1327 -0
- package/dist/tools/goal-tools.d.ts +9 -0
- package/dist/tools/goal-tools.js +159 -0
- package/dist/tools/mcp-server.d.ts +13 -0
- package/dist/tools/mcp-server.js +141 -0
- package/dist/tools/memory-tools.d.ts +10 -0
- package/dist/tools/memory-tools.js +568 -0
- package/dist/tools/session-tools.d.ts +6 -0
- package/dist/tools/session-tools.js +146 -0
- package/dist/tools/shared.d.ts +216 -0
- package/dist/tools/shared.js +340 -0
- package/dist/tools/team-tools.d.ts +6 -0
- package/dist/tools/team-tools.js +447 -0
- package/dist/tools/tool-meta.d.ts +34 -0
- package/dist/tools/tool-meta.js +133 -0
- package/dist/tools/vault-tools.d.ts +8 -0
- package/dist/tools/vault-tools.js +457 -0
- package/dist/types.d.ts +716 -0
- package/dist/types.js +16 -0
- package/dist/vault-migrations/0001-add-execution-framework.d.ts +10 -0
- package/dist/vault-migrations/0001-add-execution-framework.js +47 -0
- package/dist/vault-migrations/0002-add-agentic-communication.d.ts +12 -0
- package/dist/vault-migrations/0002-add-agentic-communication.js +79 -0
- package/dist/vault-migrations/0003-update-execution-pipeline-narration.d.ts +11 -0
- package/dist/vault-migrations/0003-update-execution-pipeline-narration.js +73 -0
- package/dist/vault-migrations/helpers.d.ts +14 -0
- package/dist/vault-migrations/helpers.js +44 -0
- package/dist/vault-migrations/runner.d.ts +14 -0
- package/dist/vault-migrations/runner.js +139 -0
- package/dist/vault-migrations/types.d.ts +42 -0
- package/dist/vault-migrations/types.js +9 -0
- package/install.sh +320 -0
- package/package.json +84 -0
- package/scripts/postinstall.js +125 -0
- package/vault/00-System/AGENTS.md +66 -0
- package/vault/00-System/CRON.md +71 -0
- package/vault/00-System/HEARTBEAT.md +58 -0
- package/vault/00-System/MEMORY.md +16 -0
- package/vault/00-System/SOUL.md +96 -0
- package/vault/05-Tasks/TASKS.md +19 -0
- package/vault/06-Templates/_Daily-Template.md +28 -0
- package/vault/06-Templates/_People-Template.md +22 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clementine TypeScript — MCP Server Bridge.
|
|
3
|
+
*
|
|
4
|
+
* Discovers external MCP servers from Claude Desktop config, Claude Code
|
|
5
|
+
* settings, and user-managed config. Merges them into the SDK mcpServers
|
|
6
|
+
* option alongside Clementine's own MCP server.
|
|
7
|
+
*/
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
10
|
+
import os from 'node:os';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import pino from 'pino';
|
|
13
|
+
import { BASE_DIR } from '../config.js';
|
|
14
|
+
const logger = pino({ name: 'clementine.mcp-bridge' });
|
|
15
|
+
const MCP_SERVERS_FILE = path.join(BASE_DIR, 'mcp-servers.json');
|
|
16
|
+
const INTEGRATIONS_FILE = path.join(BASE_DIR, 'claude-integrations.json');
|
|
17
|
+
const CACHE_TTL_MS = 60_000; // 60s cache
|
|
18
|
+
// ── Known server descriptions ───────────────────────────────────────
|
|
19
|
+
const KNOWN_DESCRIPTIONS = {
|
|
20
|
+
slack: 'Slack workspace messaging and channels',
|
|
21
|
+
linear: 'Linear issue tracking and project management',
|
|
22
|
+
notion: 'Notion workspace — pages, databases, search',
|
|
23
|
+
github: 'GitHub repositories, issues, PRs',
|
|
24
|
+
gitlab: 'GitLab repository management',
|
|
25
|
+
supabase: 'Supabase database and auth',
|
|
26
|
+
firecrawl: 'Web crawling and scraping',
|
|
27
|
+
exa: 'Neural web search',
|
|
28
|
+
playwright: 'Browser testing and automation',
|
|
29
|
+
kernel: 'Kernel browser automation',
|
|
30
|
+
context7: 'Library documentation lookup',
|
|
31
|
+
dataforseo: 'SEO data, keyword research, SERP analysis',
|
|
32
|
+
'Bright Data': 'Web scraping and data collection',
|
|
33
|
+
browsermcp: 'Browser automation via MCP',
|
|
34
|
+
ElevenLabs: 'Voice synthesis, text-to-speech, audio AI',
|
|
35
|
+
apify: 'Web scraping actors and automation',
|
|
36
|
+
vapi: 'Voice AI phone calls and assistants',
|
|
37
|
+
greptile: 'Codebase search and understanding',
|
|
38
|
+
terraform: 'Infrastructure as code management',
|
|
39
|
+
discord: 'Discord bot integration',
|
|
40
|
+
imessage: 'iMessage — read and send messages on macOS',
|
|
41
|
+
figma: 'Figma design files — read, inspect, and export',
|
|
42
|
+
};
|
|
43
|
+
// ── Cache ────────────────────────────────────────────────────────────
|
|
44
|
+
let _cachedServers = null;
|
|
45
|
+
let _cacheExpiry = 0;
|
|
46
|
+
function invalidateCache() {
|
|
47
|
+
_cachedServers = null;
|
|
48
|
+
_cacheExpiry = 0;
|
|
49
|
+
}
|
|
50
|
+
// ── Discovery ───────────────────────────────────────────────────────
|
|
51
|
+
/** Discover all available MCP servers from Claude Desktop, Claude Code, and user config. */
|
|
52
|
+
export function discoverMcpServers() {
|
|
53
|
+
const now = Date.now();
|
|
54
|
+
if (_cachedServers && now < _cacheExpiry)
|
|
55
|
+
return _cachedServers;
|
|
56
|
+
const servers = new Map();
|
|
57
|
+
// 1. Claude Desktop config
|
|
58
|
+
const desktopConfig = path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
59
|
+
try {
|
|
60
|
+
if (existsSync(desktopConfig)) {
|
|
61
|
+
const data = JSON.parse(readFileSync(desktopConfig, 'utf-8'));
|
|
62
|
+
for (const [name, config] of Object.entries(data.mcpServers ?? {})) {
|
|
63
|
+
const cfg = config;
|
|
64
|
+
servers.set(name, {
|
|
65
|
+
name,
|
|
66
|
+
type: cfg.type || 'stdio',
|
|
67
|
+
command: cfg.command,
|
|
68
|
+
args: cfg.args,
|
|
69
|
+
url: cfg.url,
|
|
70
|
+
headers: cfg.headers,
|
|
71
|
+
env: cfg.env,
|
|
72
|
+
description: KNOWN_DESCRIPTIONS[name] ?? `${name} MCP server`,
|
|
73
|
+
enabled: true,
|
|
74
|
+
source: 'auto-detected',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch { /* ignore */ }
|
|
80
|
+
// 2. Claude Code settings — project-level MCP configs
|
|
81
|
+
try {
|
|
82
|
+
const claudeDir = path.join(os.homedir(), '.claude');
|
|
83
|
+
const settingsFile = path.join(claudeDir, 'settings.json');
|
|
84
|
+
if (existsSync(settingsFile)) {
|
|
85
|
+
const settings = JSON.parse(readFileSync(settingsFile, 'utf-8'));
|
|
86
|
+
// Check mcpServers in settings
|
|
87
|
+
for (const [name, config] of Object.entries(settings.mcpServers ?? {})) {
|
|
88
|
+
if (servers.has(name))
|
|
89
|
+
continue;
|
|
90
|
+
const cfg = config;
|
|
91
|
+
servers.set(name, {
|
|
92
|
+
name,
|
|
93
|
+
type: cfg.type || 'stdio',
|
|
94
|
+
command: cfg.command,
|
|
95
|
+
args: cfg.args,
|
|
96
|
+
url: cfg.url,
|
|
97
|
+
headers: cfg.headers,
|
|
98
|
+
env: cfg.env,
|
|
99
|
+
description: KNOWN_DESCRIPTIONS[name] ?? `${name} MCP server`,
|
|
100
|
+
enabled: true,
|
|
101
|
+
source: 'auto-detected',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch { /* ignore */ }
|
|
107
|
+
// 3. Claude Desktop Extensions (newer format — ant.dir.* directories)
|
|
108
|
+
try {
|
|
109
|
+
const extensionsDir = path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'Claude Extensions');
|
|
110
|
+
const extensionsSettingsDir = path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'Claude Extensions Settings');
|
|
111
|
+
if (existsSync(extensionsDir) && existsSync(extensionsSettingsDir)) {
|
|
112
|
+
const settingsFiles = readdirSync(extensionsSettingsDir).filter(f => f.endsWith('.json'));
|
|
113
|
+
for (const settingsFile of settingsFiles) {
|
|
114
|
+
try {
|
|
115
|
+
const settings = JSON.parse(readFileSync(path.join(extensionsSettingsDir, settingsFile), 'utf-8'));
|
|
116
|
+
if (!settings.isEnabled)
|
|
117
|
+
continue;
|
|
118
|
+
const extId = settingsFile.replace('.json', '');
|
|
119
|
+
const extDir = path.join(extensionsDir, extId);
|
|
120
|
+
if (!existsSync(extDir))
|
|
121
|
+
continue;
|
|
122
|
+
// Read package.json for name and entry point
|
|
123
|
+
const pkgPath = path.join(extDir, 'package.json');
|
|
124
|
+
if (!existsSync(pkgPath))
|
|
125
|
+
continue;
|
|
126
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
127
|
+
const serverEntry = path.join(extDir, pkg.main || 'server/index.js');
|
|
128
|
+
// Derive a friendly name from the extension ID
|
|
129
|
+
// ant.dir.ant.anthropic.imessage → imessage
|
|
130
|
+
// ant.dir.ant.figma.figma → figma
|
|
131
|
+
const parts = extId.split('.');
|
|
132
|
+
const friendlyName = parts[parts.length - 1] || extId;
|
|
133
|
+
if (servers.has(friendlyName))
|
|
134
|
+
continue;
|
|
135
|
+
servers.set(friendlyName, {
|
|
136
|
+
name: friendlyName,
|
|
137
|
+
type: 'stdio',
|
|
138
|
+
command: 'node',
|
|
139
|
+
args: [serverEntry],
|
|
140
|
+
description: pkg.description || KNOWN_DESCRIPTIONS[friendlyName] || `${friendlyName} (Claude Extension)`,
|
|
141
|
+
enabled: true,
|
|
142
|
+
source: 'auto-detected',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch { /* skip malformed extension */ }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch { /* ignore */ }
|
|
150
|
+
// 4. User-managed config (overrides auto-detected)
|
|
151
|
+
try {
|
|
152
|
+
if (existsSync(MCP_SERVERS_FILE)) {
|
|
153
|
+
const userServers = JSON.parse(readFileSync(MCP_SERVERS_FILE, 'utf-8'));
|
|
154
|
+
for (const [name, cfg] of Object.entries(userServers)) {
|
|
155
|
+
servers.set(name, {
|
|
156
|
+
name,
|
|
157
|
+
type: cfg.type || 'stdio',
|
|
158
|
+
command: cfg.command,
|
|
159
|
+
args: cfg.args,
|
|
160
|
+
url: cfg.url,
|
|
161
|
+
headers: cfg.headers,
|
|
162
|
+
env: cfg.env,
|
|
163
|
+
description: cfg.description || KNOWN_DESCRIPTIONS[name] || `${name} MCP server`,
|
|
164
|
+
enabled: cfg.enabled !== false,
|
|
165
|
+
source: cfg.source === 'auto-detected' ? 'auto-detected' : 'user',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch { /* ignore */ }
|
|
171
|
+
const result = [...servers.values()];
|
|
172
|
+
_cachedServers = result;
|
|
173
|
+
_cacheExpiry = now + CACHE_TTL_MS;
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
/** Get enabled MCP servers as SDK-compatible config, filtered by allowed list. */
|
|
177
|
+
export function getMcpServersForAgent(allowedMcpServers) {
|
|
178
|
+
const servers = discoverMcpServers().filter(s => s.enabled);
|
|
179
|
+
// Filter by agent's allowed list (if specified)
|
|
180
|
+
const filtered = allowedMcpServers
|
|
181
|
+
? servers.filter(s => allowedMcpServers.includes(s.name))
|
|
182
|
+
: servers;
|
|
183
|
+
const result = {};
|
|
184
|
+
for (const s of filtered) {
|
|
185
|
+
if (s.type === 'stdio' && s.command) {
|
|
186
|
+
result[s.name] = {
|
|
187
|
+
type: 'stdio',
|
|
188
|
+
command: s.command,
|
|
189
|
+
args: s.args ?? [],
|
|
190
|
+
env: s.env ?? {},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
else if ((s.type === 'http' || s.type === 'sse') && s.url) {
|
|
194
|
+
result[s.name] = {
|
|
195
|
+
type: s.type,
|
|
196
|
+
url: s.url,
|
|
197
|
+
headers: s.headers ?? {},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
// ── User Config Management ──────────────────────────────────────────
|
|
204
|
+
export function loadUserMcpServers() {
|
|
205
|
+
try {
|
|
206
|
+
if (existsSync(MCP_SERVERS_FILE)) {
|
|
207
|
+
return JSON.parse(readFileSync(MCP_SERVERS_FILE, 'utf-8'));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch { /* ignore */ }
|
|
211
|
+
return {};
|
|
212
|
+
}
|
|
213
|
+
export function saveUserMcpServers(servers) {
|
|
214
|
+
writeFileSync(MCP_SERVERS_FILE, JSON.stringify(servers, null, 2));
|
|
215
|
+
invalidateCache();
|
|
216
|
+
}
|
|
217
|
+
export function upsertMcpServer(name, config) {
|
|
218
|
+
const servers = loadUserMcpServers();
|
|
219
|
+
servers[name] = { ...servers[name], ...config, source: 'user' };
|
|
220
|
+
saveUserMcpServers(servers);
|
|
221
|
+
}
|
|
222
|
+
export function removeMcpServer(name) {
|
|
223
|
+
const servers = loadUserMcpServers();
|
|
224
|
+
delete servers[name];
|
|
225
|
+
saveUserMcpServers(servers);
|
|
226
|
+
}
|
|
227
|
+
// ── macOS Permission Checking ───────────────────────────────────────
|
|
228
|
+
/** Extensions that need macOS permissions to function. */
|
|
229
|
+
const PERMISSION_REQUIREMENTS = {
|
|
230
|
+
imessage: {
|
|
231
|
+
resource: 'Messages (iMessage)',
|
|
232
|
+
testPath: path.join(os.homedir(), 'Library', 'Messages', 'chat.db'),
|
|
233
|
+
settingsLabel: 'Full Disk Access or Files & Folders > Messages',
|
|
234
|
+
},
|
|
235
|
+
contacts: {
|
|
236
|
+
resource: 'Contacts',
|
|
237
|
+
testPath: path.join(os.homedir(), 'Library', 'Application Support', 'AddressBook'),
|
|
238
|
+
settingsLabel: 'Contacts',
|
|
239
|
+
},
|
|
240
|
+
calendar: {
|
|
241
|
+
resource: 'Calendar',
|
|
242
|
+
testPath: path.join(os.homedir(), 'Library', 'Calendars'),
|
|
243
|
+
settingsLabel: 'Calendars',
|
|
244
|
+
},
|
|
245
|
+
photos: {
|
|
246
|
+
resource: 'Photos',
|
|
247
|
+
testPath: path.join(os.homedir(), 'Pictures', 'Photos Library.photoslibrary'),
|
|
248
|
+
settingsLabel: 'Photos',
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
/** Check macOS permissions for extensions that need them. Returns only servers with permission issues. */
|
|
252
|
+
export function checkPermissions() {
|
|
253
|
+
if (process.platform !== 'darwin')
|
|
254
|
+
return [];
|
|
255
|
+
const servers = discoverMcpServers();
|
|
256
|
+
const results = [];
|
|
257
|
+
for (const s of servers) {
|
|
258
|
+
if (!s.enabled)
|
|
259
|
+
continue;
|
|
260
|
+
const req = PERMISSION_REQUIREMENTS[s.name];
|
|
261
|
+
if (!req)
|
|
262
|
+
continue;
|
|
263
|
+
let granted = false;
|
|
264
|
+
try {
|
|
265
|
+
// Try to read the protected resource — if macOS blocks it, we'll get EPERM
|
|
266
|
+
execSync(`ls "${req.testPath}" 2>&1`, { stdio: 'pipe', timeout: 3000 });
|
|
267
|
+
granted = true;
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
// Permission denied or path doesn't exist
|
|
271
|
+
granted = existsSync(req.testPath); // exists but can't read = permission issue
|
|
272
|
+
}
|
|
273
|
+
results.push({
|
|
274
|
+
server: s.name,
|
|
275
|
+
resource: req.resource,
|
|
276
|
+
granted,
|
|
277
|
+
settingsLabel: req.settingsLabel,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
return results;
|
|
281
|
+
}
|
|
282
|
+
/** Run permission checks on startup and log warnings for any issues. */
|
|
283
|
+
export function checkPermissionsOnStartup() {
|
|
284
|
+
try {
|
|
285
|
+
const issues = checkPermissions().filter(p => !p.granted);
|
|
286
|
+
if (issues.length > 0) {
|
|
287
|
+
for (const issue of issues) {
|
|
288
|
+
logger.warn({
|
|
289
|
+
server: issue.server,
|
|
290
|
+
resource: issue.resource,
|
|
291
|
+
fix: `System Settings > Privacy & Security > ${issue.settingsLabel}`,
|
|
292
|
+
}, `MCP server "${issue.server}" needs macOS permission for ${issue.resource}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
catch { /* non-fatal */ }
|
|
297
|
+
}
|
|
298
|
+
/** Get a user-friendly error message when a tool fails due to permissions. */
|
|
299
|
+
export function getPermissionErrorMessage(serverName) {
|
|
300
|
+
const req = PERMISSION_REQUIREMENTS[serverName];
|
|
301
|
+
if (!req)
|
|
302
|
+
return null;
|
|
303
|
+
return `I tried to use ${serverName} but macOS needs permission to access ${req.resource}. ` +
|
|
304
|
+
`Open your Mac and go to System Settings > Privacy & Security > ${req.settingsLabel} — ` +
|
|
305
|
+
`make sure Terminal (or the Node.js process) is allowed.`;
|
|
306
|
+
}
|
|
307
|
+
const INTEGRATION_LABELS = {
|
|
308
|
+
'Microsoft_365': 'Microsoft 365',
|
|
309
|
+
'Google_Workspace': 'Google Workspace',
|
|
310
|
+
'Google_Drive': 'Google Drive',
|
|
311
|
+
'Slack': 'Slack',
|
|
312
|
+
'Notion': 'Notion',
|
|
313
|
+
'GitHub': 'GitHub',
|
|
314
|
+
'Linear': 'Linear',
|
|
315
|
+
'Asana': 'Asana',
|
|
316
|
+
'Jira': 'Jira',
|
|
317
|
+
'Dropbox': 'Dropbox',
|
|
318
|
+
'Salesforce': 'Salesforce',
|
|
319
|
+
};
|
|
320
|
+
/**
|
|
321
|
+
* Check if a tool name is a Claude Desktop integration tool.
|
|
322
|
+
* Format: mcp__claude_ai_<IntegrationName>__<tool_name>
|
|
323
|
+
*/
|
|
324
|
+
export function isClaudeDesktopTool(toolName) {
|
|
325
|
+
return toolName.startsWith('mcp__claude_ai_');
|
|
326
|
+
}
|
|
327
|
+
/** Parse integration name and tool from a claude_ai tool name. */
|
|
328
|
+
function parseClaudeDesktopTool(toolName) {
|
|
329
|
+
const match = toolName.match(/^mcp__claude_ai_([^_]+(?:_[^_]+)*)__(.+)$/);
|
|
330
|
+
if (!match)
|
|
331
|
+
return null;
|
|
332
|
+
return { integration: match[1], tool: match[2] };
|
|
333
|
+
}
|
|
334
|
+
/** Load persisted integrations from disk. */
|
|
335
|
+
export function loadClaudeIntegrations() {
|
|
336
|
+
try {
|
|
337
|
+
if (existsSync(INTEGRATIONS_FILE)) {
|
|
338
|
+
return JSON.parse(readFileSync(INTEGRATIONS_FILE, 'utf-8'));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
catch { /* ignore corrupt file */ }
|
|
342
|
+
return {};
|
|
343
|
+
}
|
|
344
|
+
/** Save integrations to disk. */
|
|
345
|
+
function saveClaudeIntegrations(integrations) {
|
|
346
|
+
writeFileSync(INTEGRATIONS_FILE, JSON.stringify(integrations, null, 2));
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Record a Claude Desktop integration tool use.
|
|
350
|
+
* Call this whenever a mcp__claude_ai_* tool is seen in a tool_use block.
|
|
351
|
+
*/
|
|
352
|
+
export function recordClaudeIntegrationUse(toolName) {
|
|
353
|
+
const parsed = parseClaudeDesktopTool(toolName);
|
|
354
|
+
if (!parsed)
|
|
355
|
+
return;
|
|
356
|
+
const integrations = loadClaudeIntegrations();
|
|
357
|
+
const now = new Date().toISOString();
|
|
358
|
+
const existing = integrations[parsed.integration];
|
|
359
|
+
if (existing) {
|
|
360
|
+
existing.lastUsed = now;
|
|
361
|
+
existing.connected = true;
|
|
362
|
+
if (!existing.tools.includes(parsed.tool)) {
|
|
363
|
+
existing.tools.push(parsed.tool);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
integrations[parsed.integration] = {
|
|
368
|
+
name: parsed.integration,
|
|
369
|
+
label: INTEGRATION_LABELS[parsed.integration] ?? parsed.integration.replace(/_/g, ' '),
|
|
370
|
+
tools: [parsed.tool],
|
|
371
|
+
firstSeen: now,
|
|
372
|
+
lastUsed: now,
|
|
373
|
+
connected: true,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
saveClaudeIntegrations(integrations);
|
|
377
|
+
}
|
|
378
|
+
/** Get all discovered Claude Desktop integrations as a list. */
|
|
379
|
+
export function getClaudeIntegrations() {
|
|
380
|
+
return Object.values(loadClaudeIntegrations());
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Bootstrap integrations from the audit log.
|
|
384
|
+
* Call once on startup to seed the integrations file from historical data.
|
|
385
|
+
*/
|
|
386
|
+
export function bootstrapClaudeIntegrationsFromAuditLog(auditLogPath) {
|
|
387
|
+
try {
|
|
388
|
+
if (!existsSync(auditLogPath))
|
|
389
|
+
return;
|
|
390
|
+
const integrations = loadClaudeIntegrations();
|
|
391
|
+
let changed = false;
|
|
392
|
+
const content = readFileSync(auditLogPath, 'utf-8');
|
|
393
|
+
for (const line of content.split('\n')) {
|
|
394
|
+
// Audit log format: "2026-04-09 17:00:21 mcp__claude_ai_Microsoft_365__outlook_email_search — query, limit"
|
|
395
|
+
const match = line.match(/(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (mcp__claude_ai_[^\s]+)/);
|
|
396
|
+
if (!match)
|
|
397
|
+
continue;
|
|
398
|
+
const [, timestamp, toolName] = match;
|
|
399
|
+
const parsed = parseClaudeDesktopTool(toolName);
|
|
400
|
+
if (!parsed)
|
|
401
|
+
continue;
|
|
402
|
+
const existing = integrations[parsed.integration];
|
|
403
|
+
const isoTime = new Date(timestamp.replace(' ', 'T') + 'Z').toISOString();
|
|
404
|
+
if (existing) {
|
|
405
|
+
if (!existing.tools.includes(parsed.tool)) {
|
|
406
|
+
existing.tools.push(parsed.tool);
|
|
407
|
+
changed = true;
|
|
408
|
+
}
|
|
409
|
+
if (isoTime > existing.lastUsed) {
|
|
410
|
+
existing.lastUsed = isoTime;
|
|
411
|
+
changed = true;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
integrations[parsed.integration] = {
|
|
416
|
+
name: parsed.integration,
|
|
417
|
+
label: INTEGRATION_LABELS[parsed.integration] ?? parsed.integration.replace(/_/g, ' '),
|
|
418
|
+
tools: [parsed.tool],
|
|
419
|
+
firstSeen: isoTime,
|
|
420
|
+
lastUsed: isoTime,
|
|
421
|
+
connected: true,
|
|
422
|
+
};
|
|
423
|
+
changed = true;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (changed) {
|
|
427
|
+
saveClaudeIntegrations(integrations);
|
|
428
|
+
logger.info({ count: Object.keys(integrations).length }, 'Bootstrapped Claude Desktop integrations from audit log');
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
catch (err) {
|
|
432
|
+
logger.debug({ err }, 'Failed to bootstrap integrations from audit log');
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
//# sourceMappingURL=mcp-bridge.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clementine TypeScript — Metacognitive Monitor.
|
|
3
|
+
*
|
|
4
|
+
* Tracks reasoning quality signals during query/cron/unleashed execution.
|
|
5
|
+
* Detects stuck loops, circular reasoning, confidence drops, and strategy
|
|
6
|
+
* mismatches. Injects guidance when the agent appears to need a nudge.
|
|
7
|
+
*
|
|
8
|
+
* This is the foundation layer — everything else (user model, self-improve,
|
|
9
|
+
* skills) works better when the agent can evaluate its own thinking.
|
|
10
|
+
*/
|
|
11
|
+
export interface MetacognitiveSignal {
|
|
12
|
+
type: 'ok' | 'warn' | 'intervene';
|
|
13
|
+
reason?: string;
|
|
14
|
+
guidance?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface MetacognitiveAssessment {
|
|
17
|
+
confidence: 'high' | 'medium' | 'low';
|
|
18
|
+
efficiency: number;
|
|
19
|
+
signals: string[];
|
|
20
|
+
guidance?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface MetacognitiveSummary {
|
|
23
|
+
efficiency: number;
|
|
24
|
+
toolCallCount: number;
|
|
25
|
+
uniqueTools: number;
|
|
26
|
+
stuckDetected: boolean;
|
|
27
|
+
interventionCount: number;
|
|
28
|
+
confidenceFinal: 'high' | 'medium' | 'low';
|
|
29
|
+
signals: string[];
|
|
30
|
+
}
|
|
31
|
+
export declare class MetacognitiveMonitor {
|
|
32
|
+
private toolCalls;
|
|
33
|
+
private uniqueTools;
|
|
34
|
+
private consecutiveReads;
|
|
35
|
+
private turnCount;
|
|
36
|
+
private outputCharCount;
|
|
37
|
+
private interventionCount;
|
|
38
|
+
private signals;
|
|
39
|
+
private confidence;
|
|
40
|
+
/**
|
|
41
|
+
* Record a tool call. Returns a signal if the pattern is concerning.
|
|
42
|
+
*/
|
|
43
|
+
recordToolCall(name: string, input: Record<string, unknown>): MetacognitiveSignal;
|
|
44
|
+
/**
|
|
45
|
+
* Assess the quality of the current turn after the assistant responds.
|
|
46
|
+
*/
|
|
47
|
+
assessTurn(responseText: string, toolCallsThisTurn: number): MetacognitiveAssessment;
|
|
48
|
+
/**
|
|
49
|
+
* Get the final metacognitive summary for this execution.
|
|
50
|
+
*/
|
|
51
|
+
getSummary(): MetacognitiveSummary;
|
|
52
|
+
/**
|
|
53
|
+
* Build contextual guidance based on current signals.
|
|
54
|
+
*/
|
|
55
|
+
private buildGuidance;
|
|
56
|
+
/**
|
|
57
|
+
* Detect when the agent's text promises action but no tools were called.
|
|
58
|
+
* Call this after the query completes with the full response text.
|
|
59
|
+
*
|
|
60
|
+
* @returns A signal if the response looks like a stall (promised action, didn't deliver).
|
|
61
|
+
*/
|
|
62
|
+
detectPromiseWithoutAction(responseText: string, toolCallCount: number): MetacognitiveSignal;
|
|
63
|
+
/** Reset for a new execution (e.g., new phase in unleashed). */
|
|
64
|
+
reset(): void;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=metacognition.d.ts.map
|