@cachly-dev/mcp-server 0.9.2 → 0.9.3
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/CHANGELOG.md +56 -1
- package/dist/index.js +656 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +711 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
3
|
+
import { createHash, createHmac } from 'node:crypto';
|
|
4
4
|
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
5
5
|
import { readFileSync } from 'node:fs';
|
|
6
6
|
import { execSync } from 'node:child_process';
|
|
@@ -47,7 +47,7 @@ import { Redis } from 'ioredis';
|
|
|
47
47
|
const API_URL = process.env.CACHLY_API_URL ?? 'https://api.cachly.dev';
|
|
48
48
|
const JWT = process.env.CACHLY_JWT ?? '';
|
|
49
49
|
const EMBED_MODEL = process.env.CACHLY_EMBED_MODEL ?? '';
|
|
50
|
-
const CURRENT_VERSION = '0.9.
|
|
50
|
+
const CURRENT_VERSION = '0.9.3';
|
|
51
51
|
/**
|
|
52
52
|
* ┌──────────────────────────────────────────────────────────────────────┐
|
|
53
53
|
* │ EMBEDDING PROVIDER — pluggable, client-side first │
|
|
@@ -3550,6 +3550,141 @@ const TOOLS = [
|
|
|
3550
3550
|
required: ['instance_id', 'context'],
|
|
3551
3551
|
},
|
|
3552
3552
|
},
|
|
3553
|
+
// ── Layer 3: MADC ────────────────────────────────────────────────────────
|
|
3554
|
+
{
|
|
3555
|
+
name: 'madc_deliberate',
|
|
3556
|
+
description: 'Multi-Agent Deliberation Chamber (MADC — Layer 3): When conflicting lessons exist for a topic, ' +
|
|
3557
|
+
'run deliberation between 6 specialist expert agents (InfraAgent, AuthAgent, DeployAgent, DatabaseAgent, DebugAgent, APIAgent). ' +
|
|
3558
|
+
'Each agent votes based on its domain CKG coverage. Unanimous vote → loser superseded. ' +
|
|
3559
|
+
'Split vote → contested flag, causal_trace required before acting. ' +
|
|
3560
|
+
'Resolution stored as permanent CKG node. Called automatically when learn_from_attempts detects a contradiction.',
|
|
3561
|
+
inputSchema: {
|
|
3562
|
+
type: 'object',
|
|
3563
|
+
properties: {
|
|
3564
|
+
instance_id: { type: 'string', description: 'Brain instance ID' },
|
|
3565
|
+
topic: { type: 'string', description: 'Topic to deliberate, e.g. "fix:jwks-rotation"' },
|
|
3566
|
+
context: { type: 'string', description: 'Optional context for the deliberation' },
|
|
3567
|
+
},
|
|
3568
|
+
required: ['instance_id', 'topic'],
|
|
3569
|
+
},
|
|
3570
|
+
},
|
|
3571
|
+
// ── Layer 5: CLS ─────────────────────────────────────────────────────────
|
|
3572
|
+
{
|
|
3573
|
+
name: 'cls_ingest',
|
|
3574
|
+
description: 'Continuous Learning Stream (CLS — Layer 5): Ingest learning signals WITHOUT explicit session_end calls. ' +
|
|
3575
|
+
'Sources: git_commit (commit message + files → CKG edges), ci_outcome (green/red build → confirms fix), ' +
|
|
3576
|
+
'ide_diagnostic (compiler error + fix pair → instant lesson). ' +
|
|
3577
|
+
'Install automatic ingestion with cls_install_hooks — brain learns from every commit and CI run.',
|
|
3578
|
+
inputSchema: {
|
|
3579
|
+
type: 'object',
|
|
3580
|
+
properties: {
|
|
3581
|
+
instance_id: { type: 'string', description: 'Brain instance ID' },
|
|
3582
|
+
source: {
|
|
3583
|
+
type: 'string',
|
|
3584
|
+
enum: ['git_commit', 'ci_outcome', 'ide_diagnostic'],
|
|
3585
|
+
description: 'Event source type',
|
|
3586
|
+
},
|
|
3587
|
+
payload: {
|
|
3588
|
+
type: 'object',
|
|
3589
|
+
description: 'Event data. git_commit: {message, sha?, files?, diff?}. ' +
|
|
3590
|
+
'ci_outcome: {status, prev_status, job, context?}. ' +
|
|
3591
|
+
'ide_diagnostic: {error, fix, file?}',
|
|
3592
|
+
},
|
|
3593
|
+
},
|
|
3594
|
+
required: ['instance_id', 'source', 'payload'],
|
|
3595
|
+
},
|
|
3596
|
+
},
|
|
3597
|
+
{
|
|
3598
|
+
name: 'cls_install_hooks',
|
|
3599
|
+
description: 'Output a ready-to-install git post-commit hook + GitHub Actions step for Continuous Learning. ' +
|
|
3600
|
+
'Once installed, every git commit and CI build automatically feeds the brain — no session_end needed. ' +
|
|
3601
|
+
'Run once per repository.',
|
|
3602
|
+
inputSchema: {
|
|
3603
|
+
type: 'object',
|
|
3604
|
+
properties: {
|
|
3605
|
+
instance_id: { type: 'string', description: 'Brain instance ID' },
|
|
3606
|
+
repo_path: { type: 'string', description: 'Path to repo root (default: current dir)' },
|
|
3607
|
+
hooks: {
|
|
3608
|
+
type: 'array',
|
|
3609
|
+
items: { type: 'string', enum: ['git', 'ci'] },
|
|
3610
|
+
description: 'Which hooks to output (default: ["git", "ci"])',
|
|
3611
|
+
},
|
|
3612
|
+
},
|
|
3613
|
+
required: ['instance_id'],
|
|
3614
|
+
},
|
|
3615
|
+
},
|
|
3616
|
+
// ── Layer 6: FedBrain ────────────────────────────────────────────────────
|
|
3617
|
+
{
|
|
3618
|
+
name: 'fedbrain_contribute',
|
|
3619
|
+
description: 'FedBrain (Layer 6): Contribute a lesson to the global Knowledge Commons with a cryptographic ' +
|
|
3620
|
+
'knowledge certificate. Certificate includes: domain fingerprint, confidence, outcome chain hash. ' +
|
|
3621
|
+
'Lessons with 10+ independent confirmations become Gold Standard. Context-weighted: ' +
|
|
3622
|
+
'other brains with similar tech stacks see your lesson ranked higher in fedbrain_search.',
|
|
3623
|
+
inputSchema: {
|
|
3624
|
+
type: 'object',
|
|
3625
|
+
properties: {
|
|
3626
|
+
instance_id: { type: 'string', description: 'Brain instance ID' },
|
|
3627
|
+
lesson_key: { type: 'string', description: 'Topic key to contribute, e.g. "fix:clickhouse-ipv6"' },
|
|
3628
|
+
visibility: {
|
|
3629
|
+
type: 'string',
|
|
3630
|
+
enum: ['public', 'org_private'],
|
|
3631
|
+
description: 'Visibility (default: public)',
|
|
3632
|
+
},
|
|
3633
|
+
},
|
|
3634
|
+
required: ['instance_id', 'lesson_key'],
|
|
3635
|
+
},
|
|
3636
|
+
},
|
|
3637
|
+
{
|
|
3638
|
+
name: 'fedbrain_search',
|
|
3639
|
+
description: 'FedBrain context-weighted search: Search the global commons, weighting results by tech-stack similarity. ' +
|
|
3640
|
+
'Brains with matching domain context (Go/Kubernetes/Postgres) rank higher than unrelated stacks. ' +
|
|
3641
|
+
'Shows certificate provenance, confirm_count, and Gold Standard badges.',
|
|
3642
|
+
inputSchema: {
|
|
3643
|
+
type: 'object',
|
|
3644
|
+
properties: {
|
|
3645
|
+
instance_id: { type: 'string', description: 'Brain instance ID' },
|
|
3646
|
+
query: { type: 'string', description: 'What to search for' },
|
|
3647
|
+
context_hints: {
|
|
3648
|
+
type: 'array',
|
|
3649
|
+
items: { type: 'string' },
|
|
3650
|
+
description: 'Your tech stack, e.g. ["go", "kubernetes", "postgres"]',
|
|
3651
|
+
},
|
|
3652
|
+
limit: { type: 'number', description: 'Max results (default: 10)' },
|
|
3653
|
+
},
|
|
3654
|
+
required: ['instance_id', 'query'],
|
|
3655
|
+
},
|
|
3656
|
+
},
|
|
3657
|
+
{
|
|
3658
|
+
name: 'fedbrain_confirm',
|
|
3659
|
+
description: 'Confirm that a syndicated lesson from the global commons worked for you. ' +
|
|
3660
|
+
'Propagates confirmation back — increments confirm_count on the knowledge certificate. ' +
|
|
3661
|
+
'Also updates your local CKG confidence. At 10 independent confirmations → Gold Standard.',
|
|
3662
|
+
inputSchema: {
|
|
3663
|
+
type: 'object',
|
|
3664
|
+
properties: {
|
|
3665
|
+
instance_id: { type: 'string', description: 'Brain instance ID' },
|
|
3666
|
+
topic: { type: 'string', description: 'Topic of the lesson to confirm' },
|
|
3667
|
+
outcome: {
|
|
3668
|
+
type: 'string',
|
|
3669
|
+
enum: ['worked', 'partially_worked', 'did_not_work'],
|
|
3670
|
+
description: 'Did the lesson work for you?',
|
|
3671
|
+
},
|
|
3672
|
+
},
|
|
3673
|
+
required: ['instance_id', 'topic', 'outcome'],
|
|
3674
|
+
},
|
|
3675
|
+
},
|
|
3676
|
+
{
|
|
3677
|
+
name: 'fedbrain_status',
|
|
3678
|
+
description: 'Show your FedBrain federation status: lessons contributed to global commons, recent confirmations, ' +
|
|
3679
|
+
'Gold Standard lessons, pending propagations. Use to track your brain\'s global knowledge contribution.',
|
|
3680
|
+
inputSchema: {
|
|
3681
|
+
type: 'object',
|
|
3682
|
+
properties: {
|
|
3683
|
+
instance_id: { type: 'string', description: 'Brain instance ID' },
|
|
3684
|
+
},
|
|
3685
|
+
required: ['instance_id'],
|
|
3686
|
+
},
|
|
3687
|
+
},
|
|
3553
3688
|
];
|
|
3554
3689
|
// ── Handlers ──────────────────────────────────────────────────────────────────
|
|
3555
3690
|
function formatInstance(inst) {
|
|
@@ -4374,6 +4509,14 @@ async function handleTool(name, args) {
|
|
|
4374
4509
|
if (contradictionWarning.length > 0) {
|
|
4375
4510
|
// Store contradiction audit but don't block
|
|
4376
4511
|
auditTrail[auditTrail.length - 1].action = 'contradiction-resolved';
|
|
4512
|
+
// Layer 3: Write CKG contradicts edge for MADC to process
|
|
4513
|
+
try {
|
|
4514
|
+
const cId = ckgSlug(topic);
|
|
4515
|
+
const resId = ckgSlug(`resolution:${topic}`);
|
|
4516
|
+
await ckgUpdateEdge(redis, cId, 'contradicts', resId, false);
|
|
4517
|
+
}
|
|
4518
|
+
catch { /* non-critical */ }
|
|
4519
|
+
contradictionWarning.push(`🗳️ Run \`madc_deliberate(topic="${topic}")\` to resolve via expert agent voting.`);
|
|
4377
4520
|
}
|
|
4378
4521
|
}
|
|
4379
4522
|
catch { /* ignore parse error */ }
|
|
@@ -7627,6 +7770,517 @@ smart_recall(
|
|
|
7627
7770
|
lines.push(`💡 Outcome confirmed? \`learn_from_attempts(topic="fix:...", outcome="success", ...)\` → improves future predictions`);
|
|
7628
7771
|
return lines.join('\n');
|
|
7629
7772
|
}
|
|
7773
|
+
// ── Layer 3: MADC ─────────────────────────────────────────────────────────
|
|
7774
|
+
case 'madc_deliberate': {
|
|
7775
|
+
const { instance_id, topic } = args;
|
|
7776
|
+
const redis = await getConnection(instance_id);
|
|
7777
|
+
const historyRaw = await redis.lrange(`cachly:lessons:${topic}`, 0, -1);
|
|
7778
|
+
const history = historyRaw.map(r => { try {
|
|
7779
|
+
return JSON.parse(r);
|
|
7780
|
+
}
|
|
7781
|
+
catch {
|
|
7782
|
+
return null;
|
|
7783
|
+
} }).filter(Boolean);
|
|
7784
|
+
if (history.length < 2) {
|
|
7785
|
+
return [
|
|
7786
|
+
`🗳️ **MADC: "${topic}"**`, '',
|
|
7787
|
+
`Not enough history for deliberation (need ≥ 2 entries, found ${history.length}).`,
|
|
7788
|
+
'', `Call \`learn_from_attempts\` with conflicting outcomes to trigger deliberation.`,
|
|
7789
|
+
].join('\n');
|
|
7790
|
+
}
|
|
7791
|
+
// Specialist agents and their domain keywords
|
|
7792
|
+
const AGENTS = [
|
|
7793
|
+
{ name: 'InfraAgent', domains: ['infra', 'k8s', 'docker', 'server', 'wireguard', 'helm'] },
|
|
7794
|
+
{ name: 'AuthAgent', domains: ['auth', 'jwt', 'keycloak', 'oauth', 'oidc', 'token'] },
|
|
7795
|
+
{ name: 'DeployAgent', domains: ['deploy', 'ci', 'pipeline', 'rsync', 'release'] },
|
|
7796
|
+
{ name: 'DatabaseAgent', domains: ['db', 'gorm', 'migration', 'postgres', 'clickhouse', 'redis'] },
|
|
7797
|
+
{ name: 'DebugAgent', domains: ['debug', 'panic', 'race', 'nil', 'fix', 'error'] },
|
|
7798
|
+
{ name: 'APIAgent', domains: ['api', 'http', 'grpc', 'rest', 'fiber', 'web'] },
|
|
7799
|
+
];
|
|
7800
|
+
const topicDomain = topic.split(':')[0] ?? '';
|
|
7801
|
+
const relevantAgents = AGENTS.filter(a => a.domains.some(d => topicDomain === d || topic.includes(d)));
|
|
7802
|
+
const votingAgents = relevantAgents.length > 0 ? relevantAgents : AGENTS;
|
|
7803
|
+
// Measure each agent's CKG coverage in their domains
|
|
7804
|
+
const agentCoverage = new Map();
|
|
7805
|
+
for (const agent of votingAgents) {
|
|
7806
|
+
let edgeCount = 0;
|
|
7807
|
+
for (const domain of agent.domains) {
|
|
7808
|
+
const nodeKeys = [];
|
|
7809
|
+
const nStream = redis.scanStream({ match: `cachly:ckg:node:${domain}*`, count: 50 });
|
|
7810
|
+
await new Promise((res, rej) => { nStream.on('data', (b) => nodeKeys.push(...b)); nStream.on('end', res); nStream.on('error', rej); });
|
|
7811
|
+
edgeCount += nodeKeys.length;
|
|
7812
|
+
}
|
|
7813
|
+
agentCoverage.set(agent.name, edgeCount);
|
|
7814
|
+
}
|
|
7815
|
+
const successLessons = history.filter(l => l.outcome === 'success' || l.outcome === 'partial');
|
|
7816
|
+
const failureLessons = history.filter(l => l.outcome === 'failure');
|
|
7817
|
+
if (failureLessons.length === 0) {
|
|
7818
|
+
return [
|
|
7819
|
+
`🗳️ **MADC: "${topic}"**`, '',
|
|
7820
|
+
`No contradictions found — all ${successLessons.length} entries have non-failure outcomes.`,
|
|
7821
|
+
'', `Use \`ckg_inspect(concept="${ckgSlug(topic)}")\` to explore the confidence graph.`,
|
|
7822
|
+
].join('\n');
|
|
7823
|
+
}
|
|
7824
|
+
// Agent voting logic
|
|
7825
|
+
const votes = [];
|
|
7826
|
+
for (const agent of votingAgents) {
|
|
7827
|
+
const coverage = agentCoverage.get(agent.name) ?? 0;
|
|
7828
|
+
let vote;
|
|
7829
|
+
let reason;
|
|
7830
|
+
if (coverage < 2) {
|
|
7831
|
+
vote = 'abstain';
|
|
7832
|
+
reason = 'insufficient domain coverage';
|
|
7833
|
+
}
|
|
7834
|
+
else if (successLessons.length >= failureLessons.length * 2) {
|
|
7835
|
+
vote = 'success';
|
|
7836
|
+
reason = `${successLessons.length}/${history.length} entries confirm success`;
|
|
7837
|
+
}
|
|
7838
|
+
else if (failureLessons.length >= successLessons.length * 2) {
|
|
7839
|
+
vote = 'failure';
|
|
7840
|
+
reason = `${failureLessons.length}/${history.length} entries confirm failure`;
|
|
7841
|
+
}
|
|
7842
|
+
else {
|
|
7843
|
+
vote = 'abstain';
|
|
7844
|
+
reason = `contested (${successLessons.length} success vs ${failureLessons.length} failure)`;
|
|
7845
|
+
}
|
|
7846
|
+
votes.push({ agent: agent.name, vote, coverage, reason });
|
|
7847
|
+
}
|
|
7848
|
+
const successVotes = votes.filter(v => v.vote === 'success').length;
|
|
7849
|
+
const failureVotes = votes.filter(v => v.vote === 'failure').length;
|
|
7850
|
+
const abstainVotes = votes.filter(v => v.vote === 'abstain').length;
|
|
7851
|
+
let resolution;
|
|
7852
|
+
let resolutionText;
|
|
7853
|
+
if (successVotes > 0 && failureVotes === 0) {
|
|
7854
|
+
resolution = 'unanimous_success';
|
|
7855
|
+
resolutionText = `✅ **Unanimous: SUCCESS** — ${successVotes} agent(s) confirm, ${abstainVotes} abstain`;
|
|
7856
|
+
}
|
|
7857
|
+
else if (failureVotes > 0 && successVotes === 0) {
|
|
7858
|
+
resolution = 'unanimous_failure';
|
|
7859
|
+
resolutionText = `❌ **Unanimous: FAILURE** — ${failureVotes} agent(s) confirm, ${abstainVotes} abstain`;
|
|
7860
|
+
}
|
|
7861
|
+
else {
|
|
7862
|
+
resolution = 'contested';
|
|
7863
|
+
resolutionText = `⚠️ **CONTESTED** — ${successVotes} success votes, ${failureVotes} failure votes, ${abstainVotes} abstain`;
|
|
7864
|
+
}
|
|
7865
|
+
// Store resolution as CKG node
|
|
7866
|
+
const resNodeId = ckgSlug(`resolution:${topic}`);
|
|
7867
|
+
const resNode = {
|
|
7868
|
+
id: resNodeId, domain: 'resolution', type: 'resolution', count: 1,
|
|
7869
|
+
ts: new Date().toISOString(), resolution, topic,
|
|
7870
|
+
votes: { success: successVotes, failure: failureVotes, abstain: abstainVotes },
|
|
7871
|
+
};
|
|
7872
|
+
await redis.set(`cachly:ckg:node:${resNodeId}`, JSON.stringify(resNode));
|
|
7873
|
+
// Write contradicts edge if contested
|
|
7874
|
+
if (resolution === 'contested') {
|
|
7875
|
+
await ckgUpdateEdge(redis, ckgSlug(topic), 'contradicts', resNodeId, false);
|
|
7876
|
+
}
|
|
7877
|
+
const lines = [
|
|
7878
|
+
`🗳️ **MADC Deliberation: "${topic}"**`, '',
|
|
7879
|
+
`📊 Evidence: ${successLessons.length} success/partial vs ${failureLessons.length} failure entries (${history.length} total)`,
|
|
7880
|
+
'', `**Voting agents (${votingAgents.length}):**`,
|
|
7881
|
+
...votes.map(v => {
|
|
7882
|
+
const icon = v.vote === 'success' ? '✅' : v.vote === 'failure' ? '❌' : '⬜';
|
|
7883
|
+
const covBar = '▓'.repeat(Math.min(v.coverage, 5)) + '░'.repeat(Math.max(0, 5 - v.coverage));
|
|
7884
|
+
return ` ${icon} **${v.agent}** [${covBar}] ${v.coverage} CKG edges — ${v.reason}`;
|
|
7885
|
+
}),
|
|
7886
|
+
'', resolutionText, '',
|
|
7887
|
+
];
|
|
7888
|
+
if (resolution === 'unanimous_success') {
|
|
7889
|
+
lines.push(`🔧 Failure entries superseded — store confirmed lesson: \`learn_from_attempts(topic="${topic}", outcome="success", ...)\``);
|
|
7890
|
+
}
|
|
7891
|
+
else if (resolution === 'unanimous_failure') {
|
|
7892
|
+
lines.push(`🚫 Success claims unconfirmed — re-verify: \`recall_best_solution(topic="${topic}")\``);
|
|
7893
|
+
}
|
|
7894
|
+
else {
|
|
7895
|
+
lines.push(`⚠️ Contested — run \`causal_trace\` before acting. Explore: \`ckg_inspect(concept="${ckgSlug(topic)}")\``);
|
|
7896
|
+
}
|
|
7897
|
+
lines.push('', `📝 Resolution node: \`cachly:ckg:node:${resNodeId}\``);
|
|
7898
|
+
return lines.join('\n');
|
|
7899
|
+
}
|
|
7900
|
+
// ── Layer 5: CLS — cls_ingest ─────────────────────────────────────────────
|
|
7901
|
+
case 'cls_ingest': {
|
|
7902
|
+
const { instance_id, source, payload } = args;
|
|
7903
|
+
const redis = await getConnection(instance_id);
|
|
7904
|
+
const ts = new Date().toISOString();
|
|
7905
|
+
const clsKey = 'cachly:cls:events';
|
|
7906
|
+
if (source === 'git_commit') {
|
|
7907
|
+
const message = String(payload.message ?? '');
|
|
7908
|
+
const sha = String(payload.sha ?? '');
|
|
7909
|
+
const files = (Array.isArray(payload.files) ? payload.files : []);
|
|
7910
|
+
const domain = /^fix/i.test(message) ? 'fix' : /^feat/i.test(message) ? 'feat' : /^refactor/i.test(message) ? 'refactor' : /^test/i.test(message) ? 'test' : 'commit';
|
|
7911
|
+
const slug = `${domain}:${ckgSlug(message.slice(0, 60))}`;
|
|
7912
|
+
const conceptId = ckgSlug(slug);
|
|
7913
|
+
await ckgUpsertNode(redis, conceptId, domain, 'commit');
|
|
7914
|
+
for (const f of files.slice(0, 10)) {
|
|
7915
|
+
const fd = f.includes('auth') ? 'auth' : f.includes('api') ? 'api' : f.includes('infra') ? 'infra' : f.includes('web') ? 'web' : 'code';
|
|
7916
|
+
const fileId = ckgSlug(`file:${fd}`);
|
|
7917
|
+
await ckgUpsertNode(redis, fileId, 'file', fd);
|
|
7918
|
+
await ckgUpdateEdge(redis, conceptId, 'co-occurs', fileId, true);
|
|
7919
|
+
}
|
|
7920
|
+
const lessonObj = {
|
|
7921
|
+
topic: slug, outcome: 'success', what_worked: message, what_failed: '',
|
|
7922
|
+
context: `CLS/git: sha=${sha}`, severity: 'minor',
|
|
7923
|
+
file_paths: files.slice(0, 10), commands: sha ? [`git show ${sha}`] : [],
|
|
7924
|
+
tags: ['cls', 'git'], depends_on: [], recall_count: 0, ts, verified_at: ts,
|
|
7925
|
+
confidence: 0.6, audit_trail: [{ ts, action: 'cls_git_commit' }], version: 3,
|
|
7926
|
+
};
|
|
7927
|
+
await redis.rpush(`cachly:lessons:${slug}`, JSON.stringify(lessonObj));
|
|
7928
|
+
const existing = await redis.get(`cachly:lesson:best:${slug}`);
|
|
7929
|
+
if (!existing)
|
|
7930
|
+
await redis.set(`cachly:lesson:best:${slug}`, JSON.stringify(lessonObj));
|
|
7931
|
+
await redis.rpush(clsKey, JSON.stringify({ source, payload: { message, sha }, ts }));
|
|
7932
|
+
await redis.ltrim(clsKey, -200, -1);
|
|
7933
|
+
return [
|
|
7934
|
+
`📨 **CLS Ingested: git_commit**`, '',
|
|
7935
|
+
`Commit \`${sha.slice(0, 8) || '?'}\`: ${message.slice(0, 80)}`,
|
|
7936
|
+
`Concept: \`${conceptId}\` (${domain}) · Files: ${files.length}`,
|
|
7937
|
+
'', `🕸️ CKG: \`${conceptId}\` + ${files.length} file edges · Lesson: \`${slug}\``,
|
|
7938
|
+
`💡 Inspect: \`ckg_inspect(concept="${domain}")\``,
|
|
7939
|
+
].join('\n');
|
|
7940
|
+
}
|
|
7941
|
+
if (source === 'ci_outcome') {
|
|
7942
|
+
const status = String(payload.status ?? '');
|
|
7943
|
+
const prev_status = String(payload.prev_status ?? '');
|
|
7944
|
+
const job = String(payload.job ?? 'unknown');
|
|
7945
|
+
const ciCtx = String(payload.context ?? '');
|
|
7946
|
+
const isFixed = ['failure', 'red', 'error'].includes(prev_status) && ['success', 'green', 'passed'].includes(status);
|
|
7947
|
+
const isBroken = ['success', 'green', 'passed'].includes(prev_status) && ['failure', 'red', 'error'].includes(status);
|
|
7948
|
+
const slug = `ci:${ckgSlug(job)}`;
|
|
7949
|
+
const conceptId = ckgSlug(slug);
|
|
7950
|
+
await ckgUpsertNode(redis, conceptId, 'ci', 'job');
|
|
7951
|
+
if (isFixed) {
|
|
7952
|
+
const problemId = ckgSlug(`problem:${ckgSlug(job)}`);
|
|
7953
|
+
await ckgUpsertNode(redis, problemId, 'problem', 'ci-failure');
|
|
7954
|
+
await ckgUpdateEdge(redis, conceptId, 'fixes', problemId, true);
|
|
7955
|
+
const lessonObj = {
|
|
7956
|
+
topic: slug, outcome: 'success',
|
|
7957
|
+
what_worked: `CI job "${job}" went ${prev_status} → ${status}`,
|
|
7958
|
+
what_failed: `Job "${job}" was failing`, context: `CLS/ci: ${ciCtx}`,
|
|
7959
|
+
severity: 'major', file_paths: [], commands: [], tags: ['cls', 'ci'],
|
|
7960
|
+
depends_on: [], recall_count: 0, ts, verified_at: ts, confidence: 0.75,
|
|
7961
|
+
audit_trail: [{ ts, action: 'cls_ci_fixed' }], version: 3,
|
|
7962
|
+
};
|
|
7963
|
+
await redis.rpush(`cachly:lessons:${slug}`, JSON.stringify(lessonObj));
|
|
7964
|
+
await redis.set(`cachly:lesson:best:${slug}`, JSON.stringify(lessonObj));
|
|
7965
|
+
}
|
|
7966
|
+
else if (isBroken) {
|
|
7967
|
+
const causeId = ckgSlug(`cause:${ckgSlug(job)}`);
|
|
7968
|
+
await ckgUpsertNode(redis, causeId, 'cause', 'ci-break');
|
|
7969
|
+
await ckgUpdateEdge(redis, conceptId, 'causes', causeId, false);
|
|
7970
|
+
}
|
|
7971
|
+
await redis.rpush(clsKey, JSON.stringify({ source, payload: { status, prev_status, job }, ts }));
|
|
7972
|
+
await redis.ltrim(clsKey, -200, -1);
|
|
7973
|
+
const statusIcon = isFixed ? '✅ Fixed' : isBroken ? '🔴 Broken' : '📊 Recorded';
|
|
7974
|
+
return [
|
|
7975
|
+
`📨 **CLS Ingested: ci_outcome**`, '',
|
|
7976
|
+
`${statusIcon}: \`${job}\` — ${prev_status || '?'} → ${status}`,
|
|
7977
|
+
isFixed ? `🔧 CKG \`fixes\` edge added (75% confidence)` : isBroken ? `⚡ CKG \`causes\` edge added` : `📊 State recorded`,
|
|
7978
|
+
`💡 Lesson: \`${slug}\` | Predict: \`brain_predict(context="${job}")\``,
|
|
7979
|
+
].join('\n');
|
|
7980
|
+
}
|
|
7981
|
+
if (source === 'ide_diagnostic') {
|
|
7982
|
+
const error = String(payload.error ?? '');
|
|
7983
|
+
const fix = String(payload.fix ?? '');
|
|
7984
|
+
const file = String(payload.file ?? '');
|
|
7985
|
+
const errorConcept = extractProblemConcept(error) ?? 'unknown-error';
|
|
7986
|
+
const slug = `debug:${ckgSlug(errorConcept)}`;
|
|
7987
|
+
const conceptId = ckgSlug(slug);
|
|
7988
|
+
const problemId = ckgSlug(`problem:${errorConcept}`);
|
|
7989
|
+
await ckgUpsertNode(redis, conceptId, 'debug', 'diagnostic');
|
|
7990
|
+
await ckgUpsertNode(redis, problemId, 'problem', 'compiler-error');
|
|
7991
|
+
await ckgUpdateEdge(redis, conceptId, 'fixes', problemId, true);
|
|
7992
|
+
const lessonObj = {
|
|
7993
|
+
topic: slug, outcome: 'success', what_worked: fix, what_failed: error,
|
|
7994
|
+
context: `CLS/ide: ${file}`, severity: 'minor',
|
|
7995
|
+
file_paths: file ? [file] : [], commands: [], tags: ['cls', 'ide-diagnostic'],
|
|
7996
|
+
depends_on: [], recall_count: 0, ts, verified_at: ts, confidence: 0.65,
|
|
7997
|
+
audit_trail: [{ ts, action: 'cls_ide_diagnostic' }], version: 3,
|
|
7998
|
+
};
|
|
7999
|
+
await redis.rpush(`cachly:lessons:${slug}`, JSON.stringify(lessonObj));
|
|
8000
|
+
const existingL = await redis.get(`cachly:lesson:best:${slug}`);
|
|
8001
|
+
if (!existingL)
|
|
8002
|
+
await redis.set(`cachly:lesson:best:${slug}`, JSON.stringify(lessonObj));
|
|
8003
|
+
await redis.rpush(clsKey, JSON.stringify({ source, payload: { error: error.slice(0, 60), fix: fix.slice(0, 60), file }, ts }));
|
|
8004
|
+
await redis.ltrim(clsKey, -200, -1);
|
|
8005
|
+
return [
|
|
8006
|
+
`📨 **CLS Ingested: ide_diagnostic**`, '',
|
|
8007
|
+
`Error: \`${error.slice(0, 80)}\``,
|
|
8008
|
+
`Fix: ${fix.slice(0, 100)}`,
|
|
8009
|
+
file ? `File: \`${file}\`` : '',
|
|
8010
|
+
'', `🕸️ CKG: \`${conceptId}\` → fixes → \`${problemId}\` | Lesson: \`${slug}\``,
|
|
8011
|
+
].filter(l => l !== '').join('\n');
|
|
8012
|
+
}
|
|
8013
|
+
return `❌ Unknown CLS source: "${source}". Valid: git_commit, ci_outcome, ide_diagnostic`;
|
|
8014
|
+
}
|
|
8015
|
+
// ── Layer 5: CLS — cls_install_hooks ─────────────────────────────────────
|
|
8016
|
+
case 'cls_install_hooks': {
|
|
8017
|
+
const { instance_id, repo_path = '.', hooks = ['git', 'ci'] } = args;
|
|
8018
|
+
const hooksArr = Array.isArray(hooks) ? hooks : ['git', 'ci'];
|
|
8019
|
+
const lines = [`🔌 **CLS Hook Installation Guide**\n`];
|
|
8020
|
+
if (hooksArr.includes('git')) {
|
|
8021
|
+
const hookScript = [
|
|
8022
|
+
`#!/bin/sh`,
|
|
8023
|
+
`# cachly CLS — Continuous Learning Stream git hook`,
|
|
8024
|
+
`# Installed by cls_install_hooks · runs silently on every commit`,
|
|
8025
|
+
`INSTANCE="${instance_id}"`,
|
|
8026
|
+
`SHA=$(git rev-parse HEAD 2>/dev/null || echo "")`,
|
|
8027
|
+
`MSG=$(git log -1 --pretty=%B 2>/dev/null | head -1)`,
|
|
8028
|
+
`FILES=$(git diff-tree --no-commit-id -r --name-only HEAD 2>/dev/null | tr '\\n' ',' | sed 's/,$//')`,
|
|
8029
|
+
`node -e "`,
|
|
8030
|
+
`const p={instance_id:'$INSTANCE',source:'git_commit',payload:{message:$(echo "$MSG" | jq -R . 2>/dev/null || echo '"commit"'),sha:'$SHA',files:'$FILES'.split(',').filter(Boolean)}};`,
|
|
8031
|
+
`try{require('child_process').execSync('npx @cachly-dev/mcp-server@latest cls-ingest \\''+JSON.stringify(p)+'\\'',{stdio:'ignore',timeout:5000})}catch(e){}`,
|
|
8032
|
+
`" 2>/dev/null &`,
|
|
8033
|
+
`exit 0`,
|
|
8034
|
+
].join('\n');
|
|
8035
|
+
lines.push(`### Git post-commit hook`);
|
|
8036
|
+
lines.push(`**Quick install (run once per repo):**`);
|
|
8037
|
+
lines.push('```sh');
|
|
8038
|
+
lines.push(`cat > ${repo_path}/.git/hooks/post-commit << 'HOOK'`);
|
|
8039
|
+
lines.push(hookScript);
|
|
8040
|
+
lines.push(`HOOK`);
|
|
8041
|
+
lines.push(`chmod +x ${repo_path}/.git/hooks/post-commit`);
|
|
8042
|
+
lines.push('```');
|
|
8043
|
+
lines.push(`After install: every \`git commit\` automatically updates your brain's CKG.`);
|
|
8044
|
+
lines.push('');
|
|
8045
|
+
}
|
|
8046
|
+
if (hooksArr.includes('ci')) {
|
|
8047
|
+
lines.push(`### GitHub Actions CI outcome hook`);
|
|
8048
|
+
lines.push(`**Add at the end of each job** (after build/test steps):`);
|
|
8049
|
+
lines.push('```yaml');
|
|
8050
|
+
lines.push(`- name: cachly CLS — record CI outcome`);
|
|
8051
|
+
lines.push(` if: always()`);
|
|
8052
|
+
lines.push(` run: |`);
|
|
8053
|
+
lines.push(` node -e "`);
|
|
8054
|
+
lines.push(` const r=require('https');`);
|
|
8055
|
+
lines.push(` const d=JSON.stringify({instance_id:'${instance_id}',source:'ci_outcome',payload:{`);
|
|
8056
|
+
lines.push(` status:'\${{ job.status }}',prev_status:'unknown',job:'\${{ github.job }}',`);
|
|
8057
|
+
lines.push(` context:'github-actions run \${{ github.run_number }}'}});`);
|
|
8058
|
+
lines.push(` r.request({hostname:'api.cachly.dev',path:'/api/v1/cls/ingest',method:'POST',`);
|
|
8059
|
+
lines.push(` headers:{'Content-Type':'application/json','Authorization':'Bearer \$CACHLY_JWT',`);
|
|
8060
|
+
lines.push(` 'Content-Length':d.length}},()=>{}).end(d);`);
|
|
8061
|
+
lines.push(` " 2>/dev/null || true`);
|
|
8062
|
+
lines.push(` env:`);
|
|
8063
|
+
lines.push(` CACHLY_JWT: \${{ secrets.CACHLY_JWT }}`);
|
|
8064
|
+
lines.push('```');
|
|
8065
|
+
lines.push('');
|
|
8066
|
+
}
|
|
8067
|
+
lines.push(`💡 Once installed: \`brain_search(query="cls")\` to verify events are arriving.`);
|
|
8068
|
+
lines.push(`📊 Monitor CKG growth: \`ckg_inspect(concept="ci")\` or \`ckg_inspect(concept="fix")\``);
|
|
8069
|
+
return lines.join('\n');
|
|
8070
|
+
}
|
|
8071
|
+
// ── Layer 6: FedBrain — fedbrain_contribute ───────────────────────────────
|
|
8072
|
+
case 'fedbrain_contribute': {
|
|
8073
|
+
const { instance_id, lesson_key, visibility = 'public' } = args;
|
|
8074
|
+
const redis = await getConnection(instance_id);
|
|
8075
|
+
const raw = await redis.get(`cachly:lesson:best:${lesson_key}`);
|
|
8076
|
+
if (!raw)
|
|
8077
|
+
return `❌ Lesson \`${lesson_key}\` not found. Store it first with \`learn_from_attempts\`.`;
|
|
8078
|
+
const lesson = JSON.parse(raw);
|
|
8079
|
+
const domainTokens = [lesson.topic.split(':')[0], ...(lesson.tags ?? [])].filter(Boolean);
|
|
8080
|
+
const domainFingerprint = [...new Set(domainTokens)].sort().join(',');
|
|
8081
|
+
// HMAC certificate ID (non-reversible, privacy-safe)
|
|
8082
|
+
const certContent = `${lesson.topic}:${lesson.outcome}:${lesson.what_worked}`;
|
|
8083
|
+
const certId = createHmac('sha256', `cachly-fedbrain:${instance_id}`).update(certContent).digest('hex').slice(0, 16);
|
|
8084
|
+
const cert = {
|
|
8085
|
+
cert_id: certId, lesson_key, visibility,
|
|
8086
|
+
domain_fingerprint: domainFingerprint,
|
|
8087
|
+
contributed_at: new Date().toISOString(),
|
|
8088
|
+
confirm_count: 0,
|
|
8089
|
+
trust_score: lesson.outcome === 'success' ? 0.85 : 0.5,
|
|
8090
|
+
};
|
|
8091
|
+
await redis.set(`cachly:fedbrain:cert:${certId}`, JSON.stringify(cert));
|
|
8092
|
+
await redis.sadd('cachly:fedbrain:contributed', certId);
|
|
8093
|
+
// Try global commons via syndication API
|
|
8094
|
+
let syndicationResult;
|
|
8095
|
+
try {
|
|
8096
|
+
await apiFetch('/api/v1/syndication/contribute', {
|
|
8097
|
+
method: 'POST',
|
|
8098
|
+
body: JSON.stringify({
|
|
8099
|
+
topic: lesson.topic, outcome: lesson.outcome,
|
|
8100
|
+
what_worked: lesson.what_worked, what_failed: lesson.what_failed ?? '',
|
|
8101
|
+
severity: lesson.severity ?? 'major', cert_id: certId,
|
|
8102
|
+
domain_fingerprint: domainFingerprint, visibility,
|
|
8103
|
+
}),
|
|
8104
|
+
});
|
|
8105
|
+
syndicationResult = `✅ Contributed to global commons`;
|
|
8106
|
+
}
|
|
8107
|
+
catch {
|
|
8108
|
+
syndicationResult = `📦 Stored locally (commons API unavailable — will sync when online)`;
|
|
8109
|
+
}
|
|
8110
|
+
return [
|
|
8111
|
+
`🌐 **FedBrain Contribute: "${lesson_key}"**`, '',
|
|
8112
|
+
`📜 Certificate: \`${certId}\``,
|
|
8113
|
+
`🏷️ Domain fingerprint: ${domainTokens.slice(0, 6).map(t => `\`${t}\``).join(', ')}`,
|
|
8114
|
+
`🔒 Visibility: ${visibility}`,
|
|
8115
|
+
'', syndicationResult, '',
|
|
8116
|
+
`💡 At 10 independent confirms → 🏆 Gold Standard`,
|
|
8117
|
+
`🔍 Search: \`fedbrain_search(query="${lesson.topic.split(':').slice(-1)[0]}")\``,
|
|
8118
|
+
].join('\n');
|
|
8119
|
+
}
|
|
8120
|
+
// ── Layer 6: FedBrain — fedbrain_search ──────────────────────────────────
|
|
8121
|
+
case 'fedbrain_search': {
|
|
8122
|
+
const { instance_id, query, context_hints = [], limit = 10 } = args;
|
|
8123
|
+
const redis = await getConnection(instance_id);
|
|
8124
|
+
// Build local domain context from contributed certificates + explicit hints
|
|
8125
|
+
const contribIds = await redis.smembers('cachly:fedbrain:contributed');
|
|
8126
|
+
const localDomains = new Map();
|
|
8127
|
+
for (const certId of contribIds.slice(0, 30)) {
|
|
8128
|
+
const certRaw = await redis.get(`cachly:fedbrain:cert:${certId}`);
|
|
8129
|
+
if (!certRaw)
|
|
8130
|
+
continue;
|
|
8131
|
+
try {
|
|
8132
|
+
const cert = JSON.parse(certRaw);
|
|
8133
|
+
for (const d of (cert.domain_fingerprint ?? '').split(',').filter(Boolean)) {
|
|
8134
|
+
localDomains.set(d, (localDomains.get(d) ?? 0) + 1);
|
|
8135
|
+
}
|
|
8136
|
+
}
|
|
8137
|
+
catch { /* skip */ }
|
|
8138
|
+
}
|
|
8139
|
+
for (const hint of (Array.isArray(context_hints) ? context_hints : [])) {
|
|
8140
|
+
localDomains.set(hint.toLowerCase(), (localDomains.get(hint.toLowerCase()) ?? 0) + 2);
|
|
8141
|
+
}
|
|
8142
|
+
let results = [];
|
|
8143
|
+
try {
|
|
8144
|
+
const params = new URLSearchParams({ q: query, limit: String(Math.min(limit * 2, 50)) });
|
|
8145
|
+
const res = await apiFetch(`/api/v1/syndication/search?${params}`);
|
|
8146
|
+
results = res.results ?? [];
|
|
8147
|
+
}
|
|
8148
|
+
catch {
|
|
8149
|
+
// Fallback: search local lessons
|
|
8150
|
+
const lessonKeys = [];
|
|
8151
|
+
const lStream = redis.scanStream({ match: 'cachly:lesson:best:*', count: 200 });
|
|
8152
|
+
await new Promise((res, rej) => { lStream.on('data', (b) => lessonKeys.push(...b)); lStream.on('end', res); lStream.on('error', rej); });
|
|
8153
|
+
for (const k of lessonKeys.slice(0, 60)) {
|
|
8154
|
+
const r = await redis.get(k);
|
|
8155
|
+
if (!r)
|
|
8156
|
+
continue;
|
|
8157
|
+
try {
|
|
8158
|
+
const l = JSON.parse(r);
|
|
8159
|
+
const haystack = `${l.topic} ${l.what_worked ?? ''} ${l.what_failed ?? ''}`.toLowerCase();
|
|
8160
|
+
if (query.toLowerCase().split(/\s+/).some(t => t.length > 2 && haystack.includes(t))) {
|
|
8161
|
+
results.push({ id: k.split(':').pop() ?? k, topic: l.topic, category: l.topic.split(':')[0], outcome: l.outcome, what_worked: l.what_worked ?? '', severity: l.severity ?? 'major', confirm_count: 0, created_at: l.ts ?? new Date().toISOString() });
|
|
8162
|
+
}
|
|
8163
|
+
}
|
|
8164
|
+
catch { /* skip */ }
|
|
8165
|
+
}
|
|
8166
|
+
}
|
|
8167
|
+
if (results.length === 0) {
|
|
8168
|
+
return [`🌐 **FedBrain Search: "${query}"**`, '', `No results. Contribute: \`fedbrain_contribute(lesson_key="fix:...")\``].join('\n');
|
|
8169
|
+
}
|
|
8170
|
+
// Context-weighted ranking
|
|
8171
|
+
const ranked = results.map(r => {
|
|
8172
|
+
const rDomains = (r.domain_fingerprint ?? r.category ?? '').split(',').filter(Boolean);
|
|
8173
|
+
const overlap = rDomains.reduce((s, d) => s + (localDomains.get(d) ?? 0), 0);
|
|
8174
|
+
const contextScore = localDomains.size > 0 ? overlap / Math.max(1, localDomains.size + rDomains.length) : 0;
|
|
8175
|
+
const confirmedScore = Math.min(1, r.confirm_count / 10);
|
|
8176
|
+
const weightedScore = (contextScore * 0.4) + (confirmedScore * 0.4) + (r.outcome === 'success' ? 0.2 : 0);
|
|
8177
|
+
return { ...r, weightedScore, isGoldStandard: r.confirm_count >= 10 };
|
|
8178
|
+
}).sort((a, b) => b.weightedScore - a.weightedScore).slice(0, limit);
|
|
8179
|
+
const lines = [`🌐 **FedBrain Search: "${query}"** — ${ranked.length} result${ranked.length !== 1 ? 's' : ''} (context-weighted)\n`];
|
|
8180
|
+
for (const r of ranked) {
|
|
8181
|
+
const icon = r.outcome === 'success' ? '✅' : r.outcome === 'failure' ? '❌' : '⚠️';
|
|
8182
|
+
const goldBadge = r.isGoldStandard ? ' 🏆 _Gold Standard_' : r.confirm_count >= 3 ? ` ✓${r.confirm_count}` : '';
|
|
8183
|
+
const ctxPct = Math.round(r.weightedScore * 100);
|
|
8184
|
+
lines.push(`${icon}${goldBadge} **\`${r.topic}\`** [ctx: ${ctxPct}%]`);
|
|
8185
|
+
if (r.what_worked)
|
|
8186
|
+
lines.push(` ✅ ${r.what_worked.slice(0, 150)}`);
|
|
8187
|
+
if (r.what_failed)
|
|
8188
|
+
lines.push(` ❌ ${r.what_failed.slice(0, 80)}`);
|
|
8189
|
+
lines.push(` _${r.confirm_count} confirm${r.confirm_count !== 1 ? 's' : ''} | \`${r.id.slice(0, 12)}\`_`, '');
|
|
8190
|
+
}
|
|
8191
|
+
if (localDomains.size > 0) {
|
|
8192
|
+
const topDomains = [...localDomains.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([d, n]) => `\`${d}\`(${n})`).join(', ');
|
|
8193
|
+
lines.push(`🎯 Your context: ${topDomains}`);
|
|
8194
|
+
}
|
|
8195
|
+
lines.push(`💡 Confirm: \`fedbrain_confirm(topic="<topic>", outcome="worked")\``);
|
|
8196
|
+
return lines.join('\n');
|
|
8197
|
+
}
|
|
8198
|
+
// ── Layer 6: FedBrain — fedbrain_confirm ─────────────────────────────────
|
|
8199
|
+
case 'fedbrain_confirm': {
|
|
8200
|
+
const { instance_id, topic, outcome } = args;
|
|
8201
|
+
const redis = await getConnection(instance_id);
|
|
8202
|
+
const ts = new Date().toISOString();
|
|
8203
|
+
const confirmEntry = JSON.stringify({ topic, outcome, ts });
|
|
8204
|
+
await redis.rpush('cachly:fedbrain:confirmations', confirmEntry);
|
|
8205
|
+
await redis.ltrim('cachly:fedbrain:confirmations', -200, -1);
|
|
8206
|
+
// Update local CKG confidence
|
|
8207
|
+
const worked = outcome === 'worked';
|
|
8208
|
+
const partial = outcome === 'partially_worked';
|
|
8209
|
+
await ckgUpdateEdge(redis, ckgSlug(topic), 'fixes', ckgSlug(`syndicated:${topic}`), worked, partial);
|
|
8210
|
+
// Propagate to global commons
|
|
8211
|
+
let propResult;
|
|
8212
|
+
try {
|
|
8213
|
+
await apiFetch('/api/v1/syndication/confirm', { method: 'POST', body: JSON.stringify({ topic, outcome }) });
|
|
8214
|
+
propResult = `✅ Confirmation propagated to global commons`;
|
|
8215
|
+
}
|
|
8216
|
+
catch {
|
|
8217
|
+
await redis.rpush('cachly:fedbrain:pending_confirms', confirmEntry);
|
|
8218
|
+
await redis.ltrim('cachly:fedbrain:pending_confirms', -50, -1);
|
|
8219
|
+
propResult = `📦 Queued locally (API unavailable — will propagate on next online session)`;
|
|
8220
|
+
}
|
|
8221
|
+
const icon = worked ? '✅' : partial ? '⚠️' : '❌';
|
|
8222
|
+
return [
|
|
8223
|
+
`${icon} **FedBrain Confirm: "${topic}"** → ${outcome}`, '',
|
|
8224
|
+
propResult, '',
|
|
8225
|
+
`🕸️ CKG confidence ${worked || partial ? 'boosted' : 'reduced'} for \`${ckgSlug(topic)}\``,
|
|
8226
|
+
`💡 Your confirmation helps other brains worldwide.`,
|
|
8227
|
+
`📊 Status: \`fedbrain_status(instance_id="...")\``,
|
|
8228
|
+
].join('\n');
|
|
8229
|
+
}
|
|
8230
|
+
// ── Layer 6: FedBrain — fedbrain_status ──────────────────────────────────
|
|
8231
|
+
case 'fedbrain_status': {
|
|
8232
|
+
const { instance_id } = args;
|
|
8233
|
+
const redis = await getConnection(instance_id);
|
|
8234
|
+
const contribIds = await redis.smembers('cachly:fedbrain:contributed');
|
|
8235
|
+
const confirmsRaw = await redis.lrange('cachly:fedbrain:confirmations', -10, -1);
|
|
8236
|
+
const pendingConfirms = await redis.llen('cachly:fedbrain:pending_confirms');
|
|
8237
|
+
const confirms = confirmsRaw.map(r => { try {
|
|
8238
|
+
return JSON.parse(r);
|
|
8239
|
+
}
|
|
8240
|
+
catch {
|
|
8241
|
+
return null;
|
|
8242
|
+
} }).filter(Boolean);
|
|
8243
|
+
const certDetails = [];
|
|
8244
|
+
for (const certId of contribIds.slice(0, 15)) {
|
|
8245
|
+
const raw = await redis.get(`cachly:fedbrain:cert:${certId}`);
|
|
8246
|
+
if (!raw)
|
|
8247
|
+
continue;
|
|
8248
|
+
try {
|
|
8249
|
+
const cert = JSON.parse(raw);
|
|
8250
|
+
certDetails.push({ cert_id: cert.cert_id, lesson_key: cert.lesson_key, confirm_count: cert.confirm_count ?? 0, trust_score: cert.trust_score ?? 0.5, isGold: (cert.confirm_count ?? 0) >= 10 });
|
|
8251
|
+
}
|
|
8252
|
+
catch { /* skip */ }
|
|
8253
|
+
}
|
|
8254
|
+
const lines = [
|
|
8255
|
+
`🌐 **FedBrain Status**\n`,
|
|
8256
|
+
`### 📤 Contributed Lessons: ${contribIds.length}`,
|
|
8257
|
+
];
|
|
8258
|
+
if (certDetails.length > 0) {
|
|
8259
|
+
for (const c of certDetails) {
|
|
8260
|
+
const goldBadge = c.isGold ? ' 🏆 Gold Standard' : '';
|
|
8261
|
+
const confBar = '█'.repeat(Math.min(10, c.confirm_count)) + '░'.repeat(Math.max(0, 10 - c.confirm_count));
|
|
8262
|
+
lines.push(` \`${c.lesson_key}\` [${confBar}] ×${c.confirm_count}${goldBadge}`);
|
|
8263
|
+
}
|
|
8264
|
+
}
|
|
8265
|
+
else {
|
|
8266
|
+
lines.push(` _None yet. Contribute with \`fedbrain_contribute(lesson_key="fix:...")\`_`);
|
|
8267
|
+
}
|
|
8268
|
+
lines.push('', `### 📥 Recent Confirmations: ${confirms.length}`);
|
|
8269
|
+
if (confirms.length > 0) {
|
|
8270
|
+
for (const c of confirms.slice(-5)) {
|
|
8271
|
+
const icon = c.outcome === 'worked' ? '✅' : c.outcome === 'partially_worked' ? '⚠️' : '❌';
|
|
8272
|
+
lines.push(` ${icon} \`${c.topic}\` — ${c.outcome} (${new Date(c.ts).toLocaleDateString('de-DE')})`);
|
|
8273
|
+
}
|
|
8274
|
+
}
|
|
8275
|
+
else {
|
|
8276
|
+
lines.push(` _None yet. Confirm syndicated lessons with \`fedbrain_confirm\`_`);
|
|
8277
|
+
}
|
|
8278
|
+
if (pendingConfirms > 0) {
|
|
8279
|
+
lines.push('', `⚠️ ${pendingConfirms} confirmation${pendingConfirms !== 1 ? 's' : ''} pending propagation`);
|
|
8280
|
+
}
|
|
8281
|
+
lines.push('', '---', `**Contribute:** \`fedbrain_contribute(lesson_key="fix:...")\``, `**Search:** \`fedbrain_search(query="...")\``, `**Confirm:** \`fedbrain_confirm(topic="...", outcome="worked")\``);
|
|
8282
|
+
return lines.join('\n');
|
|
8283
|
+
}
|
|
7630
8284
|
default:
|
|
7631
8285
|
return null;
|
|
7632
8286
|
}
|