kirograph 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/bin/commands/attack-surface.js +3 -1
- package/dist/bin/commands/attack-surface.js.map +2 -2
- package/dist/bin/commands/help.js +1 -1
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/memory.js +4 -4
- package/dist/bin/commands/memory.js.map +2 -2
- package/dist/bin/commands/security-export.js +3 -1
- package/dist/bin/commands/security-export.js.map +2 -2
- package/dist/bin/commands/wiki.js +2 -0
- package/dist/bin/commands/wiki.js.map +2 -2
- package/dist/bin/installer/config-prompt.js +13 -9
- package/dist/bin/installer/config-prompt.js.map +2 -2
- package/dist/bin/installer/index.js +34 -8
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/kirograph.js +1 -1
- package/dist/config.js +15 -5
- package/dist/config.js.map +2 -2
- package/dist/jev/client.js.map +2 -2
- package/dist/jev/factory.js +36 -0
- package/dist/jev/factory.js.map +7 -0
- package/dist/jev/strands-client.js +89 -0
- package/dist/jev/strands-client.js.map +7 -0
- package/dist/jev/types.js.map +1 -1
- package/dist/mcp/handlers/memory.js +4 -3
- package/dist/mcp/handlers/memory.js.map +2 -2
- package/dist/mcp/handlers/security.js +3 -1
- package/dist/mcp/handlers/security.js.map +2 -2
- package/dist/mcp/handlers/wiki.js +2 -0
- package/dist/mcp/handlers/wiki.js.map +2 -2
- package/dist/memory/index.js +6 -4
- package/dist/memory/index.js.map +2 -2
- package/dist/memory/relation-jev.js.map +1 -1
- package/dist/security/attack-surface.js +6 -6
- package/dist/security/attack-surface.js.map +2 -2
- package/dist/security/auth-detection-jev.js.map +1 -1
- package/dist/wiki/contradiction-jev.js.map +1 -1
- package/dist/wiki/index.js +12 -6
- package/dist/wiki/index.js.map +2 -2
- package/dist/wiki/lint.js +3 -3
- package/dist/wiki/lint.js.map +2 -2
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/mcp/handlers/wiki.ts"],
|
|
4
|
-
"sourcesContent": ["import KiroGraph from '../../index';\n\nexport async function handleWiki(toolName: string, args: Record<string, unknown>, cg: KiroGraph): Promise<string> {\n switch (toolName) {\n case 'kirograph_wiki_ingest': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph', {\n autoResolveConflicts: (config as any).wikiAutoResolveConflicts ?? false,\n });\n wiki.initialize();\n\n const source = args.source as string ?? '';\n const sourceName = args.sourceName as string ?? 'source';\n if (!source) return 'source is required';\n\n if (config.wikiSynthesisMode === 'local') {\n wiki.queueSource(source, sourceName);\n const count = wiki.getQueueCount();\n return `Source \"${sourceName}\" queued for local wiki synthesis (${count} in queue). The agentStop hook will run \"kirograph wiki synthesize\" automatically.`;\n }\n\n const prompt = wiki.getIngestPrompt(source, sourceName);\n return `WIKI_INGEST_PROMPT\\n${prompt}\\n\\nProduce WIKI_DIFF blocks following SCHEMA.md, then call kirograph_wiki_apply_diff with the diff string.`;\n }\n\n case 'kirograph_wiki_apply_diff': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph', {\n autoResolveConflicts: (config as any).wikiAutoResolveConflicts ?? false,\n });\n wiki.initialize();\n\n const diff = args.diff as string ?? '';\n if (!diff) return 'diff is required';\n\n const result = wiki.applyDiff(diff);\n const lines: string[] = ['\u2713 Wiki diff applied'];\n if (result.created.length) lines.push(` Created: ${result.created.join(', ')}`);\n if (result.updated.length) lines.push(` Updated: ${result.updated.join(', ')}`);\n if (result.conflictsResolved.length) lines.push(` Conflicts auto-resolved: ${result.conflictsResolved.join(', ')}`);\n if (result.conflictsPending.length) {\n lines.push(` \u26A1 Conflicts pending (${result.conflictsPending.length}):`);\n for (const c of result.conflictsPending) {\n lines.push(` ${c.page} \u00A7${c.section}: \"${c.existing}\" vs \"${c.incoming}\" (source: ${c.source})`);\n }\n }\n return lines.join('\\n');\n }\n\n case 'kirograph_wiki_search': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph');\n wiki.initialize();\n\n const query = args.query as string ?? '';\n if (!query) return 'query is required';\n const limit = Math.min(Number(args.limit ?? 5), 20);\n\n const results = wiki.search(query, limit);\n if (results.length === 0) return `No wiki pages found for \"${query}\".`;\n\n const lines = [`Wiki Search: ${query}`, ''];\n for (const { page, score } of results) {\n lines.push(`[${page.slug}] ${page.title} (score: ${score.toFixed(3)})`);\n // Show first non-heading line as preview\n const preview = page.content.split('\\n').find(l => l.trim() && !l.startsWith('#'));\n if (preview) lines.push(` ${preview.trim().slice(0, 120)}`);\n lines.push('');\n }\n return lines.join('\\n').trim();\n }\n\n case 'kirograph_wiki_page': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph');\n wiki.initialize();\n\n const slug = args.slug as string ?? '';\n if (!slug) return 'slug is required';\n\n const page = wiki.getPage(slug);\n if (!page) return `Wiki page \"${slug}\" not found. Use kirograph_wiki_list to see available pages.`;\n\n return page.content;\n }\n\n case 'kirograph_wiki_lint': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph', {\n wikiContradictionMode: config.wikiContradictionMode,\n wikiContradictionConfidenceThreshold: config.wikiContradictionConfidenceThreshold,\n jevApiKey: config.jevApiKey,\n jevBaseUrl: config.jevBaseUrl,\n jevModel: config.jevModel,\n typedPages: config.wikiTypedPages,\n });\n wiki.initialize();\n\n let issues;\n try {\n issues = await wiki.lint();\n } catch (err) {\n return `Error: ${err instanceof Error ? err.message : String(err)}`;\n }\n if (issues.length === 0) return '\u2713 Wiki lint passed \u2014 no issues found.';\n\n const lines = [`Wiki Lint \u2014 ${issues.length} issue(s)`, ''];\n for (const issue of issues) {\n const icon = issue.kind === 'contradiction' ? '\u26A1' : issue.kind === 'orphan' ? '\u25CB' : issue.kind === 'broken_link' ? '\uD83D\uDD17' : issue.kind === 'schema_error' ? '\u2716' : '\u26A0';\n lines.push(`${icon} [${issue.kind}] ${issue.slug}`);\n lines.push(` ${issue.detail}`);\n lines.push('');\n }\n return lines.join('\\n').trim();\n }\n\n case 'kirograph_wiki_list': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph');\n wiki.initialize();\n\n const pages = wiki.listPages();\n if (pages.length === 0) return 'Wiki is empty. Use kirograph_wiki_ingest to add pages.';\n\n const stats = wiki.getStats();\n const lines = [\n `Wiki \u2014 ${stats.pageCount} page(s), ${stats.totalSources} total sources`,\n '',\n ];\n for (const page of pages) {\n const date = new Date(page.updatedAt).toISOString().slice(0, 10);\n lines.push(` ${page.slug.padEnd(32)} ${page.title} (${page.sourceCount} src, ${date})`);\n }\n return lines.join('\\n');\n }\n\n case 'kirograph_wiki_synthesize': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = args.projectPath as string ?? cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n if (config.wikiSynthesisMode !== 'local') return 'wikiSynthesisMode is not \"local\". This tool is for local-model synthesis only.';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph', {\n autoResolveConflicts: (config as any).wikiAutoResolveConflicts ?? false,\n });\n wiki.initialize();\n\n const queueCount = wiki.getQueueCount();\n if (queueCount === 0) return 'Wiki queue is empty \u2014 nothing to synthesize.';\n\n const result = await wiki.synthesize(config.wikiLocalModel, true);\n const synthLines = [`Wiki synthesis complete \u2014 processed ${result.processed} source(s).`];\n if (result.created.length) synthLines.push(`Created: ${result.created.join(', ')}`);\n if (result.updated.length) synthLines.push(`Updated: ${result.updated.join(', ')}`);\n if (result.errors.length) synthLines.push(`Errors: ${result.errors.join('; ')}`);\n return synthLines.join('\\n');\n }\n\n case 'kirograph_wiki_init': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = args.projectPath as string ?? cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph', {\n autoResolveConflicts: (config as any).wikiAutoResolveConflicts ?? false,\n });\n wiki.initWiki();\n return 'Wiki initialized. SCHEMA.md and MANIFEST.md created in .kirograph/wiki/.';\n }\n\n case 'kirograph_wiki_reindex': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = args.projectPath as string ?? cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph');\n wiki.initialize();\n\n const count = wiki.reindex();\n return `Reindexed ${count} wiki page(s) from .kirograph/wiki/*.md.`;\n }\n\n case 'kirograph_wiki_status': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = args.projectPath as string ?? cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph');\n wiki.initialize();\n\n const stats = wiki.getStats();\n const oldest = stats.oldestPage ? new Date(stats.oldestPage).toISOString().slice(0, 10) : 'n/a';\n const newest = stats.newestPage ? new Date(stats.newestPage).toISOString().slice(0, 10) : 'n/a';\n return [\n 'Wiki Status:',\n ` Pages: ${stats.pageCount}`,\n ` Total sources: ${stats.totalSources}`,\n ` Oldest page: ${oldest}`,\n ` Newest page: ${newest}`,\n ` Wiki dir: .kirograph/wiki/`,\n ].join('\\n');\n }\n\n default:\n return `Unknown tool: ${toolName}`;\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,eAAsB,WAAW,UAAkB,MAA+B,IAAgC;AAChH,UAAQ,UAAU;AAAA,IAChB,KAAK,yBAAyB;AAC5B,YAAM,EAAE,WAAW,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,iBAAiB,CAAC;AACpF,YAAM,cAAc,GAAG,eAAe;AACtC,YAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,UAAI,CAAC,OAAO,WAAY,QAAO;AAE/B,YAAM,EAAE,cAAc,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,qBAAqB,CAAC;AAC3F,YAAM,KAAK,GAAG,YAAY;AAC1B,SAAG,gBAAgB;AACnB,YAAM,OAAO,IAAI,cAAc,GAAG,SAAS,GAAG,cAAc,eAAe;AAAA,QACzE,sBAAuB,OAAe,4BAA4B;AAAA,MACpE,CAAC;AACD,WAAK,WAAW;AAEhB,YAAM,SAAS,KAAK,UAAoB;AACxC,YAAM,aAAa,KAAK,cAAwB;AAChD,UAAI,CAAC,OAAQ,QAAO;AAEpB,UAAI,OAAO,sBAAsB,SAAS;AACxC,aAAK,YAAY,QAAQ,UAAU;AACnC,cAAM,QAAQ,KAAK,cAAc;AACjC,eAAO,WAAW,UAAU,sCAAsC,KAAK;AAAA,MACzE;AAEA,YAAM,SAAS,KAAK,gBAAgB,QAAQ,UAAU;AACtD,aAAO;AAAA,EAAuB,MAAM;AAAA;AAAA;AAAA,IACtC;AAAA,IAEA,KAAK,6BAA6B;AAChC,YAAM,EAAE,WAAW,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,iBAAiB,CAAC;AACpF,YAAM,cAAc,GAAG,eAAe;AACtC,YAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,UAAI,CAAC,OAAO,WAAY,QAAO;AAE/B,YAAM,EAAE,cAAc,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,qBAAqB,CAAC;AAC3F,YAAM,KAAK,GAAG,YAAY;AAC1B,SAAG,gBAAgB;AACnB,YAAM,OAAO,IAAI,cAAc,GAAG,SAAS,GAAG,cAAc,eAAe;AAAA,QACzE,sBAAuB,OAAe,4BAA4B;AAAA,MACpE,CAAC;AACD,WAAK,WAAW;AAEhB,YAAM,OAAO,KAAK,QAAkB;AACpC,UAAI,CAAC,KAAM,QAAO;AAElB,YAAM,SAAS,KAAK,UAAU,IAAI;AAClC,YAAM,QAAkB,CAAC,0BAAqB;AAC9C,UAAI,OAAO,QAAQ,OAAQ,OAAM,KAAK,cAAc,OAAO,QAAQ,KAAK,IAAI,CAAC,EAAE;AAC/E,UAAI,OAAO,QAAQ,OAAQ,OAAM,KAAK,cAAc,OAAO,QAAQ,KAAK,IAAI,CAAC,EAAE;AAC/E,UAAI,OAAO,kBAAkB,OAAQ,OAAM,KAAK,8BAA8B,OAAO,kBAAkB,KAAK,IAAI,CAAC,EAAE;AACnH,UAAI,OAAO,iBAAiB,QAAQ;AAClC,cAAM,KAAK,+BAA0B,OAAO,iBAAiB,MAAM,IAAI;AACvE,mBAAW,KAAK,OAAO,kBAAkB;AACvC,gBAAM,KAAK,OAAO,EAAE,IAAI,QAAK,EAAE,OAAO,MAAM,EAAE,QAAQ,SAAS,EAAE,QAAQ,cAAc,EAAE,MAAM,GAAG;AAAA,QACpG;AAAA,MACF;AACA,aAAO,MAAM,KAAK,IAAI;AAAA,IACxB;AAAA,IAEA,KAAK,yBAAyB;AAC5B,YAAM,EAAE,WAAW,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,iBAAiB,CAAC;AACpF,YAAM,cAAc,GAAG,eAAe;AACtC,YAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,UAAI,CAAC,OAAO,WAAY,QAAO;AAE/B,YAAM,EAAE,cAAc,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,qBAAqB,CAAC;AAC3F,YAAM,KAAK,GAAG,YAAY;AAC1B,SAAG,gBAAgB;AACnB,YAAM,OAAO,IAAI,cAAc,GAAG,SAAS,GAAG,cAAc,aAAa;AACzE,WAAK,WAAW;AAEhB,YAAM,QAAQ,KAAK,SAAmB;AACtC,UAAI,CAAC,MAAO,QAAO;AACnB,YAAM,QAAQ,KAAK,IAAI,OAAO,KAAK,SAAS,CAAC,GAAG,EAAE;AAElD,YAAM,UAAU,KAAK,OAAO,OAAO,KAAK;AACxC,UAAI,QAAQ,WAAW,EAAG,QAAO,4BAA4B,KAAK;AAElE,YAAM,QAAQ,CAAC,gBAAgB,KAAK,IAAI,EAAE;AAC1C,iBAAW,EAAE,MAAM,MAAM,KAAK,SAAS;AACrC,cAAM,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,KAAK,aAAa,MAAM,QAAQ,CAAC,CAAC,GAAG;AAEvE,cAAM,UAAU,KAAK,QAAQ,MAAM,IAAI,EAAE,KAAK,OAAK,EAAE,KAAK,KAAK,CAAC,EAAE,WAAW,GAAG,CAAC;AACjF,YAAI,QAAS,OAAM,KAAK,KAAK,QAAQ,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,EAAE;AAC3D,cAAM,KAAK,EAAE;AAAA,MACf;AACA,aAAO,MAAM,KAAK,IAAI,EAAE,KAAK;AAAA,IAC/B;AAAA,IAEA,KAAK,uBAAuB;AAC1B,YAAM,EAAE,WAAW,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,iBAAiB,CAAC;AACpF,YAAM,cAAc,GAAG,eAAe;AACtC,YAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,UAAI,CAAC,OAAO,WAAY,QAAO;AAE/B,YAAM,EAAE,cAAc,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,qBAAqB,CAAC;AAC3F,YAAM,KAAK,GAAG,YAAY;AAC1B,SAAG,gBAAgB;AACnB,YAAM,OAAO,IAAI,cAAc,GAAG,SAAS,GAAG,cAAc,aAAa;AACzE,WAAK,WAAW;AAEhB,YAAM,OAAO,KAAK,QAAkB;AACpC,UAAI,CAAC,KAAM,QAAO;AAElB,YAAM,OAAO,KAAK,QAAQ,IAAI;AAC9B,UAAI,CAAC,KAAM,QAAO,cAAc,IAAI;AAEpC,aAAO,KAAK;AAAA,IACd;AAAA,IAEA,KAAK,uBAAuB;AAC1B,YAAM,EAAE,WAAW,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,iBAAiB,CAAC;AACpF,YAAM,cAAc,GAAG,eAAe;AACtC,YAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,UAAI,CAAC,OAAO,WAAY,QAAO;AAE/B,YAAM,EAAE,cAAc,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,qBAAqB,CAAC;AAC3F,YAAM,KAAK,GAAG,YAAY;AAC1B,SAAG,gBAAgB;AACnB,YAAM,OAAO,IAAI,cAAc,GAAG,SAAS,GAAG,cAAc,eAAe;AAAA,QACzE,uBAAuB,OAAO;AAAA,QAC9B,sCAAsC,OAAO;AAAA,QAC7C,WAAW,OAAO;AAAA,QAClB,YAAY,OAAO;AAAA,QACnB,UAAU,OAAO;AAAA,QACjB,YAAY,OAAO;AAAA,MACrB,CAAC;AACD,WAAK,WAAW;AAEhB,UAAI;AACJ,UAAI;AACF,iBAAS,MAAM,KAAK,KAAK;AAAA,MAC3B,SAAS,KAAK;AACZ,eAAO,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MACnE;AACA,UAAI,OAAO,WAAW,EAAG,QAAO;AAEhC,YAAM,QAAQ,CAAC,oBAAe,OAAO,MAAM,aAAa,EAAE;AAC1D,iBAAW,SAAS,QAAQ;AAC1B,cAAM,OAAO,MAAM,SAAS,kBAAkB,WAAM,MAAM,SAAS,WAAW,WAAM,MAAM,SAAS,gBAAgB,cAAO,MAAM,SAAS,iBAAiB,WAAM;AAChK,cAAM,KAAK,GAAG,IAAI,KAAK,MAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAClD,cAAM,KAAK,KAAK,MAAM,MAAM,EAAE;AAC9B,cAAM,KAAK,EAAE;AAAA,MACf;AACA,aAAO,MAAM,KAAK,IAAI,EAAE,KAAK;AAAA,IAC/B;AAAA,IAEA,KAAK,uBAAuB;AAC1B,YAAM,EAAE,WAAW,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,iBAAiB,CAAC;AACpF,YAAM,cAAc,GAAG,eAAe;AACtC,YAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,UAAI,CAAC,OAAO,WAAY,QAAO;AAE/B,YAAM,EAAE,cAAc,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,qBAAqB,CAAC;AAC3F,YAAM,KAAK,GAAG,YAAY;AAC1B,SAAG,gBAAgB;AACnB,YAAM,OAAO,IAAI,cAAc,GAAG,SAAS,GAAG,cAAc,aAAa;AACzE,WAAK,WAAW;AAEhB,YAAM,QAAQ,KAAK,UAAU;AAC7B,UAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,YAAM,QAAQ,KAAK,SAAS;AAC5B,YAAM,QAAQ;AAAA,QACZ,eAAU,MAAM,SAAS,aAAa,MAAM,YAAY;AAAA,QACxD;AAAA,MACF;AACA,iBAAW,QAAQ,OAAO;AACxB,cAAM,OAAO,IAAI,KAAK,KAAK,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAC/D,cAAM,KAAK,KAAK,KAAK,KAAK,OAAO,EAAE,CAAC,IAAI,KAAK,KAAK,MAAM,KAAK,WAAW,SAAS,IAAI,GAAG;AAAA,MAC1F;AACA,aAAO,MAAM,KAAK,IAAI;AAAA,IACxB;AAAA,IAEA,KAAK,6BAA6B;AAChC,YAAM,EAAE,WAAW,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,iBAAiB,CAAC;AACpF,YAAM,cAAc,KAAK,eAAyB,GAAG,eAAe;AACpE,YAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,UAAI,CAAC,OAAO,WAAY,QAAO;AAC/B,UAAI,OAAO,sBAAsB,QAAS,QAAO;AAEjD,YAAM,EAAE,cAAc,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,qBAAqB,CAAC;AAC3F,YAAM,KAAK,GAAG,YAAY;AAC1B,SAAG,gBAAgB;AACnB,YAAM,OAAO,IAAI,cAAc,GAAG,SAAS,GAAG,cAAc,eAAe;AAAA,QACzE,sBAAuB,OAAe,4BAA4B;AAAA,MACpE,CAAC;AACD,WAAK,WAAW;AAEhB,YAAM,aAAa,KAAK,cAAc;AACtC,UAAI,eAAe,EAAG,QAAO;AAE7B,YAAM,SAAS,MAAM,KAAK,WAAW,OAAO,gBAAgB,IAAI;AAChE,YAAM,aAAa,CAAC,4CAAuC,OAAO,SAAS,aAAa;AACxF,UAAI,OAAO,QAAQ,OAAQ,YAAW,KAAK,YAAY,OAAO,QAAQ,KAAK,IAAI,CAAC,EAAE;AAClF,UAAI,OAAO,QAAQ,OAAQ,YAAW,KAAK,YAAY,OAAO,QAAQ,KAAK,IAAI,CAAC,EAAE;AAClF,UAAI,OAAO,OAAO,OAAQ,YAAW,KAAK,WAAW,OAAO,OAAO,KAAK,IAAI,CAAC,EAAE;AAC/E,aAAO,WAAW,KAAK,IAAI;AAAA,IAC7B;AAAA,IAEA,KAAK,uBAAuB;AAC1B,YAAM,EAAE,WAAW,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,iBAAiB,CAAC;AACpF,YAAM,cAAc,KAAK,eAAyB,GAAG,eAAe;AACpE,YAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,UAAI,CAAC,OAAO,WAAY,QAAO;AAE/B,YAAM,EAAE,cAAc,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,qBAAqB,CAAC;AAC3F,YAAM,KAAK,GAAG,YAAY;AAC1B,SAAG,gBAAgB;AACnB,YAAM,OAAO,IAAI,cAAc,GAAG,SAAS,GAAG,cAAc,eAAe;AAAA,QACzE,sBAAuB,OAAe,4BAA4B;AAAA,MACpE,CAAC;AACD,WAAK,SAAS;AACd,aAAO;AAAA,IACT;AAAA,IAEA,KAAK,0BAA0B;AAC7B,YAAM,EAAE,WAAW,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,iBAAiB,CAAC;AACpF,YAAM,cAAc,KAAK,eAAyB,GAAG,eAAe;AACpE,YAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,UAAI,CAAC,OAAO,WAAY,QAAO;AAE/B,YAAM,EAAE,cAAc,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,qBAAqB,CAAC;AAC3F,YAAM,KAAK,GAAG,YAAY;AAC1B,SAAG,gBAAgB;AACnB,YAAM,OAAO,IAAI,cAAc,GAAG,SAAS,GAAG,cAAc,aAAa;AACzE,WAAK,WAAW;AAEhB,YAAM,QAAQ,KAAK,QAAQ;AAC3B,aAAO,aAAa,KAAK;AAAA,IAC3B;AAAA,IAEA,KAAK,yBAAyB;AAC5B,YAAM,EAAE,WAAW,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,iBAAiB,CAAC;AACpF,YAAM,cAAc,KAAK,eAAyB,GAAG,eAAe;AACpE,YAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,UAAI,CAAC,OAAO,WAAY,QAAO;AAE/B,YAAM,EAAE,cAAc,IAAI,MAAM,QAAQ,QAAQ,EAAE,KAAK,MAAM,QAAQ,qBAAqB,CAAC;AAC3F,YAAM,KAAK,GAAG,YAAY;AAC1B,SAAG,gBAAgB;AACnB,YAAM,OAAO,IAAI,cAAc,GAAG,SAAS,GAAG,cAAc,aAAa;AACzE,WAAK,WAAW;AAEhB,YAAM,QAAQ,KAAK,SAAS;AAC5B,YAAM,SAAS,MAAM,aAAa,IAAI,KAAK,MAAM,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,IAAI;AAC1F,YAAM,SAAS,MAAM,aAAa,IAAI,KAAK,MAAM,UAAU,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,IAAI;AAC1F,aAAO;AAAA,QACL;AAAA,QACA,oBAAoB,MAAM,SAAS;AAAA,QACnC,oBAAoB,MAAM,YAAY;AAAA,QACtC,oBAAoB,MAAM;AAAA,QAC1B,oBAAoB,MAAM;AAAA,QAC1B;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AAAA,IAEA;AACE,aAAO,iBAAiB,QAAQ;AAAA,EACpC;AACF;",
|
|
4
|
+
"sourcesContent": ["import KiroGraph from '../../index';\n\nexport async function handleWiki(toolName: string, args: Record<string, unknown>, cg: KiroGraph): Promise<string> {\n switch (toolName) {\n case 'kirograph_wiki_ingest': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph', {\n autoResolveConflicts: (config as any).wikiAutoResolveConflicts ?? false,\n });\n wiki.initialize();\n\n const source = args.source as string ?? '';\n const sourceName = args.sourceName as string ?? 'source';\n if (!source) return 'source is required';\n\n if (config.wikiSynthesisMode === 'local') {\n wiki.queueSource(source, sourceName);\n const count = wiki.getQueueCount();\n return `Source \"${sourceName}\" queued for local wiki synthesis (${count} in queue). The agentStop hook will run \"kirograph wiki synthesize\" automatically.`;\n }\n\n const prompt = wiki.getIngestPrompt(source, sourceName);\n return `WIKI_INGEST_PROMPT\\n${prompt}\\n\\nProduce WIKI_DIFF blocks following SCHEMA.md, then call kirograph_wiki_apply_diff with the diff string.`;\n }\n\n case 'kirograph_wiki_apply_diff': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph', {\n autoResolveConflicts: (config as any).wikiAutoResolveConflicts ?? false,\n });\n wiki.initialize();\n\n const diff = args.diff as string ?? '';\n if (!diff) return 'diff is required';\n\n const result = wiki.applyDiff(diff);\n const lines: string[] = ['\u2713 Wiki diff applied'];\n if (result.created.length) lines.push(` Created: ${result.created.join(', ')}`);\n if (result.updated.length) lines.push(` Updated: ${result.updated.join(', ')}`);\n if (result.conflictsResolved.length) lines.push(` Conflicts auto-resolved: ${result.conflictsResolved.join(', ')}`);\n if (result.conflictsPending.length) {\n lines.push(` \u26A1 Conflicts pending (${result.conflictsPending.length}):`);\n for (const c of result.conflictsPending) {\n lines.push(` ${c.page} \u00A7${c.section}: \"${c.existing}\" vs \"${c.incoming}\" (source: ${c.source})`);\n }\n }\n return lines.join('\\n');\n }\n\n case 'kirograph_wiki_search': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph');\n wiki.initialize();\n\n const query = args.query as string ?? '';\n if (!query) return 'query is required';\n const limit = Math.min(Number(args.limit ?? 5), 20);\n\n const results = wiki.search(query, limit);\n if (results.length === 0) return `No wiki pages found for \"${query}\".`;\n\n const lines = [`Wiki Search: ${query}`, ''];\n for (const { page, score } of results) {\n lines.push(`[${page.slug}] ${page.title} (score: ${score.toFixed(3)})`);\n // Show first non-heading line as preview\n const preview = page.content.split('\\n').find(l => l.trim() && !l.startsWith('#'));\n if (preview) lines.push(` ${preview.trim().slice(0, 120)}`);\n lines.push('');\n }\n return lines.join('\\n').trim();\n }\n\n case 'kirograph_wiki_page': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph');\n wiki.initialize();\n\n const slug = args.slug as string ?? '';\n if (!slug) return 'slug is required';\n\n const page = wiki.getPage(slug);\n if (!page) return `Wiki page \"${slug}\" not found. Use kirograph_wiki_list to see available pages.`;\n\n return page.content;\n }\n\n case 'kirograph_wiki_lint': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph', {\n wikiContradictionMode: config.wikiContradictionMode,\n wikiContradictionConfidenceThreshold: config.wikiContradictionConfidenceThreshold,\n jevApiKey: config.jevApiKey,\n jevBaseUrl: config.jevBaseUrl,\n jevModel: config.jevModel,\n strandsBaseUrl: config.strandsBaseUrl,\n strandsModel: config.strandsModel,\n typedPages: config.wikiTypedPages,\n });\n wiki.initialize();\n\n let issues;\n try {\n issues = await wiki.lint();\n } catch (err) {\n return `Error: ${err instanceof Error ? err.message : String(err)}`;\n }\n if (issues.length === 0) return '\u2713 Wiki lint passed \u2014 no issues found.';\n\n const lines = [`Wiki Lint \u2014 ${issues.length} issue(s)`, ''];\n for (const issue of issues) {\n const icon = issue.kind === 'contradiction' ? '\u26A1' : issue.kind === 'orphan' ? '\u25CB' : issue.kind === 'broken_link' ? '\uD83D\uDD17' : issue.kind === 'schema_error' ? '\u2716' : '\u26A0';\n lines.push(`${icon} [${issue.kind}] ${issue.slug}`);\n lines.push(` ${issue.detail}`);\n lines.push('');\n }\n return lines.join('\\n').trim();\n }\n\n case 'kirograph_wiki_list': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph');\n wiki.initialize();\n\n const pages = wiki.listPages();\n if (pages.length === 0) return 'Wiki is empty. Use kirograph_wiki_ingest to add pages.';\n\n const stats = wiki.getStats();\n const lines = [\n `Wiki \u2014 ${stats.pageCount} page(s), ${stats.totalSources} total sources`,\n '',\n ];\n for (const page of pages) {\n const date = new Date(page.updatedAt).toISOString().slice(0, 10);\n lines.push(` ${page.slug.padEnd(32)} ${page.title} (${page.sourceCount} src, ${date})`);\n }\n return lines.join('\\n');\n }\n\n case 'kirograph_wiki_synthesize': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = args.projectPath as string ?? cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n if (config.wikiSynthesisMode !== 'local') return 'wikiSynthesisMode is not \"local\". This tool is for local-model synthesis only.';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph', {\n autoResolveConflicts: (config as any).wikiAutoResolveConflicts ?? false,\n });\n wiki.initialize();\n\n const queueCount = wiki.getQueueCount();\n if (queueCount === 0) return 'Wiki queue is empty \u2014 nothing to synthesize.';\n\n const result = await wiki.synthesize(config.wikiLocalModel, true);\n const synthLines = [`Wiki synthesis complete \u2014 processed ${result.processed} source(s).`];\n if (result.created.length) synthLines.push(`Created: ${result.created.join(', ')}`);\n if (result.updated.length) synthLines.push(`Updated: ${result.updated.join(', ')}`);\n if (result.errors.length) synthLines.push(`Errors: ${result.errors.join('; ')}`);\n return synthLines.join('\\n');\n }\n\n case 'kirograph_wiki_init': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = args.projectPath as string ?? cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph', {\n autoResolveConflicts: (config as any).wikiAutoResolveConflicts ?? false,\n });\n wiki.initWiki();\n return 'Wiki initialized. SCHEMA.md and MANIFEST.md created in .kirograph/wiki/.';\n }\n\n case 'kirograph_wiki_reindex': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = args.projectPath as string ?? cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph');\n wiki.initialize();\n\n const count = wiki.reindex();\n return `Reindexed ${count} wiki page(s) from .kirograph/wiki/*.md.`;\n }\n\n case 'kirograph_wiki_status': {\n const { loadConfig } = await Promise.resolve().then(() => require('../../config.js'));\n const projectRoot = args.projectPath as string ?? cg.getProjectRoot();\n const config = await loadConfig(projectRoot);\n if (!config.enableWiki) return 'Wiki is not enabled. Set enableWiki: true in .kirograph/config.json';\n\n const { KiroGraphWiki } = await Promise.resolve().then(() => require('../../wiki/index.js'));\n const db = cg.getDatabase();\n db.applyWikiSchema();\n const wiki = new KiroGraphWiki(db.getRawDb(), projectRoot + '/.kirograph');\n wiki.initialize();\n\n const stats = wiki.getStats();\n const oldest = stats.oldestPage ? new Date(stats.oldestPage).toISOString().slice(0, 10) : 'n/a';\n const newest = stats.newestPage ? new Date(stats.newestPage).toISOString().slice(0, 10) : 'n/a';\n return [\n 'Wiki Status:',\n ` Pages: ${stats.pageCount}`,\n ` Total sources: ${stats.totalSources}`,\n ` Oldest page: ${oldest}`,\n ` Newest page: ${newest}`,\n ` Wiki dir: .kirograph/wiki/`,\n ].join('\\n');\n }\n\n default:\n return `Unknown tool: ${toolName}`;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/memory/index.js
CHANGED
|
@@ -280,21 +280,23 @@ class MemoryManager {
|
|
|
280
280
|
const obsB = this.memDb.getObservation(obsBId);
|
|
281
281
|
if (!obsA) throw new Error(`Observation not found: ${observationA}`);
|
|
282
282
|
if (!obsB) throw new Error(`Observation not found: ${observationB}`);
|
|
283
|
-
const {
|
|
283
|
+
const { createDecisionClient } = await Promise.resolve().then(() => require("../jev/factory.js"));
|
|
284
284
|
const { classifyRelationWithJev } = await Promise.resolve().then(() => require("./relation-jev.js"));
|
|
285
|
-
const
|
|
285
|
+
const cfg = this.config;
|
|
286
|
+
const backend = cfg.memoryRelationMode === "strands" ? "strands" : "jev";
|
|
287
|
+
const client = await createDecisionClient(backend, cfg);
|
|
286
288
|
const { relation, confidence } = await classifyRelationWithJev(client, obsA.content, obsB.content);
|
|
287
289
|
const relationId = this.memDb.insertRelation({
|
|
288
290
|
observationA: obsAId,
|
|
289
291
|
observationB: obsBId,
|
|
290
292
|
relation,
|
|
291
293
|
confidence,
|
|
292
|
-
reason:
|
|
294
|
+
reason: `Auto-classified by ${backend}`
|
|
293
295
|
});
|
|
294
296
|
const threshold = this.config.memoryRelationConfidenceThreshold ?? 0.8;
|
|
295
297
|
const autoJudged = confidence >= threshold;
|
|
296
298
|
if (autoJudged) {
|
|
297
|
-
this.memDb.judgeRelation(relationId, relation, confidence,
|
|
299
|
+
this.memDb.judgeRelation(relationId, relation, confidence, `Auto-judged by ${backend} (confidence >= memoryRelationConfidenceThreshold)`);
|
|
298
300
|
}
|
|
299
301
|
return { relationId, relation, confidence, autoJudged };
|
|
300
302
|
}
|
package/dist/memory/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/memory/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * KiroGraph Memory \u2014 Public API\n *\n * MemoryManager is the single entry point for all memory operations.\n * Coordinates compression, storage, symbol detection, and embedding.\n */\n\nimport type { KiroGraphConfig } from '../config';\nimport type {\n MemObservation,\n MemObservationInput,\n MemSession,\n ScoredObservation,\n MemSearchOptions,\n MemTimelineOptions,\n MemStats,\n CompressResult,\n WatchmenReadyResult,\n MemRelation,\n MemRelationInput,\n RelationType,\n MemPrompt,\n} from './types';\nimport { MemoryDatabase } from './database';\nimport { compressObservation, type CavemanMode } from './compress';\nimport { detectSymbols } from './symbols';\nimport { MemoryVectorManager } from './vectors';\nimport { WatchmenChecker } from '../watchmen';\nimport { logDebug } from '../errors';\nimport { validateObservationInput, validateObservationFields } from './schema';\nimport * as path from 'path';\n\nexport { MemoryDatabase } from './database';\nexport { compressObservation, extractIdentifiers, type CavemanMode } from './compress';\nexport { detectSymbols } from './symbols';\nexport { MemoryVectorManager } from './vectors';\nexport { MemorySchemaError, OBSERVATION_KINDS, validateObservationInput, validateObservationFields } from './schema';\nexport * from './types';\n\n// \u2500\u2500 MemoryManager \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class MemoryManager {\n private memDb: MemoryDatabase;\n private vectorMgr: MemoryVectorManager;\n private config: KiroGraphConfig;\n private db: any; // raw sqlite handle for symbol detection\n private cavemanMode: CavemanMode;\n private excludePatterns: string[];\n private watchmenChecker: WatchmenChecker | null;\n private projectRoot: string;\n\n constructor(config: KiroGraphConfig, db: any, projectRoot = process.cwd()) {\n this.config = config;\n this.db = db;\n this.projectRoot = projectRoot;\n this.memDb = new MemoryDatabase(db);\n const kirographDir = require('path').join(projectRoot, '.kirograph');\n this.vectorMgr = new MemoryVectorManager(config, this.memDb, kirographDir);\n this.cavemanMode = (config as any).cavemanMode ?? 'off';\n this.excludePatterns = (config as any).memoryExcludePatterns ?? [];\n this.watchmenChecker = config.enableWatchmen\n ? new WatchmenChecker(config.watchmenThreshold)\n : null;\n }\n\n /**\n * Initialize memory tables. Must be called before any operations.\n */\n initialize(): void {\n this.memDb.initialize();\n }\n\n /**\n * Check if memory is enabled in config.\n */\n static isEnabled(config: KiroGraphConfig): boolean {\n return !!(config as any).enableMemory;\n }\n\n // \u2500\u2500 Store \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Store an observation. Handles:\n * 1. Privacy stripping (<private> blocks)\n * 2. Exclude pattern checking\n * 3. Caveman compression (if enabled)\n * 4. Deduplication (content hash)\n * 5. Symbol detection and linking\n * 6. Embedding (if enabled)\n * 7. Watchmen threshold check (if enabled) \u2014 returns WatchmenReadyResult when synthesis should run\n *\n * Returns the observation ID, a WatchmenReadyResult, or null if skipped (duplicate or excluded).\n * @throws MemorySchemaError if `memoryStrictWrites` is enabled and the input doesn't conform.\n */\n async store(input: MemObservationInput, ide = 'kiro'): Promise<string | WatchmenReadyResult | null> {\n // 0. Opt-in schema validation \u2014 rejects instead of silently corrupting the store.\n if ((this.config as any).memoryStrictWrites) {\n validateObservationInput(input);\n }\n if ((this.config as any).memorySchemaValidation) {\n validateObservationFields(input, path.join(this.projectRoot, '.kirograph', 'memory-schemas'));\n }\n\n let text = input.content;\n\n // 1. Strip <private> blocks\n text = MemoryDatabase.stripPrivate(text);\n if (!text.trim()) return null;\n\n // 2. Check exclude patterns\n if (this.shouldExclude(text)) return null;\n\n // 3. Compress (if caveman is on)\n let contentRaw: string | undefined;\n if (this.cavemanMode !== 'off') {\n const result = compressObservation(text, this.cavemanMode);\n contentRaw = text;\n text = result.compressed;\n }\n\n // 4. Get or create session\n const cwd = process.cwd();\n const sessionTimeout = (this.config as any).memorySessionTimeout ?? 7200;\n const sessionId = this.memDb.getOrCreateSession(ide, cwd, sessionTimeout);\n\n // 5. Insert observation (deduplicates via content_hash)\n const id = this.memDb.insertObservation(text, {\n contentRaw: this.cavemanMode !== 'off' ? contentRaw : undefined,\n kind: input.kind ?? 'note',\n source: input.source ?? 'manual',\n tags: input.tags,\n sessionId,\n topicKey: input.topicKey,\n reviewAfter: input.reviewAfter,\n fields: input.fields,\n });\n\n if (!id) {\n logDebug('Memory: duplicate observation skipped');\n return null;\n }\n\n // 6. Detect and link symbols\n const symbols = detectSymbols(text, this.db);\n if (symbols.length > 0) {\n this.memDb.linkToSymbols(id, symbols.map(s => s.qualifiedName));\n }\n\n // Also detect in raw text if different\n if (contentRaw) {\n const rawSymbols = detectSymbols(contentRaw, this.db);\n const extraSymbols = rawSymbols.filter(\n s => !symbols.some(existing => existing.qualifiedName === s.qualifiedName)\n );\n if (extraSymbols.length > 0) {\n this.memDb.linkToSymbols(id, extraSymbols.map(s => s.qualifiedName));\n }\n }\n\n // 7. Embed (async, non-blocking for the store operation)\n const obs = this.memDb.getObservation(id);\n if (obs) {\n await this.vectorMgr.embedObservation(obs);\n }\n\n // 8. Watchmen threshold check\n if (this.watchmenChecker && input.kind !== 'summary') {\n const check = this.watchmenChecker.shouldSynthesize(this.memDb);\n if (check.ready) {\n return this.watchmenChecker.buildReadyResponse(id, check.pendingCount, this.projectRoot);\n }\n }\n\n return id;\n }\n\n // \u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Hybrid search: combines FTS5 and vector search with configurable alpha.\n */\n async search(query: string, opts: MemSearchOptions = {}): Promise<ScoredObservation[]> {\n const limit = opts.limit ?? 10;\n const alpha = opts.alpha ?? (this.config as any).memorySearchAlpha ?? 0.5;\n\n // FTS search\n const ftsResults = this.memDb.searchFTS(query, { ...opts, limit: limit * 2 });\n\n // Vector search (if enabled and no model mismatch)\n let vectorResults: ScoredObservation[] = [];\n if (this.vectorMgr.isEnabled() && !this.vectorMgr.hasModelMismatch()) {\n vectorResults = await this.vectorMgr.search(query, limit * 2);\n }\n\n // If only one source has results, return it directly\n if (vectorResults.length === 0) {\n const sliced = ftsResults.slice(0, limit);\n const ids = sliced.map(r => r.observation.id);\n const relMap = this.memDb.getRelationsForObservations(ids);\n for (const r of sliced) {\n const rels = relMap.get(r.observation.id);\n if (rels && rels.length > 0) r.relations = rels;\n }\n return sliced;\n }\n if (ftsResults.length === 0) {\n const sliced = vectorResults.slice(0, limit);\n const ids = sliced.map(r => r.observation.id);\n const relMap = this.memDb.getRelationsForObservations(ids);\n for (const r of sliced) {\n const rels = relMap.get(r.observation.id);\n if (rels && rels.length > 0) r.relations = rels;\n }\n return sliced;\n }\n\n // Hybrid merge: normalize scores and blend\n const merged = this.mergeResults(ftsResults, vectorResults, alpha);\n const sliced = merged.slice(0, limit);\n\n // Enrich with relations\n const ids = sliced.map(r => r.observation.id);\n const relMap = this.memDb.getRelationsForObservations(ids);\n for (const r of sliced) {\n const rels = relMap.get(r.observation.id);\n if (rels && rels.length > 0) r.relations = rels;\n }\n\n return sliced;\n }\n\n /**\n * Get observations linked to specific symbols.\n * Used by kirograph_context and kirograph_impact.\n */\n getLinkedObservations(qualifiedNames: string[], limit = 3, threshold = 0.3): ScoredObservation[] {\n const results = this.memDb.getLinkedObservationsForSymbols(qualifiedNames, limit * 2);\n return results.filter(r => r.score >= threshold).slice(0, limit);\n }\n\n // \u2500\u2500 Timeline \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * List sessions with their observation counts.\n */\n timeline(opts: MemTimelineOptions = {}): { sessions: MemSession[]; observations: Map<string, MemObservation[]> } {\n const limit = opts.limit ?? 5;\n\n if (opts.sessionId) {\n const session = this.memDb.getSession(opts.sessionId);\n if (!session) return { sessions: [], observations: new Map() };\n const obs = this.memDb.getObservationsBySession(opts.sessionId);\n const map = new Map<string, MemObservation[]>();\n map.set(opts.sessionId, obs);\n return { sessions: [session], observations: map };\n }\n\n const sessions = this.memDb.listSessions(limit);\n const observations = new Map<string, MemObservation[]>();\n for (const session of sessions) {\n const obs = this.memDb.getObservationsBySession(session.id, 10);\n observations.set(session.id, obs);\n }\n return { sessions, observations };\n }\n\n // \u2500\u2500 Status \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(): MemStats {\n const modelName = (this.config as any).embeddingModel ?? 'nomic-ai/nomic-embed-text-v1.5';\n return this.memDb.getStats(modelName);\n }\n\n // \u2500\u2500 Session management \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n endCurrentSession(ide = 'kiro'): void {\n const cwd = process.cwd();\n const sessionTimeout = (this.config as any).memorySessionTimeout ?? 7200;\n // Find active session and close it\n const sessionId = this.memDb.getOrCreateSession(ide, cwd, sessionTimeout);\n this.memDb.endSession(sessionId);\n }\n\n // \u2500\u2500 Maintenance \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n prune(olderThanMs: number): number {\n const cutoff = Date.now() - olderThanMs;\n return this.memDb.prune(cutoff);\n }\n\n async reembed(batchSize = 32): Promise<number> {\n return this.vectorMgr.reembed(batchSize);\n }\n\n lint(): { staleLinks: number; modelMismatch: boolean; staleSessions: number } {\n const staleLinks = this.memDb.findStaleLinks().length;\n const modelMismatch = this.vectorMgr.hasModelMismatch();\n const sessionTimeout = ((this.config as any).memorySessionTimeout ?? 7200) * 1000;\n const staleSessions = this.memDb.closeStaleSessionsOlderThan(sessionTimeout);\n return { staleLinks, modelMismatch, staleSessions };\n }\n\n removeStaleLinks(): number {\n return this.memDb.removeStaleLinks();\n }\n\n // \u2500\u2500 Relations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Resolve an observation reference (ID or topic_key) to its ID. Returns the input unchanged if neither resolves. */\n private resolveObservationId(ref: string): string {\n if (this.memDb.getObservation(ref)) return ref;\n const byKey = this.memDb.resolveObservationByTopicKey(ref);\n return byKey ? byKey.id : ref;\n }\n\n compareObservations(input: MemRelationInput): string {\n const obsA = this.resolveObservationId(input.observationA);\n const obsB = this.resolveObservationId(input.observationB);\n return this.memDb.insertRelation({ ...input, observationA: obsA, observationB: obsB });\n }\n\n /**\n * Classify the relation between two observations via jev instead of\n * requiring the caller to supply `relation`/`confidence` itself. Only\n * used when `memoryRelationMode: 'jev'`. Auto-judges the relation when\n * jev's confidence is at or above `memoryRelationConfidenceThreshold`;\n * otherwise leaves it `pending` for review via the existing judge flow.\n */\n async autoCompareObservations(observationA: string, observationB: string): Promise<{\n relationId: string;\n relation: RelationType;\n confidence: number;\n autoJudged: boolean;\n }> {\n const obsAId = this.resolveObservationId(observationA);\n const obsBId = this.resolveObservationId(observationB);\n const obsA = this.memDb.getObservation(obsAId);\n const obsB = this.memDb.getObservation(obsBId);\n if (!obsA) throw new Error(`Observation not found: ${observationA}`);\n if (!obsB) throw new Error(`Observation not found: ${observationB}`);\n\n const { createJevClientFromConfig } = await Promise.resolve().then(() => require('../jev/client.js'));\n const { classifyRelationWithJev } = await Promise.resolve().then(() => require('./relation-jev.js'));\n const client = createJevClientFromConfig(this.config as unknown as { jevApiKey?: string; jevBaseUrl?: string; jevModel?: string });\n const { relation, confidence } = await classifyRelationWithJev(client, obsA.content, obsB.content);\n\n const relationId = this.memDb.insertRelation({\n observationA: obsAId,\n observationB: obsBId,\n relation,\n confidence,\n reason: 'Auto-classified by jev',\n });\n\n const threshold = (this.config as unknown as { memoryRelationConfidenceThreshold?: number }).memoryRelationConfidenceThreshold ?? 0.8;\n const autoJudged = confidence >= threshold;\n if (autoJudged) {\n this.memDb.judgeRelation(relationId, relation, confidence, 'Auto-judged by jev (confidence >= memoryRelationConfidenceThreshold)');\n }\n\n return { relationId, relation, confidence, autoJudged };\n }\n\n judgeRelation(relationId: string, relation: RelationType, confidence: number, reason?: string, evidence?: string): void {\n this.memDb.judgeRelation(relationId, relation, confidence, reason, evidence);\n }\n\n ignoreRelation(relationId: string): void {\n this.memDb.ignoreRelation(relationId);\n }\n\n getPendingRelations(limit = 20): MemRelation[] {\n return this.memDb.getPendingRelations(limit);\n }\n\n // \u2500\u2500 Review \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getObservationsForReview(limit = 20): MemObservation[] {\n return this.memDb.getObservationsForReview(limit);\n }\n\n markReviewed(id: string): void {\n this.memDb.markReviewed(id);\n }\n\n // \u2500\u2500 Prompts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async savePrompt(content: string, ide = 'kiro'): Promise<string> {\n const cwd = process.cwd();\n const sessionTimeout = (this.config as any).memorySessionTimeout ?? 7200;\n const sessionId = this.memDb.getOrCreateSession(ide, cwd, sessionTimeout);\n return this.memDb.insertPrompt(sessionId, content);\n }\n\n // \u2500\u2500 Passive capture \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n capturePassive(content: string, sessionId?: string): Array<{ id: string | null; kind: string; content: string }> {\n const kindMap: Record<string, string> = {\n 'key learnings': 'pattern',\n 'learnings': 'pattern',\n 'observations': 'note',\n 'decisions': 'decision',\n 'key changes': 'pattern',\n 'errors': 'error',\n 'patterns': 'pattern',\n };\n\n const results: Array<{ id: string | null; kind: string; content: string }> = [];\n const lines = content.split('\\n');\n let currentKind: string | null = null;\n\n for (const line of lines) {\n const headingMatch = line.match(/^#{1,3}\\s+(.+)/);\n if (headingMatch) {\n const heading = headingMatch[1].toLowerCase().trim();\n currentKind = kindMap[heading] ?? null;\n continue;\n }\n if (!currentKind) continue;\n const bulletMatch = line.match(/^[\\s]*[-*\u2022]\\s+(.+)|^[\\s]*\\d+[.)]\\s+(.+)/);\n if (bulletMatch) {\n const text = (bulletMatch[1] || bulletMatch[2]).trim();\n if (text.length < 5) continue;\n const id = this.memDb.insertObservation(text, { kind: currentKind, source: 'passive', sessionId });\n const symbols = detectSymbols(text, this.db);\n if (id && symbols.length > 0) this.memDb.linkToSymbols(id, symbols.map(s => s.qualifiedName));\n results.push({ id, kind: currentKind, content: text });\n }\n }\n\n return results;\n }\n\n // \u2500\u2500 Topic key \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n suggestTopicKey(kind: string, title: string): string {\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, '')\n .trim()\n .split(/\\s+/)\n .slice(0, 6)\n .join('-');\n return `${kind}/${slug}`;\n }\n\n // \u2500\u2500 Conflict scan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n scanConflicts(limit = 50): Array<{ observationA: MemObservation; observationB: MemObservation; similarity: number }> {\n const recent = this.memDb.getObservationsByTimeRange(0, Date.now(), limit);\n const candidates: Array<{ observationA: MemObservation; observationB: MemObservation; similarity: number }> = [];\n const seen = new Set<string>();\n\n for (const obs of recent) {\n if (!obs.content || obs.content.length < 20) continue;\n const words = obs.content.split(/\\s+/).slice(0, 8).join(' ');\n const similar = this.memDb.searchFTS(words, { limit: 5 });\n for (const match of similar) {\n if (match.observation.id === obs.id) continue;\n const pairKey = [obs.id, match.observation.id].sort().join('|');\n if (seen.has(pairKey)) continue;\n seen.add(pairKey);\n const existing = this.memDb.getRelationsForObservations([obs.id]);\n const alreadyLinked = existing.get(obs.id)?.some(r => r.observationA === match.observation.id || r.observationB === match.observation.id);\n if (!alreadyLinked && match.score > 0.3) {\n candidates.push({ observationA: obs, observationB: match.observation, similarity: match.score });\n }\n }\n }\n\n return candidates.slice(0, 20);\n }\n\n // \u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private shouldExclude(text: string): boolean {\n if (this.excludePatterns.length === 0) return false;\n\n // Simple check: if any exclude pattern appears as a substring in the text\n // This is a basic implementation; could use picomatch for glob matching\n for (const pattern of this.excludePatterns) {\n const normalized = pattern.replace(/\\*/g, '');\n if (normalized && text.includes(normalized)) return true;\n }\n return false;\n }\n\n private mergeResults(\n ftsResults: ScoredObservation[],\n vectorResults: ScoredObservation[],\n alpha: number\n ): ScoredObservation[] {\n // Normalize FTS scores (rank is negative in FTS5, lower = better)\n const maxFts = Math.max(...ftsResults.map(r => r.score), 1);\n const normalizedFts = new Map<string, number>();\n for (const r of ftsResults) {\n normalizedFts.set(r.observation.id, r.score / maxFts);\n }\n\n // Vector scores are already 0-1 (cosine similarity)\n const normalizedVec = new Map<string, number>();\n for (const r of vectorResults) {\n normalizedVec.set(r.observation.id, r.score);\n }\n\n // Merge: collect all unique observation IDs\n const allIds = new Set([...normalizedFts.keys(), ...normalizedVec.keys()]);\n const merged: ScoredObservation[] = [];\n\n // Build a lookup for observations\n const obsMap = new Map<string, MemObservation>();\n for (const r of ftsResults) obsMap.set(r.observation.id, r.observation);\n for (const r of vectorResults) obsMap.set(r.observation.id, r.observation);\n\n for (const id of allIds) {\n const ftsScore = normalizedFts.get(id) ?? 0;\n const vecScore = normalizedVec.get(id) ?? 0;\n const hybridScore = (1 - alpha) * ftsScore + alpha * vecScore;\n const obs = obsMap.get(id)!;\n merged.push({ observation: obs, score: hybridScore, scoreSource: 'hybrid' });\n }\n\n merged.sort((a, b) => b.score - a.score);\n return merged;\n }\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Memory \u2014 Public API\n *\n * MemoryManager is the single entry point for all memory operations.\n * Coordinates compression, storage, symbol detection, and embedding.\n */\n\nimport type { KiroGraphConfig } from '../config';\nimport type {\n MemObservation,\n MemObservationInput,\n MemSession,\n ScoredObservation,\n MemSearchOptions,\n MemTimelineOptions,\n MemStats,\n CompressResult,\n WatchmenReadyResult,\n MemRelation,\n MemRelationInput,\n RelationType,\n MemPrompt,\n} from './types';\nimport { MemoryDatabase } from './database';\nimport { compressObservation, type CavemanMode } from './compress';\nimport { detectSymbols } from './symbols';\nimport { MemoryVectorManager } from './vectors';\nimport { WatchmenChecker } from '../watchmen';\nimport { logDebug } from '../errors';\nimport { validateObservationInput, validateObservationFields } from './schema';\nimport * as path from 'path';\n\nexport { MemoryDatabase } from './database';\nexport { compressObservation, extractIdentifiers, type CavemanMode } from './compress';\nexport { detectSymbols } from './symbols';\nexport { MemoryVectorManager } from './vectors';\nexport { MemorySchemaError, OBSERVATION_KINDS, validateObservationInput, validateObservationFields } from './schema';\nexport * from './types';\n\n// \u2500\u2500 MemoryManager \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class MemoryManager {\n private memDb: MemoryDatabase;\n private vectorMgr: MemoryVectorManager;\n private config: KiroGraphConfig;\n private db: any; // raw sqlite handle for symbol detection\n private cavemanMode: CavemanMode;\n private excludePatterns: string[];\n private watchmenChecker: WatchmenChecker | null;\n private projectRoot: string;\n\n constructor(config: KiroGraphConfig, db: any, projectRoot = process.cwd()) {\n this.config = config;\n this.db = db;\n this.projectRoot = projectRoot;\n this.memDb = new MemoryDatabase(db);\n const kirographDir = require('path').join(projectRoot, '.kirograph');\n this.vectorMgr = new MemoryVectorManager(config, this.memDb, kirographDir);\n this.cavemanMode = (config as any).cavemanMode ?? 'off';\n this.excludePatterns = (config as any).memoryExcludePatterns ?? [];\n this.watchmenChecker = config.enableWatchmen\n ? new WatchmenChecker(config.watchmenThreshold)\n : null;\n }\n\n /**\n * Initialize memory tables. Must be called before any operations.\n */\n initialize(): void {\n this.memDb.initialize();\n }\n\n /**\n * Check if memory is enabled in config.\n */\n static isEnabled(config: KiroGraphConfig): boolean {\n return !!(config as any).enableMemory;\n }\n\n // \u2500\u2500 Store \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Store an observation. Handles:\n * 1. Privacy stripping (<private> blocks)\n * 2. Exclude pattern checking\n * 3. Caveman compression (if enabled)\n * 4. Deduplication (content hash)\n * 5. Symbol detection and linking\n * 6. Embedding (if enabled)\n * 7. Watchmen threshold check (if enabled) \u2014 returns WatchmenReadyResult when synthesis should run\n *\n * Returns the observation ID, a WatchmenReadyResult, or null if skipped (duplicate or excluded).\n * @throws MemorySchemaError if `memoryStrictWrites` is enabled and the input doesn't conform.\n */\n async store(input: MemObservationInput, ide = 'kiro'): Promise<string | WatchmenReadyResult | null> {\n // 0. Opt-in schema validation \u2014 rejects instead of silently corrupting the store.\n if ((this.config as any).memoryStrictWrites) {\n validateObservationInput(input);\n }\n if ((this.config as any).memorySchemaValidation) {\n validateObservationFields(input, path.join(this.projectRoot, '.kirograph', 'memory-schemas'));\n }\n\n let text = input.content;\n\n // 1. Strip <private> blocks\n text = MemoryDatabase.stripPrivate(text);\n if (!text.trim()) return null;\n\n // 2. Check exclude patterns\n if (this.shouldExclude(text)) return null;\n\n // 3. Compress (if caveman is on)\n let contentRaw: string | undefined;\n if (this.cavemanMode !== 'off') {\n const result = compressObservation(text, this.cavemanMode);\n contentRaw = text;\n text = result.compressed;\n }\n\n // 4. Get or create session\n const cwd = process.cwd();\n const sessionTimeout = (this.config as any).memorySessionTimeout ?? 7200;\n const sessionId = this.memDb.getOrCreateSession(ide, cwd, sessionTimeout);\n\n // 5. Insert observation (deduplicates via content_hash)\n const id = this.memDb.insertObservation(text, {\n contentRaw: this.cavemanMode !== 'off' ? contentRaw : undefined,\n kind: input.kind ?? 'note',\n source: input.source ?? 'manual',\n tags: input.tags,\n sessionId,\n topicKey: input.topicKey,\n reviewAfter: input.reviewAfter,\n fields: input.fields,\n });\n\n if (!id) {\n logDebug('Memory: duplicate observation skipped');\n return null;\n }\n\n // 6. Detect and link symbols\n const symbols = detectSymbols(text, this.db);\n if (symbols.length > 0) {\n this.memDb.linkToSymbols(id, symbols.map(s => s.qualifiedName));\n }\n\n // Also detect in raw text if different\n if (contentRaw) {\n const rawSymbols = detectSymbols(contentRaw, this.db);\n const extraSymbols = rawSymbols.filter(\n s => !symbols.some(existing => existing.qualifiedName === s.qualifiedName)\n );\n if (extraSymbols.length > 0) {\n this.memDb.linkToSymbols(id, extraSymbols.map(s => s.qualifiedName));\n }\n }\n\n // 7. Embed (async, non-blocking for the store operation)\n const obs = this.memDb.getObservation(id);\n if (obs) {\n await this.vectorMgr.embedObservation(obs);\n }\n\n // 8. Watchmen threshold check\n if (this.watchmenChecker && input.kind !== 'summary') {\n const check = this.watchmenChecker.shouldSynthesize(this.memDb);\n if (check.ready) {\n return this.watchmenChecker.buildReadyResponse(id, check.pendingCount, this.projectRoot);\n }\n }\n\n return id;\n }\n\n // \u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Hybrid search: combines FTS5 and vector search with configurable alpha.\n */\n async search(query: string, opts: MemSearchOptions = {}): Promise<ScoredObservation[]> {\n const limit = opts.limit ?? 10;\n const alpha = opts.alpha ?? (this.config as any).memorySearchAlpha ?? 0.5;\n\n // FTS search\n const ftsResults = this.memDb.searchFTS(query, { ...opts, limit: limit * 2 });\n\n // Vector search (if enabled and no model mismatch)\n let vectorResults: ScoredObservation[] = [];\n if (this.vectorMgr.isEnabled() && !this.vectorMgr.hasModelMismatch()) {\n vectorResults = await this.vectorMgr.search(query, limit * 2);\n }\n\n // If only one source has results, return it directly\n if (vectorResults.length === 0) {\n const sliced = ftsResults.slice(0, limit);\n const ids = sliced.map(r => r.observation.id);\n const relMap = this.memDb.getRelationsForObservations(ids);\n for (const r of sliced) {\n const rels = relMap.get(r.observation.id);\n if (rels && rels.length > 0) r.relations = rels;\n }\n return sliced;\n }\n if (ftsResults.length === 0) {\n const sliced = vectorResults.slice(0, limit);\n const ids = sliced.map(r => r.observation.id);\n const relMap = this.memDb.getRelationsForObservations(ids);\n for (const r of sliced) {\n const rels = relMap.get(r.observation.id);\n if (rels && rels.length > 0) r.relations = rels;\n }\n return sliced;\n }\n\n // Hybrid merge: normalize scores and blend\n const merged = this.mergeResults(ftsResults, vectorResults, alpha);\n const sliced = merged.slice(0, limit);\n\n // Enrich with relations\n const ids = sliced.map(r => r.observation.id);\n const relMap = this.memDb.getRelationsForObservations(ids);\n for (const r of sliced) {\n const rels = relMap.get(r.observation.id);\n if (rels && rels.length > 0) r.relations = rels;\n }\n\n return sliced;\n }\n\n /**\n * Get observations linked to specific symbols.\n * Used by kirograph_context and kirograph_impact.\n */\n getLinkedObservations(qualifiedNames: string[], limit = 3, threshold = 0.3): ScoredObservation[] {\n const results = this.memDb.getLinkedObservationsForSymbols(qualifiedNames, limit * 2);\n return results.filter(r => r.score >= threshold).slice(0, limit);\n }\n\n // \u2500\u2500 Timeline \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * List sessions with their observation counts.\n */\n timeline(opts: MemTimelineOptions = {}): { sessions: MemSession[]; observations: Map<string, MemObservation[]> } {\n const limit = opts.limit ?? 5;\n\n if (opts.sessionId) {\n const session = this.memDb.getSession(opts.sessionId);\n if (!session) return { sessions: [], observations: new Map() };\n const obs = this.memDb.getObservationsBySession(opts.sessionId);\n const map = new Map<string, MemObservation[]>();\n map.set(opts.sessionId, obs);\n return { sessions: [session], observations: map };\n }\n\n const sessions = this.memDb.listSessions(limit);\n const observations = new Map<string, MemObservation[]>();\n for (const session of sessions) {\n const obs = this.memDb.getObservationsBySession(session.id, 10);\n observations.set(session.id, obs);\n }\n return { sessions, observations };\n }\n\n // \u2500\u2500 Status \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(): MemStats {\n const modelName = (this.config as any).embeddingModel ?? 'nomic-ai/nomic-embed-text-v1.5';\n return this.memDb.getStats(modelName);\n }\n\n // \u2500\u2500 Session management \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n endCurrentSession(ide = 'kiro'): void {\n const cwd = process.cwd();\n const sessionTimeout = (this.config as any).memorySessionTimeout ?? 7200;\n // Find active session and close it\n const sessionId = this.memDb.getOrCreateSession(ide, cwd, sessionTimeout);\n this.memDb.endSession(sessionId);\n }\n\n // \u2500\u2500 Maintenance \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n prune(olderThanMs: number): number {\n const cutoff = Date.now() - olderThanMs;\n return this.memDb.prune(cutoff);\n }\n\n async reembed(batchSize = 32): Promise<number> {\n return this.vectorMgr.reembed(batchSize);\n }\n\n lint(): { staleLinks: number; modelMismatch: boolean; staleSessions: number } {\n const staleLinks = this.memDb.findStaleLinks().length;\n const modelMismatch = this.vectorMgr.hasModelMismatch();\n const sessionTimeout = ((this.config as any).memorySessionTimeout ?? 7200) * 1000;\n const staleSessions = this.memDb.closeStaleSessionsOlderThan(sessionTimeout);\n return { staleLinks, modelMismatch, staleSessions };\n }\n\n removeStaleLinks(): number {\n return this.memDb.removeStaleLinks();\n }\n\n // \u2500\u2500 Relations \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Resolve an observation reference (ID or topic_key) to its ID. Returns the input unchanged if neither resolves. */\n private resolveObservationId(ref: string): string {\n if (this.memDb.getObservation(ref)) return ref;\n const byKey = this.memDb.resolveObservationByTopicKey(ref);\n return byKey ? byKey.id : ref;\n }\n\n compareObservations(input: MemRelationInput): string {\n const obsA = this.resolveObservationId(input.observationA);\n const obsB = this.resolveObservationId(input.observationB);\n return this.memDb.insertRelation({ ...input, observationA: obsA, observationB: obsB });\n }\n\n /**\n * Classify the relation between two observations via jev instead of\n * requiring the caller to supply `relation`/`confidence` itself. Only\n * used when `memoryRelationMode: 'jev'`. Auto-judges the relation when\n * jev's confidence is at or above `memoryRelationConfidenceThreshold`;\n * otherwise leaves it `pending` for review via the existing judge flow.\n */\n async autoCompareObservations(observationA: string, observationB: string): Promise<{\n relationId: string;\n relation: RelationType;\n confidence: number;\n autoJudged: boolean;\n }> {\n const obsAId = this.resolveObservationId(observationA);\n const obsBId = this.resolveObservationId(observationB);\n const obsA = this.memDb.getObservation(obsAId);\n const obsB = this.memDb.getObservation(obsBId);\n if (!obsA) throw new Error(`Observation not found: ${observationA}`);\n if (!obsB) throw new Error(`Observation not found: ${observationB}`);\n\n const { createDecisionClient } = await Promise.resolve().then(() => require('../jev/factory.js'));\n const { classifyRelationWithJev } = await Promise.resolve().then(() => require('./relation-jev.js'));\n const cfg = this.config as unknown as {\n memoryRelationMode?: 'agent' | 'jev' | 'strands';\n jevApiKey?: string; jevBaseUrl?: string; jevModel?: string;\n strandsBaseUrl?: string; strandsModel?: string;\n };\n const backend = cfg.memoryRelationMode === 'strands' ? 'strands' : 'jev';\n const client = await createDecisionClient(backend, cfg);\n const { relation, confidence } = await classifyRelationWithJev(client, obsA.content, obsB.content);\n\n const relationId = this.memDb.insertRelation({\n observationA: obsAId,\n observationB: obsBId,\n relation,\n confidence,\n reason: `Auto-classified by ${backend}`,\n });\n\n const threshold = (this.config as unknown as { memoryRelationConfidenceThreshold?: number }).memoryRelationConfidenceThreshold ?? 0.8;\n const autoJudged = confidence >= threshold;\n if (autoJudged) {\n this.memDb.judgeRelation(relationId, relation, confidence, `Auto-judged by ${backend} (confidence >= memoryRelationConfidenceThreshold)`);\n }\n\n return { relationId, relation, confidence, autoJudged };\n }\n\n judgeRelation(relationId: string, relation: RelationType, confidence: number, reason?: string, evidence?: string): void {\n this.memDb.judgeRelation(relationId, relation, confidence, reason, evidence);\n }\n\n ignoreRelation(relationId: string): void {\n this.memDb.ignoreRelation(relationId);\n }\n\n getPendingRelations(limit = 20): MemRelation[] {\n return this.memDb.getPendingRelations(limit);\n }\n\n // \u2500\u2500 Review \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getObservationsForReview(limit = 20): MemObservation[] {\n return this.memDb.getObservationsForReview(limit);\n }\n\n markReviewed(id: string): void {\n this.memDb.markReviewed(id);\n }\n\n // \u2500\u2500 Prompts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async savePrompt(content: string, ide = 'kiro'): Promise<string> {\n const cwd = process.cwd();\n const sessionTimeout = (this.config as any).memorySessionTimeout ?? 7200;\n const sessionId = this.memDb.getOrCreateSession(ide, cwd, sessionTimeout);\n return this.memDb.insertPrompt(sessionId, content);\n }\n\n // \u2500\u2500 Passive capture \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n capturePassive(content: string, sessionId?: string): Array<{ id: string | null; kind: string; content: string }> {\n const kindMap: Record<string, string> = {\n 'key learnings': 'pattern',\n 'learnings': 'pattern',\n 'observations': 'note',\n 'decisions': 'decision',\n 'key changes': 'pattern',\n 'errors': 'error',\n 'patterns': 'pattern',\n };\n\n const results: Array<{ id: string | null; kind: string; content: string }> = [];\n const lines = content.split('\\n');\n let currentKind: string | null = null;\n\n for (const line of lines) {\n const headingMatch = line.match(/^#{1,3}\\s+(.+)/);\n if (headingMatch) {\n const heading = headingMatch[1].toLowerCase().trim();\n currentKind = kindMap[heading] ?? null;\n continue;\n }\n if (!currentKind) continue;\n const bulletMatch = line.match(/^[\\s]*[-*\u2022]\\s+(.+)|^[\\s]*\\d+[.)]\\s+(.+)/);\n if (bulletMatch) {\n const text = (bulletMatch[1] || bulletMatch[2]).trim();\n if (text.length < 5) continue;\n const id = this.memDb.insertObservation(text, { kind: currentKind, source: 'passive', sessionId });\n const symbols = detectSymbols(text, this.db);\n if (id && symbols.length > 0) this.memDb.linkToSymbols(id, symbols.map(s => s.qualifiedName));\n results.push({ id, kind: currentKind, content: text });\n }\n }\n\n return results;\n }\n\n // \u2500\u2500 Topic key \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n suggestTopicKey(kind: string, title: string): string {\n const slug = title\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, '')\n .trim()\n .split(/\\s+/)\n .slice(0, 6)\n .join('-');\n return `${kind}/${slug}`;\n }\n\n // \u2500\u2500 Conflict scan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n scanConflicts(limit = 50): Array<{ observationA: MemObservation; observationB: MemObservation; similarity: number }> {\n const recent = this.memDb.getObservationsByTimeRange(0, Date.now(), limit);\n const candidates: Array<{ observationA: MemObservation; observationB: MemObservation; similarity: number }> = [];\n const seen = new Set<string>();\n\n for (const obs of recent) {\n if (!obs.content || obs.content.length < 20) continue;\n const words = obs.content.split(/\\s+/).slice(0, 8).join(' ');\n const similar = this.memDb.searchFTS(words, { limit: 5 });\n for (const match of similar) {\n if (match.observation.id === obs.id) continue;\n const pairKey = [obs.id, match.observation.id].sort().join('|');\n if (seen.has(pairKey)) continue;\n seen.add(pairKey);\n const existing = this.memDb.getRelationsForObservations([obs.id]);\n const alreadyLinked = existing.get(obs.id)?.some(r => r.observationA === match.observation.id || r.observationB === match.observation.id);\n if (!alreadyLinked && match.score > 0.3) {\n candidates.push({ observationA: obs, observationB: match.observation, similarity: match.score });\n }\n }\n }\n\n return candidates.slice(0, 20);\n }\n\n // \u2500\u2500 Private \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private shouldExclude(text: string): boolean {\n if (this.excludePatterns.length === 0) return false;\n\n // Simple check: if any exclude pattern appears as a substring in the text\n // This is a basic implementation; could use picomatch for glob matching\n for (const pattern of this.excludePatterns) {\n const normalized = pattern.replace(/\\*/g, '');\n if (normalized && text.includes(normalized)) return true;\n }\n return false;\n }\n\n private mergeResults(\n ftsResults: ScoredObservation[],\n vectorResults: ScoredObservation[],\n alpha: number\n ): ScoredObservation[] {\n // Normalize FTS scores (rank is negative in FTS5, lower = better)\n const maxFts = Math.max(...ftsResults.map(r => r.score), 1);\n const normalizedFts = new Map<string, number>();\n for (const r of ftsResults) {\n normalizedFts.set(r.observation.id, r.score / maxFts);\n }\n\n // Vector scores are already 0-1 (cosine similarity)\n const normalizedVec = new Map<string, number>();\n for (const r of vectorResults) {\n normalizedVec.set(r.observation.id, r.score);\n }\n\n // Merge: collect all unique observation IDs\n const allIds = new Set([...normalizedFts.keys(), ...normalizedVec.keys()]);\n const merged: ScoredObservation[] = [];\n\n // Build a lookup for observations\n const obsMap = new Map<string, MemObservation>();\n for (const r of ftsResults) obsMap.set(r.observation.id, r.observation);\n for (const r of vectorResults) obsMap.set(r.observation.id, r.observation);\n\n for (const id of allIds) {\n const ftsScore = normalizedFts.get(id) ?? 0;\n const vecScore = normalizedVec.get(id) ?? 0;\n const hybridScore = (1 - alpha) * ftsScore + alpha * vecScore;\n const obs = obsMap.get(id)!;\n merged.push({ observation: obs, score: hybridScore, scoreSource: 'hybrid' });\n }\n\n merged.sort((a, b) => b.score - a.score);\n return merged;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_database", "import_compress", "import_symbols", "import_vectors", "import_schema", "sliced", "ids", "relMap"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/memory/relation-jev.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * KiroGraph Memory \u2014 jev-based relation classification (opt-in, memoryRelationMode: 'jev')\n *\n * Classifies the relation between two observations via a single jev Choice\n * question instead of requiring the calling agent to reason it out manually\n * and supply `relation`/`confidence` itself.\n */\nimport type {
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Memory \u2014 jev-based relation classification (opt-in, memoryRelationMode: 'jev')\n *\n * Classifies the relation between two observations via a single jev Choice\n * question instead of requiring the calling agent to reason it out manually\n * and supply `relation`/`confidence` itself.\n */\nimport type { DecisionClient } from '../jev/types';\nimport type { RelationType } from './types';\n\nconst RELATION_CRITERIA: Record<RelationType, string> = {\n supersedes: 'Observation B replaces or invalidates Observation A as the current decision or fact \u2014 A is now outdated.',\n conflicts_with: 'A and B make contradictory claims about the same thing that cannot both be true at the same time.',\n compatible: 'A and B are both true and can coexist without contradiction, even if they discuss related things.',\n scoped: 'A and B both apply, but in different, non-overlapping contexts or scopes (e.g. different environments, services, or time periods).',\n related: 'A and B are on the same topic and worth cross-referencing, but neither conflicts with nor supersedes the other.',\n not_conflict: 'A and B only appear related on the surface (shared keywords) but are not meaningfully connected.',\n};\n\nexport interface JevRelationClassification {\n relation: RelationType;\n confidence: number;\n}\n\n/** Classify how observation B relates to observation A. */\nexport async function classifyRelationWithJev(\n client: DecisionClient,\n contentA: string,\n contentB: string,\n): Promise<JevRelationClassification> {\n const state = `Observation A: ${contentA}\\n\\nObservation B: ${contentB}`;\n const response = await client.ask(state, {\n relation: {\n type: 'choice',\n instructions: 'How does Observation B relate to Observation A?',\n criteria: RELATION_CRITERIA,\n },\n });\n\n const answer = response.answers.relation;\n if (!answer || answer.type !== 'choice') {\n throw new Error('jev did not return a choice answer for \"relation\"');\n }\n if (!(answer.choice in RELATION_CRITERIA)) {\n throw new Error(`jev returned an unrecognized relation \"${answer.choice}\"`);\n }\n\n return {\n relation: answer.choice as RelationType,\n confidence: answer.confidence ?? 0,\n };\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,MAAM,oBAAkD;AAAA,EACtD,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,cAAc;AAChB;AAQA,eAAsB,wBACpB,QACA,UACA,UACoC;AACpC,QAAM,QAAQ,kBAAkB,QAAQ;AAAA;AAAA,iBAAsB,QAAQ;AACtE,QAAM,WAAW,MAAM,OAAO,IAAI,OAAO;AAAA,IACvC,UAAU;AAAA,MACR,MAAM;AAAA,MACN,cAAc;AAAA,MACd,UAAU;AAAA,IACZ;AAAA,EACF,CAAC;AAED,QAAM,SAAS,SAAS,QAAQ;AAChC,MAAI,CAAC,UAAU,OAAO,SAAS,UAAU;AACvC,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AACA,MAAI,EAAE,OAAO,UAAU,oBAAoB;AACzC,UAAM,IAAI,MAAM,0CAA0C,OAAO,MAAM,GAAG;AAAA,EAC5E;AAEA,SAAO;AAAA,IACL,UAAU,OAAO;AAAA,IACjB,YAAY,OAAO,cAAc;AAAA,EACnC;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -42,10 +42,10 @@ class AttackSurfaceAnalyzer {
|
|
|
42
42
|
}
|
|
43
43
|
async analyze() {
|
|
44
44
|
const rawDb = this.db.getRawDb();
|
|
45
|
-
let
|
|
46
|
-
if (this.opts.authDetectionMode === "jev") {
|
|
47
|
-
const {
|
|
48
|
-
|
|
45
|
+
let decisionClient;
|
|
46
|
+
if (this.opts.authDetectionMode === "jev" || this.opts.authDetectionMode === "strands") {
|
|
47
|
+
const { createDecisionClient } = await Promise.resolve().then(() => require("../jev/factory.js"));
|
|
48
|
+
decisionClient = await createDecisionClient(this.opts.authDetectionMode, this.opts);
|
|
49
49
|
}
|
|
50
50
|
const routeRows = rawDb.all(
|
|
51
51
|
`SELECT id, name, file_path FROM nodes WHERE kind = 'route'`
|
|
@@ -152,10 +152,10 @@ class AttackSurfaceAnalyzer {
|
|
|
152
152
|
const lowerName = (row.name ?? "").toLowerCase();
|
|
153
153
|
return AUTH_PATTERNS.some((pattern) => lowerName.includes(pattern));
|
|
154
154
|
});
|
|
155
|
-
if (!isAuthenticated &&
|
|
155
|
+
if (!isAuthenticated && decisionClient) {
|
|
156
156
|
const { checkAuthWithJev } = await Promise.resolve().then(() => require("./auth-detection-jev.js"));
|
|
157
157
|
const jevResult = await checkAuthWithJev(
|
|
158
|
-
|
|
158
|
+
decisionClient,
|
|
159
159
|
route.name ?? route.id,
|
|
160
160
|
pathNodeNames.map((r) => r.name).filter((n) => !!n)
|
|
161
161
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/security/attack-surface.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * KiroGraph-Sec Attack Surface Analyzer\n *\n * Maps all route nodes in the knowledge graph to their reachable vulnerable\n * dependencies, computing exposure levels and risk scores per route.\n */\n\nimport type { GraphDatabase } from '../db/database';\n\n/** Auth-related name patterns for heuristic isAuthenticated detection. */\nconst AUTH_PATTERNS = [\n 'auth',\n 'authenticate',\n 'middleware',\n 'guard',\n 'protect',\n 'requirelogin',\n 'isloggedin',\n 'verifytoken',\n 'checkauth',\n 'ensureauth',\n] as const;\n\n/** Route path patterns that indicate a public-facing API. */\nconst PUBLIC_ROUTE_PATTERNS = ['/api/', '/v1/', '/v2/', '/v3/', '/graphql', '/rest/'];\n\n/** Route path patterns that indicate an internal/admin route. */\nconst INTERNAL_ROUTE_PATTERNS = ['/internal/', '/admin/'];\n\nexport interface AttackSurfaceEntry {\n route: string;\n nodeId: string;\n filePath: string;\n isAuthenticated: boolean;\n exposureLevel: 'public' | 'authenticated' | 'internal' | 'unknown';\n vulnerableDeps: Array<{\n cveId: string;\n packageName: string;\n severity: number | null;\n epssScore: number | null;\n riskScore: number | null;\n verdict: string | null;\n hopCount: number;\n }>;\n patternMatches: Array<{\n patternId: string;\n severity: string;\n line: number;\n symbolNodeId: string;\n symbolName: string;\n }>;\n patternRiskLevel: 'none' | 'low' | 'medium' | 'high' | 'critical';\n riskScore: number;\n}\n\nexport interface AttackSurfaceResult {\n totalRoutes: number;\n publicRoutes: number;\n authenticatedRoutes: number;\n routesWithVulns: number;\n criticalPaths: AttackSurfaceEntry[];\n allRoutes: AttackSurfaceEntry[];\n}\n\nexport interface AttackSurfaceAnalyzerOptions {\n /**\n * 'heuristic' (default): substring match only.\n * 'jev': when the heuristic finds no auth pattern, ask jev to confirm/override.\n */\n authDetectionMode?: 'heuristic' | 'jev';\n /** Confidence (0.0\u20131.0) above which a jev auth judgment overrides the heuristic's \"not authenticated\" default. Default: 0.6. */\n authConfidenceThreshold?: number;\n jevApiKey?: string;\n jevBaseUrl?: string;\n jevModel?: string;\n}\n\nexport class AttackSurfaceAnalyzer {\n constructor(\n private readonly db: GraphDatabase,\n private readonly opts: AttackSurfaceAnalyzerOptions = {},\n ) {}\n\n async analyze(): Promise<AttackSurfaceResult> {\n const rawDb = this.db.getRawDb();\n\n let jevClient: import('../jev/client').JevClient | undefined;\n if (this.opts.authDetectionMode === 'jev') {\n const { createJevClientFromConfig } = await Promise.resolve().then(() => require('../jev/client.js'));\n jevClient = createJevClientFromConfig(this.opts);\n }\n\n // Step 1: Query all route nodes\n const routeRows: Array<{ id: string; name: string; file_path: string }> = rawDb.all(\n `SELECT id, name, file_path FROM nodes WHERE kind = 'route'`,\n );\n\n if (routeRows.length === 0) {\n return {\n totalRoutes: 0,\n publicRoutes: 0,\n authenticatedRoutes: 0,\n routesWithVulns: 0,\n criticalPaths: [],\n allRoutes: [],\n };\n }\n\n const entries: AttackSurfaceEntry[] = [];\n\n for (const route of routeRows) {\n // Step 2: BFS via SQL to find reachable dependency nodes (max 5 hops)\n const reachableDeps: Array<{\n node_id: string;\n package_name: string;\n hop_count: number;\n }> = rawDb.all(\n `WITH RECURSIVE reachable(node_id, depth) AS (\n SELECT target, 1 FROM edges WHERE source = ? AND kind IN ('calls', 'imports', 'references')\n UNION\n SELECT e.target, r.depth + 1\n FROM edges e\n JOIN reachable r ON e.source = r.node_id\n WHERE r.depth < 5 AND e.kind IN ('calls', 'imports', 'references')\n )\n SELECT d.node_id, d.package_name, MIN(r.depth) as hop_count\n FROM reachable r\n JOIN sec_dependencies d ON d.node_id = r.node_id\n GROUP BY d.node_id, d.package_name`,\n [route.id],\n );\n\n // Step 3: For each reachable dep, find affected/under_investigation vulns\n const vulnerableDeps: AttackSurfaceEntry['vulnerableDeps'] = [];\n\n for (const dep of reachableDeps) {\n const vulnRows: Array<{\n cve_id: string;\n package_name: string;\n severity_score: number | null;\n epss_score: number | null;\n risk_score: number | null;\n verdict: string | null;\n }> = rawDb.all(\n `SELECT v.cve_id, d.package_name, v.severity_score, v.epss_score, v.risk_score, r.verdict\n FROM sec_vulnerabilities v\n JOIN edges e ON e.target = v.node_id AND e.kind = 'has_vulnerability'\n JOIN sec_dependencies d ON d.node_id = e.source\n LEFT JOIN sec_reachability r ON r.vulnerability_node_id = v.node_id\n WHERE d.node_id = ?\n AND (r.verdict IN ('affected', 'under_investigation') OR r.verdict IS NULL)`,\n [dep.node_id],\n );\n\n for (const vuln of vulnRows) {\n vulnerableDeps.push({\n cveId: vuln.cve_id,\n packageName: vuln.package_name ?? dep.package_name,\n severity: vuln.severity_score,\n epssScore: vuln.epss_score,\n riskScore: vuln.risk_score,\n verdict: vuln.verdict,\n hopCount: dep.hop_count,\n });\n }\n }\n\n // Step 3b: Find pattern matches on the call path (table may not exist)\n const routeNodeId = route.id;\n let patternRows: Array<{\n symbol_node_id: string;\n symbol_name: string;\n pattern_id: string;\n severity: string;\n line: number;\n }> = [];\n try {\n patternRows = rawDb.all(\n `WITH RECURSIVE reachable(node_id, depth) AS (\n SELECT target, 1 FROM edges WHERE source = ? AND kind IN ('calls', 'imports', 'references')\n UNION\n SELECT e.target, r.depth + 1\n FROM edges e\n JOIN reachable r ON e.source = r.node_id\n WHERE r.depth < 5 AND e.kind IN ('calls', 'imports', 'references')\n )\n SELECT DISTINCT pm.symbol_node_id, n.name as symbol_name, pm.pattern_id, pm.severity, pm.line\n FROM reachable r\n JOIN pattern_matches pm ON pm.symbol_node_id = r.node_id\n JOIN nodes n ON n.id = pm.symbol_node_id\n WHERE pm.symbol_node_id IS NOT NULL\n ORDER BY CASE pm.severity WHEN 'critical' THEN 4 WHEN 'high' THEN 3 WHEN 'medium' THEN 2 ELSE 1 END DESC\n LIMIT 10`,\n [routeNodeId],\n );\n } catch {\n // pattern_matches table does not exist yet \u2014 degrade silently\n patternRows = [];\n }\n\n const patternMatches: AttackSurfaceEntry['patternMatches'] = patternRows.map(r => ({\n patternId: r.pattern_id,\n severity: r.severity,\n line: r.line,\n symbolNodeId: r.symbol_node_id,\n symbolName: r.symbol_name,\n }));\n\n const patternRiskLevel: AttackSurfaceEntry['patternRiskLevel'] =\n patternMatches.length === 0\n ? 'none'\n : (patternMatches[0].severity as AttackSurfaceEntry['patternRiskLevel']);\n\n // Step 4: Heuristic \u2014 isAuthenticated via path node names\n const pathNodeNames: Array<{ name: string }> = rawDb.all(\n `WITH RECURSIVE reachable(node_id, depth) AS (\n SELECT target, 1 FROM edges WHERE source = ? AND kind IN ('calls', 'imports', 'references')\n UNION\n SELECT e.target, r.depth + 1\n FROM edges e\n JOIN reachable r ON e.source = r.node_id\n WHERE r.depth < 5 AND e.kind IN ('calls', 'imports', 'references')\n )\n SELECT DISTINCT n.name\n FROM reachable r\n JOIN nodes n ON n.id = r.node_id\n WHERE n.name IS NOT NULL`,\n [route.id],\n );\n\n let isAuthenticated = pathNodeNames.some(row => {\n const lowerName = (row.name ?? '').toLowerCase();\n return AUTH_PATTERNS.some(pattern => lowerName.includes(pattern));\n });\n\n // jev backstop: only consulted when the heuristic found nothing, to\n // catch custom-named auth wrappers without re-checking every route.\n if (!isAuthenticated && jevClient) {\n const { checkAuthWithJev } = await Promise.resolve().then(() => require('./auth-detection-jev.js'));\n const jevResult = await checkAuthWithJev(\n jevClient,\n route.name ?? route.id,\n pathNodeNames.map(r => r.name).filter((n): n is string => !!n),\n );\n const threshold = this.opts.authConfidenceThreshold ?? 0.6;\n if (jevResult.authenticated && jevResult.confidence >= threshold) {\n isAuthenticated = true;\n }\n }\n\n // Step 5: Determine exposure level\n const routeName = (route.name ?? '').toLowerCase();\n let exposureLevel: AttackSurfaceEntry['exposureLevel'];\n\n if (INTERNAL_ROUTE_PATTERNS.some(p => routeName.includes(p))) {\n exposureLevel = 'internal';\n } else if (isAuthenticated) {\n exposureLevel = 'authenticated';\n } else if (PUBLIC_ROUTE_PATTERNS.some(p => routeName.includes(p))) {\n exposureLevel = 'public';\n } else {\n exposureLevel = 'unknown';\n }\n\n // Step 6: riskScore = max risk_score of reachable affected vulns, plus pattern bonus\n const affectedVulns = vulnerableDeps.filter(v => v.verdict === 'affected' || v.verdict === 'under_investigation');\n const baseRiskScore = affectedVulns.reduce((max, v) => {\n if (v.riskScore != null && v.riskScore > max) return v.riskScore;\n return max;\n }, 0);\n\n const patternBonus: Record<AttackSurfaceEntry['patternRiskLevel'], number> = {\n none: 0, low: 0.3, medium: 0.6, high: 0.8, critical: 1.0,\n };\n const riskScore = Math.min(10, baseRiskScore + (patternBonus[patternRiskLevel] ?? 0));\n\n entries.push({\n route: route.name ?? route.id,\n nodeId: route.id,\n filePath: route.file_path ?? '',\n isAuthenticated,\n exposureLevel,\n vulnerableDeps,\n patternMatches,\n patternRiskLevel,\n riskScore,\n });\n }\n\n // Compute summary stats\n const publicRoutes = entries.filter(e => e.exposureLevel === 'public').length;\n const authenticatedRoutes = entries.filter(\n e => e.exposureLevel === 'authenticated' || e.exposureLevel === 'internal',\n ).length;\n const routesWithVulns = entries.filter(e => e.vulnerableDeps.length > 0).length;\n\n // Critical paths: routes with affected/under_investigation vulns, sorted by riskScore desc\n const criticalPaths = entries\n .filter(e => e.vulnerableDeps.length > 0)\n .sort((a, b) => b.riskScore - a.riskScore);\n\n const allRoutes = entries.sort((a, b) => b.riskScore - a.riskScore);\n\n return {\n totalRoutes: entries.length,\n publicRoutes,\n authenticatedRoutes,\n routesWithVulns,\n criticalPaths,\n allRoutes,\n };\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,MAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,MAAM,wBAAwB,CAAC,SAAS,QAAQ,QAAQ,QAAQ,YAAY,QAAQ;AAGpF,MAAM,0BAA0B,CAAC,cAAc,SAAS;
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph-Sec Attack Surface Analyzer\n *\n * Maps all route nodes in the knowledge graph to their reachable vulnerable\n * dependencies, computing exposure levels and risk scores per route.\n */\n\nimport type { GraphDatabase } from '../db/database';\n\n/** Auth-related name patterns for heuristic isAuthenticated detection. */\nconst AUTH_PATTERNS = [\n 'auth',\n 'authenticate',\n 'middleware',\n 'guard',\n 'protect',\n 'requirelogin',\n 'isloggedin',\n 'verifytoken',\n 'checkauth',\n 'ensureauth',\n] as const;\n\n/** Route path patterns that indicate a public-facing API. */\nconst PUBLIC_ROUTE_PATTERNS = ['/api/', '/v1/', '/v2/', '/v3/', '/graphql', '/rest/'];\n\n/** Route path patterns that indicate an internal/admin route. */\nconst INTERNAL_ROUTE_PATTERNS = ['/internal/', '/admin/'];\n\nexport interface AttackSurfaceEntry {\n route: string;\n nodeId: string;\n filePath: string;\n isAuthenticated: boolean;\n exposureLevel: 'public' | 'authenticated' | 'internal' | 'unknown';\n vulnerableDeps: Array<{\n cveId: string;\n packageName: string;\n severity: number | null;\n epssScore: number | null;\n riskScore: number | null;\n verdict: string | null;\n hopCount: number;\n }>;\n patternMatches: Array<{\n patternId: string;\n severity: string;\n line: number;\n symbolNodeId: string;\n symbolName: string;\n }>;\n patternRiskLevel: 'none' | 'low' | 'medium' | 'high' | 'critical';\n riskScore: number;\n}\n\nexport interface AttackSurfaceResult {\n totalRoutes: number;\n publicRoutes: number;\n authenticatedRoutes: number;\n routesWithVulns: number;\n criticalPaths: AttackSurfaceEntry[];\n allRoutes: AttackSurfaceEntry[];\n}\n\nexport interface AttackSurfaceAnalyzerOptions {\n /**\n * 'heuristic' (default): substring match only.\n * 'jev': when the heuristic finds no auth pattern, ask jev (cloud) to confirm/override.\n * 'strands': same backstop, judged by a local strands-decider server.\n */\n authDetectionMode?: 'heuristic' | 'jev' | 'strands';\n /** Confidence (0.0\u20131.0) above which a model auth judgment overrides the heuristic's \"not authenticated\" default. Default: 0.6. */\n authConfidenceThreshold?: number;\n jevApiKey?: string;\n jevBaseUrl?: string;\n jevModel?: string;\n strandsBaseUrl?: string;\n strandsModel?: string;\n}\n\nexport class AttackSurfaceAnalyzer {\n constructor(\n private readonly db: GraphDatabase,\n private readonly opts: AttackSurfaceAnalyzerOptions = {},\n ) {}\n\n async analyze(): Promise<AttackSurfaceResult> {\n const rawDb = this.db.getRawDb();\n\n let decisionClient: import('../jev/types').DecisionClient | undefined;\n if (this.opts.authDetectionMode === 'jev' || this.opts.authDetectionMode === 'strands') {\n const { createDecisionClient } = await Promise.resolve().then(() => require('../jev/factory.js'));\n decisionClient = await createDecisionClient(this.opts.authDetectionMode, this.opts);\n }\n\n // Step 1: Query all route nodes\n const routeRows: Array<{ id: string; name: string; file_path: string }> = rawDb.all(\n `SELECT id, name, file_path FROM nodes WHERE kind = 'route'`,\n );\n\n if (routeRows.length === 0) {\n return {\n totalRoutes: 0,\n publicRoutes: 0,\n authenticatedRoutes: 0,\n routesWithVulns: 0,\n criticalPaths: [],\n allRoutes: [],\n };\n }\n\n const entries: AttackSurfaceEntry[] = [];\n\n for (const route of routeRows) {\n // Step 2: BFS via SQL to find reachable dependency nodes (max 5 hops)\n const reachableDeps: Array<{\n node_id: string;\n package_name: string;\n hop_count: number;\n }> = rawDb.all(\n `WITH RECURSIVE reachable(node_id, depth) AS (\n SELECT target, 1 FROM edges WHERE source = ? AND kind IN ('calls', 'imports', 'references')\n UNION\n SELECT e.target, r.depth + 1\n FROM edges e\n JOIN reachable r ON e.source = r.node_id\n WHERE r.depth < 5 AND e.kind IN ('calls', 'imports', 'references')\n )\n SELECT d.node_id, d.package_name, MIN(r.depth) as hop_count\n FROM reachable r\n JOIN sec_dependencies d ON d.node_id = r.node_id\n GROUP BY d.node_id, d.package_name`,\n [route.id],\n );\n\n // Step 3: For each reachable dep, find affected/under_investigation vulns\n const vulnerableDeps: AttackSurfaceEntry['vulnerableDeps'] = [];\n\n for (const dep of reachableDeps) {\n const vulnRows: Array<{\n cve_id: string;\n package_name: string;\n severity_score: number | null;\n epss_score: number | null;\n risk_score: number | null;\n verdict: string | null;\n }> = rawDb.all(\n `SELECT v.cve_id, d.package_name, v.severity_score, v.epss_score, v.risk_score, r.verdict\n FROM sec_vulnerabilities v\n JOIN edges e ON e.target = v.node_id AND e.kind = 'has_vulnerability'\n JOIN sec_dependencies d ON d.node_id = e.source\n LEFT JOIN sec_reachability r ON r.vulnerability_node_id = v.node_id\n WHERE d.node_id = ?\n AND (r.verdict IN ('affected', 'under_investigation') OR r.verdict IS NULL)`,\n [dep.node_id],\n );\n\n for (const vuln of vulnRows) {\n vulnerableDeps.push({\n cveId: vuln.cve_id,\n packageName: vuln.package_name ?? dep.package_name,\n severity: vuln.severity_score,\n epssScore: vuln.epss_score,\n riskScore: vuln.risk_score,\n verdict: vuln.verdict,\n hopCount: dep.hop_count,\n });\n }\n }\n\n // Step 3b: Find pattern matches on the call path (table may not exist)\n const routeNodeId = route.id;\n let patternRows: Array<{\n symbol_node_id: string;\n symbol_name: string;\n pattern_id: string;\n severity: string;\n line: number;\n }> = [];\n try {\n patternRows = rawDb.all(\n `WITH RECURSIVE reachable(node_id, depth) AS (\n SELECT target, 1 FROM edges WHERE source = ? AND kind IN ('calls', 'imports', 'references')\n UNION\n SELECT e.target, r.depth + 1\n FROM edges e\n JOIN reachable r ON e.source = r.node_id\n WHERE r.depth < 5 AND e.kind IN ('calls', 'imports', 'references')\n )\n SELECT DISTINCT pm.symbol_node_id, n.name as symbol_name, pm.pattern_id, pm.severity, pm.line\n FROM reachable r\n JOIN pattern_matches pm ON pm.symbol_node_id = r.node_id\n JOIN nodes n ON n.id = pm.symbol_node_id\n WHERE pm.symbol_node_id IS NOT NULL\n ORDER BY CASE pm.severity WHEN 'critical' THEN 4 WHEN 'high' THEN 3 WHEN 'medium' THEN 2 ELSE 1 END DESC\n LIMIT 10`,\n [routeNodeId],\n );\n } catch {\n // pattern_matches table does not exist yet \u2014 degrade silently\n patternRows = [];\n }\n\n const patternMatches: AttackSurfaceEntry['patternMatches'] = patternRows.map(r => ({\n patternId: r.pattern_id,\n severity: r.severity,\n line: r.line,\n symbolNodeId: r.symbol_node_id,\n symbolName: r.symbol_name,\n }));\n\n const patternRiskLevel: AttackSurfaceEntry['patternRiskLevel'] =\n patternMatches.length === 0\n ? 'none'\n : (patternMatches[0].severity as AttackSurfaceEntry['patternRiskLevel']);\n\n // Step 4: Heuristic \u2014 isAuthenticated via path node names\n const pathNodeNames: Array<{ name: string }> = rawDb.all(\n `WITH RECURSIVE reachable(node_id, depth) AS (\n SELECT target, 1 FROM edges WHERE source = ? AND kind IN ('calls', 'imports', 'references')\n UNION\n SELECT e.target, r.depth + 1\n FROM edges e\n JOIN reachable r ON e.source = r.node_id\n WHERE r.depth < 5 AND e.kind IN ('calls', 'imports', 'references')\n )\n SELECT DISTINCT n.name\n FROM reachable r\n JOIN nodes n ON n.id = r.node_id\n WHERE n.name IS NOT NULL`,\n [route.id],\n );\n\n let isAuthenticated = pathNodeNames.some(row => {\n const lowerName = (row.name ?? '').toLowerCase();\n return AUTH_PATTERNS.some(pattern => lowerName.includes(pattern));\n });\n\n // jev backstop: only consulted when the heuristic found nothing, to\n // catch custom-named auth wrappers without re-checking every route.\n if (!isAuthenticated && decisionClient) {\n const { checkAuthWithJev } = await Promise.resolve().then(() => require('./auth-detection-jev.js'));\n const jevResult = await checkAuthWithJev(\n decisionClient,\n route.name ?? route.id,\n pathNodeNames.map(r => r.name).filter((n): n is string => !!n),\n );\n const threshold = this.opts.authConfidenceThreshold ?? 0.6;\n if (jevResult.authenticated && jevResult.confidence >= threshold) {\n isAuthenticated = true;\n }\n }\n\n // Step 5: Determine exposure level\n const routeName = (route.name ?? '').toLowerCase();\n let exposureLevel: AttackSurfaceEntry['exposureLevel'];\n\n if (INTERNAL_ROUTE_PATTERNS.some(p => routeName.includes(p))) {\n exposureLevel = 'internal';\n } else if (isAuthenticated) {\n exposureLevel = 'authenticated';\n } else if (PUBLIC_ROUTE_PATTERNS.some(p => routeName.includes(p))) {\n exposureLevel = 'public';\n } else {\n exposureLevel = 'unknown';\n }\n\n // Step 6: riskScore = max risk_score of reachable affected vulns, plus pattern bonus\n const affectedVulns = vulnerableDeps.filter(v => v.verdict === 'affected' || v.verdict === 'under_investigation');\n const baseRiskScore = affectedVulns.reduce((max, v) => {\n if (v.riskScore != null && v.riskScore > max) return v.riskScore;\n return max;\n }, 0);\n\n const patternBonus: Record<AttackSurfaceEntry['patternRiskLevel'], number> = {\n none: 0, low: 0.3, medium: 0.6, high: 0.8, critical: 1.0,\n };\n const riskScore = Math.min(10, baseRiskScore + (patternBonus[patternRiskLevel] ?? 0));\n\n entries.push({\n route: route.name ?? route.id,\n nodeId: route.id,\n filePath: route.file_path ?? '',\n isAuthenticated,\n exposureLevel,\n vulnerableDeps,\n patternMatches,\n patternRiskLevel,\n riskScore,\n });\n }\n\n // Compute summary stats\n const publicRoutes = entries.filter(e => e.exposureLevel === 'public').length;\n const authenticatedRoutes = entries.filter(\n e => e.exposureLevel === 'authenticated' || e.exposureLevel === 'internal',\n ).length;\n const routesWithVulns = entries.filter(e => e.vulnerableDeps.length > 0).length;\n\n // Critical paths: routes with affected/under_investigation vulns, sorted by riskScore desc\n const criticalPaths = entries\n .filter(e => e.vulnerableDeps.length > 0)\n .sort((a, b) => b.riskScore - a.riskScore);\n\n const allRoutes = entries.sort((a, b) => b.riskScore - a.riskScore);\n\n return {\n totalRoutes: entries.length,\n publicRoutes,\n authenticatedRoutes,\n routesWithVulns,\n criticalPaths,\n allRoutes,\n };\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/security/auth-detection-jev.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * KiroGraph-Sec \u2014 jev-based auth detection (opt-in, securityAuthDetectionMode: 'jev')\n *\n * Backstop for AttackSurfaceAnalyzer's isAuthenticated heuristic: only called\n * when the substring-match heuristic finds no auth pattern on the call path,\n * to catch custom-named auth wrappers the heuristic would otherwise silently\n * misclassify as unauthenticated. The heuristic's positive matches are\n * trusted as-is and never re-checked, keeping this cheap.\n */\nimport type {
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph-Sec \u2014 jev-based auth detection (opt-in, securityAuthDetectionMode: 'jev')\n *\n * Backstop for AttackSurfaceAnalyzer's isAuthenticated heuristic: only called\n * when the substring-match heuristic finds no auth pattern on the call path,\n * to catch custom-named auth wrappers the heuristic would otherwise silently\n * misclassify as unauthenticated. The heuristic's positive matches are\n * trusted as-is and never re-checked, keeping this cheap.\n */\nimport type { DecisionClient } from '../jev/types';\n\nexport interface JevAuthResult {\n authenticated: boolean;\n confidence: number;\n}\n\nexport async function checkAuthWithJev(\n client: DecisionClient,\n routeName: string,\n callPathNames: string[],\n): Promise<JevAuthResult> {\n const state = `Route: ${routeName}\\n\\nFunctions/middleware on its call path: ${callPathNames.length > 0 ? callPathNames.join(', ') : '(none found)'}`;\n const response = await client.ask(state, {\n authenticated: {\n type: 'noul',\n instructions: 'This route is protected by authentication or authorization before its handler logic runs (a session check, token verification, login guard, permission check, etc. somewhere on its call path).',\n },\n });\n\n const answer = response.answers.authenticated;\n if (!answer || answer.type !== 'noul') {\n throw new Error('jev did not return a noul answer for \"authenticated\"');\n }\n\n return {\n authenticated: answer.noul >= 0.5,\n confidence: answer.confidence ?? answer.noul,\n };\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,eAAsB,iBACpB,QACA,WACA,eACwB;AACxB,QAAM,QAAQ,UAAU,SAAS;AAAA;AAAA,yCAA8C,cAAc,SAAS,IAAI,cAAc,KAAK,IAAI,IAAI,cAAc;AACnJ,QAAM,WAAW,MAAM,OAAO,IAAI,OAAO;AAAA,IACvC,eAAe;AAAA,MACb,MAAM;AAAA,MACN,cAAc;AAAA,IAChB;AAAA,EACF,CAAC;AAED,QAAM,SAAS,SAAS,QAAQ;AAChC,MAAI,CAAC,UAAU,OAAO,SAAS,QAAQ;AACrC,UAAM,IAAI,MAAM,sDAAsD;AAAA,EACxE;AAEA,SAAO;AAAA,IACL,eAAe,OAAO,QAAQ;AAAA,IAC9B,YAAY,OAAO,cAAc,OAAO;AAAA,EAC1C;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/wiki/contradiction-jev.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * KiroGraph Wiki \u2014 jev-based contradiction detection (opt-in, wikiContradictionMode: 'jev')\n *\n * Replaces the keyword-heuristic contradiction check (co-occurring negation\n * words like \"instead of\"/\"superseded\" on FTS-similar pages) with an actual\n * judgment call, still restricted to the same FTS-similar candidate pairs.\n */\nimport type {
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Wiki \u2014 jev-based contradiction detection (opt-in, wikiContradictionMode: 'jev')\n *\n * Replaces the keyword-heuristic contradiction check (co-occurring negation\n * words like \"instead of\"/\"superseded\" on FTS-similar pages) with an actual\n * judgment call, still restricted to the same FTS-similar candidate pairs.\n */\nimport type { DecisionClient } from '../jev/types';\n\nexport interface JevContradictionResult {\n contradicts: boolean;\n confidence: number;\n}\n\nexport async function checkContradictionWithJev(\n client: DecisionClient,\n contentA: string,\n contentB: string,\n): Promise<JevContradictionResult> {\n const state = `Page A:\\n${contentA}\\n\\nPage B:\\n${contentB}`;\n const response = await client.ask(state, {\n contradicts: {\n type: 'noul',\n instructions: 'Page A and Page B make claims that directly contradict each other about the same subject \u2014 not just related, actually incompatible.',\n },\n });\n\n const answer = response.answers.contradicts;\n if (!answer || answer.type !== 'noul') {\n throw new Error('jev did not return a noul answer for \"contradicts\"');\n }\n\n return {\n contradicts: answer.noul >= 0.5,\n confidence: answer.confidence ?? answer.noul,\n };\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAcA,eAAsB,0BACpB,QACA,UACA,UACiC;AACjC,QAAM,QAAQ;AAAA,EAAY,QAAQ;AAAA;AAAA;AAAA,EAAgB,QAAQ;AAC1D,QAAM,WAAW,MAAM,OAAO,IAAI,OAAO;AAAA,IACvC,aAAa;AAAA,MACX,MAAM;AAAA,MACN,cAAc;AAAA,IAChB;AAAA,EACF,CAAC;AAED,QAAM,SAAS,SAAS,QAAQ;AAChC,MAAI,CAAC,UAAU,OAAO,SAAS,QAAQ;AACrC,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AAEA,SAAO;AAAA,IACL,aAAa,OAAO,QAAQ;AAAA,IAC5B,YAAY,OAAO,cAAc,OAAO;AAAA,EAC1C;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/wiki/index.js
CHANGED
|
@@ -48,7 +48,13 @@ class KiroGraphWiki {
|
|
|
48
48
|
this.autoResolveConflicts = opts.autoResolveConflicts ?? false;
|
|
49
49
|
this.contradictionMode = opts.wikiContradictionMode ?? "heuristic";
|
|
50
50
|
this.contradictionConfidenceThreshold = opts.wikiContradictionConfidenceThreshold ?? 0.7;
|
|
51
|
-
this.
|
|
51
|
+
this.decisionConfig = {
|
|
52
|
+
jevApiKey: opts.jevApiKey,
|
|
53
|
+
jevBaseUrl: opts.jevBaseUrl,
|
|
54
|
+
jevModel: opts.jevModel,
|
|
55
|
+
strandsBaseUrl: opts.strandsBaseUrl,
|
|
56
|
+
strandsModel: opts.strandsModel
|
|
57
|
+
};
|
|
52
58
|
this.typedPages = opts.typedPages ?? false;
|
|
53
59
|
}
|
|
54
60
|
initialize() {
|
|
@@ -124,15 +130,15 @@ class KiroGraphWiki {
|
|
|
124
130
|
// ── Lint ───────────────────────────────────────────────────────────────────
|
|
125
131
|
async lint() {
|
|
126
132
|
this.initialize();
|
|
127
|
-
let
|
|
128
|
-
if (this.contradictionMode === "jev") {
|
|
129
|
-
const {
|
|
130
|
-
|
|
133
|
+
let decisionClient;
|
|
134
|
+
if (this.contradictionMode === "jev" || this.contradictionMode === "strands") {
|
|
135
|
+
const { createDecisionClient } = await Promise.resolve().then(() => require("../jev/factory.js"));
|
|
136
|
+
decisionClient = await createDecisionClient(this.contradictionMode, this.decisionConfig);
|
|
131
137
|
}
|
|
132
138
|
return (0, import_lint.lintWiki)(this.wikiDb, {
|
|
133
139
|
contradictionMode: this.contradictionMode,
|
|
134
140
|
contradictionConfidenceThreshold: this.contradictionConfidenceThreshold,
|
|
135
|
-
|
|
141
|
+
decisionClient,
|
|
136
142
|
...this.typedPages ? { wikiSchemasDir: this.wikiSchemasDir } : {}
|
|
137
143
|
});
|
|
138
144
|
}
|
package/dist/wiki/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/wiki/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * KiroGraph Wiki \u2014 Public API\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { WikiDatabase } from './database';\nimport { DEFAULT_SCHEMA } from './schema';\nimport { parseWikiDiff } from './schema';\nimport { buildIngestPrompt, applyDiff, reindexFromDisk, updateManifest } from './ingest';\nimport { lintWiki } from './lint';\nimport { getPageLinks, type WikiPageLinks } from './links';\nimport { renamePage, type RenamePageResult } from './rename';\nimport type {\n WikiPage,\n ScoredWikiPage,\n WikiDiff,\n WikiLintIssue,\n WikiStats,\n} from './types';\n\nexport type { WikiPage, ScoredWikiPage, WikiDiff, WikiLintIssue, WikiStats, WikiPageLinks, RenamePageResult };\n\nexport class KiroGraphWiki {\n private wikiDb: WikiDatabase;\n private wikiDir: string;\n private wikiSchemasDir: string;\n private autoResolveConflicts: boolean;\n private contradictionMode: 'heuristic' | 'jev';\n private contradictionConfidenceThreshold: number;\n private
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,SAAoB;AACpB,WAAsB;AACtB,sBAA6B;AAC7B,oBAA+B;AAC/B,IAAAA,iBAA8B;AAC9B,oBAA8E;AAC9E,kBAAyB;AACzB,mBAAiD;AACjD,oBAAkD;AAW3C,MAAM,cAAc;AAAA,
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Wiki \u2014 Public API\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { WikiDatabase } from './database';\nimport { DEFAULT_SCHEMA } from './schema';\nimport { parseWikiDiff } from './schema';\nimport { buildIngestPrompt, applyDiff, reindexFromDisk, updateManifest } from './ingest';\nimport { lintWiki } from './lint';\nimport { getPageLinks, type WikiPageLinks } from './links';\nimport { renamePage, type RenamePageResult } from './rename';\nimport type {\n WikiPage,\n ScoredWikiPage,\n WikiDiff,\n WikiLintIssue,\n WikiStats,\n} from './types';\n\nexport type { WikiPage, ScoredWikiPage, WikiDiff, WikiLintIssue, WikiStats, WikiPageLinks, RenamePageResult };\n\nexport class KiroGraphWiki {\n private wikiDb: WikiDatabase;\n private wikiDir: string;\n private wikiSchemasDir: string;\n private autoResolveConflicts: boolean;\n private contradictionMode: 'heuristic' | 'jev' | 'strands';\n private contradictionConfidenceThreshold: number;\n private decisionConfig: {\n jevApiKey?: string; jevBaseUrl?: string; jevModel?: string;\n strandsBaseUrl?: string; strandsModel?: string;\n };\n private typedPages: boolean;\n\n constructor(\n db: any,\n kirographDir: string,\n opts: {\n autoResolveConflicts?: boolean;\n wikiContradictionMode?: 'heuristic' | 'jev' | 'strands';\n wikiContradictionConfidenceThreshold?: number;\n jevApiKey?: string;\n jevBaseUrl?: string;\n jevModel?: string;\n strandsBaseUrl?: string;\n strandsModel?: string;\n typedPages?: boolean;\n } = {}\n ) {\n this.wikiDb = new WikiDatabase(db);\n this.wikiDir = path.join(kirographDir, 'wiki');\n this.wikiSchemasDir = path.join(kirographDir, 'wiki-schemas');\n this.autoResolveConflicts = opts.autoResolveConflicts ?? false;\n this.contradictionMode = opts.wikiContradictionMode ?? 'heuristic';\n this.contradictionConfidenceThreshold = opts.wikiContradictionConfidenceThreshold ?? 0.7;\n this.decisionConfig = {\n jevApiKey: opts.jevApiKey, jevBaseUrl: opts.jevBaseUrl, jevModel: opts.jevModel,\n strandsBaseUrl: opts.strandsBaseUrl, strandsModel: opts.strandsModel,\n };\n this.typedPages = opts.typedPages ?? false;\n }\n\n initialize(): void {\n this.wikiDb.initialize();\n fs.mkdirSync(this.wikiDir, { recursive: true });\n }\n\n // \u2500\u2500 Init \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n initWiki(): void {\n this.initialize();\n const schemaPath = path.join(this.wikiDir, 'SCHEMA.md');\n if (!fs.existsSync(schemaPath)) {\n fs.writeFileSync(schemaPath, DEFAULT_SCHEMA, 'utf8');\n }\n const manifestPath = path.join(this.wikiDir, 'MANIFEST.md');\n if (!fs.existsSync(manifestPath)) {\n fs.writeFileSync(manifestPath, '# Wiki Manifest\\n\\n(empty)\\n', 'utf8');\n }\n }\n\n // \u2500\u2500 Ingest \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Returns the ingest prompt for the LLM (agent mode).\n * The agent uses this to produce a WIKI_DIFF, then calls applyDiff.\n */\n getIngestPrompt(source: string, sourceName = 'source'): string {\n this.initialize();\n return buildIngestPrompt(this.wikiDir, source, sourceName);\n }\n\n // \u2500\u2500 Queue (local synthesis mode) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n queueSource(sourceText: string, sourceName: string): void {\n this.initialize();\n this.wikiDb.queueSource(sourceName, sourceText);\n }\n\n getPendingQueue(): Array<{ id: number; sourceName: string; sourceText: string }> {\n this.initialize();\n return this.wikiDb.getPendingQueue();\n }\n\n clearQueue(): void {\n this.initialize();\n this.wikiDb.clearQueue();\n }\n\n getQueueCount(): number {\n this.initialize();\n return this.wikiDb.getQueueCount();\n }\n\n async synthesize(modelName: string, quiet = false) {\n this.initialize();\n const { runWikiLocalSynthesis } = await Promise.resolve().then(() => require('./synthesize.js'));\n return runWikiLocalSynthesis(this, modelName, quiet);\n }\n\n /**\n * Parse and apply a WIKI_DIFF string produced by the LLM.\n */\n applyDiff(rawDiff: string) {\n this.initialize();\n const diff = parseWikiDiff(rawDiff);\n return applyDiff(diff, this.wikiDir, this.wikiDb, {\n autoResolveConflicts: this.autoResolveConflicts,\n });\n }\n\n // \u2500\u2500 Search & Read \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n search(query: string, limit = 10): ScoredWikiPage[] {\n this.initialize();\n return this.wikiDb.search(query, limit);\n }\n\n getPage(slug: string): WikiPage | null {\n this.initialize();\n return this.wikiDb.getPage(slug);\n }\n\n listPages(): WikiPage[] {\n this.initialize();\n return this.wikiDb.listPages();\n }\n\n // \u2500\u2500 Lint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async lint(): Promise<WikiLintIssue[]> {\n this.initialize();\n let decisionClient: import('../jev/types').DecisionClient | undefined;\n if (this.contradictionMode === 'jev' || this.contradictionMode === 'strands') {\n const { createDecisionClient } = await Promise.resolve().then(() => require('../jev/factory.js'));\n decisionClient = await createDecisionClient(this.contradictionMode, this.decisionConfig);\n }\n return lintWiki(this.wikiDb, {\n contradictionMode: this.contradictionMode,\n contradictionConfidenceThreshold: this.contradictionConfidenceThreshold,\n decisionClient,\n ...(this.typedPages ? { wikiSchemasDir: this.wikiSchemasDir } : {}),\n });\n }\n\n // \u2500\u2500 Links \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Outgoing/broken/incoming `[[slug]]` links for one page. Null if the slug is unknown. */\n getLinks(slug: string): WikiPageLinks | null {\n this.initialize();\n return getPageLinks(this.wikiDb, slug);\n }\n\n /** Rename a page's slug, moving its file and rewriting every `[[slug]]` reference to it. */\n rename(fromSlug: string, toSlug: string): RenamePageResult {\n this.initialize();\n return renamePage(this.wikiDir, this.wikiDb, fromSlug, toSlug);\n }\n\n // \u2500\u2500 Reindex \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n reindex(): number {\n this.initialize();\n return reindexFromDisk(this.wikiDir, this.wikiDb);\n }\n\n // \u2500\u2500 Stats \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n getStats(): WikiStats {\n this.initialize();\n return this.wikiDb.getStats();\n }\n\n // \u2500\u2500 Context enrichment \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Returns wiki pages relevant to a query, for inclusion in kirograph_context.\n * Returns up to `limit` pages with score >= threshold.\n */\n getContextPages(query: string, limit = 3, threshold = 0.4): WikiPage[] {\n this.initialize();\n return this.wikiDb\n .search(query, limit * 2)\n .filter(r => r.score >= threshold)\n .slice(0, limit)\n .map(r => r.page);\n }\n\n get dir(): string {\n return this.wikiDir;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_schema"]
|
|
7
7
|
}
|