modular-studio 0.2.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/LICENSE +21 -0
- package/README.md +261 -0
- package/dist/assets/graphPopulator-C6jg83nL.js +1 -0
- package/dist/assets/index-CXhIX28x.js +634 -0
- package/dist/assets/index-CeNF0r-K.css +1 -0
- package/dist/assets/jszip.min-BlpRodxc.js +2 -0
- package/dist/index.html +16 -0
- package/dist/vite.svg +1 -0
- package/dist-server/bin/modular-mcp.d.ts +8 -0
- package/dist-server/bin/modular-mcp.d.ts.map +1 -0
- package/dist-server/bin/modular-mcp.js +158 -0
- package/dist-server/bin/modular-mcp.js.map +1 -0
- package/dist-server/bin/modular-studio.d.ts +3 -0
- package/dist-server/bin/modular-studio.d.ts.map +1 -0
- package/dist-server/bin/modular-studio.js +63 -0
- package/dist-server/bin/modular-studio.js.map +1 -0
- package/dist-server/server/config.d.ts +4 -0
- package/dist-server/server/config.d.ts.map +1 -0
- package/dist-server/server/config.js +33 -0
- package/dist-server/server/config.js.map +1 -0
- package/dist-server/server/data/mcp-clients.json +5 -0
- package/dist-server/server/index.d.ts +3 -0
- package/dist-server/server/index.d.ts.map +1 -0
- package/dist-server/server/index.js +174 -0
- package/dist-server/server/index.js.map +1 -0
- package/dist-server/server/mcp/manager.d.ts +47 -0
- package/dist-server/server/mcp/manager.d.ts.map +1 -0
- package/dist-server/server/mcp/manager.js +203 -0
- package/dist-server/server/mcp/manager.js.map +1 -0
- package/dist-server/server/mcp/modular-server.d.ts +55 -0
- package/dist-server/server/mcp/modular-server.d.ts.map +1 -0
- package/dist-server/server/mcp/modular-server.js +492 -0
- package/dist-server/server/mcp/modular-server.js.map +1 -0
- package/dist-server/server/mcp/transport.d.ts +29 -0
- package/dist-server/server/mcp/transport.d.ts.map +1 -0
- package/dist-server/server/mcp/transport.js +61 -0
- package/dist-server/server/mcp/transport.js.map +1 -0
- package/dist-server/server/routes/agent-sdk.d.ts +3 -0
- package/dist-server/server/routes/agent-sdk.d.ts.map +1 -0
- package/dist-server/server/routes/agent-sdk.js +99 -0
- package/dist-server/server/routes/agent-sdk.js.map +1 -0
- package/dist-server/server/routes/agents.d.ts +10 -0
- package/dist-server/server/routes/agents.d.ts.map +1 -0
- package/dist-server/server/routes/agents.js +61 -0
- package/dist-server/server/routes/agents.js.map +1 -0
- package/dist-server/server/routes/auth-codex.d.ts +3 -0
- package/dist-server/server/routes/auth-codex.d.ts.map +1 -0
- package/dist-server/server/routes/auth-codex.js +51 -0
- package/dist-server/server/routes/auth-codex.js.map +1 -0
- package/dist-server/server/routes/capabilities.d.ts +3 -0
- package/dist-server/server/routes/capabilities.d.ts.map +1 -0
- package/dist-server/server/routes/capabilities.js +32 -0
- package/dist-server/server/routes/capabilities.js.map +1 -0
- package/dist-server/server/routes/claude-config.d.ts +3 -0
- package/dist-server/server/routes/claude-config.d.ts.map +1 -0
- package/dist-server/server/routes/claude-config.js +146 -0
- package/dist-server/server/routes/claude-config.js.map +1 -0
- package/dist-server/server/routes/connectors.d.ts +12 -0
- package/dist-server/server/routes/connectors.d.ts.map +1 -0
- package/dist-server/server/routes/connectors.js +325 -0
- package/dist-server/server/routes/connectors.js.map +1 -0
- package/dist-server/server/routes/embeddings.d.ts +6 -0
- package/dist-server/server/routes/embeddings.d.ts.map +1 -0
- package/dist-server/server/routes/embeddings.js +130 -0
- package/dist-server/server/routes/embeddings.js.map +1 -0
- package/dist-server/server/routes/health.d.ts +9 -0
- package/dist-server/server/routes/health.d.ts.map +1 -0
- package/dist-server/server/routes/health.js +284 -0
- package/dist-server/server/routes/health.js.map +1 -0
- package/dist-server/server/routes/knowledge.d.ts +3 -0
- package/dist-server/server/routes/knowledge.d.ts.map +1 -0
- package/dist-server/server/routes/knowledge.js +534 -0
- package/dist-server/server/routes/knowledge.js.map +1 -0
- package/dist-server/server/routes/llm.d.ts +3 -0
- package/dist-server/server/routes/llm.d.ts.map +1 -0
- package/dist-server/server/routes/llm.js +200 -0
- package/dist-server/server/routes/llm.js.map +1 -0
- package/dist-server/server/routes/mcp-oauth.d.ts +12 -0
- package/dist-server/server/routes/mcp-oauth.d.ts.map +1 -0
- package/dist-server/server/routes/mcp-oauth.js +137 -0
- package/dist-server/server/routes/mcp-oauth.js.map +1 -0
- package/dist-server/server/routes/mcp.d.ts +3 -0
- package/dist-server/server/routes/mcp.d.ts.map +1 -0
- package/dist-server/server/routes/mcp.js +177 -0
- package/dist-server/server/routes/mcp.js.map +1 -0
- package/dist-server/server/routes/pipeline.d.ts +45 -0
- package/dist-server/server/routes/pipeline.d.ts.map +1 -0
- package/dist-server/server/routes/pipeline.js +483 -0
- package/dist-server/server/routes/pipeline.js.map +1 -0
- package/dist-server/server/routes/providers.d.ts +3 -0
- package/dist-server/server/routes/providers.d.ts.map +1 -0
- package/dist-server/server/routes/providers.js +204 -0
- package/dist-server/server/routes/providers.js.map +1 -0
- package/dist-server/server/routes/qualification.d.ts +3 -0
- package/dist-server/server/routes/qualification.d.ts.map +1 -0
- package/dist-server/server/routes/qualification.js +105 -0
- package/dist-server/server/routes/qualification.js.map +1 -0
- package/dist-server/server/routes/repo-index.d.ts +4 -0
- package/dist-server/server/routes/repo-index.d.ts.map +1 -0
- package/dist-server/server/routes/repo-index.js +318 -0
- package/dist-server/server/routes/repo-index.js.map +1 -0
- package/dist-server/server/routes/runtime.d.ts +3 -0
- package/dist-server/server/routes/runtime.d.ts.map +1 -0
- package/dist-server/server/routes/runtime.js +122 -0
- package/dist-server/server/routes/runtime.js.map +1 -0
- package/dist-server/server/routes/skills-search.d.ts +3 -0
- package/dist-server/server/routes/skills-search.d.ts.map +1 -0
- package/dist-server/server/routes/skills-search.js +198 -0
- package/dist-server/server/routes/skills-search.js.map +1 -0
- package/dist-server/server/routes/worktrees.d.ts +3 -0
- package/dist-server/server/routes/worktrees.d.ts.map +1 -0
- package/dist-server/server/routes/worktrees.js +70 -0
- package/dist-server/server/routes/worktrees.js.map +1 -0
- package/dist-server/server/services/__tests__/embeddingService.test.d.ts +5 -0
- package/dist-server/server/services/__tests__/embeddingService.test.d.ts.map +1 -0
- package/dist-server/server/services/__tests__/embeddingService.test.js +233 -0
- package/dist-server/server/services/__tests__/embeddingService.test.js.map +1 -0
- package/dist-server/server/services/agentRunner.d.ts +46 -0
- package/dist-server/server/services/agentRunner.d.ts.map +1 -0
- package/dist-server/server/services/agentRunner.js +295 -0
- package/dist-server/server/services/agentRunner.js.map +1 -0
- package/dist-server/server/services/agentStore.d.ts +40 -0
- package/dist-server/server/services/agentStore.d.ts.map +1 -0
- package/dist-server/server/services/agentStore.js +62 -0
- package/dist-server/server/services/agentStore.js.map +1 -0
- package/dist-server/server/services/contentStore.d.ts +32 -0
- package/dist-server/server/services/contentStore.d.ts.map +1 -0
- package/dist-server/server/services/contentStore.js +68 -0
- package/dist-server/server/services/contentStore.js.map +1 -0
- package/dist-server/server/services/embeddingService.d.ts +53 -0
- package/dist-server/server/services/embeddingService.d.ts.map +1 -0
- package/dist-server/server/services/embeddingService.js +199 -0
- package/dist-server/server/services/embeddingService.js.map +1 -0
- package/dist-server/server/services/factExtractor.d.ts +14 -0
- package/dist-server/server/services/factExtractor.d.ts.map +1 -0
- package/dist-server/server/services/factExtractor.js +126 -0
- package/dist-server/server/services/factExtractor.js.map +1 -0
- package/dist-server/server/services/githubIndexer.d.ts +59 -0
- package/dist-server/server/services/githubIndexer.d.ts.map +1 -0
- package/dist-server/server/services/githubIndexer.js +183 -0
- package/dist-server/server/services/githubIndexer.js.map +1 -0
- package/dist-server/server/services/mcpOAuth.d.ts +32 -0
- package/dist-server/server/services/mcpOAuth.d.ts.map +1 -0
- package/dist-server/server/services/mcpOAuth.js +264 -0
- package/dist-server/server/services/mcpOAuth.js.map +1 -0
- package/dist-server/server/services/memoryScorer.d.ts +19 -0
- package/dist-server/server/services/memoryScorer.d.ts.map +1 -0
- package/dist-server/server/services/memoryScorer.js +147 -0
- package/dist-server/server/services/memoryScorer.js.map +1 -0
- package/dist-server/server/services/repoIndexer.d.ts +91 -0
- package/dist-server/server/services/repoIndexer.d.ts.map +1 -0
- package/dist-server/server/services/repoIndexer.js +512 -0
- package/dist-server/server/services/repoIndexer.js.map +1 -0
- package/dist-server/server/services/teamRunner.d.ts +39 -0
- package/dist-server/server/services/teamRunner.d.ts.map +1 -0
- package/dist-server/server/services/teamRunner.js +76 -0
- package/dist-server/server/services/teamRunner.js.map +1 -0
- package/dist-server/server/services/worktreeManager.d.ts +27 -0
- package/dist-server/server/services/worktreeManager.d.ts.map +1 -0
- package/dist-server/server/services/worktreeManager.js +107 -0
- package/dist-server/server/services/worktreeManager.js.map +1 -0
- package/dist-server/server/types.d.ts +30 -0
- package/dist-server/server/types.d.ts.map +1 -0
- package/dist-server/server/types.js +2 -0
- package/dist-server/server/types.js.map +1 -0
- package/dist-server/server/utils/pathSecurity.d.ts +34 -0
- package/dist-server/server/utils/pathSecurity.d.ts.map +1 -0
- package/dist-server/server/utils/pathSecurity.js +78 -0
- package/dist-server/server/utils/pathSecurity.js.map +1 -0
- package/dist-server/src/services/budgetAllocator.d.ts +37 -0
- package/dist-server/src/services/budgetAllocator.d.ts.map +1 -0
- package/dist-server/src/services/budgetAllocator.js +120 -0
- package/dist-server/src/services/budgetAllocator.js.map +1 -0
- package/dist-server/src/services/contradictionDetector.d.ts +18 -0
- package/dist-server/src/services/contradictionDetector.d.ts.map +1 -0
- package/dist-server/src/services/contradictionDetector.js +111 -0
- package/dist-server/src/services/contradictionDetector.js.map +1 -0
- package/dist-server/src/services/treeIndexer.d.ts +91 -0
- package/dist-server/src/services/treeIndexer.d.ts.map +1 -0
- package/dist-server/src/services/treeIndexer.js +289 -0
- package/dist-server/src/services/treeIndexer.js.map +1 -0
- package/dist-server/src/store/knowledgeBase.d.ts +130 -0
- package/dist-server/src/store/knowledgeBase.d.ts.map +1 -0
- package/dist-server/src/store/knowledgeBase.js +299 -0
- package/dist-server/src/store/knowledgeBase.js.map +1 -0
- package/dist-server/tsconfig.server.tsbuildinfo +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Store — Persistent agent state on disk
|
|
3
|
+
* Directory: ~/.modular-studio/agents/
|
|
4
|
+
* Each agent: {id}.json containing full state snapshot
|
|
5
|
+
*/
|
|
6
|
+
export interface SavedAgentState {
|
|
7
|
+
id: string;
|
|
8
|
+
version: string;
|
|
9
|
+
savedAt: string;
|
|
10
|
+
agentMeta: {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
icon: string;
|
|
14
|
+
category: string;
|
|
15
|
+
tags: string[];
|
|
16
|
+
avatar: string;
|
|
17
|
+
};
|
|
18
|
+
instructionState: Record<string, unknown>;
|
|
19
|
+
workflowSteps: Record<string, unknown>[];
|
|
20
|
+
channels: Record<string, unknown>[];
|
|
21
|
+
mcpServers: Record<string, unknown>[];
|
|
22
|
+
skills: Record<string, unknown>[];
|
|
23
|
+
connectors: Record<string, unknown>[];
|
|
24
|
+
agentConfig: Record<string, unknown>;
|
|
25
|
+
exportTarget: string;
|
|
26
|
+
outputFormat: string;
|
|
27
|
+
outputFormats: string[];
|
|
28
|
+
tokenBudget: number;
|
|
29
|
+
prompt: string;
|
|
30
|
+
}
|
|
31
|
+
export interface AgentSummary {
|
|
32
|
+
id: string;
|
|
33
|
+
agentMeta: SavedAgentState['agentMeta'];
|
|
34
|
+
savedAt: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function saveAgent(id: string, state: SavedAgentState): void;
|
|
37
|
+
export declare function loadAgent(id: string): SavedAgentState | null;
|
|
38
|
+
export declare function listAgents(): AgentSummary[];
|
|
39
|
+
export declare function deleteAgent(id: string): boolean;
|
|
40
|
+
//# sourceMappingURL=agentStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentStore.d.ts","sourceRoot":"","sources":["../../../server/services/agentStore.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmBH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE;QACT,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,EAAE,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAKlE;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAQ5D;AAED,wBAAgB,UAAU,IAAI,YAAY,EAAE,CAiB3C;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAK/C"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Store — Persistent agent state on disk
|
|
3
|
+
* Directory: ~/.modular-studio/agents/
|
|
4
|
+
* Each agent: {id}.json containing full state snapshot
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, readdirSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
const AGENTS_DIR = join(homedir(), '.modular-studio', 'agents');
|
|
10
|
+
function ensureDir() {
|
|
11
|
+
if (!existsSync(AGENTS_DIR)) {
|
|
12
|
+
mkdirSync(AGENTS_DIR, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function agentPath(id) {
|
|
16
|
+
const safe = id.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
17
|
+
return join(AGENTS_DIR, `${safe}.json`);
|
|
18
|
+
}
|
|
19
|
+
export function saveAgent(id, state) {
|
|
20
|
+
ensureDir();
|
|
21
|
+
state.id = id;
|
|
22
|
+
state.savedAt = new Date().toISOString();
|
|
23
|
+
writeFileSync(agentPath(id), JSON.stringify(state, null, 2), 'utf-8');
|
|
24
|
+
}
|
|
25
|
+
export function loadAgent(id) {
|
|
26
|
+
const p = agentPath(id);
|
|
27
|
+
if (!existsSync(p))
|
|
28
|
+
return null;
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(readFileSync(p, 'utf-8'));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function listAgents() {
|
|
37
|
+
ensureDir();
|
|
38
|
+
const files = readdirSync(AGENTS_DIR).filter((f) => f.endsWith('.json'));
|
|
39
|
+
const summaries = [];
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
try {
|
|
42
|
+
const raw = JSON.parse(readFileSync(join(AGENTS_DIR, file), 'utf-8'));
|
|
43
|
+
summaries.push({
|
|
44
|
+
id: raw.id ?? file.replace('.json', ''),
|
|
45
|
+
agentMeta: raw.agentMeta ?? { name: '', description: '', icon: 'brain', category: 'general', tags: [], avatar: 'bot' },
|
|
46
|
+
savedAt: raw.savedAt ?? '',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// skip corrupt files
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return summaries;
|
|
54
|
+
}
|
|
55
|
+
export function deleteAgent(id) {
|
|
56
|
+
const p = agentPath(id);
|
|
57
|
+
if (!existsSync(p))
|
|
58
|
+
return false;
|
|
59
|
+
unlinkSync(p);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=agentStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentStore.js","sourceRoot":"","sources":["../../../server/services/agentStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface ContentRepoMeta {
|
|
2
|
+
name: string;
|
|
3
|
+
stack: string[] | Record<string, string>;
|
|
4
|
+
totalFiles: number;
|
|
5
|
+
totalTokens: number;
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
features: {
|
|
8
|
+
name: string;
|
|
9
|
+
keyFiles: string[];
|
|
10
|
+
}[];
|
|
11
|
+
}
|
|
12
|
+
export interface StoredContent {
|
|
13
|
+
sourceId: string;
|
|
14
|
+
name: string;
|
|
15
|
+
overviewMarkdown: string;
|
|
16
|
+
knowledgeDocs: Record<string, string>;
|
|
17
|
+
repoMeta: ContentRepoMeta;
|
|
18
|
+
}
|
|
19
|
+
export interface ContentListItem {
|
|
20
|
+
sourceId: string;
|
|
21
|
+
name: string;
|
|
22
|
+
repoMeta: ContentRepoMeta;
|
|
23
|
+
}
|
|
24
|
+
export declare function saveContent(sourceId: string, data: Omit<StoredContent, 'sourceId'>): void;
|
|
25
|
+
export declare function loadContent(sourceId: string): StoredContent | null;
|
|
26
|
+
export declare function listContent(): ContentListItem[];
|
|
27
|
+
export declare function deleteContent(sourceId: string): boolean;
|
|
28
|
+
/** Derive a deterministic sourceId from a GitHub URL */
|
|
29
|
+
export declare function githubSourceId(url: string): string;
|
|
30
|
+
/** Derive a deterministic sourceId from a local path */
|
|
31
|
+
export declare function localSourceId(repoPath: string): string;
|
|
32
|
+
//# sourceMappingURL=contentStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentStore.d.ts","sourceRoot":"","sources":["../../../server/services/contentStore.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;CAClD;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAYD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,GAAG,IAAI,CAIzF;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAQlE;AAED,wBAAgB,WAAW,IAAI,eAAe,EAAE,CAa/C;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CASvD;AAED,wDAAwD;AACxD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGtD"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
const CONTENT_DIR = join(homedir(), '.modular-studio', 'content');
|
|
5
|
+
function ensureDir() {
|
|
6
|
+
if (!existsSync(CONTENT_DIR)) {
|
|
7
|
+
mkdirSync(CONTENT_DIR, { recursive: true });
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function filePath(sourceId) {
|
|
11
|
+
return join(CONTENT_DIR, `${sourceId}.json`);
|
|
12
|
+
}
|
|
13
|
+
export function saveContent(sourceId, data) {
|
|
14
|
+
ensureDir();
|
|
15
|
+
const record = { sourceId, ...data };
|
|
16
|
+
writeFileSync(filePath(sourceId), JSON.stringify(record, null, 2), 'utf-8');
|
|
17
|
+
}
|
|
18
|
+
export function loadContent(sourceId) {
|
|
19
|
+
const fp = filePath(sourceId);
|
|
20
|
+
if (!existsSync(fp))
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(readFileSync(fp, 'utf-8'));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function listContent() {
|
|
30
|
+
ensureDir();
|
|
31
|
+
const files = readdirSync(CONTENT_DIR).filter(f => f.endsWith('.json'));
|
|
32
|
+
const items = [];
|
|
33
|
+
for (const f of files) {
|
|
34
|
+
try {
|
|
35
|
+
const raw = JSON.parse(readFileSync(join(CONTENT_DIR, f), 'utf-8'));
|
|
36
|
+
items.push({ sourceId: raw.sourceId, name: raw.name, repoMeta: raw.repoMeta });
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// skip corrupt files
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return items;
|
|
43
|
+
}
|
|
44
|
+
export function deleteContent(sourceId) {
|
|
45
|
+
const fp = filePath(sourceId);
|
|
46
|
+
if (!existsSync(fp))
|
|
47
|
+
return false;
|
|
48
|
+
try {
|
|
49
|
+
unlinkSync(fp);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Derive a deterministic sourceId from a GitHub URL */
|
|
57
|
+
export function githubSourceId(url) {
|
|
58
|
+
const match = url.match(/github\.com\/([^/]+)\/([^/.]+)/);
|
|
59
|
+
if (match)
|
|
60
|
+
return `github-${match[1]}-${match[2]}`.toLowerCase();
|
|
61
|
+
return `github-${url.replace(/[^a-zA-Z0-9-]/g, '-')}`.toLowerCase();
|
|
62
|
+
}
|
|
63
|
+
/** Derive a deterministic sourceId from a local path */
|
|
64
|
+
export function localSourceId(repoPath) {
|
|
65
|
+
const sanitized = repoPath.replace(/[\\/]/g, '-').replace(/[^a-zA-Z0-9-]/g, '').replace(/-+/g, '-').replace(/^-|-$/g, '');
|
|
66
|
+
return `local-${sanitized}`.toLowerCase();
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=contentStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentStore.js","sourceRoot":"","sources":["../../../server/services/contentStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side embedding service using HuggingFace Transformers.js
|
|
3
|
+
*
|
|
4
|
+
* This service provides semantic embeddings for text, with caching and
|
|
5
|
+
* similarity operations. Uses the Xenova/all-MiniLM-L6-v2 model which
|
|
6
|
+
* is well-tested with transformers.js in Node.js environments.
|
|
7
|
+
*/
|
|
8
|
+
export interface EmbeddingService {
|
|
9
|
+
initialize(): Promise<void>;
|
|
10
|
+
embed(text: string): Promise<number[]>;
|
|
11
|
+
embedBatch(texts: string[]): Promise<number[][]>;
|
|
12
|
+
similarity(a: number[], b: number[]): number;
|
|
13
|
+
nearestK(query: number[], corpus: number[][], k: number): {
|
|
14
|
+
index: number;
|
|
15
|
+
score: number;
|
|
16
|
+
}[];
|
|
17
|
+
isReady(): boolean;
|
|
18
|
+
}
|
|
19
|
+
declare class EmbeddingServiceImpl implements EmbeddingService {
|
|
20
|
+
private model;
|
|
21
|
+
private ready;
|
|
22
|
+
private initPromise;
|
|
23
|
+
private cache;
|
|
24
|
+
private maxCacheSize;
|
|
25
|
+
initialize(): Promise<void>;
|
|
26
|
+
private _doInitialize;
|
|
27
|
+
isReady(): boolean;
|
|
28
|
+
private hashText;
|
|
29
|
+
private evictOldestCacheEntry;
|
|
30
|
+
private _embed;
|
|
31
|
+
embed(text: string): Promise<number[]>;
|
|
32
|
+
/** Maximum input length in characters (~256 tokens for all-MiniLM-L6-v2) */
|
|
33
|
+
private maxInputChars;
|
|
34
|
+
private truncateForModel;
|
|
35
|
+
embedBatch(texts: string[]): Promise<number[][]>;
|
|
36
|
+
similarity(a: number[], b: number[]): number;
|
|
37
|
+
nearestK(query: number[], corpus: number[][], k: number): {
|
|
38
|
+
index: number;
|
|
39
|
+
score: number;
|
|
40
|
+
}[];
|
|
41
|
+
getHealth(): {
|
|
42
|
+
ready: boolean;
|
|
43
|
+
model: string;
|
|
44
|
+
cacheSize: number;
|
|
45
|
+
maxCacheSize: number;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
declare const embeddingService: EmbeddingServiceImpl;
|
|
49
|
+
/** Reset internal state — for tests only */
|
|
50
|
+
export declare function _resetForTesting(): void;
|
|
51
|
+
export { embeddingService };
|
|
52
|
+
export default embeddingService;
|
|
53
|
+
//# sourceMappingURL=embeddingService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddingService.d.ts","sourceRoot":"","sources":["../../../server/services/embeddingService.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACvC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACjD,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,EAAE,CAAC;IAC3F,OAAO,IAAI,OAAO,CAAC;CACpB;AAOD,cAAM,oBAAqB,YAAW,gBAAgB;IACpD,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,WAAW,CAA8B;IAGjD,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,YAAY,CAAS;IAEvB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YASnB,aAAa;IAwB3B,OAAO,IAAI,OAAO;IAIlB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,qBAAqB;YAgBf,MAAM;IA+Bd,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQ5C,4EAA4E;IAC5E,OAAO,CAAC,aAAa,CAAQ;IAE7B,OAAO,CAAC,gBAAgB;IAIlB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;IA2DtD,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM;IA4B5C,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,EAAE;IAgB1F,SAAS;;;;;;CAQV;AAGD,QAAA,MAAM,gBAAgB,sBAA6B,CAAC;AAEpD,4CAA4C;AAC5C,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAC5B,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side embedding service using HuggingFace Transformers.js
|
|
3
|
+
*
|
|
4
|
+
* This service provides semantic embeddings for text, with caching and
|
|
5
|
+
* similarity operations. Uses the Xenova/all-MiniLM-L6-v2 model which
|
|
6
|
+
* is well-tested with transformers.js in Node.js environments.
|
|
7
|
+
*/
|
|
8
|
+
import { createHash } from 'node:crypto';
|
|
9
|
+
class EmbeddingServiceImpl {
|
|
10
|
+
model = null;
|
|
11
|
+
ready = false;
|
|
12
|
+
initPromise = null;
|
|
13
|
+
// LRU cache: key = hash(text), value = embedding vector
|
|
14
|
+
cache = new Map();
|
|
15
|
+
maxCacheSize = 10000;
|
|
16
|
+
async initialize() {
|
|
17
|
+
if (this.initPromise) {
|
|
18
|
+
return this.initPromise;
|
|
19
|
+
}
|
|
20
|
+
this.initPromise = this._doInitialize();
|
|
21
|
+
return this.initPromise;
|
|
22
|
+
}
|
|
23
|
+
async _doInitialize() {
|
|
24
|
+
try {
|
|
25
|
+
console.log('[Embedding] Loading model Xenova/all-MiniLM-L6-v2...');
|
|
26
|
+
const startTime = Date.now();
|
|
27
|
+
// Dynamic import to avoid top-level side effects and enable mocking in tests
|
|
28
|
+
const { pipeline, env } = await import('@huggingface/transformers');
|
|
29
|
+
env.allowLocalModels = false;
|
|
30
|
+
env.useBrowserCache = false;
|
|
31
|
+
this.model = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
|
|
32
|
+
const loadTime = Date.now() - startTime;
|
|
33
|
+
console.log(`[Embedding] Model loaded in ${loadTime}ms`);
|
|
34
|
+
this.ready = true;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.error('[Embedding] Failed to load model:', error);
|
|
38
|
+
this.ready = false;
|
|
39
|
+
this.initPromise = null; // Allow retry
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
isReady() {
|
|
44
|
+
return this.ready;
|
|
45
|
+
}
|
|
46
|
+
hashText(text) {
|
|
47
|
+
return createHash('sha256').update(text.trim()).digest('hex');
|
|
48
|
+
}
|
|
49
|
+
evictOldestCacheEntry() {
|
|
50
|
+
let oldestKey = null;
|
|
51
|
+
let oldestTime = Date.now();
|
|
52
|
+
for (const [key, entry] of this.cache) {
|
|
53
|
+
if (entry.lastAccessed < oldestTime) {
|
|
54
|
+
oldestTime = entry.lastAccessed;
|
|
55
|
+
oldestKey = key;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (oldestKey) {
|
|
59
|
+
this.cache.delete(oldestKey);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async _embed(text) {
|
|
63
|
+
if (!this.ready || !this.model) {
|
|
64
|
+
await this.initialize();
|
|
65
|
+
}
|
|
66
|
+
const hash = this.hashText(text);
|
|
67
|
+
// Check cache first
|
|
68
|
+
const cached = this.cache.get(hash);
|
|
69
|
+
if (cached) {
|
|
70
|
+
cached.lastAccessed = Date.now();
|
|
71
|
+
return cached.embedding;
|
|
72
|
+
}
|
|
73
|
+
// Generate embedding
|
|
74
|
+
const result = await this.model(text, { pooling: 'mean', normalize: true });
|
|
75
|
+
const embedding = Array.from(result.data);
|
|
76
|
+
// Cache the result
|
|
77
|
+
if (this.cache.size >= this.maxCacheSize) {
|
|
78
|
+
this.evictOldestCacheEntry();
|
|
79
|
+
}
|
|
80
|
+
this.cache.set(hash, {
|
|
81
|
+
embedding,
|
|
82
|
+
lastAccessed: Date.now(),
|
|
83
|
+
});
|
|
84
|
+
return embedding;
|
|
85
|
+
}
|
|
86
|
+
async embed(text) {
|
|
87
|
+
if (!text || text.trim().length === 0) {
|
|
88
|
+
throw new Error('Text cannot be empty');
|
|
89
|
+
}
|
|
90
|
+
return this._embed(this.truncateForModel(text.trim()));
|
|
91
|
+
}
|
|
92
|
+
/** Maximum input length in characters (~256 tokens for all-MiniLM-L6-v2) */
|
|
93
|
+
maxInputChars = 1024; // ~256 tokens × 4 chars/token
|
|
94
|
+
truncateForModel(text) {
|
|
95
|
+
return text.length > this.maxInputChars ? text.slice(0, this.maxInputChars) : text;
|
|
96
|
+
}
|
|
97
|
+
async embedBatch(texts) {
|
|
98
|
+
if (!texts || texts.length === 0) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
if (!this.ready || !this.model) {
|
|
102
|
+
await this.initialize();
|
|
103
|
+
}
|
|
104
|
+
// Split into batches and use native model batching (pass array of strings)
|
|
105
|
+
const batchSize = 32;
|
|
106
|
+
const results = [];
|
|
107
|
+
for (let i = 0; i < texts.length; i += batchSize) {
|
|
108
|
+
const batch = texts.slice(i, i + batchSize).map(t => this.truncateForModel(t.trim()));
|
|
109
|
+
// Check cache for each text, collect uncached
|
|
110
|
+
const batchResults = batch.map(t => {
|
|
111
|
+
const hash = this.hashText(t);
|
|
112
|
+
const cached = this.cache.get(hash);
|
|
113
|
+
if (cached) {
|
|
114
|
+
cached.lastAccessed = Date.now();
|
|
115
|
+
return cached.embedding;
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
});
|
|
119
|
+
const uncachedIndices = batchResults
|
|
120
|
+
.map((r, idx) => r === null ? idx : -1)
|
|
121
|
+
.filter(idx => idx >= 0);
|
|
122
|
+
if (uncachedIndices.length > 0) {
|
|
123
|
+
// Native batch inference — one model call for all uncached texts
|
|
124
|
+
const uncachedTexts = uncachedIndices.map(idx => batch[idx]);
|
|
125
|
+
const batchOutput = await this.model(uncachedTexts, { pooling: 'mean', normalize: true });
|
|
126
|
+
// batchOutput.data is a flat Float32Array: [dim * N]
|
|
127
|
+
const dim = batchOutput.dims?.[1] ?? (batchOutput.data.length / uncachedTexts.length);
|
|
128
|
+
for (let j = 0; j < uncachedIndices.length; j++) {
|
|
129
|
+
const embedding = Array.from(batchOutput.data.slice(j * dim, (j + 1) * dim));
|
|
130
|
+
const idx = uncachedIndices[j];
|
|
131
|
+
batchResults[idx] = embedding;
|
|
132
|
+
// Cache the result
|
|
133
|
+
const hash = this.hashText(batch[idx]);
|
|
134
|
+
if (this.cache.size >= this.maxCacheSize) {
|
|
135
|
+
this.evictOldestCacheEntry();
|
|
136
|
+
}
|
|
137
|
+
this.cache.set(hash, { embedding, lastAccessed: Date.now() });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
results.push(...batchResults);
|
|
141
|
+
}
|
|
142
|
+
return results;
|
|
143
|
+
}
|
|
144
|
+
similarity(a, b) {
|
|
145
|
+
if (a.length !== b.length) {
|
|
146
|
+
throw new Error('Vectors must have the same length');
|
|
147
|
+
}
|
|
148
|
+
if (a.length === 0) {
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
// Cosine similarity: dot product of normalized vectors
|
|
152
|
+
let dotProduct = 0;
|
|
153
|
+
let normA = 0;
|
|
154
|
+
let normB = 0;
|
|
155
|
+
for (let i = 0; i < a.length; i++) {
|
|
156
|
+
dotProduct += a[i] * b[i];
|
|
157
|
+
normA += a[i] * a[i];
|
|
158
|
+
normB += b[i] * b[i];
|
|
159
|
+
}
|
|
160
|
+
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
|
|
161
|
+
if (magnitude === 0) {
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
return dotProduct / magnitude;
|
|
165
|
+
}
|
|
166
|
+
nearestK(query, corpus, k) {
|
|
167
|
+
if (corpus.length === 0) {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
const scores = corpus.map((embedding, index) => ({
|
|
171
|
+
index,
|
|
172
|
+
score: this.similarity(query, embedding),
|
|
173
|
+
}));
|
|
174
|
+
// Sort by score descending and take top k
|
|
175
|
+
scores.sort((a, b) => b.score - a.score);
|
|
176
|
+
return scores.slice(0, k);
|
|
177
|
+
}
|
|
178
|
+
// Health check information
|
|
179
|
+
getHealth() {
|
|
180
|
+
return {
|
|
181
|
+
ready: this.ready,
|
|
182
|
+
model: 'Xenova/all-MiniLM-L6-v2',
|
|
183
|
+
cacheSize: this.cache.size,
|
|
184
|
+
maxCacheSize: this.maxCacheSize,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Singleton instance
|
|
189
|
+
const embeddingService = new EmbeddingServiceImpl();
|
|
190
|
+
/** Reset internal state — for tests only */
|
|
191
|
+
export function _resetForTesting() {
|
|
192
|
+
embeddingService.model = null;
|
|
193
|
+
embeddingService.ready = false;
|
|
194
|
+
embeddingService.initPromise = null;
|
|
195
|
+
embeddingService.cache.clear();
|
|
196
|
+
}
|
|
197
|
+
export { embeddingService };
|
|
198
|
+
export default embeddingService;
|
|
199
|
+
//# sourceMappingURL=embeddingService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddingService.js","sourceRoot":"","sources":["../../../server/services/embeddingService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ExtractedFact {
|
|
2
|
+
key: string;
|
|
3
|
+
value: string;
|
|
4
|
+
epistemicType: 'observation' | 'inference' | 'decision' | 'hypothesis' | 'contract';
|
|
5
|
+
confidence: number;
|
|
6
|
+
source: string;
|
|
7
|
+
importance?: number;
|
|
8
|
+
created_at?: number;
|
|
9
|
+
accessed_at?: number;
|
|
10
|
+
access_count?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function extractFacts(agentOutput: string, agentId: string): ExtractedFact[];
|
|
13
|
+
export declare function extractFactsWithLlm(agentOutput: string, agentId: string, providerId: string, model: string): Promise<ExtractedFact[]>;
|
|
14
|
+
//# sourceMappingURL=factExtractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factExtractor.d.ts","sourceRoot":"","sources":["../../../server/services/factExtractor.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,GAAG,WAAW,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,CAAC;IACpF,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAgCD,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAgClF;AAED,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,EAAE,CAAC,CA+E1B"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { readConfig } from '../config.js';
|
|
2
|
+
const PATTERNS = [
|
|
3
|
+
// Decisions
|
|
4
|
+
{ pattern: /I (?:decided|chose|picked|selected|went with|will use|am going to use) (?:to )?(.+?)(?:\.|$)/gim, epistemicType: 'decision', confidence: 0.9, keyPrefix: 'decision' },
|
|
5
|
+
{ pattern: /(?:Let's|We should|I'll|I will) (?:use|go with|implement|create|build) (.+?)(?:\.|$)/gim, epistemicType: 'decision', confidence: 0.8, keyPrefix: 'decision' },
|
|
6
|
+
// Observations
|
|
7
|
+
{ pattern: /The (?:file|directory|folder|API|endpoint|function|module|class) (.+?) (?:exports?|contains?|has|returns?|provides?) (.+?)(?:\.|$)/gim, epistemicType: 'observation', confidence: 0.85, keyPrefix: 'observation' },
|
|
8
|
+
{ pattern: /I (?:found|noticed|observed|see|saw) (?:that )?(.+?)(?:\.|$)/gim, epistemicType: 'observation', confidence: 0.8, keyPrefix: 'observation' },
|
|
9
|
+
{ pattern: /I (?:created|wrote|added|updated|modified|deleted|removed) (?:the )?(?:file )?(.+?)(?:\.|$)/gim, epistemicType: 'observation', confidence: 0.95, keyPrefix: 'file_action' },
|
|
10
|
+
// Inferences
|
|
11
|
+
{ pattern: /(?:This means|Therefore|So|Thus|Hence|It follows that|This implies) (.+?)(?:\.|$)/gim, epistemicType: 'inference', confidence: 0.7, keyPrefix: 'inference' },
|
|
12
|
+
{ pattern: /(?:It (?:seems|appears|looks) (?:like|that)) (.+?)(?:\.|$)/gim, epistemicType: 'inference', confidence: 0.5, keyPrefix: 'inference' },
|
|
13
|
+
// Hypotheses
|
|
14
|
+
{ pattern: /(?:I think|I believe|I suspect|Maybe|Perhaps|Possibly|My guess is) (.+?)(?:\.|$)/gim, epistemicType: 'hypothesis', confidence: 0.4, keyPrefix: 'hypothesis' },
|
|
15
|
+
// Contracts — interfaces, types, schemas
|
|
16
|
+
{ pattern: /(?:^|\n)((?:export )?interface \w+[\s\S]*?\n\})/gm, epistemicType: 'contract', confidence: 0.95, keyPrefix: 'contract' },
|
|
17
|
+
{ pattern: /(?:^|\n)((?:export )?type \w+ = [\s\S]*?(?:;|\n\}))/gm, epistemicType: 'contract', confidence: 0.95, keyPrefix: 'contract' },
|
|
18
|
+
];
|
|
19
|
+
function makeKey(prefix, index, value) {
|
|
20
|
+
const slug = value.slice(0, 40).replace(/[^a-zA-Z0-9]/g, '_').replace(/_+/g, '_').replace(/_$/, '').toLowerCase();
|
|
21
|
+
return `${prefix}_${index}_${slug}`;
|
|
22
|
+
}
|
|
23
|
+
export function extractFacts(agentOutput, agentId) {
|
|
24
|
+
const facts = [];
|
|
25
|
+
const seen = new Set();
|
|
26
|
+
let globalIdx = 0;
|
|
27
|
+
for (const rule of PATTERNS) {
|
|
28
|
+
// Reset regex state
|
|
29
|
+
rule.pattern.lastIndex = 0;
|
|
30
|
+
let match;
|
|
31
|
+
while ((match = rule.pattern.exec(agentOutput)) !== null) {
|
|
32
|
+
const value = (match[2] ?? match[1] ?? match[0]).trim();
|
|
33
|
+
if (!value || value.length < 3)
|
|
34
|
+
continue;
|
|
35
|
+
const dedupKey = `${rule.epistemicType}:${value.slice(0, 80)}`;
|
|
36
|
+
if (seen.has(dedupKey))
|
|
37
|
+
continue;
|
|
38
|
+
seen.add(dedupKey);
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
facts.push({
|
|
41
|
+
key: makeKey(rule.keyPrefix, globalIdx++, value),
|
|
42
|
+
value,
|
|
43
|
+
epistemicType: rule.epistemicType,
|
|
44
|
+
confidence: rule.confidence,
|
|
45
|
+
source: agentId,
|
|
46
|
+
importance: rule.confidence * 0.8,
|
|
47
|
+
created_at: now,
|
|
48
|
+
accessed_at: now,
|
|
49
|
+
access_count: 0,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return facts;
|
|
54
|
+
}
|
|
55
|
+
export async function extractFactsWithLlm(agentOutput, agentId, providerId, model) {
|
|
56
|
+
const config = readConfig();
|
|
57
|
+
const provider = config.providers.find((p) => p.id === providerId);
|
|
58
|
+
if (!provider)
|
|
59
|
+
throw new Error(`Provider "${providerId}" not found`);
|
|
60
|
+
if (!provider.baseUrl)
|
|
61
|
+
throw new Error(`Provider "${providerId}" has no baseUrl`);
|
|
62
|
+
const extractionPrompt = `Extract structured facts from the following agent output. Return a JSON array of objects with these fields:
|
|
63
|
+
- key: short snake_case identifier
|
|
64
|
+
- value: the fact content
|
|
65
|
+
- epistemicType: one of "observation", "inference", "decision", "hypothesis", "contract"
|
|
66
|
+
- confidence: number 0-1
|
|
67
|
+
|
|
68
|
+
Only return the JSON array, no other text.
|
|
69
|
+
|
|
70
|
+
Agent output:
|
|
71
|
+
${agentOutput}`;
|
|
72
|
+
const messages = [{ role: 'user', content: extractionPrompt }];
|
|
73
|
+
let url;
|
|
74
|
+
let headers;
|
|
75
|
+
let body;
|
|
76
|
+
if (provider.type === 'anthropic') {
|
|
77
|
+
url = `${provider.baseUrl}/messages`;
|
|
78
|
+
headers = {
|
|
79
|
+
'x-api-key': provider.apiKey,
|
|
80
|
+
'anthropic-version': '2023-06-01',
|
|
81
|
+
'content-type': 'application/json',
|
|
82
|
+
};
|
|
83
|
+
body = JSON.stringify({ model, max_tokens: 4096, messages });
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
url = `${provider.baseUrl}/chat/completions`;
|
|
87
|
+
headers = {
|
|
88
|
+
'Authorization': `Bearer ${provider.apiKey}`,
|
|
89
|
+
'Content-Type': 'application/json',
|
|
90
|
+
};
|
|
91
|
+
body = JSON.stringify({ model, messages });
|
|
92
|
+
}
|
|
93
|
+
const response = await fetch(url, { method: 'POST', headers, body });
|
|
94
|
+
if (!response.ok) {
|
|
95
|
+
const errText = await response.text();
|
|
96
|
+
throw new Error(`LLM extraction failed (${response.status}): ${errText}`);
|
|
97
|
+
}
|
|
98
|
+
const data = await response.json();
|
|
99
|
+
let text;
|
|
100
|
+
if (provider.type === 'anthropic') {
|
|
101
|
+
const content = data.content;
|
|
102
|
+
text = content?.find((c) => c.type === 'text')?.text ?? '';
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const choices = data.choices;
|
|
106
|
+
text = choices?.[0]?.message?.content ?? '';
|
|
107
|
+
}
|
|
108
|
+
// Parse JSON from response
|
|
109
|
+
const jsonMatch = text.match(/\[[\s\S]*\]/);
|
|
110
|
+
if (!jsonMatch)
|
|
111
|
+
return [];
|
|
112
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
return parsed.map((f) => ({
|
|
115
|
+
key: f.key,
|
|
116
|
+
value: f.value,
|
|
117
|
+
epistemicType: f.epistemicType,
|
|
118
|
+
confidence: f.confidence,
|
|
119
|
+
source: agentId,
|
|
120
|
+
importance: f.confidence * 0.8,
|
|
121
|
+
created_at: now,
|
|
122
|
+
accessed_at: now,
|
|
123
|
+
access_count: 0,
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=factExtractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factExtractor.js","sourceRoot":"","sources":["../../../server/services/factExtractor.ts"],"names":[],"mappings":"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"}
|