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,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clementine TypeScript — Insight Engine.
|
|
3
|
+
*
|
|
4
|
+
* Proactive conversation initiation: runs during heartbeat ticks to
|
|
5
|
+
* identify events or patterns the user should know about. Generates
|
|
6
|
+
* urgency-rated insights and dispatches via NotificationDispatcher.
|
|
7
|
+
*
|
|
8
|
+
* Throttling: max 3 proactive messages per day, minimum 2-hour cooldown.
|
|
9
|
+
* Adapts based on user acknowledgment (doubles cooldown after 3 ignored).
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import pino from 'pino';
|
|
14
|
+
import { GOALS_DIR, BASE_DIR } from '../config.js';
|
|
15
|
+
const logger = pino({ name: 'clementine.insight-engine' });
|
|
16
|
+
const BASE_COOLDOWN_MS = 2 * 60 * 60 * 1000; // 2 hours
|
|
17
|
+
const MAX_DAILY_INSIGHTS = 3;
|
|
18
|
+
const UNACKED_THRESHOLD = 3; // double cooldown after this many ignored
|
|
19
|
+
/**
|
|
20
|
+
* Check if it's too soon to send another proactive message.
|
|
21
|
+
*/
|
|
22
|
+
export function canSendInsight(state) {
|
|
23
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
24
|
+
// Reset daily count on new day
|
|
25
|
+
if (state.currentDate !== today) {
|
|
26
|
+
state.sentToday = [];
|
|
27
|
+
state.currentDate = today;
|
|
28
|
+
// Don't reset cooldownMultiplier — that persists across days
|
|
29
|
+
}
|
|
30
|
+
// Daily limit
|
|
31
|
+
if (state.sentToday.length >= MAX_DAILY_INSIGHTS) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
// Cooldown check
|
|
35
|
+
if (state.lastSentAt) {
|
|
36
|
+
const elapsed = Date.now() - new Date(state.lastSentAt).getTime();
|
|
37
|
+
const cooldown = BASE_COOLDOWN_MS * state.cooldownMultiplier;
|
|
38
|
+
if (elapsed < cooldown) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Record that a proactive message was sent.
|
|
46
|
+
*/
|
|
47
|
+
export function recordInsightSent(state) {
|
|
48
|
+
const now = new Date().toISOString();
|
|
49
|
+
state.sentToday.push(now);
|
|
50
|
+
state.lastSentAt = now;
|
|
51
|
+
state.unackedCount++;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Record that the user acknowledged a proactive message (replied to it).
|
|
55
|
+
* Resets the unacked counter and lowers cooldown.
|
|
56
|
+
*/
|
|
57
|
+
export function recordInsightAcked(state) {
|
|
58
|
+
state.unackedCount = 0;
|
|
59
|
+
state.cooldownMultiplier = 1;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if cooldown should be increased due to ignored messages.
|
|
63
|
+
*/
|
|
64
|
+
export function maybeIncreaseCooldown(state) {
|
|
65
|
+
if (state.unackedCount >= UNACKED_THRESHOLD) {
|
|
66
|
+
state.cooldownMultiplier = Math.min(state.cooldownMultiplier * 2, 8); // cap at 16 hours
|
|
67
|
+
state.unackedCount = 0; // reset counter after adjustment
|
|
68
|
+
logger.info({ multiplier: state.cooldownMultiplier }, 'Proactive message cooldown increased — messages going unacknowledged');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Gather raw signals for insight generation (no LLM call — pure data).
|
|
73
|
+
* Returns structured event summaries that can be passed to an LLM for urgency rating.
|
|
74
|
+
*/
|
|
75
|
+
export function gatherInsightSignals(gateway) {
|
|
76
|
+
const signals = [];
|
|
77
|
+
const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
|
|
78
|
+
// 1. Check for goal progress/failures
|
|
79
|
+
try {
|
|
80
|
+
const progressDir = path.join(GOALS_DIR, 'progress');
|
|
81
|
+
if (existsSync(progressDir)) {
|
|
82
|
+
const files = readdirSync(progressDir).filter(f => f.endsWith('.progress.jsonl'));
|
|
83
|
+
for (const file of files.slice(0, 5)) {
|
|
84
|
+
const lines = readFileSync(path.join(progressDir, file), 'utf-8').trim().split('\n').filter(Boolean);
|
|
85
|
+
const recent = lines.slice(-3);
|
|
86
|
+
for (const line of recent) {
|
|
87
|
+
try {
|
|
88
|
+
const entry = JSON.parse(line);
|
|
89
|
+
if (new Date(entry.timestamp) > new Date(twoHoursAgo)) {
|
|
90
|
+
if (entry.status === 'error') {
|
|
91
|
+
signals.push(`Goal work session failed: ${entry.focus} (${entry.resultSnippet?.slice(0, 100)})`);
|
|
92
|
+
}
|
|
93
|
+
else if (entry.status === 'progress') {
|
|
94
|
+
signals.push(`Goal progress: ${entry.focus}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch { /* non-fatal */ }
|
|
106
|
+
// 2. Check for cron job failures
|
|
107
|
+
try {
|
|
108
|
+
const runLogDir = path.join(BASE_DIR, 'cron', 'run-log');
|
|
109
|
+
if (existsSync(runLogDir)) {
|
|
110
|
+
const logFiles = readdirSync(runLogDir).filter(f => f.endsWith('.jsonl')).slice(0, 10);
|
|
111
|
+
for (const file of logFiles) {
|
|
112
|
+
const lines = readFileSync(path.join(runLogDir, file), 'utf-8').trim().split('\n').filter(Boolean);
|
|
113
|
+
const lastLine = lines[lines.length - 1];
|
|
114
|
+
if (!lastLine)
|
|
115
|
+
continue;
|
|
116
|
+
try {
|
|
117
|
+
const entry = JSON.parse(lastLine);
|
|
118
|
+
if (new Date(entry.timestamp) > new Date(twoHoursAgo) && entry.status === 'error') {
|
|
119
|
+
signals.push(`Cron job "${entry.jobName || file.replace('.jsonl', '')}" failed: ${(entry.error || '').slice(0, 100)}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch { /* non-fatal */ }
|
|
129
|
+
// 3. Check recent activity for patterns worth surfacing
|
|
130
|
+
try {
|
|
131
|
+
const activity = gateway.getRecentActivity(twoHoursAgo);
|
|
132
|
+
const sessionCount = new Set(activity.map(a => a.sessionKey)).size;
|
|
133
|
+
if (sessionCount === 0) {
|
|
134
|
+
// No recent activity — could note quiet period if there are pending goals
|
|
135
|
+
try {
|
|
136
|
+
if (existsSync(GOALS_DIR)) {
|
|
137
|
+
const goals = readdirSync(GOALS_DIR).filter(f => f.endsWith('.json'));
|
|
138
|
+
const activeHighPriority = goals.filter(f => {
|
|
139
|
+
try {
|
|
140
|
+
const g = JSON.parse(readFileSync(path.join(GOALS_DIR, f), 'utf-8'));
|
|
141
|
+
return g.status === 'active' && g.priority === 'high';
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
if (activeHighPriority.length > 0) {
|
|
148
|
+
signals.push(`Quiet period: ${activeHighPriority.length} high-priority goal(s) active but no recent user interaction`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch { /* non-fatal */ }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch { /* non-fatal */ }
|
|
156
|
+
// 4. Check advisor events (model escalations, circuit breakers)
|
|
157
|
+
try {
|
|
158
|
+
const eventsPath = path.join(BASE_DIR, 'cron', 'advisor-events.jsonl');
|
|
159
|
+
if (existsSync(eventsPath)) {
|
|
160
|
+
const lines = readFileSync(eventsPath, 'utf-8').trim().split('\n').filter(Boolean);
|
|
161
|
+
const recent = lines.slice(-5);
|
|
162
|
+
for (const line of recent) {
|
|
163
|
+
try {
|
|
164
|
+
const evt = JSON.parse(line);
|
|
165
|
+
if (new Date(evt.timestamp) > new Date(twoHoursAgo)) {
|
|
166
|
+
if (evt.type === 'circuit-breaker') {
|
|
167
|
+
signals.push(`Circuit breaker tripped for "${evt.jobName}": ${evt.detail}`);
|
|
168
|
+
}
|
|
169
|
+
else if (evt.type === 'escalation') {
|
|
170
|
+
signals.push(`Job "${evt.jobName}" escalated: ${evt.detail}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch { /* non-fatal */ }
|
|
181
|
+
return signals;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Build a prompt for urgency rating (to be sent to a lightweight LLM).
|
|
185
|
+
* Returns null if there are no signals worth evaluating.
|
|
186
|
+
*/
|
|
187
|
+
export function buildInsightPrompt(signals) {
|
|
188
|
+
if (signals.length === 0)
|
|
189
|
+
return null;
|
|
190
|
+
return (`You are a proactive assistant analyzing recent events for your owner.\n\n` +
|
|
191
|
+
`## Recent Events\n${signals.map(s => `- ${s}`).join('\n')}\n\n` +
|
|
192
|
+
`## Task\n` +
|
|
193
|
+
`Based on these events, determine if the user should be notified.\n\n` +
|
|
194
|
+
`Rate urgency 1-5:\n` +
|
|
195
|
+
`1 = trivial (don't notify)\n` +
|
|
196
|
+
`2 = low (don't notify)\n` +
|
|
197
|
+
`3 = informational (batch into next check-in)\n` +
|
|
198
|
+
`4 = important (notify during active hours)\n` +
|
|
199
|
+
`5 = critical (notify immediately)\n\n` +
|
|
200
|
+
`If urgency >= 3, write a brief, conversational message (1-3 sentences) for the user.\n\n` +
|
|
201
|
+
`Output ONLY JSON:\n` +
|
|
202
|
+
`{ "urgency": <1-5>, "message": "<text or null>", "source": "<which event triggered this>" }`);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Parse the LLM response into an InsightResult.
|
|
206
|
+
*/
|
|
207
|
+
export function parseInsightResponse(response) {
|
|
208
|
+
try {
|
|
209
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
210
|
+
if (!jsonMatch)
|
|
211
|
+
return null;
|
|
212
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
213
|
+
if (!parsed.urgency || parsed.urgency < 3 || !parsed.message)
|
|
214
|
+
return null;
|
|
215
|
+
return {
|
|
216
|
+
message: parsed.message,
|
|
217
|
+
urgency: parsed.urgency,
|
|
218
|
+
source: parsed.source || 'unknown',
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=insight-engine.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clementine TypeScript — Intent Classifier & Response Strategy Router.
|
|
3
|
+
*
|
|
4
|
+
* Lightweight heuristic classifier that determines message intent BEFORE
|
|
5
|
+
* the main SDK query, enabling dynamic tuning of maxTurns, effort level,
|
|
6
|
+
* and response formatting guidance.
|
|
7
|
+
*/
|
|
8
|
+
export type IntentType = 'question' | 'task' | 'feedback' | 'casual' | 'followup' | 'correction';
|
|
9
|
+
export type ResponseStrategy = 'brief-answer' | 'structured-response' | 'plan-and-execute' | 'acknowledge-and-adapt' | 'conversational';
|
|
10
|
+
export interface IntentClassification {
|
|
11
|
+
type: IntentType;
|
|
12
|
+
confidence: number;
|
|
13
|
+
suggestedStrategy: ResponseStrategy;
|
|
14
|
+
/** Suggested maxTurns for this intent type */
|
|
15
|
+
suggestedMaxTurns: number;
|
|
16
|
+
/** Suggested effort level for the SDK */
|
|
17
|
+
suggestedEffort: 'low' | 'medium' | 'high';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Classify a user message's intent using heuristics.
|
|
21
|
+
*
|
|
22
|
+
* @param text - The user's raw message
|
|
23
|
+
* @param recentExchanges - Recent conversation history for followup detection
|
|
24
|
+
*/
|
|
25
|
+
export declare function classifyIntent(text: string, recentExchanges?: Array<{
|
|
26
|
+
user: string;
|
|
27
|
+
assistant: string;
|
|
28
|
+
}>): IntentClassification;
|
|
29
|
+
/**
|
|
30
|
+
* Generate system prompt guidance text based on the response strategy.
|
|
31
|
+
* Injected into the system prompt to steer the agent's response style.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getStrategyGuidance(strategy: ResponseStrategy): string;
|
|
34
|
+
/**
|
|
35
|
+
* Generate a follow-up suggestion prompt suffix based on completed work.
|
|
36
|
+
*
|
|
37
|
+
* @param toolCallCount - Number of tool calls made during the query
|
|
38
|
+
* @param responseLength - Length of the response text
|
|
39
|
+
* @param hasActiveGoals - Whether there are relevant active goals
|
|
40
|
+
* @param pendingWorkItems - Number of pending items in the work queue
|
|
41
|
+
*/
|
|
42
|
+
export declare function buildFollowUpContext(opts: {
|
|
43
|
+
toolCallCount: number;
|
|
44
|
+
responseLength: number;
|
|
45
|
+
hasActiveGoals: boolean;
|
|
46
|
+
pendingWorkItems: number;
|
|
47
|
+
}): string | null;
|
|
48
|
+
//# sourceMappingURL=intent-classifier.d.ts.map
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clementine TypeScript — Intent Classifier & Response Strategy Router.
|
|
3
|
+
*
|
|
4
|
+
* Lightweight heuristic classifier that determines message intent BEFORE
|
|
5
|
+
* the main SDK query, enabling dynamic tuning of maxTurns, effort level,
|
|
6
|
+
* and response formatting guidance.
|
|
7
|
+
*/
|
|
8
|
+
// ── Constants ─────────────────────────────────────────────────────────
|
|
9
|
+
const QUESTION_STARTERS = /^(what|who|where|when|why|how|is|are|was|were|do|does|did|can|could|would|should|will|which|have|has|tell me)\b/i;
|
|
10
|
+
const ACTION_VERBS = /\b(create|write|send|update|deploy|build|fix|add|remove|delete|install|configure|set up|draft|schedule|implement|refactor|change|move|rename|edit|modify|generate|make|run|execute|push|pull|check|review|analyze|search|find|fetch|get|list|show)\b/i;
|
|
11
|
+
const FEEDBACK_POSITIVE = /^(thanks|thank you|ty|thx|great|perfect|nice|awesome|love it|good job|well done|excellent|looks good|lgtm|cool|sweet|beautiful|brilliant|exactly|yes|yep|yeah|correct|right|ok)\b/i;
|
|
12
|
+
const FEEDBACK_NEGATIVE = /^(no|nope|wrong|not right|that's not|that isn't|ugh|nah|bad|incorrect|stop|don't|actually no|wait no)\b/i;
|
|
13
|
+
const CORRECTION_PATTERNS = /\b(actually|instead|rather|not that|I meant|I said|what I wanted|correction|try again|redo|no I|that's wrong)\b/i;
|
|
14
|
+
const CASUAL_PATTERNS = /^(hi|hey|hello|good morning|good afternoon|good evening|sup|yo|what's up|howdy|morning|gm|gn|good night)\b/i;
|
|
15
|
+
// ── Classifier ────────────────────────────────────────────────────────
|
|
16
|
+
/**
|
|
17
|
+
* Classify a user message's intent using heuristics.
|
|
18
|
+
*
|
|
19
|
+
* @param text - The user's raw message
|
|
20
|
+
* @param recentExchanges - Recent conversation history for followup detection
|
|
21
|
+
*/
|
|
22
|
+
export function classifyIntent(text, recentExchanges) {
|
|
23
|
+
const trimmed = text.trim();
|
|
24
|
+
const wordCount = trimmed.split(/\s+/).length;
|
|
25
|
+
const endsWithQuestion = trimmed.endsWith('?');
|
|
26
|
+
const hasRecentContext = (recentExchanges?.length ?? 0) > 0;
|
|
27
|
+
// Score each intent type
|
|
28
|
+
const scores = {
|
|
29
|
+
question: 0,
|
|
30
|
+
task: 0,
|
|
31
|
+
feedback: 0,
|
|
32
|
+
casual: 0,
|
|
33
|
+
followup: 0,
|
|
34
|
+
correction: 0,
|
|
35
|
+
};
|
|
36
|
+
// ── Question signals ──
|
|
37
|
+
if (endsWithQuestion)
|
|
38
|
+
scores.question += 0.4;
|
|
39
|
+
if (QUESTION_STARTERS.test(trimmed))
|
|
40
|
+
scores.question += 0.35;
|
|
41
|
+
if (wordCount < 15 && endsWithQuestion)
|
|
42
|
+
scores.question += 0.15;
|
|
43
|
+
// ── Task signals ──
|
|
44
|
+
if (ACTION_VERBS.test(trimmed))
|
|
45
|
+
scores.task += 0.35;
|
|
46
|
+
if (wordCount > 20)
|
|
47
|
+
scores.task += 0.1; // Longer messages tend to be tasks
|
|
48
|
+
if (trimmed.includes('\n') || trimmed.includes('```'))
|
|
49
|
+
scores.task += 0.15; // Multi-line or code blocks
|
|
50
|
+
if (/\b(please|can you|could you|I need|I want)\b/i.test(trimmed))
|
|
51
|
+
scores.task += 0.2;
|
|
52
|
+
// If it has action verbs AND doesn't end with ?, lean toward task over question
|
|
53
|
+
if (ACTION_VERBS.test(trimmed) && !endsWithQuestion)
|
|
54
|
+
scores.task += 0.15;
|
|
55
|
+
// ── Feedback signals ──
|
|
56
|
+
if (FEEDBACK_POSITIVE.test(trimmed))
|
|
57
|
+
scores.feedback += 0.5;
|
|
58
|
+
if (FEEDBACK_NEGATIVE.test(trimmed))
|
|
59
|
+
scores.feedback += 0.4;
|
|
60
|
+
if (wordCount <= 5)
|
|
61
|
+
scores.feedback += 0.15; // Short messages are often feedback
|
|
62
|
+
// ── Casual signals ──
|
|
63
|
+
if (CASUAL_PATTERNS.test(trimmed))
|
|
64
|
+
scores.casual += 0.6;
|
|
65
|
+
if (wordCount <= 3 && !endsWithQuestion && !ACTION_VERBS.test(trimmed))
|
|
66
|
+
scores.casual += 0.2;
|
|
67
|
+
// ── Correction signals ──
|
|
68
|
+
if (CORRECTION_PATTERNS.test(trimmed) && hasRecentContext)
|
|
69
|
+
scores.correction += 0.5;
|
|
70
|
+
if (FEEDBACK_NEGATIVE.test(trimmed) && hasRecentContext && wordCount > 5) {
|
|
71
|
+
scores.correction += 0.3; // Negative feedback + more detail = likely correction
|
|
72
|
+
scores.feedback -= 0.2; // Reduce feedback score
|
|
73
|
+
}
|
|
74
|
+
// ── Followup signals ──
|
|
75
|
+
if (hasRecentContext) {
|
|
76
|
+
const lastAssistant = recentExchanges[recentExchanges.length - 1]?.assistant ?? '';
|
|
77
|
+
// References to previous response content
|
|
78
|
+
if (/\b(that|this|it|those|these|the one|above|earlier)\b/i.test(trimmed)) {
|
|
79
|
+
scores.followup += 0.25;
|
|
80
|
+
}
|
|
81
|
+
// Short messages in active conversation are likely followups
|
|
82
|
+
if (wordCount < 10 && !CASUAL_PATTERNS.test(trimmed)) {
|
|
83
|
+
scores.followup += 0.15;
|
|
84
|
+
}
|
|
85
|
+
// If the assistant asked a question and the user responds briefly
|
|
86
|
+
if (lastAssistant.endsWith('?') && wordCount < 20) {
|
|
87
|
+
scores.followup += 0.3;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// ── Pick winner ──
|
|
91
|
+
let bestType = 'question';
|
|
92
|
+
let bestScore = 0;
|
|
93
|
+
for (const [type, score] of Object.entries(scores)) {
|
|
94
|
+
if (score > bestScore) {
|
|
95
|
+
bestType = type;
|
|
96
|
+
bestScore = score;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Default fallback: if no strong signal, classify based on simple heuristics
|
|
100
|
+
if (bestScore < 0.2) {
|
|
101
|
+
if (endsWithQuestion)
|
|
102
|
+
bestType = 'question';
|
|
103
|
+
else if (wordCount > 15)
|
|
104
|
+
bestType = 'task';
|
|
105
|
+
else
|
|
106
|
+
bestType = 'question'; // Safe default
|
|
107
|
+
bestScore = 0.3;
|
|
108
|
+
}
|
|
109
|
+
const confidence = Math.min(bestScore, 1);
|
|
110
|
+
return {
|
|
111
|
+
type: bestType,
|
|
112
|
+
confidence,
|
|
113
|
+
suggestedStrategy: intentToStrategy(bestType),
|
|
114
|
+
suggestedMaxTurns: intentToMaxTurns(bestType),
|
|
115
|
+
suggestedEffort: intentToEffort(bestType),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
// ── Strategy Mapping ──────────────────────────────────────────────────
|
|
119
|
+
function intentToStrategy(intent) {
|
|
120
|
+
switch (intent) {
|
|
121
|
+
case 'question': return 'brief-answer';
|
|
122
|
+
case 'task': return 'plan-and-execute';
|
|
123
|
+
case 'feedback': return 'acknowledge-and-adapt';
|
|
124
|
+
case 'casual': return 'conversational';
|
|
125
|
+
case 'followup': return 'brief-answer';
|
|
126
|
+
case 'correction': return 'acknowledge-and-adapt';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function intentToMaxTurns(intent) {
|
|
130
|
+
switch (intent) {
|
|
131
|
+
case 'question': return 8;
|
|
132
|
+
case 'task': return 15;
|
|
133
|
+
case 'feedback': return 3;
|
|
134
|
+
case 'casual': return 3;
|
|
135
|
+
case 'followup': return 8;
|
|
136
|
+
case 'correction': return 10;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function intentToEffort(intent) {
|
|
140
|
+
switch (intent) {
|
|
141
|
+
case 'question': return 'medium';
|
|
142
|
+
case 'task': return 'high';
|
|
143
|
+
case 'feedback': return 'low';
|
|
144
|
+
case 'casual': return 'low';
|
|
145
|
+
case 'followup': return 'medium';
|
|
146
|
+
case 'correction': return 'medium';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// ── Response Strategy Guidance ────────────────────────────────────────
|
|
150
|
+
/**
|
|
151
|
+
* Generate system prompt guidance text based on the response strategy.
|
|
152
|
+
* Injected into the system prompt to steer the agent's response style.
|
|
153
|
+
*/
|
|
154
|
+
export function getStrategyGuidance(strategy) {
|
|
155
|
+
switch (strategy) {
|
|
156
|
+
case 'brief-answer':
|
|
157
|
+
return `## Response Style: Direct Answer
|
|
158
|
+
Respond concisely — 1-3 sentences for simple questions, a short paragraph with key details for complex ones.
|
|
159
|
+
If you know the answer, give it immediately — no preamble.
|
|
160
|
+
If you need to look something up, say what you're checking and why ("Let me check memory for that — I think we discussed it last week"), then share what you found.`;
|
|
161
|
+
case 'structured-response':
|
|
162
|
+
return `## Response Style: Structured
|
|
163
|
+
Use headers, bullet points, or numbered lists for clarity.
|
|
164
|
+
Start with a brief summary, then provide details.
|
|
165
|
+
Include a "Next Steps" or "Summary" section at the end if applicable.`;
|
|
166
|
+
case 'plan-and-execute':
|
|
167
|
+
return `## Response Style: Agentic Execution
|
|
168
|
+
This is a task — work through it like a capable assistant who owns the outcome.
|
|
169
|
+
|
|
170
|
+
**Assess scope first**: Before diving in, quickly assess whether this is a 2-minute task or a 20-minute job.
|
|
171
|
+
- **Simple task** (one file, one API call, one edit): Just do it. No need to explain the plan.
|
|
172
|
+
- **Multi-step task** (several items to process, research + action, multiple files): Tell the user what you're going to do, then use sub-agents (Agent tool) to parallelize the work. Example: "This touches 3 accounts — I'll spin up research on each in parallel and have results in a few minutes."
|
|
173
|
+
- **Complex/long task**: Offer to run it in the background: "This is going to take some real work. Want me to kick it off in deep mode? I'll keep you posted as I go."
|
|
174
|
+
|
|
175
|
+
**During execution**: Narrate findings and decisions, not tool calls. If something unexpected happens, explain what you're doing about it.
|
|
176
|
+
**End**: Summarize what you did and suggest natural next steps.
|
|
177
|
+
|
|
178
|
+
**Parallelization**: When processing multiple items (prospects, files, accounts), ALWAYS use the Agent tool to spawn sub-agents that work in parallel. Don't process 10 things one at a time when you can batch them across 3-5 sub-agents.`;
|
|
179
|
+
case 'acknowledge-and-adapt':
|
|
180
|
+
return `## Response Style: Adaptive
|
|
181
|
+
The user is giving feedback or correcting you. Acknowledge it briefly and naturally.
|
|
182
|
+
If it's positive feedback: a brief "glad that worked" type response. Don't be effusive.
|
|
183
|
+
If it's a correction: acknowledge what was wrong, explain briefly what you understand now, then fix it or adjust. Show that you understood the correction, don't just say "sorry."`;
|
|
184
|
+
case 'conversational':
|
|
185
|
+
return `## Response Style: Casual
|
|
186
|
+
Keep it natural and brief. Match the user's energy.
|
|
187
|
+
No tool calls needed. Just be conversational.
|
|
188
|
+
If there's relevant context from recent work or pending items, briefly mention it.`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Generate a follow-up suggestion prompt suffix based on completed work.
|
|
193
|
+
*
|
|
194
|
+
* @param toolCallCount - Number of tool calls made during the query
|
|
195
|
+
* @param responseLength - Length of the response text
|
|
196
|
+
* @param hasActiveGoals - Whether there are relevant active goals
|
|
197
|
+
* @param pendingWorkItems - Number of pending items in the work queue
|
|
198
|
+
*/
|
|
199
|
+
export function buildFollowUpContext(opts) {
|
|
200
|
+
// Only suggest follow-ups after substantive work
|
|
201
|
+
if (opts.toolCallCount < 5 || opts.responseLength < 200)
|
|
202
|
+
return null;
|
|
203
|
+
const parts = [];
|
|
204
|
+
if (opts.hasActiveGoals) {
|
|
205
|
+
parts.push('There are active goals that may relate to this work.');
|
|
206
|
+
}
|
|
207
|
+
if (opts.pendingWorkItems > 0) {
|
|
208
|
+
parts.push(`There are ${opts.pendingWorkItems} pending work items in the queue.`);
|
|
209
|
+
}
|
|
210
|
+
if (parts.length === 0)
|
|
211
|
+
return null;
|
|
212
|
+
return `\n\n[POST_WORK_CONTEXT: ${parts.join(' ')} Consider suggesting 1-2 natural next steps as questions, if they'd be genuinely useful. Don't force it.]`;
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=intent-classifier.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clementine TypeScript — Proactive link understanding.
|
|
3
|
+
*
|
|
4
|
+
* Extracts URLs from user messages, fetches their content, and returns
|
|
5
|
+
* readable text so the agent has context without needing a tool call.
|
|
6
|
+
*/
|
|
7
|
+
export interface LinkContext {
|
|
8
|
+
url: string;
|
|
9
|
+
title: string;
|
|
10
|
+
content: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Extract and fetch URLs found in the given text.
|
|
15
|
+
* Returns content for up to LINK_EXTRACT_MAX_URLS unique URLs.
|
|
16
|
+
* Never throws — errors are captured per-URL.
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractLinks(text: string): Promise<LinkContext[]>;
|
|
19
|
+
//# sourceMappingURL=link-extractor.d.ts.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clementine TypeScript — Proactive link understanding.
|
|
3
|
+
*
|
|
4
|
+
* Extracts URLs from user messages, fetches their content, and returns
|
|
5
|
+
* readable text so the agent has context without needing a tool call.
|
|
6
|
+
*/
|
|
7
|
+
import { LINK_EXTRACT_MAX_URLS, LINK_EXTRACT_MAX_CHARS } from '../config.js';
|
|
8
|
+
import { PRIVATE_URL_PATTERNS } from './hooks.js';
|
|
9
|
+
// Match https:// URLs, skip common image/video/audio extensions
|
|
10
|
+
const URL_RE = /https?:\/\/[^\s<>"')\]]+/gi;
|
|
11
|
+
const SKIP_EXT_RE = /\.(png|jpg|jpeg|gif|webp|svg|ico|mp4|mp3|wav|webm|avi|mov|pdf)(\?[^\s]*)?$/i;
|
|
12
|
+
/** Strip HTML tags of a given type (including content). */
|
|
13
|
+
function stripTags(html, ...tags) {
|
|
14
|
+
let result = html;
|
|
15
|
+
for (const tag of tags) {
|
|
16
|
+
result = result.replace(new RegExp(`<${tag}[\\s>][\\s\\S]*?</${tag}>`, 'gi'), '');
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
/** Extract the <title> text from HTML. */
|
|
21
|
+
function extractTitle(html) {
|
|
22
|
+
const match = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(html);
|
|
23
|
+
return match ? match[1].replace(/\s+/g, ' ').trim() : '';
|
|
24
|
+
}
|
|
25
|
+
/** Check if a URL targets a private/internal network. */
|
|
26
|
+
function isPrivateUrl(url) {
|
|
27
|
+
return PRIVATE_URL_PATTERNS.some(p => p.test(url));
|
|
28
|
+
}
|
|
29
|
+
async function fetchLink(url) {
|
|
30
|
+
if (isPrivateUrl(url)) {
|
|
31
|
+
return { url, title: '', content: '', error: 'private/internal URL blocked' };
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const response = await fetch(url, {
|
|
35
|
+
headers: { 'User-Agent': 'Clementine/1.0' },
|
|
36
|
+
signal: AbortSignal.timeout(10_000),
|
|
37
|
+
redirect: 'follow',
|
|
38
|
+
});
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
return { url, title: '', content: '', error: `HTTP ${response.status}` };
|
|
41
|
+
}
|
|
42
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
43
|
+
if (!contentType.includes('text/html') && !contentType.includes('text/plain')) {
|
|
44
|
+
return { url, title: '', content: '', error: `unsupported content-type: ${contentType}` };
|
|
45
|
+
}
|
|
46
|
+
const html = await response.text();
|
|
47
|
+
const title = extractTitle(html);
|
|
48
|
+
// Strip non-content tags, then all remaining HTML
|
|
49
|
+
let text = stripTags(html, 'script', 'style', 'nav', 'footer', 'header', 'aside');
|
|
50
|
+
text = text.replace(/<[^>]+>/g, ' ');
|
|
51
|
+
// Collapse whitespace
|
|
52
|
+
text = text.replace(/[ \t]+/g, ' ').replace(/\n{3,}/g, '\n\n').trim();
|
|
53
|
+
// Truncate
|
|
54
|
+
if (text.length > LINK_EXTRACT_MAX_CHARS) {
|
|
55
|
+
text = text.slice(0, LINK_EXTRACT_MAX_CHARS) + '…';
|
|
56
|
+
}
|
|
57
|
+
return { url, title, content: text };
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
61
|
+
return { url, title: '', content: '', error: message };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Extract and fetch URLs found in the given text.
|
|
66
|
+
* Returns content for up to LINK_EXTRACT_MAX_URLS unique URLs.
|
|
67
|
+
* Never throws — errors are captured per-URL.
|
|
68
|
+
*/
|
|
69
|
+
export async function extractLinks(text) {
|
|
70
|
+
const matches = text.match(URL_RE);
|
|
71
|
+
if (!matches)
|
|
72
|
+
return [];
|
|
73
|
+
// Deduplicate and filter
|
|
74
|
+
const seen = new Set();
|
|
75
|
+
const urls = [];
|
|
76
|
+
for (const raw of matches) {
|
|
77
|
+
// Strip trailing punctuation that's likely not part of the URL
|
|
78
|
+
const url = raw.replace(/[.,;:!?)]+$/, '');
|
|
79
|
+
if (seen.has(url) || SKIP_EXT_RE.test(url))
|
|
80
|
+
continue;
|
|
81
|
+
seen.add(url);
|
|
82
|
+
urls.push(url);
|
|
83
|
+
if (urls.length >= LINK_EXTRACT_MAX_URLS)
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
if (!urls.length)
|
|
87
|
+
return [];
|
|
88
|
+
return Promise.all(urls.map(fetchLink));
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=link-extractor.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
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 type { ManagedMcpServer } from '../types.js';
|
|
9
|
+
/** Discover all available MCP servers from Claude Desktop, Claude Code, and user config. */
|
|
10
|
+
export declare function discoverMcpServers(): ManagedMcpServer[];
|
|
11
|
+
/** Get enabled MCP servers as SDK-compatible config, filtered by allowed list. */
|
|
12
|
+
export declare function getMcpServersForAgent(allowedMcpServers?: string[]): Record<string, any>;
|
|
13
|
+
export declare function loadUserMcpServers(): Record<string, any>;
|
|
14
|
+
export declare function saveUserMcpServers(servers: Record<string, any>): void;
|
|
15
|
+
export declare function upsertMcpServer(name: string, config: Partial<ManagedMcpServer>): void;
|
|
16
|
+
export declare function removeMcpServer(name: string): void;
|
|
17
|
+
export interface PermissionStatus {
|
|
18
|
+
server: string;
|
|
19
|
+
resource: string;
|
|
20
|
+
granted: boolean;
|
|
21
|
+
settingsLabel: string;
|
|
22
|
+
}
|
|
23
|
+
/** Check macOS permissions for extensions that need them. Returns only servers with permission issues. */
|
|
24
|
+
export declare function checkPermissions(): PermissionStatus[];
|
|
25
|
+
/** Run permission checks on startup and log warnings for any issues. */
|
|
26
|
+
export declare function checkPermissionsOnStartup(): void;
|
|
27
|
+
/** Get a user-friendly error message when a tool fails due to permissions. */
|
|
28
|
+
export declare function getPermissionErrorMessage(serverName: string): string | null;
|
|
29
|
+
export interface ClaudeIntegration {
|
|
30
|
+
/** Integration name, e.g. "Microsoft_365" */
|
|
31
|
+
name: string;
|
|
32
|
+
/** Human-friendly label */
|
|
33
|
+
label: string;
|
|
34
|
+
/** Tools discovered for this integration */
|
|
35
|
+
tools: string[];
|
|
36
|
+
/** When first seen */
|
|
37
|
+
firstSeen: string;
|
|
38
|
+
/** When last used */
|
|
39
|
+
lastUsed: string;
|
|
40
|
+
/** Whether the user has it connected (true if we've seen it work) */
|
|
41
|
+
connected: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check if a tool name is a Claude Desktop integration tool.
|
|
45
|
+
* Format: mcp__claude_ai_<IntegrationName>__<tool_name>
|
|
46
|
+
*/
|
|
47
|
+
export declare function isClaudeDesktopTool(toolName: string): boolean;
|
|
48
|
+
/** Load persisted integrations from disk. */
|
|
49
|
+
export declare function loadClaudeIntegrations(): Record<string, ClaudeIntegration>;
|
|
50
|
+
/**
|
|
51
|
+
* Record a Claude Desktop integration tool use.
|
|
52
|
+
* Call this whenever a mcp__claude_ai_* tool is seen in a tool_use block.
|
|
53
|
+
*/
|
|
54
|
+
export declare function recordClaudeIntegrationUse(toolName: string): void;
|
|
55
|
+
/** Get all discovered Claude Desktop integrations as a list. */
|
|
56
|
+
export declare function getClaudeIntegrations(): ClaudeIntegration[];
|
|
57
|
+
/**
|
|
58
|
+
* Bootstrap integrations from the audit log.
|
|
59
|
+
* Call once on startup to seed the integrations file from historical data.
|
|
60
|
+
*/
|
|
61
|
+
export declare function bootstrapClaudeIntegrationsFromAuditLog(auditLogPath: string): void;
|
|
62
|
+
//# sourceMappingURL=mcp-bridge.d.ts.map
|