wyrm-mcp 7.2.0 → 7.2.2
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/LICENSE +26 -667
- package/NOTICE +14 -33
- package/dist/activation.d.ts.map +1 -1
- package/dist/activation.js +1 -44
- package/dist/activation.js.map +1 -1
- package/dist/agent-daemon.js +4 -281
- package/dist/agent-loop.js +7 -332
- package/dist/analytics.js +13 -236
- package/dist/attribution.js +1 -49
- package/dist/audit.js +2 -457
- package/dist/auto-capture.js +3 -138
- package/dist/auto-orchestrator.js +1 -325
- package/dist/autoconfig.js +39 -840
- package/dist/buddy-runner.js +1 -109
- package/dist/buddy.js +14 -564
- package/dist/build-flags.js +1 -17
- package/dist/capabilities.js +3 -183
- package/dist/capture.js +1 -56
- package/dist/causality.js +6 -107
- package/dist/cli.js +20 -281
- package/dist/cloud/cli.js +5 -541
- package/dist/cloud/client.js +1 -221
- package/dist/cloud/crypto.js +1 -85
- package/dist/cloud/machine-id.js +2 -113
- package/dist/cloud/recovery.js +1 -60
- package/dist/cloud/sync-engine.js +7 -543
- package/dist/cloud-backup.js +5 -579
- package/dist/cloud-profile.js +1 -138
- package/dist/cloud-sync-entrypoint.js +1 -47
- package/dist/cloud-sync.js +2 -309
- package/dist/constellation.js +12 -168
- package/dist/context-build-budgeted.js +4 -144
- package/dist/context-ranking.js +1 -69
- package/dist/crypto.js +1 -179
- package/dist/daemon-write-endpoint.js +1 -290
- package/dist/daemon-writer.js +2 -406
- package/dist/database.js +43 -1110
- package/dist/deprecations.js +2 -162
- package/dist/design.js +13 -141
- package/dist/event-replication.js +1 -112
- package/dist/events-sse.js +7 -43
- package/dist/events.js +6 -238
- package/dist/failure-patterns.js +42 -659
- package/dist/federation.js +12 -236
- package/dist/goals.js +13 -101
- package/dist/golden.js +3 -355
- package/dist/handlers/agent.js +4 -165
- package/dist/handlers/alias-adapters.js +1 -129
- package/dist/handlers/aliases.js +1 -171
- package/dist/handlers/audit.js +1 -87
- package/dist/handlers/boundary.js +1 -221
- package/dist/handlers/capture.js +73 -1109
- package/dist/handlers/causality.js +7 -114
- package/dist/handlers/cloud.js +85 -382
- package/dist/handlers/companion.js +28 -459
- package/dist/handlers/datalake.js +7 -187
- package/dist/handlers/dispatch-context.js +0 -22
- package/dist/handlers/entity.js +25 -256
- package/dist/handlers/events.js +16 -335
- package/dist/handlers/failure.js +13 -340
- package/dist/handlers/goals.js +4 -296
- package/dist/handlers/intelligence.js +126 -674
- package/dist/handlers/invoicing.js +1 -70
- package/dist/handlers/mcpclient.js +6 -137
- package/dist/handlers/orchestration.js +40 -125
- package/dist/handlers/output-schemas.js +1 -24
- package/dist/handlers/presence.js +3 -99
- package/dist/handlers/project.js +28 -182
- package/dist/handlers/prompts.js +6 -157
- package/dist/handlers/quest.js +4 -224
- package/dist/handlers/recall.js +11 -218
- package/dist/handlers/registry.js +1 -167
- package/dist/handlers/resources.js +1 -288
- package/dist/handlers/review.js +11 -74
- package/dist/handlers/run.js +17 -487
- package/dist/handlers/search.js +15 -326
- package/dist/handlers/session.js +28 -615
- package/dist/handlers/share.js +8 -184
- package/dist/handlers/shims.js +1 -464
- package/dist/handlers/skill.js +67 -449
- package/dist/handlers/survivors.js +1 -120
- package/dist/handlers/symbols.js +8 -109
- package/dist/handlers/syncops.js +4 -302
- package/dist/handlers/types.js +1 -27
- package/dist/harvest.js +5 -191
- package/dist/hours.js +7 -156
- package/dist/http-auth.js +3 -321
- package/dist/http-fast.js +21 -1137
- package/dist/icons.js +1 -47
- package/dist/index.js +2 -924
- package/dist/indexer.js +4 -145
- package/dist/intelligence.js +31 -261
- package/dist/internal-dispatch.js +3 -212
- package/dist/keyset.js +1 -110
- package/dist/knowledge-graph.js +12 -176
- package/dist/license.d.ts +11 -0
- package/dist/license.d.ts.map +1 -1
- package/dist/license.js +2 -414
- package/dist/license.js.map +1 -1
- package/dist/logger.js +2 -199
- package/dist/maintenance.js +2 -148
- package/dist/mcp-client.js +6 -262
- package/dist/memory-artifacts.js +30 -449
- package/dist/migrate-prompt.js +2 -124
- package/dist/migrations.js +40 -655
- package/dist/performance.js +1 -228
- package/dist/presence.js +11 -140
- package/dist/priority-embed.js +5 -164
- package/dist/providers/embedding-provider.js +1 -196
- package/dist/readonly-gate.js +1 -29
- package/dist/rehydration.js +9 -157
- package/dist/reindex.js +1 -88
- package/dist/render-target.js +21 -514
- package/dist/render.js +4 -280
- package/dist/repl-guard.js +1 -173
- package/dist/replication-daemon-entrypoint.js +1 -31
- package/dist/replication-daemon.js +2 -262
- package/dist/resilience.js +1 -591
- package/dist/reverse-bridge.js +5 -360
- package/dist/security.js +1 -244
- package/dist/session-seen.js +3 -51
- package/dist/setup.js +1 -260
- package/dist/skill-author.js +5 -168
- package/dist/spec-kit.js +1 -191
- package/dist/sqlite-busy.js +1 -154
- package/dist/statusline.js +11 -315
- package/dist/sub-agent.js +13 -262
- package/dist/summarizer.js +13 -139
- package/dist/symbols.js +7 -283
- package/dist/sync.js +5 -359
- package/dist/tasks-dispatch.js +1 -84
- package/dist/tasks.js +1 -282
- package/dist/token-budget.js +1 -143
- package/dist/tool-analytics.js +7 -129
- package/dist/tool-annotations.js +1 -365
- package/dist/tool-manifest-v2.json +1 -1
- package/dist/tool-manifest.json +1 -1
- package/dist/tool-profiles.js +1 -75
- package/dist/trace-harvest.js +6 -244
- package/dist/types.js +1 -30
- package/dist/ui-dashboard.js +41 -50
- package/dist/ulid.js +1 -81
- package/dist/validate.js +1 -129
- package/dist/vault.js +1 -534
- package/dist/vectors.js +3 -184
- package/dist/version-check.js +4 -136
- package/dist/visibility.js +19 -155
- package/dist/wyrm-cli.js +98 -2451
- package/dist/wyrm-cli.js.map +1 -1
- package/dist/wyrm-guard.js +14 -424
- package/dist/wyrm-loop.js +3 -150
- package/dist/wyrm-manifest.json +1 -1
- package/dist/wyrm-statusline-daemon.js +1 -11
- package/dist/wyrm-statusline.js +4 -56
- package/dist/wyrm-ui.js +9 -77
- package/package.json +4 -2
|
@@ -1,217 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
* Internal tool dispatcher for the AgentLoop (v7 F4 T036 — extracted from the
|
|
3
|
-
* index.ts monolith). A curated subset of read-mostly + bounded-write tools the
|
|
4
|
-
* autonomous OODA loop is allowed to call; the whitelist itself is enforced in
|
|
5
|
-
* agent-loop.ts (SAFE_INTERNAL_TOOLS). Lifted VERBATIM out of index.ts behind a
|
|
6
|
-
* factory that captures the same subsystem singletons it always closed over —
|
|
7
|
-
* the single-quoted `case` labels here are what tests/tool-surface-integrity.ts
|
|
8
|
-
* scans to keep the whitelist and the dispatcher in lockstep.
|
|
9
|
-
*
|
|
10
|
-
* @copyright 2026 Ghost Protocol (Pvt) Ltd.
|
|
11
|
-
* @license AGPL-3.0-or-later
|
|
12
|
-
*/
|
|
13
|
-
import { sanitizeFtsQuery } from "./security.js";
|
|
14
|
-
import { classifyCapture } from "./capture.js";
|
|
15
|
-
export function makeInternalDispatch(deps) {
|
|
16
|
-
const { db, failures, symbols, rehydration, causality, presence, federation, memory, groundTruths } = deps;
|
|
17
|
-
const internalDispatch = async (name, args) => {
|
|
18
|
-
try {
|
|
19
|
-
switch (name) {
|
|
20
|
-
case 'wyrm_search': {
|
|
21
|
-
const q = String(args.query ?? args.q ?? '').slice(0, 200);
|
|
22
|
-
if (!q)
|
|
23
|
-
return { ok: false, error: 'query required' };
|
|
24
|
-
const sanitized = sanitizeFtsQuery(q);
|
|
25
|
-
const rows = db.getDatabase().prepare(`
|
|
1
|
+
import{sanitizeFtsQuery as k}from"./security.js";import{classifyCapture as _}from"./capture.js";function q(p){const{db:r,failures:i,symbols:a,rehydration:m,causality:u,presence:f,federation:j,memory:c,groundTruths:y}=p;return async(d,s)=>{try{switch(d){case"wyrm_search":{const e=String(s.query??s.q??"").slice(0,200);if(!e)return{ok:!1,error:"query required"};const t=k(e);return{ok:!0,result:r.getDatabase().prepare(`
|
|
26
2
|
SELECT s.id, s.date, s.summary FROM sessions s
|
|
27
3
|
JOIN sessions_fts fts ON fts.rowid = s.id
|
|
28
4
|
WHERE sessions_fts MATCH ? LIMIT 10
|
|
29
|
-
`).all(
|
|
30
|
-
return { ok: true, result: rows };
|
|
31
|
-
}
|
|
32
|
-
case 'wyrm_all_quests': {
|
|
33
|
-
return { ok: true, result: db.getAllPendingQuests().slice(0, 30) };
|
|
34
|
-
}
|
|
35
|
-
case 'wyrm_truth_get': {
|
|
36
|
-
const projPath = args.projectPath;
|
|
37
|
-
const proj = projPath ? db.getProject(projPath) : null;
|
|
38
|
-
const rows = proj
|
|
39
|
-
? db.getDatabase().prepare(`
|
|
5
|
+
`).all(t)}}case"wyrm_all_quests":return{ok:!0,result:r.getAllPendingQuests().slice(0,30)};case"wyrm_truth_get":{const e=s.projectPath,t=e?r.getProject(e):null;return{ok:!0,result:t?r.getDatabase().prepare(`
|
|
40
6
|
SELECT category, key, value, rationale FROM ground_truths
|
|
41
7
|
WHERE project_id = ? AND is_current = 1 ORDER BY confidence DESC LIMIT 30
|
|
42
|
-
`).all(
|
|
43
|
-
: [];
|
|
44
|
-
return { ok: true, result: rows };
|
|
45
|
-
}
|
|
46
|
-
case 'wyrm_failure_check': {
|
|
47
|
-
const a = args;
|
|
48
|
-
const proj = a.projectPath ? db.getProject(a.projectPath) : null;
|
|
49
|
-
return { ok: true, result: failures.check(a.scope, a.target, a.description, proj?.id ?? null) };
|
|
50
|
-
}
|
|
51
|
-
case 'wyrm_failure_list': {
|
|
52
|
-
const a = args;
|
|
53
|
-
const proj = a.projectPath ? db.getProject(a.projectPath) : null;
|
|
54
|
-
return { ok: true, result: failures.list(proj?.id ?? null, Math.min(50, a.limit ?? 20)) };
|
|
55
|
-
}
|
|
56
|
-
case 'wyrm_failure_record': {
|
|
57
|
-
const a = args;
|
|
58
|
-
const proj = a.projectPath ? db.getProject(a.projectPath) : null;
|
|
59
|
-
return { ok: true, result: failures.record({ project_id: proj?.id ?? null, scope: a.scope, target: a.target, description: a.description, why_failed: a.why_failed, severity: a.severity }) };
|
|
60
|
-
}
|
|
61
|
-
case 'wyrm_symbol_search': {
|
|
62
|
-
const a = args;
|
|
63
|
-
if (!a.symbol)
|
|
64
|
-
return { ok: false, error: 'symbol required' };
|
|
65
|
-
const proj = a.projectPath ? db.getProject(a.projectPath) : null;
|
|
66
|
-
return { ok: true, result: symbols.search(a.symbol, { projectId: proj?.id, kind: a.kind, language: a.language, exact: a.exact, limit: Math.min(50, a.limit ?? 20) }) };
|
|
67
|
-
}
|
|
68
|
-
case 'wyrm_symbol_callers': {
|
|
69
|
-
const a = args;
|
|
70
|
-
const proj = a.projectPath ? db.getProject(a.projectPath) : null;
|
|
71
|
-
return { ok: true, result: symbols.callers(a.symbol, proj?.id).slice(0, 30) };
|
|
72
|
-
}
|
|
73
|
-
case 'wyrm_symbol_stats': {
|
|
74
|
-
const a = args;
|
|
75
|
-
const proj = a.projectPath ? db.getProject(a.projectPath) : null;
|
|
76
|
-
return { ok: true, result: symbols.stats(proj?.id) };
|
|
77
|
-
}
|
|
78
|
-
case 'wyrm_session_rehydrate': {
|
|
79
|
-
const a = args;
|
|
80
|
-
const brief = rehydration.rehydrate(a.session_id);
|
|
81
|
-
return { ok: brief != null, result: brief, error: brief ? undefined : 'session not found' };
|
|
82
|
-
}
|
|
83
|
-
case 'wyrm_decision_upstream': {
|
|
84
|
-
const a = args;
|
|
85
|
-
return { ok: true, result: causality.upstreamOf(a.kind, a.id) };
|
|
86
|
-
}
|
|
87
|
-
case 'wyrm_decision_downstream': {
|
|
88
|
-
const a = args;
|
|
89
|
-
return { ok: true, result: causality.downstreamOf(a.kind, a.id) };
|
|
90
|
-
}
|
|
91
|
-
case 'wyrm_decided_because': {
|
|
92
|
-
const a = args;
|
|
93
|
-
const proj = db.getProject(a.projectPath);
|
|
94
|
-
if (!proj)
|
|
95
|
-
return { ok: false, error: 'project not found' };
|
|
96
|
-
return { ok: true, result: causality.link({ project_id: proj.id, ...a }) };
|
|
97
|
-
}
|
|
98
|
-
case 'wyrm_presence_list': {
|
|
99
|
-
const a = args;
|
|
100
|
-
const proj = a.projectPath ? db.getProject(a.projectPath) : null;
|
|
101
|
-
return { ok: true, result: presence.liveAgents(proj?.id ?? null) };
|
|
102
|
-
}
|
|
103
|
-
case 'wyrm_sync_conflicts': {
|
|
104
|
-
return { ok: true, result: federation.unresolvedConflicts(20) };
|
|
105
|
-
}
|
|
106
|
-
case 'wyrm_quest_add': {
|
|
107
|
-
const a = args;
|
|
108
|
-
const proj = db.getProject(a.projectPath);
|
|
109
|
-
if (!proj)
|
|
110
|
-
return { ok: false, error: 'project not found' };
|
|
111
|
-
const q = db.addQuest(proj.id, a.title, a.description, a.priority || 'medium', a.tags);
|
|
112
|
-
return { ok: true, result: { id: q.id, title: q.title } };
|
|
113
|
-
}
|
|
114
|
-
case 'wyrm_quest_complete': {
|
|
115
|
-
const a = args;
|
|
116
|
-
const q = db.updateQuest(a.questId, 'completed');
|
|
117
|
-
return { ok: true, result: { id: q.id, status: 'completed' } };
|
|
118
|
-
}
|
|
119
|
-
case 'wyrm_project_context': {
|
|
120
|
-
const a = args;
|
|
121
|
-
const proj = db.getProject(a.projectPath);
|
|
122
|
-
if (!proj)
|
|
123
|
-
return { ok: false, error: 'project not found' };
|
|
124
|
-
// Summary: open quests + latest session
|
|
125
|
-
const ctx = {
|
|
126
|
-
project: proj.name,
|
|
127
|
-
quests: db.getAllPendingQuests().filter(q => q.project_id === proj.id).slice(0, 10),
|
|
128
|
-
last_session: db.getDatabase().prepare('SELECT id, date, summary FROM sessions WHERE project_id = ? ORDER BY date DESC LIMIT 1').get(proj.id),
|
|
129
|
-
};
|
|
130
|
-
return { ok: true, result: ctx };
|
|
131
|
-
}
|
|
132
|
-
// ── Memory ops the OODA loop is whitelisted for (SAFE_INTERNAL_TOOLS).
|
|
133
|
-
// These were promised by the whitelist but had no dispatch case until 6.8.1,
|
|
134
|
-
// so the loop silently failed on them. Kept in lockstep by
|
|
135
|
-
// tests/tool-surface-integrity.test.ts.
|
|
136
|
-
case 'wyrm_global_context': {
|
|
137
|
-
const a = args;
|
|
138
|
-
const projects = db.getAllProjects(a.maxProjects ?? 20).map((p) => ({
|
|
139
|
-
name: p.name, stack: p.stack ?? null, stats: db.getProjectStats(p.id),
|
|
140
|
-
}));
|
|
141
|
-
const result = { globalContext: db.getAllGlobalContext(), projects };
|
|
142
|
-
if (a.includeQuests)
|
|
143
|
-
result.quests = db.getAllPendingQuests().slice(0, 20);
|
|
144
|
-
return { ok: true, result };
|
|
145
|
-
}
|
|
146
|
-
case 'wyrm_recall': {
|
|
147
|
-
const a = args;
|
|
148
|
-
const proj = a.projectPath ? db.getProject(a.projectPath) : null;
|
|
149
|
-
if (!proj)
|
|
150
|
-
return { ok: false, error: 'project not found' };
|
|
151
|
-
if (!a.query)
|
|
152
|
-
return { ok: false, error: 'query required' };
|
|
153
|
-
const results = memory.recall(proj.id, a.query, { kind: a.kind, limit: Math.min(20, a.limit ?? 10), minConfidence: a.minConfidence });
|
|
154
|
-
return { ok: true, result: results.map((r) => ({ id: r.artifact.id, kind: r.artifact.kind, problem: r.artifact.problem, validated_fix: r.artifact.validated_fix, relevance: r.relevance_score })) };
|
|
155
|
-
}
|
|
156
|
-
case 'wyrm_remember': {
|
|
157
|
-
const a = args;
|
|
158
|
-
const proj = a.projectPath ? db.getProject(a.projectPath) : null;
|
|
159
|
-
if (!proj)
|
|
160
|
-
return { ok: false, error: 'project not found' };
|
|
161
|
-
if (!a.problem)
|
|
162
|
-
return { ok: false, error: 'problem required' };
|
|
163
|
-
const art = memory.add(proj.id, { kind: a.kind, problem: a.problem, constraints: a.constraints, validatedFix: a.validatedFix, whyItWorked: a.whyItWorked, outcome: a.outcome, tags: a.tags, confidence: a.confidence, sourceSessionId: a.sourceSessionId });
|
|
164
|
-
return { ok: true, result: { id: art.id, kind: art.kind } };
|
|
165
|
-
}
|
|
166
|
-
case 'wyrm_distill': {
|
|
167
|
-
const a = args;
|
|
168
|
-
const proj = a.projectPath ? db.getProject(a.projectPath) : null;
|
|
169
|
-
if (!proj)
|
|
170
|
-
return { ok: false, error: 'project not found' };
|
|
171
|
-
if (!a.candidates?.length)
|
|
172
|
-
return { ok: false, error: 'candidates required' };
|
|
173
|
-
const ids = a.candidates.map((c) => memory.add(proj.id, { kind: c.kind, problem: c.title, validatedFix: c.content, tags: c.tags ?? [], confidence: c.confidence ?? 0.7, needsReview: 1 }).id);
|
|
174
|
-
return { ok: true, result: { queued: ids.length, ids } };
|
|
175
|
-
}
|
|
176
|
-
case 'wyrm_capture': {
|
|
177
|
-
const a = args;
|
|
178
|
-
if (!a.content)
|
|
179
|
-
return { ok: false, error: 'content required' };
|
|
180
|
-
let cls = classifyCapture(a.content);
|
|
181
|
-
if (a.mode && a.mode !== 'auto') {
|
|
182
|
-
const sub = { quest: 'quest', truth: 'decision', memory: 'pattern' };
|
|
183
|
-
cls = { type: a.mode, subtype: sub[a.mode] ?? a.mode, confidence: 100, reasoning: `mode:${a.mode}` };
|
|
184
|
-
}
|
|
185
|
-
const pid = a.project_id ?? null;
|
|
186
|
-
if (cls.type === 'quest') {
|
|
187
|
-
if (pid === null)
|
|
188
|
-
return { ok: false, error: 'project_id required for quest' };
|
|
189
|
-
const q = db.addQuest(pid, a.content.slice(0, 200), '', 'medium', a.tags?.join(','));
|
|
190
|
-
return { ok: true, result: { type: 'quest', id: q.id } };
|
|
191
|
-
}
|
|
192
|
-
if (cls.type === 'truth') {
|
|
193
|
-
if (pid === null)
|
|
194
|
-
return { ok: false, error: 'project_id required for truth' };
|
|
195
|
-
if (a.mode === 'truth' || cls.confidence >= 100) {
|
|
196
|
-
const t = groundTruths.set(pid, { category: 'decision', key: a.content.slice(0, 60), value: a.content });
|
|
197
|
-
return { ok: true, result: { type: 'truth', id: t.id } };
|
|
198
|
-
}
|
|
199
|
-
const art = memory.add(pid, { kind: 'pattern', problem: a.content, tags: a.tags ?? [], confidence: cls.confidence / 100, needsReview: 1 });
|
|
200
|
-
return { ok: true, result: { type: 'memory_review', id: art.id } };
|
|
201
|
-
}
|
|
202
|
-
if (pid === null)
|
|
203
|
-
return { ok: false, error: 'project_id required for memory' };
|
|
204
|
-
const art = memory.add(pid, { kind: 'pattern', problem: a.content, tags: a.tags ?? [], confidence: (cls.confidence ?? 70) / 100, needsReview: 1 });
|
|
205
|
-
return { ok: true, result: { type: 'memory', id: art.id } };
|
|
206
|
-
}
|
|
207
|
-
default:
|
|
208
|
-
return { ok: false, error: `Internal tool '${name}' not implemented in dispatcher` };
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
catch (err) {
|
|
212
|
-
return { ok: false, error: err.message };
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
return internalDispatch;
|
|
216
|
-
}
|
|
217
|
-
//# sourceMappingURL=internal-dispatch.js.map
|
|
8
|
+
`).all(t.id):[]}}case"wyrm_failure_check":{const e=s,t=e.projectPath?r.getProject(e.projectPath):null;return{ok:!0,result:i.check(e.scope,e.target,e.description,t?.id??null)}}case"wyrm_failure_list":{const e=s,t=e.projectPath?r.getProject(e.projectPath):null;return{ok:!0,result:i.list(t?.id??null,Math.min(50,e.limit??20))}}case"wyrm_failure_record":{const e=s,t=e.projectPath?r.getProject(e.projectPath):null;return{ok:!0,result:i.record({project_id:t?.id??null,scope:e.scope,target:e.target,description:e.description,why_failed:e.why_failed,severity:e.severity})}}case"wyrm_symbol_search":{const e=s;if(!e.symbol)return{ok:!1,error:"symbol required"};const t=e.projectPath?r.getProject(e.projectPath):null;return{ok:!0,result:a.search(e.symbol,{projectId:t?.id,kind:e.kind,language:e.language,exact:e.exact,limit:Math.min(50,e.limit??20)})}}case"wyrm_symbol_callers":{const e=s,t=e.projectPath?r.getProject(e.projectPath):null;return{ok:!0,result:a.callers(e.symbol,t?.id).slice(0,30)}}case"wyrm_symbol_stats":{const e=s,t=e.projectPath?r.getProject(e.projectPath):null;return{ok:!0,result:a.stats(t?.id)}}case"wyrm_session_rehydrate":{const e=s,t=m.rehydrate(e.session_id);return{ok:t!=null,result:t,error:t?void 0:"session not found"}}case"wyrm_decision_upstream":{const e=s;return{ok:!0,result:u.upstreamOf(e.kind,e.id)}}case"wyrm_decision_downstream":{const e=s;return{ok:!0,result:u.downstreamOf(e.kind,e.id)}}case"wyrm_decided_because":{const e=s,t=r.getProject(e.projectPath);return t?{ok:!0,result:u.link({project_id:t.id,...e})}:{ok:!1,error:"project not found"}}case"wyrm_presence_list":{const e=s,t=e.projectPath?r.getProject(e.projectPath):null;return{ok:!0,result:f.liveAgents(t?.id??null)}}case"wyrm_sync_conflicts":return{ok:!0,result:j.unresolvedConflicts(20)};case"wyrm_quest_add":{const e=s,t=r.getProject(e.projectPath);if(!t)return{ok:!1,error:"project not found"};const o=r.addQuest(t.id,e.title,e.description,e.priority||"medium",e.tags);return{ok:!0,result:{id:o.id,title:o.title}}}case"wyrm_quest_complete":{const e=s;return{ok:!0,result:{id:r.updateQuest(e.questId,"completed").id,status:"completed"}}}case"wyrm_project_context":{const e=s,t=r.getProject(e.projectPath);return t?{ok:!0,result:{project:t.name,quests:r.getAllPendingQuests().filter(n=>n.project_id===t.id).slice(0,10),last_session:r.getDatabase().prepare("SELECT id, date, summary FROM sessions WHERE project_id = ? ORDER BY date DESC LIMIT 1").get(t.id)}}:{ok:!1,error:"project not found"}}case"wyrm_global_context":{const e=s,t=r.getAllProjects(e.maxProjects??20).map(n=>({name:n.name,stack:n.stack??null,stats:r.getProjectStats(n.id)})),o={globalContext:r.getAllGlobalContext(),projects:t};return e.includeQuests&&(o.quests=r.getAllPendingQuests().slice(0,20)),{ok:!0,result:o}}case"wyrm_recall":{const e=s,t=e.projectPath?r.getProject(e.projectPath):null;return t?e.query?{ok:!0,result:c.recall(t.id,e.query,{kind:e.kind,limit:Math.min(20,e.limit??10),minConfidence:e.minConfidence}).map(n=>({id:n.artifact.id,kind:n.artifact.kind,problem:n.artifact.problem,validated_fix:n.artifact.validated_fix,relevance:n.relevance_score}))}:{ok:!1,error:"query required"}:{ok:!1,error:"project not found"}}case"wyrm_remember":{const e=s,t=e.projectPath?r.getProject(e.projectPath):null;if(!t)return{ok:!1,error:"project not found"};if(!e.problem)return{ok:!1,error:"problem required"};const o=c.add(t.id,{kind:e.kind,problem:e.problem,constraints:e.constraints,validatedFix:e.validatedFix,whyItWorked:e.whyItWorked,outcome:e.outcome,tags:e.tags,confidence:e.confidence,sourceSessionId:e.sourceSessionId});return{ok:!0,result:{id:o.id,kind:o.kind}}}case"wyrm_distill":{const e=s,t=e.projectPath?r.getProject(e.projectPath):null;if(!t)return{ok:!1,error:"project not found"};if(!e.candidates?.length)return{ok:!1,error:"candidates required"};const o=e.candidates.map(n=>c.add(t.id,{kind:n.kind,problem:n.title,validatedFix:n.content,tags:n.tags??[],confidence:n.confidence??.7,needsReview:1}).id);return{ok:!0,result:{queued:o.length,ids:o}}}case"wyrm_capture":{const e=s;if(!e.content)return{ok:!1,error:"content required"};let t=_(e.content);if(e.mode&&e.mode!=="auto"){const l={quest:"quest",truth:"decision",memory:"pattern"};t={type:e.mode,subtype:l[e.mode]??e.mode,confidence:100,reasoning:`mode:${e.mode}`}}const o=e.project_id??null;return t.type==="quest"?o===null?{ok:!1,error:"project_id required for quest"}:{ok:!0,result:{type:"quest",id:r.addQuest(o,e.content.slice(0,200),"","medium",e.tags?.join(",")).id}}:t.type==="truth"?o===null?{ok:!1,error:"project_id required for truth"}:e.mode==="truth"||t.confidence>=100?{ok:!0,result:{type:"truth",id:y.set(o,{category:"decision",key:e.content.slice(0,60),value:e.content}).id}}:{ok:!0,result:{type:"memory_review",id:c.add(o,{kind:"pattern",problem:e.content,tags:e.tags??[],confidence:t.confidence/100,needsReview:1}).id}}:o===null?{ok:!1,error:"project_id required for memory"}:{ok:!0,result:{type:"memory",id:c.add(o,{kind:"pattern",problem:e.content,tags:e.tags??[],confidence:(t.confidence??70)/100,needsReview:1}).id}}}default:return{ok:!1,error:`Internal tool '${d}' not implemented in dispatcher`}}}catch(e){return{ok:!1,error:e.message}}}}export{q as makeInternalDispatch};
|
package/dist/keyset.js
CHANGED
|
@@ -1,110 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Keyset (cursor) pagination — the v7 F4 T035 context-economy primitive.
|
|
3
|
-
*
|
|
4
|
-
* THE MOVE: a big listing returns a PAGE plus an opaque `nextCursor`, not the
|
|
5
|
-
* whole table. The cursor is a COMPOSITE `(sortKey, id)` — never a bare
|
|
6
|
-
* sortKey. This is deliberate: the wyrm-cloud sync keyset bug (fixed at
|
|
7
|
-
* cloud/client.ts:299 with a `"<updated_at>:<id>"` composite) is the
|
|
8
|
-
* cautionary tale — a bare `sortKey` cursor SILENTLY DROPS every row that
|
|
9
|
-
* shares the boundary row's sort value (e.g. three artifacts with identical
|
|
10
|
-
* `confidence` straddling a page edge). The id tiebreak makes the keyset
|
|
11
|
-
* total-ordered, so `(sortKey, id) > (lastSortKey, lastId)` resumes EXACTLY
|
|
12
|
-
* after the last emitted row, dropping nothing and repeating nothing.
|
|
13
|
-
*
|
|
14
|
-
* The cursor is OPAQUE on the wire: a base64url-encoded `"<sortKey>:<id>"`
|
|
15
|
-
* string. Callers MUST treat it as a token (round-trip it verbatim); they
|
|
16
|
-
* never parse it. A malformed/garbage cursor decodes to `null` and the caller
|
|
17
|
-
* degrades to "start from the first page" (never throws on the wire — a
|
|
18
|
-
* resource-less or buggy client can't be wedged by a bad token).
|
|
19
|
-
*
|
|
20
|
-
* Article III: pure, deterministic, no network, no clock, no LLM. Same rows in
|
|
21
|
-
* ⇒ same page + same cursor out. Article VII: hard default + max caps so an
|
|
22
|
-
* unbounded `limit` can never be requested; the encode/decode is total (no
|
|
23
|
-
* throw on adversarial input).
|
|
24
|
-
*
|
|
25
|
-
* @copyright 2026 Ghost Protocol (Pvt) Ltd.
|
|
26
|
-
* @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
|
|
27
|
-
*/
|
|
28
|
-
/** Default page size when a caller passes no `limit`. */
|
|
29
|
-
export const DEFAULT_PAGE_SIZE = 50;
|
|
30
|
-
/** Absolute ceiling on a single page — a caller can never exceed this. */
|
|
31
|
-
export const MAX_PAGE_SIZE = 200;
|
|
32
|
-
/**
|
|
33
|
-
* Clamp a requested limit into `[1, MAX_PAGE_SIZE]`, defaulting to
|
|
34
|
-
* `DEFAULT_PAGE_SIZE` when absent/NaN/≤0. The hard cap holds regardless of
|
|
35
|
-
* what the caller asks for (Article VII: bounded by construction).
|
|
36
|
-
*/
|
|
37
|
-
export function clampPageSize(requested, opts = {}) {
|
|
38
|
-
const def = opts.defaultSize ?? DEFAULT_PAGE_SIZE;
|
|
39
|
-
const max = opts.maxSize ?? MAX_PAGE_SIZE;
|
|
40
|
-
if (requested == null || !Number.isFinite(requested) || requested <= 0)
|
|
41
|
-
return Math.min(def, max);
|
|
42
|
-
return Math.min(Math.floor(requested), max);
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Encode a composite `(sortKey, id)` anchor into an opaque base64url token.
|
|
46
|
-
* The sortKey is length-prefixed (`<len>.<sortKey>:<id>`) so a sortKey that
|
|
47
|
-
* itself contains a `:` (e.g. an ISO timestamp `2026-06-13T10:00:00`) round-
|
|
48
|
-
* trips losslessly — decode reads exactly `<len>` chars for the sortKey.
|
|
49
|
-
*/
|
|
50
|
-
export function encodeCursor(cursor) {
|
|
51
|
-
const sk = String(cursor.sortKey);
|
|
52
|
-
const raw = `${sk.length}.${sk}:${cursor.id}`;
|
|
53
|
-
return Buffer.from(raw, 'utf8').toString('base64url');
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Decode an opaque cursor token back into `(sortKey, id)`. Returns `null` for
|
|
57
|
-
* any malformed/garbage/empty token — the caller degrades to the first page
|
|
58
|
-
* rather than throwing on the wire (Article VII: total on adversarial input).
|
|
59
|
-
*/
|
|
60
|
-
export function decodeCursor(token) {
|
|
61
|
-
if (typeof token !== 'string' || token.length === 0)
|
|
62
|
-
return null;
|
|
63
|
-
let raw;
|
|
64
|
-
try {
|
|
65
|
-
raw = Buffer.from(token, 'base64url').toString('utf8');
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
// Grammar: <len>.<sortKey(len chars)>:<id>
|
|
71
|
-
const dot = raw.indexOf('.');
|
|
72
|
-
if (dot <= 0)
|
|
73
|
-
return null;
|
|
74
|
-
const lenStr = raw.slice(0, dot);
|
|
75
|
-
if (!/^\d{1,9}$/.test(lenStr))
|
|
76
|
-
return null;
|
|
77
|
-
const len = Number(lenStr);
|
|
78
|
-
const after = raw.slice(dot + 1);
|
|
79
|
-
if (after.length < len + 1)
|
|
80
|
-
return null;
|
|
81
|
-
const sortKey = after.slice(0, len);
|
|
82
|
-
const sep = after.charAt(len);
|
|
83
|
-
if (sep !== ':')
|
|
84
|
-
return null;
|
|
85
|
-
const idStr = after.slice(len + 1);
|
|
86
|
-
if (!/^\d{1,15}$/.test(idStr))
|
|
87
|
-
return null;
|
|
88
|
-
const id = Number(idStr);
|
|
89
|
-
if (!Number.isSafeInteger(id) || id < 0)
|
|
90
|
-
return null;
|
|
91
|
-
return { sortKey, id };
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Build a page from an OVER-FETCHED row array. Call the underlying query with
|
|
95
|
-
* `limit = pageSize + 1`; pass the result here. If `pageSize + 1` rows came
|
|
96
|
-
* back there is a further page: slice to `pageSize` and mint a `nextCursor`
|
|
97
|
-
* from the LAST emitted row via `anchorOf`. The over-fetch-by-one is how we
|
|
98
|
-
* know "is there more" without a second COUNT query.
|
|
99
|
-
*
|
|
100
|
-
* `anchorOf` must return the SAME `(sortKey, id)` the query's WHERE/ORDER use,
|
|
101
|
-
* or pages will skip/repeat. Keep them in lockstep at the call site.
|
|
102
|
-
*/
|
|
103
|
-
export function buildPage(rows, pageSize, anchorOf) {
|
|
104
|
-
if (rows.length <= pageSize)
|
|
105
|
-
return { items: rows };
|
|
106
|
-
const items = rows.slice(0, pageSize);
|
|
107
|
-
const last = items[items.length - 1];
|
|
108
|
-
return { items, nextCursor: encodeCursor(anchorOf(last)) };
|
|
109
|
-
}
|
|
110
|
-
//# sourceMappingURL=keyset.js.map
|
|
1
|
+
const a=50,m=200;function S(t,n={}){const r=n.defaultSize??50,e=n.maxSize??200;return t==null||!Number.isFinite(t)||t<=0?Math.min(r,e):Math.min(Math.floor(t),e)}function c(t){const n=String(t.sortKey),r=`${n.length}.${n}:${t.id}`;return Buffer.from(r,"utf8").toString("base64url")}function g(t){if(typeof t!="string"||t.length===0)return null;let n;try{n=Buffer.from(t,"base64url").toString("utf8")}catch{return null}const r=n.indexOf(".");if(r<=0)return null;const e=n.slice(0,r);if(!/^\d{1,9}$/.test(e))return null;const l=Number(e),u=n.slice(r+1);if(u.length<l+1)return null;const s=u.slice(0,l);if(u.charAt(l)!==":")return null;const o=u.slice(l+1);if(!/^\d{1,15}$/.test(o))return null;const i=Number(o);return!Number.isSafeInteger(i)||i<0?null:{sortKey:s,id:i}}function d(t,n,r){if(t.length<=n)return{items:t};const e=t.slice(0,n),l=e[e.length-1];return{items:e,nextCursor:c(r(l))}}export{a as DEFAULT_PAGE_SIZE,m as MAX_PAGE_SIZE,d as buildPage,S as clampPageSize,g as decodeCursor,c as encodeCursor};
|
package/dist/knowledge-graph.js
CHANGED
|
@@ -1,139 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
* Wyrm Knowledge Graph — Entity & Relationship Management
|
|
3
|
-
*
|
|
4
|
-
* @copyright 2026 Ghost Protocol (Pvt) Ltd.
|
|
5
|
-
* @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
|
|
6
|
-
*
|
|
7
|
-
* Manual-first knowledge graph with provenance tracking:
|
|
8
|
-
* - Named entities with types and metadata
|
|
9
|
-
* - Typed, directed relationships between entities
|
|
10
|
-
* - Aliases for deduplication
|
|
11
|
-
* - Graph traversal via recursive CTEs (neighborhood, paths)
|
|
12
|
-
* - FTS5 search on entity names
|
|
13
|
-
*/
|
|
14
|
-
const MAX_DEPTH = 5;
|
|
15
|
-
const MAX_RESULTS = 200;
|
|
16
|
-
/**
|
|
17
|
-
* Knowledge graph operations backed by SQLite.
|
|
18
|
-
*/
|
|
19
|
-
export class KnowledgeGraph {
|
|
20
|
-
db;
|
|
21
|
-
constructor(db) {
|
|
22
|
-
this.db = db;
|
|
23
|
-
}
|
|
24
|
-
// ==================== ENTITIES ====================
|
|
25
|
-
addEntity(projectId, name, type, metadata, createdBy) {
|
|
26
|
-
const result = this.db.prepare(`
|
|
1
|
+
const p=5,_=200;class l{db;constructor(t){this.db=t}addEntity(t,e,i,n,r){const s=this.db.prepare(`
|
|
27
2
|
INSERT INTO entities (project_id, name, type, metadata, created_by)
|
|
28
3
|
VALUES (?, ?, ?, ?, ?)
|
|
29
|
-
`).run(
|
|
30
|
-
return this.db.prepare('SELECT * FROM entities WHERE id = ?').get(result.lastInsertRowid);
|
|
31
|
-
}
|
|
32
|
-
getEntity(id) {
|
|
33
|
-
return this.db.prepare('SELECT * FROM entities WHERE id = ?').get(id) ?? null;
|
|
34
|
-
}
|
|
35
|
-
findEntity(projectId, name, type) {
|
|
36
|
-
if (type) {
|
|
37
|
-
return this.db.prepare('SELECT * FROM entities WHERE project_id = ? AND name = ? AND type = ?').get(projectId, name, type) ?? null;
|
|
38
|
-
}
|
|
39
|
-
return this.db.prepare('SELECT * FROM entities WHERE project_id = ? AND name = ?').get(projectId, name) ?? null;
|
|
40
|
-
}
|
|
41
|
-
updateEntity(id, updates) {
|
|
42
|
-
const entity = this.getEntity(id);
|
|
43
|
-
if (!entity)
|
|
44
|
-
return null;
|
|
45
|
-
this.db.prepare(`
|
|
4
|
+
`).run(t,e.trim(),i.trim(),n??null,r??"local");return this.db.prepare("SELECT * FROM entities WHERE id = ?").get(s.lastInsertRowid)}getEntity(t){return this.db.prepare("SELECT * FROM entities WHERE id = ?").get(t)??null}findEntity(t,e,i){return i?this.db.prepare("SELECT * FROM entities WHERE project_id = ? AND name = ? AND type = ?").get(t,e,i)??null:this.db.prepare("SELECT * FROM entities WHERE project_id = ? AND name = ?").get(t,e)??null}updateEntity(t,e){return this.getEntity(t)?(this.db.prepare(`
|
|
46
5
|
UPDATE entities SET
|
|
47
6
|
name = COALESCE(?, name),
|
|
48
7
|
type = COALESCE(?, type),
|
|
49
8
|
metadata = COALESCE(?, metadata),
|
|
50
9
|
updated_at = datetime('now')
|
|
51
10
|
WHERE id = ?
|
|
52
|
-
`).run(
|
|
53
|
-
return this.getEntity(id);
|
|
54
|
-
}
|
|
55
|
-
deleteEntity(id) {
|
|
56
|
-
const result = this.db.prepare('DELETE FROM entities WHERE id = ?').run(id);
|
|
57
|
-
return result.changes > 0;
|
|
58
|
-
}
|
|
59
|
-
listEntities(projectId, options) {
|
|
60
|
-
let sql = 'SELECT * FROM entities WHERE project_id = ?';
|
|
61
|
-
const params = [projectId];
|
|
62
|
-
if (options?.type) {
|
|
63
|
-
sql += ' AND type = ?';
|
|
64
|
-
params.push(options.type);
|
|
65
|
-
}
|
|
66
|
-
sql += ' ORDER BY name ASC';
|
|
67
|
-
sql += ` LIMIT ? OFFSET ?`;
|
|
68
|
-
params.push(options?.limit ?? 50, options?.offset ?? 0);
|
|
69
|
-
return this.db.prepare(sql).all(...params);
|
|
70
|
-
}
|
|
71
|
-
searchEntities(projectId, query, limit = 20) {
|
|
72
|
-
// Try FTS first, fall back to LIKE
|
|
73
|
-
try {
|
|
74
|
-
const ftsResults = this.db.prepare(`
|
|
11
|
+
`).run(e.name??null,e.type??null,e.metadata??null,t),this.getEntity(t)):null}deleteEntity(t){return this.db.prepare("DELETE FROM entities WHERE id = ?").run(t).changes>0}listEntities(t,e){let i="SELECT * FROM entities WHERE project_id = ?";const n=[t];return e?.type&&(i+=" AND type = ?",n.push(e.type)),i+=" ORDER BY name ASC",i+=" LIMIT ? OFFSET ?",n.push(e?.limit??50,e?.offset??0),this.db.prepare(i).all(...n)}searchEntities(t,e,i=20){try{const n=this.db.prepare(`
|
|
75
12
|
SELECT e.* FROM entities e
|
|
76
13
|
JOIN entities_fts f ON f.rowid = e.id
|
|
77
14
|
WHERE f.entities_fts MATCH ? AND e.project_id = ?
|
|
78
15
|
LIMIT ?
|
|
79
|
-
`).all(
|
|
80
|
-
if (ftsResults.length > 0)
|
|
81
|
-
return ftsResults;
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
// FTS query syntax error — fall back to LIKE
|
|
85
|
-
}
|
|
86
|
-
return this.db.prepare(`
|
|
16
|
+
`).all(e,t,i);if(n.length>0)return n}catch{}return this.db.prepare(`
|
|
87
17
|
SELECT * FROM entities WHERE project_id = ? AND (name LIKE ? OR type LIKE ?)
|
|
88
18
|
ORDER BY name ASC LIMIT ?
|
|
89
|
-
`).all(
|
|
90
|
-
}
|
|
91
|
-
// ==================== ALIASES ====================
|
|
92
|
-
addAlias(entityId, alias) {
|
|
93
|
-
this.db.prepare('INSERT OR IGNORE INTO entity_aliases (entity_id, alias) VALUES (?, ?)').run(entityId, alias.trim());
|
|
94
|
-
}
|
|
95
|
-
getAliases(entityId) {
|
|
96
|
-
const rows = this.db.prepare('SELECT alias FROM entity_aliases WHERE entity_id = ?').all(entityId);
|
|
97
|
-
return rows.map(r => r.alias);
|
|
98
|
-
}
|
|
99
|
-
findByAlias(projectId, alias) {
|
|
100
|
-
return this.db.prepare(`
|
|
19
|
+
`).all(t,`%${e}%`,`%${e}%`,i)}addAlias(t,e){this.db.prepare("INSERT OR IGNORE INTO entity_aliases (entity_id, alias) VALUES (?, ?)").run(t,e.trim())}getAliases(t){return this.db.prepare("SELECT alias FROM entity_aliases WHERE entity_id = ?").all(t).map(i=>i.alias)}findByAlias(t,e){return this.db.prepare(`
|
|
101
20
|
SELECT e.* FROM entities e
|
|
102
21
|
JOIN entity_aliases a ON a.entity_id = e.id
|
|
103
22
|
WHERE e.project_id = ? AND a.alias = ?
|
|
104
|
-
`).get(
|
|
105
|
-
}
|
|
106
|
-
// ==================== RELATIONSHIPS ====================
|
|
107
|
-
addRelationship(projectId, sourceId, targetId, type, options) {
|
|
108
|
-
const result = this.db.prepare(`
|
|
23
|
+
`).get(t,e)??null}addRelationship(t,e,i,n,r){const s=this.db.prepare(`
|
|
109
24
|
INSERT INTO relationships (
|
|
110
25
|
project_id, source_entity_id, target_entity_id, relationship_type,
|
|
111
26
|
weight, confidence, source_memory, extraction_method, metadata, created_by
|
|
112
27
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
113
|
-
`).run(
|
|
114
|
-
return this.db.prepare('SELECT * FROM relationships WHERE id = ?').get(result.lastInsertRowid);
|
|
115
|
-
}
|
|
116
|
-
getRelationships(entityId, direction = 'both') {
|
|
117
|
-
switch (direction) {
|
|
118
|
-
case 'outgoing':
|
|
119
|
-
return this.db.prepare('SELECT * FROM relationships WHERE source_entity_id = ?').all(entityId);
|
|
120
|
-
case 'incoming':
|
|
121
|
-
return this.db.prepare('SELECT * FROM relationships WHERE target_entity_id = ?').all(entityId);
|
|
122
|
-
case 'both':
|
|
123
|
-
return this.db.prepare('SELECT * FROM relationships WHERE source_entity_id = ? OR target_entity_id = ?').all(entityId, entityId);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
deleteRelationship(id) {
|
|
127
|
-
return this.db.prepare('DELETE FROM relationships WHERE id = ?').run(id).changes > 0;
|
|
128
|
-
}
|
|
129
|
-
// ==================== GRAPH QUERIES ====================
|
|
130
|
-
/**
|
|
131
|
-
* Get neighborhood — all nodes within N hops of a given entity.
|
|
132
|
-
* Uses recursive CTE with cycle detection.
|
|
133
|
-
*/
|
|
134
|
-
getNeighborhood(entityId, maxDepth = 2) {
|
|
135
|
-
const depth = Math.min(maxDepth, MAX_DEPTH);
|
|
136
|
-
const nodes = this.db.prepare(`
|
|
28
|
+
`).run(t,e,i,n.trim(),r?.weight??1,r?.confidence??1,r?.sourceMemory??null,r?.extractionMethod??"manual",r?.metadata??null,r?.createdBy??"local");return this.db.prepare("SELECT * FROM relationships WHERE id = ?").get(s.lastInsertRowid)}getRelationships(t,e="both"){switch(e){case"outgoing":return this.db.prepare("SELECT * FROM relationships WHERE source_entity_id = ?").all(t);case"incoming":return this.db.prepare("SELECT * FROM relationships WHERE target_entity_id = ?").all(t);case"both":return this.db.prepare("SELECT * FROM relationships WHERE source_entity_id = ? OR target_entity_id = ?").all(t,t)}}deleteRelationship(t){return this.db.prepare("DELETE FROM relationships WHERE id = ?").run(t).changes>0}getNeighborhood(t,e=2){const i=Math.min(e,5),n=this.db.prepare(`
|
|
137
29
|
WITH RECURSIVE neighbors(id, depth, visited) AS (
|
|
138
30
|
SELECT ?, 0, ',' || ? || ','
|
|
139
31
|
UNION ALL
|
|
@@ -152,31 +44,16 @@ export class KnowledgeGraph {
|
|
|
152
44
|
GROUP BY e.id
|
|
153
45
|
ORDER BY depth ASC
|
|
154
46
|
LIMIT ?
|
|
155
|
-
`).all(
|
|
156
|
-
// Get edges between all discovered nodes
|
|
157
|
-
const nodeIds = nodes.map(n => n.id);
|
|
158
|
-
if (nodeIds.length === 0)
|
|
159
|
-
return { nodes: [], edges: [] };
|
|
160
|
-
const placeholders = nodeIds.map(() => '?').join(',');
|
|
161
|
-
const edges = this.db.prepare(`
|
|
47
|
+
`).all(t,t,i,200),r=n.map(a=>a.id);if(r.length===0)return{nodes:[],edges:[]};const s=r.map(()=>"?").join(","),E=this.db.prepare(`
|
|
162
48
|
SELECT r.source_entity_id as source_id, s.name as source_name,
|
|
163
49
|
r.target_entity_id as target_id, t.name as target_name,
|
|
164
50
|
r.relationship_type, r.weight
|
|
165
51
|
FROM relationships r
|
|
166
52
|
JOIN entities s ON s.id = r.source_entity_id
|
|
167
53
|
JOIN entities t ON t.id = r.target_entity_id
|
|
168
|
-
WHERE r.source_entity_id IN (${
|
|
169
|
-
AND r.target_entity_id IN (${
|
|
170
|
-
`).all(...
|
|
171
|
-
return { nodes, edges };
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Find path between two entities using BFS with cycle detection.
|
|
175
|
-
* Returns the shortest path as a list of entity IDs, or null if no path exists.
|
|
176
|
-
*/
|
|
177
|
-
findPath(sourceId, targetId, maxDepth = 5) {
|
|
178
|
-
const depth = Math.min(maxDepth, MAX_DEPTH);
|
|
179
|
-
const rows = this.db.prepare(`
|
|
54
|
+
WHERE r.source_entity_id IN (${s})
|
|
55
|
+
AND r.target_entity_id IN (${s})
|
|
56
|
+
`).all(...r,...r);return{nodes:n,edges:E}}findPath(t,e,i=5){const n=Math.min(i,5),r=this.db.prepare(`
|
|
180
57
|
WITH RECURSIVE path_finder(id, path, depth) AS (
|
|
181
58
|
SELECT ?, CAST(? AS TEXT), 0
|
|
182
59
|
UNION ALL
|
|
@@ -191,45 +68,4 @@ export class KnowledgeGraph {
|
|
|
191
68
|
AND INSTR(',' || pf.path || ',', ',' || CASE WHEN r.source_entity_id = pf.id THEN r.target_entity_id ELSE r.source_entity_id END || ',') = 0
|
|
192
69
|
)
|
|
193
70
|
SELECT path FROM path_finder WHERE id = ? LIMIT 1
|
|
194
|
-
`).all(
|
|
195
|
-
if (rows.length === 0)
|
|
196
|
-
return null;
|
|
197
|
-
return rows[0].path.split(',').map(Number);
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Merge two entities: moves all relationships and aliases from source to target,
|
|
201
|
-
* adds source name as alias on target, then deletes source.
|
|
202
|
-
*/
|
|
203
|
-
mergeEntities(sourceId, targetId) {
|
|
204
|
-
const source = this.getEntity(sourceId);
|
|
205
|
-
const target = this.getEntity(targetId);
|
|
206
|
-
if (!source || !target)
|
|
207
|
-
return null;
|
|
208
|
-
const merge = this.db.transaction(() => {
|
|
209
|
-
// Add source name as alias on target
|
|
210
|
-
this.addAlias(targetId, source.name);
|
|
211
|
-
// Move source's aliases to target
|
|
212
|
-
this.db.prepare('UPDATE entity_aliases SET entity_id = ? WHERE entity_id = ?').run(targetId, sourceId);
|
|
213
|
-
// Redirect relationships from source to target
|
|
214
|
-
this.db.prepare('UPDATE relationships SET source_entity_id = ? WHERE source_entity_id = ?').run(targetId, sourceId);
|
|
215
|
-
this.db.prepare('UPDATE relationships SET target_entity_id = ? WHERE target_entity_id = ?').run(targetId, sourceId);
|
|
216
|
-
// Remove self-loops created by merge
|
|
217
|
-
this.db.prepare('DELETE FROM relationships WHERE source_entity_id = target_entity_id').run();
|
|
218
|
-
// Delete source entity
|
|
219
|
-
this.deleteEntity(sourceId);
|
|
220
|
-
});
|
|
221
|
-
merge();
|
|
222
|
-
return this.getEntity(targetId);
|
|
223
|
-
}
|
|
224
|
-
// ==================== STATS ====================
|
|
225
|
-
getStats(projectId) {
|
|
226
|
-
const entities = this.db.prepare('SELECT COUNT(*) as c FROM entities WHERE project_id = ?').get(projectId).c;
|
|
227
|
-
const relationships = this.db.prepare('SELECT COUNT(*) as c FROM relationships WHERE project_id = ?').get(projectId).c;
|
|
228
|
-
const typeRows = this.db.prepare('SELECT type, COUNT(*) as cnt FROM entities WHERE project_id = ? GROUP BY type').all(projectId);
|
|
229
|
-
const entityTypes = {};
|
|
230
|
-
for (const r of typeRows)
|
|
231
|
-
entityTypes[r.type] = r.cnt;
|
|
232
|
-
return { entities, relationships, entityTypes };
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
//# sourceMappingURL=knowledge-graph.js.map
|
|
71
|
+
`).all(t,t,n,e);return r.length===0?null:r[0].path.split(",").map(Number)}mergeEntities(t,e){const i=this.getEntity(t),n=this.getEntity(e);return!i||!n?null:(this.db.transaction(()=>{this.addAlias(e,i.name),this.db.prepare("UPDATE entity_aliases SET entity_id = ? WHERE entity_id = ?").run(e,t),this.db.prepare("UPDATE relationships SET source_entity_id = ? WHERE source_entity_id = ?").run(e,t),this.db.prepare("UPDATE relationships SET target_entity_id = ? WHERE target_entity_id = ?").run(e,t),this.db.prepare("DELETE FROM relationships WHERE source_entity_id = target_entity_id").run(),this.deleteEntity(t)})(),this.getEntity(e))}getStats(t){const e=this.db.prepare("SELECT COUNT(*) as c FROM entities WHERE project_id = ?").get(t).c,i=this.db.prepare("SELECT COUNT(*) as c FROM relationships WHERE project_id = ?").get(t).c,n=this.db.prepare("SELECT type, COUNT(*) as cnt FROM entities WHERE project_id = ? GROUP BY type").all(t),r={};for(const s of n)r[s.type]=s.cnt;return{entities:e,relationships:i,entityTypes:r}}}export{l as KnowledgeGraph};
|
package/dist/license.d.ts
CHANGED
|
@@ -115,6 +115,17 @@ export declare function signLicense(license: WyrmLicense, privateKeyPem: string)
|
|
|
115
115
|
* @param publicKeyPem - Optional PEM override; defaults to the embedded key
|
|
116
116
|
*/
|
|
117
117
|
export declare function verifyLicense(signed: SignedLicense, publicKeyPem?: string): LicenseValidation;
|
|
118
|
+
/**
|
|
119
|
+
* Known-Answer Test for the official build: confirm the verifier still REJECTS a
|
|
120
|
+
* structurally-valid but badly-signed license. If a fork stubs `verifyLicense` to
|
|
121
|
+
* always-accept (so it can run on a forged `license.json` and bypass the activation
|
|
122
|
+
* gate), this self-test trips. It's a deterrent layer — a determined forker can
|
|
123
|
+
* remove this too — but it raises the effort beyond a one-line edit. Pure + offline.
|
|
124
|
+
*/
|
|
125
|
+
export declare function selfTestVerifier(): {
|
|
126
|
+
ok: boolean;
|
|
127
|
+
reason?: string;
|
|
128
|
+
};
|
|
118
129
|
/**
|
|
119
130
|
* Check whether a specific feature is available in the current license.
|
|
120
131
|
*
|
package/dist/license.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../src/license.ts"],"names":[],"mappings":"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"}
|