modular-studio 1.0.3 → 1.0.5

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.
Files changed (107) hide show
  1. package/README.md +101 -20
  2. package/dist/assets/Badge-22Ai0eyi.js +1 -0
  3. package/dist/assets/Input-Bgp734xs.js +1 -0
  4. package/dist/assets/KnowledgeTab-DABxirZh.js +4 -0
  5. package/dist/assets/MemoryTab-DZeYElIT.js +16 -0
  6. package/dist/assets/QualificationTab-Dfpy3J30.js +1 -0
  7. package/dist/assets/ReviewTab-SD8lQuCc.js +103 -0
  8. package/dist/assets/Section-DoJrmytO.js +1 -0
  9. package/dist/assets/TestTab-PDyMF8Fw.js +33 -0
  10. package/dist/assets/ToolsTab-B83qGCmG.js +1 -0
  11. package/dist/assets/conversationStore-CkfEU2eV.js +1 -0
  12. package/dist/assets/icons-C2EV-le6.js +1 -0
  13. package/dist/assets/index-DkpMAxX7.css +1 -0
  14. package/dist/assets/index-q24ug5Qs.js +143 -0
  15. package/dist/assets/{jszip.min-DByyLalk.js → jszip.min-wf-D3Ix_.js} +1 -1
  16. package/dist/assets/markdown-DWF7F0i0.js +29 -0
  17. package/dist/assets/services-BaKotDf0.js +343 -0
  18. package/dist/assets/stores-CeKWz7ou.js +1 -0
  19. package/dist/assets/vendor-D1h_O76p.js +9 -0
  20. package/dist/index.html +8 -4
  21. package/dist-server/bin/modular-mcp.js +0 -1
  22. package/dist-server/bin/modular-studio.js +0 -1
  23. package/dist-server/server/config.js +0 -1
  24. package/dist-server/server/data/mcp-tokens.json +3 -3
  25. package/dist-server/server/index.d.ts.map +1 -1
  26. package/dist-server/server/index.js +12 -1
  27. package/dist-server/server/index.js.map +1 -1
  28. package/dist-server/server/mcp/manager.js +0 -1
  29. package/dist-server/server/mcp/modular-server.js +0 -1
  30. package/dist-server/server/mcp/transport.js +0 -1
  31. package/dist-server/server/routes/agent-sdk.js +0 -1
  32. package/dist-server/server/routes/agents.d.ts +9 -5
  33. package/dist-server/server/routes/agents.d.ts.map +1 -1
  34. package/dist-server/server/routes/agents.js +81 -8
  35. package/dist-server/server/routes/auth-codex.js +0 -1
  36. package/dist-server/server/routes/capabilities.js +0 -1
  37. package/dist-server/server/routes/claude-config.js +0 -1
  38. package/dist-server/server/routes/connectors.d.ts.map +1 -1
  39. package/dist-server/server/routes/connectors.js +194 -1
  40. package/dist-server/server/routes/conversations.js +0 -1
  41. package/dist-server/server/routes/embeddings.d.ts.map +1 -1
  42. package/dist-server/server/routes/embeddings.js +16 -2
  43. package/dist-server/server/routes/embeddings.js.map +1 -1
  44. package/dist-server/server/routes/health.d.ts.map +1 -1
  45. package/dist-server/server/routes/health.js +10 -2
  46. package/dist-server/server/routes/health.js.map +1 -1
  47. package/dist-server/server/routes/knowledge.js +0 -1
  48. package/dist-server/server/routes/llm.js +0 -1
  49. package/dist-server/server/routes/mcp-oauth.js +0 -1
  50. package/dist-server/server/routes/mcp.js +0 -1
  51. package/dist-server/server/routes/memory.d.ts +3 -0
  52. package/dist-server/server/routes/memory.d.ts.map +1 -0
  53. package/dist-server/server/routes/memory.js +283 -0
  54. package/dist-server/server/routes/pipeline.js +0 -1
  55. package/dist-server/server/routes/providers.js +0 -1
  56. package/dist-server/server/routes/qualification.d.ts.map +1 -1
  57. package/dist-server/server/routes/qualification.js +382 -74
  58. package/dist-server/server/routes/repo-index.js +0 -1
  59. package/dist-server/server/routes/runtime.js +0 -1
  60. package/dist-server/server/routes/skills-search.d.ts.map +1 -1
  61. package/dist-server/server/routes/skills-search.js +44 -5
  62. package/dist-server/server/routes/skills-search.js.map +1 -1
  63. package/dist-server/server/routes/worktrees.js +0 -1
  64. package/dist-server/server/services/__tests__/embeddingService.test.js +0 -1
  65. package/dist-server/server/services/adapters/postgresAdapter.d.ts +29 -0
  66. package/dist-server/server/services/adapters/postgresAdapter.d.ts.map +1 -0
  67. package/dist-server/server/services/adapters/postgresAdapter.js +224 -0
  68. package/dist-server/server/services/adapters/sqliteAdapter.d.ts +28 -0
  69. package/dist-server/server/services/adapters/sqliteAdapter.d.ts.map +1 -0
  70. package/dist-server/server/services/adapters/sqliteAdapter.js +219 -0
  71. package/dist-server/server/services/adapters/storageAdapter.d.ts +22 -0
  72. package/dist-server/server/services/adapters/storageAdapter.d.ts.map +1 -0
  73. package/dist-server/server/services/adapters/storageAdapter.js +1 -0
  74. package/dist-server/server/services/agentRunner.js +0 -1
  75. package/dist-server/server/services/agentStore.d.ts +18 -3
  76. package/dist-server/server/services/agentStore.d.ts.map +1 -1
  77. package/dist-server/server/services/agentStore.js +116 -23
  78. package/dist-server/server/services/contentStore.js +0 -1
  79. package/dist-server/server/services/embeddingService.d.ts +2 -0
  80. package/dist-server/server/services/embeddingService.d.ts.map +1 -1
  81. package/dist-server/server/services/embeddingService.js +30 -19
  82. package/dist-server/server/services/factExtractor.js +0 -1
  83. package/dist-server/server/services/githubIndexer.js +0 -1
  84. package/dist-server/server/services/mcpOAuth.js +0 -1
  85. package/dist-server/server/services/memoryScorer.js +0 -1
  86. package/dist-server/server/services/repoIndexer.js +0 -1
  87. package/dist-server/server/services/sqliteStore.js +0 -1
  88. package/dist-server/server/services/teamRunner.js +0 -1
  89. package/dist-server/server/services/worktreeManager.js +0 -1
  90. package/dist-server/server/types.d.ts +5 -0
  91. package/dist-server/server/types.d.ts.map +1 -1
  92. package/dist-server/server/types.js +0 -1
  93. package/dist-server/server/utils/pathSecurity.js +0 -1
  94. package/dist-server/src/services/budgetAllocator.js +0 -1
  95. package/dist-server/src/services/contradictionDetector.js +0 -1
  96. package/dist-server/src/services/treeIndexer.js +0 -1
  97. package/dist-server/src/store/knowledgeBase.d.ts +10 -0
  98. package/dist-server/src/store/knowledgeBase.d.ts.map +1 -1
  99. package/dist-server/src/store/knowledgeBase.js +13 -1
  100. package/dist-server/src/store/memoryStore.d.ts +107 -0
  101. package/dist-server/src/store/memoryStore.d.ts.map +1 -0
  102. package/dist-server/src/store/memoryStore.js +263 -0
  103. package/dist-server/tsconfig.server.tsbuildinfo +1 -1
  104. package/package.json +104 -97
  105. package/dist/assets/graphPopulator-DH0qM1Fu.js +0 -1
  106. package/dist/assets/index-B0RRTCSi.css +0 -1
  107. package/dist/assets/index-LWCi_4Hu.js +0 -662
