clementine-agent 1.18.136 → 1.18.138
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/dist/agent/mcp-bridge.d.ts +1 -0
- package/dist/agent/mcp-bridge.js +9 -1
- package/dist/agent/self-improve.js +16 -52
- package/dist/cli/dashboard.js +18 -66
- package/dist/types.d.ts +2 -4
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* option alongside Clementine's own MCP server.
|
|
7
7
|
*/
|
|
8
8
|
import type { ManagedMcpServer } from '../types.js';
|
|
9
|
+
export declare const KNOWN_MCP_DESCRIPTIONS: Record<string, string>;
|
|
9
10
|
/** Discover all available MCP servers from Claude Desktop, Claude Code, and user config. */
|
|
10
11
|
export declare function discoverMcpServers(): ManagedMcpServer[];
|
|
11
12
|
/** Get enabled MCP servers as SDK-compatible config, filtered by allowed list. */
|
package/dist/agent/mcp-bridge.js
CHANGED
|
@@ -16,7 +16,12 @@ const MCP_SERVERS_FILE = path.join(BASE_DIR, 'mcp-servers.json');
|
|
|
16
16
|
const INTEGRATIONS_FILE = path.join(BASE_DIR, 'claude-integrations.json');
|
|
17
17
|
const CACHE_TTL_MS = 60_000; // 60s cache
|
|
18
18
|
// ── Known server descriptions ───────────────────────────────────────
|
|
19
|
-
|
|
19
|
+
//
|
|
20
|
+
// Single source of truth for MCP server descriptions. Exported so the
|
|
21
|
+
// dashboard, builder UI, and any other surface that wants a friendly
|
|
22
|
+
// label for a known server name can pull from one place — keeps chat
|
|
23
|
+
// and dashboard aligned.
|
|
24
|
+
export const KNOWN_MCP_DESCRIPTIONS = {
|
|
20
25
|
slack: 'Slack workspace messaging and channels',
|
|
21
26
|
linear: 'Linear issue tracking and project management',
|
|
22
27
|
notion: 'Notion workspace — pages, databases, search',
|
|
@@ -39,7 +44,10 @@ const KNOWN_DESCRIPTIONS = {
|
|
|
39
44
|
discord: 'Discord bot integration',
|
|
40
45
|
imessage: 'iMessage — read and send messages on macOS',
|
|
41
46
|
figma: 'Figma design files — read, inspect, and export',
|
|
47
|
+
serena: 'Code-aware AI assistant',
|
|
42
48
|
};
|
|
49
|
+
// Internal alias kept so the existing read sites in this file keep working.
|
|
50
|
+
const KNOWN_DESCRIPTIONS = KNOWN_MCP_DESCRIPTIONS;
|
|
43
51
|
// ── Cache ────────────────────────────────────────────────────────────
|
|
44
52
|
let _cachedServers = null;
|
|
45
53
|
let _cacheExpiry = 0;
|
|
@@ -14,7 +14,7 @@ import matter from 'gray-matter';
|
|
|
14
14
|
import { load as yamlLoad } from 'js-yaml';
|
|
15
15
|
import path from 'node:path';
|
|
16
16
|
import pino from 'pino';
|
|
17
|
-
import { BASE_DIR, SELF_IMPROVE_DIR, SOUL_FILE,
|
|
17
|
+
import { BASE_DIR, SELF_IMPROVE_DIR, SOUL_FILE, CRON_FILE, WORKFLOWS_DIR, VAULT_DIR, MEMORY_DB_PATH, AGENTS_DIR, CRON_REFLECTIONS_DIR, GOALS_DIR, } from '../config.js';
|
|
18
18
|
import { listAllGoals } from '../tools/shared.js';
|
|
19
19
|
import { MemoryStore } from '../memory/store.js';
|
|
20
20
|
const logger = pino({ name: 'clementine.self-improve' });
|
|
@@ -26,15 +26,18 @@ const DEFAULT_CONFIG = {
|
|
|
26
26
|
acceptThreshold: 0.7,
|
|
27
27
|
surfaceThreshold: 0.85,
|
|
28
28
|
plateauLimit: 3,
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
29
|
+
// Areas the autoresearch loop targets. Removed in 1.18.137 cleanup:
|
|
30
|
+
// 'source' (engine TS edits — quarantined since Phase 1, every layer rejected)
|
|
31
|
+
// 'communication' (AGENTS.md — interactive-only; loop fires from cron so
|
|
32
|
+
// improvements are invisible to the next evaluation pass)
|
|
33
|
+
// 'memory' (MEMORY.md is rewritten by the extraction pipeline; proposals
|
|
34
|
+
// get clobbered within days)
|
|
35
|
+
// Each remaining area maps to a file the runtime actually consumes.
|
|
32
36
|
areas: [
|
|
33
|
-
'soul', 'cron', 'workflow', '
|
|
37
|
+
'soul', 'cron', 'workflow', 'agent', 'goal',
|
|
34
38
|
'advisor-rule', 'prompt-override', 'skill',
|
|
35
39
|
],
|
|
36
40
|
autoApply: true,
|
|
37
|
-
sourceMode: 'skip',
|
|
38
41
|
};
|
|
39
42
|
// ── Paths ────────────────────────────────────────────────────────────
|
|
40
43
|
const EXPERIMENT_LOG = path.join(SELF_IMPROVE_DIR, 'experiment-log.jsonl');
|
|
@@ -139,9 +142,10 @@ function checkDrift(proposedContent) {
|
|
|
139
142
|
return { ok: similarity >= DRIFT_SIMILARITY_THRESHOLD, similarity };
|
|
140
143
|
}
|
|
141
144
|
/** Classify the risk level of a proposed change.
|
|
142
|
-
* - low: agent prompts, individual cron job prompts,
|
|
143
|
-
*
|
|
144
|
-
* -
|
|
145
|
+
* - low: agent prompts, individual cron job prompts, workflows, advisor
|
|
146
|
+
* rules, prompt overrides — small-blast-radius config files
|
|
147
|
+
* - medium: SOUL.md, goals, skills — fan out across many runs; the owner
|
|
148
|
+
* should review before they ship
|
|
145
149
|
*/
|
|
146
150
|
function classifyRisk(area) {
|
|
147
151
|
switch (area) {
|
|
@@ -150,7 +154,7 @@ function classifyRisk(area) {
|
|
|
150
154
|
case 'workflow': return 'low';
|
|
151
155
|
case 'advisor-rule': return 'low'; // YAML files, hot-reloaded, easily deleted
|
|
152
156
|
case 'prompt-override': return 'low'; // Markdown files, hot-reloaded, easily deleted
|
|
153
|
-
// 1.18.136 — 'skill' is
|
|
157
|
+
// 1.18.136 — 'skill' is a first-class self-improve target.
|
|
154
158
|
// Skills are user-facing recipes that fire across many tasks; a
|
|
155
159
|
// bad change can cascade into every cron that pins them. Medium
|
|
156
160
|
// tier so changes always surface for owner approval, never auto-
|
|
@@ -158,11 +162,8 @@ function classifyRisk(area) {
|
|
|
158
162
|
// proposal can even reach the queue (see validateProposal).
|
|
159
163
|
case 'skill': return 'medium';
|
|
160
164
|
case 'soul': return 'medium'; // Core personality — needs approval
|
|
161
|
-
case 'communication': return 'medium'; // Global operating instructions
|
|
162
|
-
case 'memory': return 'medium'; // Memory config
|
|
163
165
|
case 'goal': return 'medium'; // New goals need owner review before activating
|
|
164
|
-
|
|
165
|
-
default: return 'high';
|
|
166
|
+
default: return 'medium'; // Unknown area → safest default
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
export const USER_MODEL_SLOT_KEYS = ['user_facts', 'goals', 'relationships', 'agent_persona'];
|
|
@@ -567,23 +568,6 @@ export class SelfImproveLoop {
|
|
|
567
568
|
state.pendingApprovals++;
|
|
568
569
|
}
|
|
569
570
|
}
|
|
570
|
-
else if (this.config.autoApply && risk === 'high') {
|
|
571
|
-
// High-risk: behavior depends on sourceMode config
|
|
572
|
-
if (this.config.sourceMode === 'skip') {
|
|
573
|
-
logger.info({ id, area: proposal.area, risk }, 'Skipped high-risk proposal in auto mode');
|
|
574
|
-
experiment.approvalStatus = 'denied';
|
|
575
|
-
experiment.reason = 'High-risk area blocked in autonomous mode (sourceMode=skip)';
|
|
576
|
-
}
|
|
577
|
-
else {
|
|
578
|
-
// propose-only: save for human review, never auto-apply
|
|
579
|
-
await this.savePendingChange(experiment, before);
|
|
580
|
-
state.pendingApprovals++;
|
|
581
|
-
if (onProposal) {
|
|
582
|
-
await onProposal(experiment);
|
|
583
|
-
}
|
|
584
|
-
logger.info({ id, area: proposal.area, risk }, 'Saved high-risk proposal for human review');
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
571
|
else {
|
|
588
572
|
// Medium-risk or manual mode: save as pending for approval
|
|
589
573
|
await this.savePendingChange(experiment, before);
|
|
@@ -1254,12 +1238,6 @@ export class SelfImproveLoop {
|
|
|
1254
1238
|
const agentFile = path.join(AGENTS_DIR, target, 'agent.md');
|
|
1255
1239
|
return existsSync(agentFile) ? readFileSync(agentFile, 'utf-8') : '';
|
|
1256
1240
|
}
|
|
1257
|
-
case 'communication':
|
|
1258
|
-
return existsSync(AGENTS_FILE) ? readFileSync(AGENTS_FILE, 'utf-8') : '';
|
|
1259
|
-
case 'memory': {
|
|
1260
|
-
const memoryFile = path.join(VAULT_DIR, '00-System', 'MEMORY.md');
|
|
1261
|
-
return existsSync(memoryFile) ? readFileSync(memoryFile, 'utf-8') : '';
|
|
1262
|
-
}
|
|
1263
1241
|
case 'goal': {
|
|
1264
1242
|
// target = "{owner}" e.g. "clementine" or an agent slug
|
|
1265
1243
|
const owner = target.split('/')[0];
|
|
@@ -1363,11 +1341,6 @@ export class SelfImproveLoop {
|
|
|
1363
1341
|
if (!targetPath) {
|
|
1364
1342
|
return `Cannot resolve target path for area=${pending.area}, target=${pending.target}`;
|
|
1365
1343
|
}
|
|
1366
|
-
// 'source' area is deprecated (Phase 1 quarantine). Reject up-front so a
|
|
1367
|
-
// misbehaving proposal cannot reach the safeSourceEdit primitive.
|
|
1368
|
-
if (pending.area === 'source') {
|
|
1369
|
-
return 'source area is deprecated — propose advisor-rule or prompt-override instead';
|
|
1370
|
-
}
|
|
1371
1344
|
// Goal area: parse JSON, inject required fields, ensure parent dir exists
|
|
1372
1345
|
if (pending.area === 'goal') {
|
|
1373
1346
|
try {
|
|
@@ -2124,10 +2097,6 @@ export class SelfImproveLoop {
|
|
|
2124
2097
|
case 'agent': {
|
|
2125
2098
|
return path.join(AGENTS_DIR, target, 'agent.md');
|
|
2126
2099
|
}
|
|
2127
|
-
case 'communication':
|
|
2128
|
-
return AGENTS_FILE;
|
|
2129
|
-
case 'memory':
|
|
2130
|
-
return path.join(VAULT_DIR, '00-System', 'MEMORY.md');
|
|
2131
2100
|
case 'goal': {
|
|
2132
2101
|
// target = "{owner}/{goalSlug}" e.g. "clementine/<goal-slug>" or "<agent-slug>/<goal-slug>"
|
|
2133
2102
|
const [owner, goalSlug] = target.split('/');
|
|
@@ -2184,7 +2153,7 @@ export function validateProposal(area, target, proposedChange) {
|
|
|
2184
2153
|
if (!proposedChange.trim()) {
|
|
2185
2154
|
return { valid: false, error: 'Proposed change is empty' };
|
|
2186
2155
|
}
|
|
2187
|
-
if (['soul', 'cron', 'workflow', 'agent'
|
|
2156
|
+
if (['soul', 'cron', 'workflow', 'agent'].includes(area)) {
|
|
2188
2157
|
try {
|
|
2189
2158
|
matter(proposedChange);
|
|
2190
2159
|
}
|
|
@@ -2220,11 +2189,6 @@ export function validateProposal(area, target, proposedChange) {
|
|
|
2220
2189
|
return { valid: false, error: `CRON.md validation failed: ${err}` };
|
|
2221
2190
|
}
|
|
2222
2191
|
}
|
|
2223
|
-
if (area === 'source') {
|
|
2224
|
-
// Deprecated — Phase 1 quarantined source self-edit. Reject up front so
|
|
2225
|
-
// a misbehaving LLM proposal doesn't even get cached.
|
|
2226
|
-
return { valid: false, error: 'source area is deprecated; propose advisor-rule or prompt-override instead' };
|
|
2227
|
-
}
|
|
2228
2192
|
if (area === 'skill') {
|
|
2229
2193
|
// 1.18.136 — skill body validation. The proposedChange is the FULL
|
|
2230
2194
|
// SKILL.md (frontmatter + body). Anthropic spec rules: name must
|
package/dist/cli/dashboard.js
CHANGED
|
@@ -17,7 +17,7 @@ import matter from 'gray-matter';
|
|
|
17
17
|
import cron from 'node-cron';
|
|
18
18
|
import { TunnelManager } from './tunnel.js';
|
|
19
19
|
import { AgentManager } from '../agent/agent-manager.js';
|
|
20
|
-
import { discoverMcpServers, getClaudeIntegrations } from '../agent/mcp-bridge.js';
|
|
20
|
+
import { discoverMcpServers, getClaudeIntegrations, KNOWN_MCP_DESCRIPTIONS } from '../agent/mcp-bridge.js';
|
|
21
21
|
import { buildBuilderEnrichedMessage, builderSessionKey } from '../dashboard/builder/prompt.js';
|
|
22
22
|
import { AGENTS_DIR, MEMORY_FILE, SESSIONS_FILE, applyOneMillionContextRecovery, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../config.js';
|
|
23
23
|
import { parseTasks } from '../tools/shared.js';
|
|
@@ -862,72 +862,24 @@ function getApiConnectionStatus() {
|
|
|
862
862
|
'Web Search': true,
|
|
863
863
|
};
|
|
864
864
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
'firecrawl': 'Web crawling and scraping',
|
|
877
|
-
'exa': 'Neural web search',
|
|
878
|
-
'linear': 'Linear issue tracking',
|
|
879
|
-
'discord': 'Discord bot integration',
|
|
880
|
-
'gitlab': 'GitLab repository management',
|
|
881
|
-
'greptile': 'Codebase search and understanding',
|
|
882
|
-
'serena': 'Code-aware AI assistant',
|
|
883
|
-
'terraform': 'Infrastructure as code management',
|
|
884
|
-
};
|
|
865
|
+
/**
|
|
866
|
+
* Surface global MCP servers for the Tools & MCP catalog.
|
|
867
|
+
*
|
|
868
|
+
* 1.18.138 — collapsed onto `discoverMcpServers` from mcp-bridge so the
|
|
869
|
+
* dashboard's view exactly matches what chat actually has access to. The
|
|
870
|
+
* old standalone walker (with its own description table + Claude Code
|
|
871
|
+
* `enabledPlugins` enumeration) was misleading: those plugins live inside
|
|
872
|
+
* Claude Code itself and Clementine cannot launch them, so listing them as
|
|
873
|
+
* "available tools" was a UX bug. Now the same source-of-truth feeds both
|
|
874
|
+
* surfaces — KNOWN_MCP_DESCRIPTIONS in mcp-bridge.ts owns the labels.
|
|
875
|
+
*/
|
|
885
876
|
function discoverGlobalMcpServers() {
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
const data = JSON.parse(readFileSync(desktopConfig, 'utf-8'));
|
|
893
|
-
for (const name of Object.keys(data.mcpServers ?? {})) {
|
|
894
|
-
if (seen.has(name.toLowerCase()))
|
|
895
|
-
continue;
|
|
896
|
-
seen.add(name.toLowerCase());
|
|
897
|
-
servers.push({
|
|
898
|
-
name,
|
|
899
|
-
description: MCP_SERVER_DESCRIPTIONS[name] ?? `${name} MCP server`,
|
|
900
|
-
type: 'global-mcp',
|
|
901
|
-
connected: true,
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
catch { /* ignore */ }
|
|
907
|
-
// 2. Claude Code enabled plugins (from settings.json)
|
|
908
|
-
try {
|
|
909
|
-
const settingsFile = path.join(os.homedir(), '.claude', 'settings.json');
|
|
910
|
-
if (existsSync(settingsFile)) {
|
|
911
|
-
const settings = JSON.parse(readFileSync(settingsFile, 'utf-8'));
|
|
912
|
-
const plugins = settings.enabledPlugins ?? {};
|
|
913
|
-
for (const [key, enabled] of Object.entries(plugins)) {
|
|
914
|
-
if (!enabled)
|
|
915
|
-
continue;
|
|
916
|
-
const pluginName = key.split('@')[0];
|
|
917
|
-
if (seen.has(pluginName.toLowerCase()))
|
|
918
|
-
continue;
|
|
919
|
-
seen.add(pluginName.toLowerCase());
|
|
920
|
-
servers.push({
|
|
921
|
-
name: pluginName,
|
|
922
|
-
description: MCP_SERVER_DESCRIPTIONS[pluginName] ?? `${pluginName} plugin`,
|
|
923
|
-
type: 'global-mcp',
|
|
924
|
-
connected: true,
|
|
925
|
-
});
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
catch { /* ignore */ }
|
|
930
|
-
return servers;
|
|
877
|
+
return discoverMcpServers().map(s => ({
|
|
878
|
+
name: s.name,
|
|
879
|
+
description: s.description ?? KNOWN_MCP_DESCRIPTIONS[s.name] ?? `${s.name} MCP server`,
|
|
880
|
+
type: 'global-mcp',
|
|
881
|
+
connected: s.enabled !== false,
|
|
882
|
+
}));
|
|
931
883
|
}
|
|
932
884
|
// ── Metrics computation ──────────────────────────────────────────────
|
|
933
885
|
function computeMetrics() {
|
package/dist/types.d.ts
CHANGED
|
@@ -957,7 +957,7 @@ export interface SelfImproveExperiment {
|
|
|
957
957
|
startedAt: string;
|
|
958
958
|
finishedAt: string;
|
|
959
959
|
durationMs: number;
|
|
960
|
-
area: 'soul' | 'cron' | 'workflow' | '
|
|
960
|
+
area: 'soul' | 'cron' | 'workflow' | 'agent' | 'goal' | 'advisor-rule' | 'prompt-override' | 'skill';
|
|
961
961
|
target: string;
|
|
962
962
|
hypothesis: string;
|
|
963
963
|
proposedChange: string;
|
|
@@ -1014,13 +1014,11 @@ export interface SelfImproveConfig {
|
|
|
1014
1014
|
*/
|
|
1015
1015
|
surfaceThreshold?: number;
|
|
1016
1016
|
plateauLimit: number;
|
|
1017
|
-
areas: ('soul' | 'cron' | 'workflow' | '
|
|
1017
|
+
areas: ('soul' | 'cron' | 'workflow' | 'agent' | 'goal' | 'advisor-rule' | 'prompt-override' | 'skill')[];
|
|
1018
1018
|
/** Enable tiered auto-apply: low-risk changes apply without approval. Default: false. */
|
|
1019
1019
|
autoApply?: boolean;
|
|
1020
1020
|
/** Target a specific agent slug (for per-agent improvement cycles). */
|
|
1021
1021
|
agentSlug?: string;
|
|
1022
|
-
/** How to handle source code proposals. 'skip' = drop silently, 'propose-only' = save for human review. Default: 'propose-only'. */
|
|
1023
|
-
sourceMode?: 'skip' | 'propose-only';
|
|
1024
1022
|
}
|
|
1025
1023
|
export interface RestartSentinel {
|
|
1026
1024
|
previousPid: number;
|