@@ -0,0 +1,224 @@
1
+ import { Pool } from 'pg';
2
+ import { textSimilarity } from '../memoryScorer.js';
3
+ export class PostgresAdapter {
4
+ pool = null;
5
+ connectionString;
6
+ lastWrite = 0;
7
+ constructor(connectionString) {
8
+ this.connectionString = connectionString;
9
+ }
10
+ async initialize() {
11
+ this.pool = new Pool({
12
+ connectionString: this.connectionString,
13
+ max: 10,
14
+ idleTimeoutMillis: 30000,
15
+ connectionTimeoutMillis: 2000,
16
+ });
17
+ const client = await this.pool.connect();
18
+ try {
19
+ await this.createTables(client);
20
+ }
21
+ finally {
22
+ client.release();
23
+ }
24
+ }
25
+ async createTables(client) {
26
+ await client.query(`CREATE TABLE IF NOT EXISTS facts (
27
+ id TEXT PRIMARY KEY,
28
+ content TEXT NOT NULL,
29
+ tags JSONB NOT NULL DEFAULT '[]'::jsonb,
30
+ type TEXT NOT NULL,
31
+ timestamp BIGINT NOT NULL,
32
+ domain TEXT NOT NULL,
33
+ granularity TEXT NOT NULL,
34
+ embedding BYTEA,
35
+ owner_agent_id TEXT,
36
+ created_at TIMESTAMP DEFAULT NOW()
37
+ )`);
38
+ // Create indexes for better performance
39
+ await client.query('CREATE INDEX IF NOT EXISTS idx_facts_domain ON facts(domain)');
40
+ await client.query('CREATE INDEX IF NOT EXISTS idx_facts_timestamp ON facts(timestamp)');
41
+ await client.query('CREATE INDEX IF NOT EXISTS idx_facts_type ON facts(type)');
42
+ // GIN index for tag queries
43
+ await client.query('CREATE INDEX IF NOT EXISTS idx_facts_tags ON facts USING gin(tags)');
44
+ // Full-text search index
45
+ await client.query(`CREATE INDEX IF NOT EXISTS idx_facts_content_fts
46
+ ON facts USING gin(to_tsvector('english', content))`);
47
+ }
48
+ async storeFact(fact) {
49
+ if (!this.pool)
50
+ await this.initialize();
51
+ if (!this.pool)
52
+ throw new Error('Pool not initialized');
53
+ const embedding = fact.embedding ? Buffer.from(new Float32Array(fact.embedding).buffer) : null;
54
+ await this.pool.query(`
55
+ INSERT INTO facts (id, content, tags, type, timestamp, domain, granularity, embedding, owner_agent_id)
56
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
57
+ ON CONFLICT (id) DO UPDATE SET
58
+ content = EXCLUDED.content,
59
+ tags = EXCLUDED.tags,
60
+ type = EXCLUDED.type,
61
+ timestamp = EXCLUDED.timestamp,
62
+ domain = EXCLUDED.domain,
63
+ granularity = EXCLUDED.granularity,
64
+ embedding = EXCLUDED.embedding,
65
+ owner_agent_id = EXCLUDED.owner_agent_id
66
+ `, [
67
+ fact.id,
68
+ fact.content,
69
+ JSON.stringify(fact.tags),
70
+ fact.type,
71
+ fact.timestamp,
72
+ fact.domain,
73
+ fact.granularity,
74
+ embedding,
75
+ fact.ownerAgentId || null
76
+ ]);
77
+ this.lastWrite = Date.now();
78
+ }
79
+ async getFacts(options) {
80
+ if (!this.pool)
81
+ await this.initialize();
82
+ if (!this.pool)
83
+ return [];
84
+ let query = 'SELECT * FROM facts';
85
+ const params = [];
86
+ let paramCount = 0;
87
+ if (options?.domain) {
88
+ query += ` WHERE domain = $${++paramCount}`;
89
+ params.push(options.domain);
90
+ }
91
+ query += ' ORDER BY timestamp DESC';
92
+ if (options?.limit) {
93
+ query += ` LIMIT $${++paramCount}`;
94
+ params.push(options.limit);
95
+ if (options.offset) {
96
+ query += ` OFFSET $${++paramCount}`;
97
+ params.push(options.offset);
98
+ }
99
+ }
100
+ const result = await this.pool.query(query, params);
101
+ return result.rows.map(row => this.rowToFact(row));
102
+ }
103
+ rowToFact(row) {
104
+ const embedding = row.embedding ? new Float32Array(new Uint8Array(row.embedding).buffer) : undefined;
105
+ return {
106
+ id: row.id,
107
+ content: row.content,
108
+ tags: JSON.parse(row.tags),
109
+ type: row.type,
110
+ timestamp: parseInt(row.timestamp),
111
+ domain: row.domain,
112
+ granularity: row.granularity,
113
+ embedding: embedding ? Array.from(embedding) : undefined,
114
+ ownerAgentId: row.owner_agent_id || undefined
115
+ };
116
+ }
117
+ async searchFacts(query, k = 5) {
118
+ if (!this.pool)
119
+ await this.initialize();
120
+ if (!this.pool)
121
+ return [];
122
+ // Try PostgreSQL full-text search first
123
+ try {
124
+ const result = await this.pool.query(`
125
+ SELECT *, ts_rank(to_tsvector('english', content), plainto_tsquery('english', $1)) as score
126
+ FROM facts
127
+ WHERE to_tsvector('english', content) @@ plainto_tsquery('english', $1)
128
+ ORDER BY score DESC
129
+ LIMIT $2
130
+ `, [query, k]);
131
+ if (result.rows.length > 0) {
132
+ return result.rows.map(row => ({
133
+ ...this.rowToFact(row),
134
+ score: parseFloat(row.score)
135
+ }));
136
+ }
137
+ }
138
+ catch (error) {
139
+ console.warn('Full-text search failed, falling back to similarity:', error);
140
+ }
141
+ // Fallback to similarity search
142
+ const allFacts = await this.getFacts({ limit: 1000 });
143
+ const scored = allFacts
144
+ .map(fact => ({
145
+ ...fact,
146
+ score: textSimilarity(fact.content, query)
147
+ }))
148
+ .filter(fact => fact.score > 0.1)
149
+ .sort((a, b) => b.score - a.score)
150
+ .slice(0, k);
151
+ return scored;
152
+ }
153
+ async deleteFact(id) {
154
+ if (!this.pool)
155
+ await this.initialize();
156
+ if (!this.pool)
157
+ return;
158
+ await this.pool.query('DELETE FROM facts WHERE id = $1', [id]);
159
+ this.lastWrite = Date.now();
160
+ }
161
+ async updateFact(id, patch) {
162
+ if (!this.pool)
163
+ await this.initialize();
164
+ if (!this.pool)
165
+ return;
166
+ const updates = [];
167
+ const params = [];
168
+ let paramCount = 0;
169
+ if (patch.content !== undefined) {
170
+ updates.push(`content = $${++paramCount}`);
171
+ params.push(patch.content);
172
+ }
173
+ if (patch.tags !== undefined) {
174
+ updates.push(`tags = $${++paramCount}`);
175
+ params.push(JSON.stringify(patch.tags));
176
+ }
177
+ if (patch.type !== undefined) {
178
+ updates.push(`type = $${++paramCount}`);
179
+ params.push(patch.type);
180
+ }
181
+ if (patch.domain !== undefined) {
182
+ updates.push(`domain = $${++paramCount}`);
183
+ params.push(patch.domain);
184
+ }
185
+ if (patch.embedding !== undefined) {
186
+ updates.push(`embedding = $${++paramCount}`);
187
+ const embedding = patch.embedding ? Buffer.from(new Float32Array(patch.embedding).buffer) : null;
188
+ params.push(embedding);
189
+ }
190
+ if (updates.length === 0)
191
+ return;
192
+ params.push(id);
193
+ await this.pool.query(`UPDATE facts SET ${updates.join(', ')} WHERE id = $${++paramCount}`, params);
194
+ this.lastWrite = Date.now();
195
+ }
196
+ async getHealth() {
197
+ if (!this.pool)
198
+ await this.initialize();
199
+ if (!this.pool)
200
+ return { status: 'error', factCount: 0 };
201
+ try {
202
+ const result = await this.pool.query('SELECT COUNT(*) as count FROM facts');
203
+ const factCount = parseInt(result.rows[0].count);
204
+ return {
205
+ status: 'healthy',
206
+ factCount,
207
+ lastWrite: this.lastWrite || undefined
208
+ };
209
+ }
210
+ catch (error) {
211
+ return {
212
+ status: 'error',
213
+ factCount: 0,
214
+ lastWrite: this.lastWrite || undefined
215
+ };
216
+ }
217
+ }
218
+ async close() {
219
+ if (this.pool) {
220
+ await this.pool.end();
221
+ this.pool = null;
222
+ }
223
+ }
224
+ }
@@ -0,0 +1,28 @@
1
+ import type { Fact } from '../../../src/store/memoryStore.js';
2
+ import type { StorageAdapter } from './storageAdapter.js';
3
+ export declare class SqliteAdapter implements StorageAdapter {
4
+ private db;
5
+ private lastWrite;
6
+ initialize(): Promise<void>;
7
+ private createTables;
8
+ private saveDb;
9
+ storeFact(fact: Fact): Promise<void>;
10
+ getFacts(options?: {
11
+ domain?: string;
12
+ limit?: number;
13
+ offset?: number;
14
+ }): Promise<Fact[]>;
15
+ private rowToFact;
16
+ searchFacts(query: string, k?: number): Promise<Array<Fact & {
17
+ score: number;
18
+ }>>;
19
+ deleteFact(id: string): Promise<void>;
20
+ updateFact(id: string, patch: Partial<Fact>): Promise<void>;
21
+ getHealth(): Promise<{
22
+ status: string;
23
+ factCount: number;
24
+ lastWrite?: number;
25
+ }>;
26
+ close(): Promise<void>;
27
+ }
28
+ //# sourceMappingURL=sqliteAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqliteAdapter.d.ts","sourceRoot":"","sources":["../../../../server/services/adapters/sqliteAdapter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mCAAmC,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAM1D,qBAAa,aAAc,YAAW,cAAc;IAClD,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,SAAS,CAAa;IAExB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBjC,OAAO,CAAC,YAAY;IAuCpB,OAAO,CAAC,MAAM;IAQR,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBpC,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IA6B/F,OAAO,CAAC,SAAS;IAeX,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,SAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAkC3E,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQrC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAoC3D,SAAS,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAc/E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAO7B"}
@@ -0,0 +1,219 @@
1
+ import initSqlJs from 'sql.js';
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { homedir } from 'node:os';
5
+ import { textSimilarity } from '../memoryScorer.js';
6
+ const DB_DIR = join(homedir(), '.modular-studio');
7
+ const MEMORY_DB_PATH = join(DB_DIR, 'memory.db');
8
+ export class SqliteAdapter {
9
+ db = null;
10
+ lastWrite = 0;
11
+ async initialize() {
12
+ const SQL = await initSqlJs();
13
+ if (!existsSync(DB_DIR)) {
14
+ mkdirSync(DB_DIR, { recursive: true, mode: 0o755 });
15
+ }
16
+ if (existsSync(MEMORY_DB_PATH)) {
17
+ const buffer = readFileSync(MEMORY_DB_PATH);
18
+ this.db = new SQL.Database(buffer);
19
+ }
20
+ else {
21
+ this.db = new SQL.Database();
22
+ }
23
+ this.createTables();
24
+ }
25
+ createTables() {
26
+ if (!this.db)
27
+ return;
28
+ this.db.run(`CREATE TABLE IF NOT EXISTS facts (
29
+ id TEXT PRIMARY KEY,
30
+ content TEXT NOT NULL,
31
+ tags TEXT NOT NULL,
32
+ type TEXT NOT NULL,
33
+ timestamp INTEGER NOT NULL,
34
+ domain TEXT NOT NULL,
35
+ granularity TEXT NOT NULL,
36
+ embedding BLOB,
37
+ owner_agent_id TEXT
38
+ )`);
39
+ // Create FTS5 table for full-text search
40
+ this.db.run(`CREATE VIRTUAL TABLE IF NOT EXISTS facts_fts USING fts5(
41
+ content,
42
+ content='facts',
43
+ content_rowid='rowid'
44
+ )`);
45
+ // Triggers to keep FTS in sync
46
+ this.db.run(`CREATE TRIGGER IF NOT EXISTS facts_ai AFTER INSERT ON facts BEGIN
47
+ INSERT INTO facts_fts(rowid, content) VALUES (new.rowid, new.content);
48
+ END`);
49
+ this.db.run(`CREATE TRIGGER IF NOT EXISTS facts_ad AFTER DELETE ON facts BEGIN
50
+ INSERT INTO facts_fts(facts_fts, rowid, content) VALUES('delete', old.rowid, old.content);
51
+ END`);
52
+ this.db.run(`CREATE TRIGGER IF NOT EXISTS facts_au AFTER UPDATE ON facts BEGIN
53
+ INSERT INTO facts_fts(facts_fts, rowid, content) VALUES('delete', old.rowid, old.content);
54
+ INSERT INTO facts_fts(rowid, content) VALUES (new.rowid, new.content);
55
+ END`);
56
+ this.saveDb();
57
+ }
58
+ saveDb() {
59
+ if (!this.db)
60
+ return;
61
+ const data = this.db.export();
62
+ const buffer = Buffer.from(data);
63
+ writeFileSync(MEMORY_DB_PATH, buffer);
64
+ this.lastWrite = Date.now();
65
+ }
66
+ async storeFact(fact) {
67
+ if (!this.db)
68
+ await this.initialize();
69
+ if (!this.db)
70
+ throw new Error('Database not initialized');
71
+ const embedding = fact.embedding ? Buffer.from(new Float32Array(fact.embedding).buffer) : null;
72
+ this.db.run(`INSERT OR REPLACE INTO facts (
73
+ id, content, tags, type, timestamp, domain, granularity, embedding, owner_agent_id
74
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
75
+ fact.id,
76
+ fact.content,
77
+ JSON.stringify(fact.tags),
78
+ fact.type,
79
+ fact.timestamp,
80
+ fact.domain,
81
+ fact.granularity,
82
+ embedding,
83
+ fact.ownerAgentId || null
84
+ ]);
85
+ this.saveDb();
86
+ }
87
+ async getFacts(options) {
88
+ if (!this.db)
89
+ await this.initialize();
90
+ if (!this.db)
91
+ return [];
92
+ let query = 'SELECT * FROM facts';
93
+ const params = [];
94
+ if (options?.domain) {
95
+ query += ' WHERE domain = ?';
96
+ params.push(options.domain);
97
+ }
98
+ query += ' ORDER BY timestamp DESC';
99
+ if (options?.limit) {
100
+ query += ' LIMIT ?';
101
+ params.push(options.limit);
102
+ if (options.offset) {
103
+ query += ' OFFSET ?';
104
+ params.push(options.offset);
105
+ }
106
+ }
107
+ const result = this.db.exec(query, params);
108
+ if (result.length === 0)
109
+ return [];
110
+ return result[0].values.map(row => this.rowToFact(row));
111
+ }
112
+ rowToFact(row) {
113
+ const embedding = row[7] ? new Float32Array(new Uint8Array(row[7]).buffer) : undefined;
114
+ return {
115
+ id: row[0],
116
+ content: row[1],
117
+ tags: JSON.parse(row[2]),
118
+ type: row[3],
119
+ timestamp: row[4],
120
+ domain: row[5],
121
+ granularity: row[6],
122
+ embedding: embedding ? Array.from(embedding) : undefined,
123
+ ownerAgentId: row[8] || undefined
124
+ };
125
+ }
126
+ async searchFacts(query, k = 5) {
127
+ if (!this.db)
128
+ await this.initialize();
129
+ if (!this.db)
130
+ return [];
131
+ // First try FTS5 full-text search
132
+ const ftsResult = this.db.exec(`
133
+ SELECT facts.*, rank FROM facts_fts
134
+ JOIN facts ON facts.rowid = facts_fts.rowid
135
+ WHERE facts_fts MATCH ?
136
+ ORDER BY rank
137
+ LIMIT ?
138
+ `, [query, k]);
139
+ if (ftsResult.length > 0) {
140
+ return ftsResult[0].values.map(row => ({
141
+ ...this.rowToFact(row),
142
+ score: 1.0 - row[9] * 0.1 // Convert rank to score
143
+ }));
144
+ }
145
+ // Fallback to similarity search
146
+ const allFacts = await this.getFacts();
147
+ const scored = allFacts
148
+ .map(fact => ({
149
+ ...fact,
150
+ score: textSimilarity(fact.content, query)
151
+ }))
152
+ .filter(fact => fact.score > 0.1)
153
+ .sort((a, b) => b.score - a.score)
154
+ .slice(0, k);
155
+ return scored;
156
+ }
157
+ async deleteFact(id) {
158
+ if (!this.db)
159
+ await this.initialize();
160
+ if (!this.db)
161
+ return;
162
+ this.db.run('DELETE FROM facts WHERE id = ?', [id]);
163
+ this.saveDb();
164
+ }
165
+ async updateFact(id, patch) {
166
+ if (!this.db)
167
+ await this.initialize();
168
+ if (!this.db)
169
+ return;
170
+ const updates = [];
171
+ const params = [];
172
+ if (patch.content !== undefined) {
173
+ updates.push('content = ?');
174
+ params.push(patch.content);
175
+ }
176
+ if (patch.tags !== undefined) {
177
+ updates.push('tags = ?');
178
+ params.push(JSON.stringify(patch.tags));
179
+ }
180
+ if (patch.type !== undefined) {
181
+ updates.push('type = ?');
182
+ params.push(patch.type);
183
+ }
184
+ if (patch.domain !== undefined) {
185
+ updates.push('domain = ?');
186
+ params.push(patch.domain);
187
+ }
188
+ if (patch.embedding !== undefined) {
189
+ updates.push('embedding = ?');
190
+ const embedding = patch.embedding ? Buffer.from(new Float32Array(patch.embedding).buffer) : null;
191
+ params.push(embedding);
192
+ }
193
+ if (updates.length === 0)
194
+ return;
195
+ params.push(id);
196
+ this.db.run(`UPDATE facts SET ${updates.join(', ')} WHERE id = ?`, params);
197
+ this.saveDb();
198
+ }
199
+ async getHealth() {
200
+ if (!this.db)
201
+ await this.initialize();
202
+ if (!this.db)
203
+ return { status: 'error', factCount: 0 };
204
+ const result = this.db.exec('SELECT COUNT(*) FROM facts');
205
+ const factCount = result[0]?.values[0]?.[0] ?? 0;
206
+ return {
207
+ status: 'healthy',
208
+ factCount,
209
+ lastWrite: this.lastWrite || undefined
210
+ };
211
+ }
212
+ async close() {
213
+ if (this.db) {
214
+ this.saveDb();
215
+ this.db.close();
216
+ this.db = null;
217
+ }
218
+ }
219
+ }
@@ -0,0 +1,22 @@
1
+ import type { Fact } from '../../../src/store/memoryStore.js';
2
+ export interface StorageAdapter {
3
+ initialize(): Promise<void>;
4
+ storeFact(fact: Fact): Promise<void>;
5
+ getFacts(options?: {
6
+ domain?: string;
7
+ limit?: number;
8
+ offset?: number;
9
+ }): Promise<Fact[]>;
10
+ searchFacts(query: string, k?: number): Promise<Array<Fact & {
11
+ score: number;
12
+ }>>;
13
+ deleteFact(id: string): Promise<void>;
14
+ updateFact(id: string, patch: Partial<Fact>): Promise<void>;
15
+ getHealth(): Promise<{
16
+ status: string;
17
+ factCount: number;
18
+ lastWrite?: number;
19
+ }>;
20
+ close(): Promise<void>;
21
+ }
22
+ //# sourceMappingURL=storageAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storageAdapter.d.ts","sourceRoot":"","sources":["../../../../server/services/adapters/storageAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mCAAmC,CAAC;AAE9D,MAAM,WAAW,cAAc;IAC7B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1F,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACjF,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,SAAS,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"}
@@ -292,4 +292,3 @@ export async function runAgent(config, onProgress) {
292
292
  };
293
293
  }
294
294
  }
295
- //# sourceMappingURL=agentRunner.js.map
@@ -1,7 +1,8 @@
1
1
  /**
2
- * Agent Store — Persistent agent state on disk
3
- * Directory: ~/.modular-studio/agents/
4
- * Each agent: {id}.json containing full state snapshot
2
+ * Agent Store — Persistent agent state on disk with versioning
3
+ * Directory: ~/.modular-studio/agents/{agentId}/
4
+ * Latest state: latest.json
5
+ * Versions: versions/{timestamp}-v{major}.{minor}.{patch}.json
5
6
  */
6
7
  export interface SavedAgentState {
7
8
  id: string;
@@ -27,13 +28,27 @@ export interface SavedAgentState {
27
28
  outputFormats: string[];
28
29
  tokenBudget: number;
29
30
  prompt: string;
31
+ selectedModel: string;
32
+ }
33
+ export interface AgentVersion {
34
+ id: string;
35
+ version: string;
36
+ timestamp: number;
37
+ label?: string;
38
+ snapshot: SavedAgentState;
30
39
  }
31
40
  export interface AgentSummary {
32
41
  id: string;
33
42
  agentMeta: SavedAgentState['agentMeta'];
34
43
  savedAt: string;
44
+ currentVersion: string;
35
45
  }
36
46
  export declare function saveAgent(id: string, state: SavedAgentState): void;
47
+ export declare function createAgentVersion(id: string, version: string, label?: string): AgentVersion | null;
48
+ export declare function listAgentVersions(id: string): AgentVersion[];
49
+ export declare function getAgentVersion(id: string, version: string): AgentVersion | null;
50
+ export declare function restoreAgentVersion(id: string, version: string): boolean;
51
+ export declare function deleteAgentVersion(id: string, version: string): boolean;
37
52
  export declare function loadAgent(id: string): SavedAgentState | null;
38
53
  export declare function listAgents(): AgentSummary[];
39
54
  export declare function deleteAgent(id: string): boolean;
@@ -1 +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"}
1
+ {"version":3,"file":"agentStore.d.ts","sourceRoot":"","sources":["../../../server/services/agentStore.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA4BH,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;IACf,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAQlE;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAqBnG;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,EAAE,CAiB5D;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAGhF;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAMxE;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAavE;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAQ5D;AAED,wBAAgB,UAAU,IAAI,YAAY,EAAE,CAyB3C;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CA2B/C"}