@osmosis-ai/core 0.1.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.
Files changed (49) hide show
  1. package/dist/api/index.d.ts +12 -0
  2. package/dist/api/index.js +87 -0
  3. package/dist/api/index.js.map +1 -0
  4. package/dist/capture/index.d.ts +10 -0
  5. package/dist/capture/index.js +52 -0
  6. package/dist/capture/index.js.map +1 -0
  7. package/dist/distill/index.d.ts +35 -0
  8. package/dist/distill/index.js +41 -0
  9. package/dist/distill/index.js.map +1 -0
  10. package/dist/fitness/index.d.ts +14 -0
  11. package/dist/fitness/index.js +39 -0
  12. package/dist/fitness/index.js.map +1 -0
  13. package/dist/index.d.ts +10 -0
  14. package/dist/index.js +17 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/retrieval/index.d.ts +11 -0
  17. package/dist/retrieval/index.js +51 -0
  18. package/dist/retrieval/index.js.map +1 -0
  19. package/dist/seeds/index.d.ts +5 -0
  20. package/dist/seeds/index.js +131 -0
  21. package/dist/seeds/index.js.map +1 -0
  22. package/dist/store/index.d.ts +43 -0
  23. package/dist/store/index.js +230 -0
  24. package/dist/store/index.js.map +1 -0
  25. package/dist/types/index.d.ts +53 -0
  26. package/dist/types/index.js +2 -0
  27. package/dist/types/index.js.map +1 -0
  28. package/dist/validation/index.d.ts +119 -0
  29. package/dist/validation/index.js +55 -0
  30. package/dist/validation/index.js.map +1 -0
  31. package/package.json +23 -0
  32. package/src/__tests__/api.test.ts +103 -0
  33. package/src/__tests__/capture.test.ts +113 -0
  34. package/src/__tests__/distill.test.ts +83 -0
  35. package/src/__tests__/fitness.test.ts +58 -0
  36. package/src/__tests__/retrieval.test.ts +83 -0
  37. package/src/__tests__/store.test.ts +262 -0
  38. package/src/__tests__/types.test.ts +92 -0
  39. package/src/api/index.ts +94 -0
  40. package/src/capture/index.ts +71 -0
  41. package/src/distill/index.ts +64 -0
  42. package/src/fitness/index.ts +56 -0
  43. package/src/index.ts +51 -0
  44. package/src/retrieval/index.ts +61 -0
  45. package/src/seeds/index.ts +146 -0
  46. package/src/store/index.ts +250 -0
  47. package/src/types/index.ts +76 -0
  48. package/src/validation/index.ts +63 -0
  49. package/tsconfig.json +20 -0
@@ -0,0 +1,250 @@
1
+ import Database from 'better-sqlite3';
2
+ import type { KnowledgeAtom, ToolAtom, NegativeAtom, CreateAtom, CreateToolAtom, CreateNegativeAtom, AtomType } from '../types/index.js';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { validateCreateAtom, validateCreateToolAtom, validateCreateNegativeAtom } from '../validation/index.js';
5
+
6
+ const SCHEMA_SQL = `
7
+ CREATE TABLE IF NOT EXISTS atoms (
8
+ id TEXT PRIMARY KEY,
9
+ type TEXT NOT NULL,
10
+ observation TEXT NOT NULL,
11
+ context TEXT NOT NULL,
12
+ confidence REAL NOT NULL,
13
+ fitness_score REAL NOT NULL,
14
+ trust_tier TEXT NOT NULL DEFAULT 'quarantine',
15
+ source_agent_hash TEXT NOT NULL,
16
+ created_at TEXT NOT NULL,
17
+ updated_at TEXT NOT NULL,
18
+ decay_rate REAL NOT NULL DEFAULT 0.99,
19
+
20
+ -- ToolAtom fields (nullable)
21
+ tool_name TEXT,
22
+ params_hash TEXT,
23
+ outcome TEXT,
24
+ error_signature TEXT,
25
+ latency_ms REAL,
26
+ reliability_score REAL,
27
+
28
+ -- NegativeAtom fields (nullable)
29
+ anti_pattern TEXT,
30
+ failure_cluster_size INTEGER,
31
+ error_type TEXT,
32
+ severity TEXT,
33
+
34
+ -- Dedup & fitness fields
35
+ evidence_count INTEGER NOT NULL DEFAULT 1,
36
+ use_count INTEGER NOT NULL DEFAULT 0,
37
+ success_after_use INTEGER NOT NULL DEFAULT 0,
38
+ failure_after_use INTEGER NOT NULL DEFAULT 0,
39
+ last_used TEXT
40
+ );
41
+
42
+ CREATE INDEX IF NOT EXISTS idx_atoms_type ON atoms(type);
43
+ CREATE INDEX IF NOT EXISTS idx_atoms_tool_name ON atoms(tool_name);
44
+ CREATE INDEX IF NOT EXISTS idx_atoms_confidence ON atoms(confidence);
45
+ CREATE INDEX IF NOT EXISTS idx_atoms_fitness ON atoms(fitness_score);
46
+ CREATE INDEX IF NOT EXISTS idx_atoms_updated_at ON atoms(updated_at);
47
+ `;
48
+
49
+ // Migration: add columns if they don't exist (for existing DBs)
50
+ const MIGRATIONS = [
51
+ `ALTER TABLE atoms ADD COLUMN evidence_count INTEGER NOT NULL DEFAULT 1`,
52
+ `ALTER TABLE atoms ADD COLUMN use_count INTEGER NOT NULL DEFAULT 0`,
53
+ `ALTER TABLE atoms ADD COLUMN success_after_use INTEGER NOT NULL DEFAULT 0`,
54
+ `ALTER TABLE atoms ADD COLUMN failure_after_use INTEGER NOT NULL DEFAULT 0`,
55
+ `ALTER TABLE atoms ADD COLUMN last_used TEXT`,
56
+ ];
57
+
58
+ /**
59
+ * Jaccard similarity between two strings (based on word bigrams).
60
+ */
61
+ export function jaccardSimilarity(a: string, b: string): number {
62
+ const bigrams = (s: string): Set<string> => {
63
+ const words = s.toLowerCase().split(/\s+/).filter(Boolean);
64
+ const set = new Set<string>();
65
+ for (let i = 0; i < words.length - 1; i++) {
66
+ set.add(words[i] + ' ' + words[i + 1]);
67
+ }
68
+ // Also add unigrams for short texts
69
+ for (const w of words) set.add(w);
70
+ return set;
71
+ };
72
+ const setA = bigrams(a);
73
+ const setB = bigrams(b);
74
+ if (setA.size === 0 && setB.size === 0) return 1;
75
+ let intersection = 0;
76
+ for (const x of setA) if (setB.has(x)) intersection++;
77
+ const union = setA.size + setB.size - intersection;
78
+ return union === 0 ? 1 : intersection / union;
79
+ }
80
+
81
+ export class AtomStore {
82
+ private db: Database.Database;
83
+
84
+ constructor(dbPath: string = ':memory:') {
85
+ this.db = new Database(dbPath);
86
+ this.db.pragma('journal_mode = WAL');
87
+ this.migrate();
88
+ }
89
+
90
+ /** Run schema migrations */
91
+ migrate(): void {
92
+ this.db.exec(SCHEMA_SQL);
93
+ // Apply column migrations for existing DBs (ignore if already exists)
94
+ for (const sql of MIGRATIONS) {
95
+ try { this.db.exec(sql); } catch { /* column already exists */ }
96
+ }
97
+ }
98
+
99
+ /** Find atoms with similar observation text */
100
+ findSimilar(observation: string, threshold: number = 0.7): KnowledgeAtom[] {
101
+ const all = this.getAll();
102
+ return all.filter(a => jaccardSimilarity(a.observation, observation) >= threshold);
103
+ }
104
+
105
+ /** Insert a base/pattern/skill/context atom (with validation and dedup) */
106
+ createAtom(data: CreateAtom): KnowledgeAtom {
107
+ validateCreateAtom(data);
108
+ // Dedup check
109
+ const similar = this.findSimilar(data.observation, 0.9);
110
+ if (similar.length > 0) {
111
+ const best = similar[0]!;
112
+ this._mergeAtom(best.id, data.fitness_score, data.confidence);
113
+ return this.getById(best.id)!;
114
+ }
115
+
116
+ const now = new Date().toISOString();
117
+ const atom: KnowledgeAtom = { id: randomUUID(), ...data, created_at: now, updated_at: now };
118
+ this.db.prepare(`
119
+ INSERT INTO atoms (id, type, observation, context, confidence, fitness_score,
120
+ trust_tier, source_agent_hash, created_at, updated_at, decay_rate, evidence_count)
121
+ VALUES (@id, @type, @observation, @context, @confidence, @fitness_score,
122
+ @trust_tier, @source_agent_hash, @created_at, @updated_at, @decay_rate, 1)
123
+ `).run(atom);
124
+ return atom;
125
+ }
126
+
127
+ /** Insert a ToolAtom (with validation and dedup) */
128
+ createToolAtom(data: CreateToolAtom): ToolAtom {
129
+ validateCreateToolAtom(data);
130
+ const similar = this.findSimilar(data.observation, 0.9);
131
+ if (similar.length > 0) {
132
+ const best = similar[0]!;
133
+ this._mergeAtom(best.id, data.fitness_score, data.confidence);
134
+ return this.getById(best.id) as ToolAtom;
135
+ }
136
+
137
+ const now = new Date().toISOString();
138
+ const atom: ToolAtom = { id: randomUUID(), ...data, created_at: now, updated_at: now };
139
+ this.db.prepare(`
140
+ INSERT INTO atoms (id, type, observation, context, confidence, fitness_score,
141
+ trust_tier, source_agent_hash, created_at, updated_at, decay_rate,
142
+ tool_name, params_hash, outcome, error_signature, latency_ms, reliability_score, evidence_count)
143
+ VALUES (@id, @type, @observation, @context, @confidence, @fitness_score,
144
+ @trust_tier, @source_agent_hash, @created_at, @updated_at, @decay_rate,
145
+ @tool_name, @params_hash, @outcome, @error_signature, @latency_ms, @reliability_score, 1)
146
+ `).run(atom);
147
+ return atom;
148
+ }
149
+
150
+ /** Insert a NegativeAtom (with validation and dedup) */
151
+ createNegativeAtom(data: CreateNegativeAtom): NegativeAtom {
152
+ validateCreateNegativeAtom(data);
153
+ const similar = this.findSimilar(data.observation, 0.9);
154
+ if (similar.length > 0) {
155
+ const best = similar[0]!;
156
+ this._mergeAtom(best.id, data.fitness_score, data.confidence);
157
+ return this.getById(best.id) as NegativeAtom;
158
+ }
159
+
160
+ const now = new Date().toISOString();
161
+ const atom: NegativeAtom = { id: randomUUID(), ...data, created_at: now, updated_at: now };
162
+ this.db.prepare(`
163
+ INSERT INTO atoms (id, type, observation, context, confidence, fitness_score,
164
+ trust_tier, source_agent_hash, created_at, updated_at, decay_rate,
165
+ anti_pattern, failure_cluster_size, error_type, severity, evidence_count)
166
+ VALUES (@id, @type, @observation, @context, @confidence, @fitness_score,
167
+ @trust_tier, @source_agent_hash, @created_at, @updated_at, @decay_rate,
168
+ @anti_pattern, @failure_cluster_size, @error_type, @severity, 1)
169
+ `).run(atom);
170
+ return atom;
171
+ }
172
+
173
+ /** Merge: keep higher fitness, increment evidence_count */
174
+ private _mergeAtom(existingId: string, newFitness: number, newConfidence: number): void {
175
+ this.db.prepare(`
176
+ UPDATE atoms SET
177
+ fitness_score = MAX(fitness_score, ?),
178
+ confidence = MAX(confidence, ?),
179
+ evidence_count = evidence_count + 1,
180
+ updated_at = ?
181
+ WHERE id = ?
182
+ `).run(newFitness, newConfidence, new Date().toISOString(), existingId);
183
+ }
184
+
185
+ /** Get atom by ID */
186
+ getById(id: string): KnowledgeAtom | null {
187
+ const row = this.db.prepare('SELECT * FROM atoms WHERE id = ?').get(id) as KnowledgeAtom | undefined;
188
+ return row ?? null;
189
+ }
190
+
191
+ /** Query atoms by type */
192
+ queryByType(type: AtomType): KnowledgeAtom[] {
193
+ return this.db.prepare('SELECT * FROM atoms WHERE type = ?').all(type) as KnowledgeAtom[];
194
+ }
195
+
196
+ /** Query tool atoms by tool_name */
197
+ queryByToolName(toolName: string): ToolAtom[] {
198
+ return this.db.prepare('SELECT * FROM atoms WHERE type = ? AND tool_name = ?').all('tool', toolName) as ToolAtom[];
199
+ }
200
+
201
+ /** Query atoms with confidence >= threshold */
202
+ queryByConfidence(threshold: number): KnowledgeAtom[] {
203
+ return this.db.prepare('SELECT * FROM atoms WHERE confidence >= ? ORDER BY confidence DESC').all(threshold) as KnowledgeAtom[];
204
+ }
205
+
206
+ /** Full-text search on observation */
207
+ search(query: string): KnowledgeAtom[] {
208
+ return this.db.prepare('SELECT * FROM atoms WHERE observation LIKE ? ORDER BY fitness_score DESC')
209
+ .all(`%${query}%`) as KnowledgeAtom[];
210
+ }
211
+
212
+ /** Update fitness score for a specific atom */
213
+ updateFitnessScore(id: string, newScore: number): void {
214
+ this.db.prepare('UPDATE atoms SET fitness_score = ?, updated_at = ? WHERE id = ?')
215
+ .run(newScore, new Date().toISOString(), id);
216
+ }
217
+
218
+ /** Record a usage event */
219
+ recordUsage(id: string, success: boolean): void {
220
+ const col = success ? 'success_after_use' : 'failure_after_use';
221
+ this.db.prepare(`
222
+ UPDATE atoms SET use_count = use_count + 1, ${col} = ${col} + 1,
223
+ last_used = ?, updated_at = ? WHERE id = ?
224
+ `).run(new Date().toISOString(), new Date().toISOString(), id);
225
+ }
226
+
227
+ /** Apply decay: multiply fitness_score by decay_rate for all atoms */
228
+ applyDecay(): number {
229
+ const result = this.db.prepare(
230
+ 'UPDATE atoms SET fitness_score = fitness_score * decay_rate, updated_at = ? WHERE fitness_score > 0'
231
+ ).run(new Date().toISOString());
232
+ return result.changes;
233
+ }
234
+
235
+ /** Delete atom by ID */
236
+ deleteAtom(id: string): boolean {
237
+ const result = this.db.prepare('DELETE FROM atoms WHERE id = ?').run(id);
238
+ return result.changes > 0;
239
+ }
240
+
241
+ /** Get all atoms */
242
+ getAll(): KnowledgeAtom[] {
243
+ return this.db.prepare('SELECT * FROM atoms').all() as KnowledgeAtom[];
244
+ }
245
+
246
+ /** Close the database */
247
+ close(): void {
248
+ this.db.close();
249
+ }
250
+ }
@@ -0,0 +1,76 @@
1
+ // ── Trust Tiers ──────────────────────────────────────────────
2
+ export type TrustTier = 'quarantine' | 'local' | 'verified' | 'canonical';
3
+
4
+ // ── Atom Types ───────────────────────────────────────────────
5
+ export type AtomType = 'tool' | 'negative' | 'pattern' | 'skill' | 'context';
6
+
7
+ // ── Outcome ──────────────────────────────────────────────────
8
+ export type Outcome = 'success' | 'failure' | 'partial';
9
+
10
+ // ── Severity ─────────────────────────────────────────────────
11
+ export type Severity = 'low' | 'medium' | 'high' | 'critical';
12
+
13
+ // ── Base KnowledgeAtom ───────────────────────────────────────
14
+ export interface KnowledgeAtom {
15
+ id: string;
16
+ type: AtomType;
17
+ observation: string;
18
+ context: string;
19
+ confidence: number; // 0–1
20
+ fitness_score: number; // 0–1
21
+ trust_tier: TrustTier;
22
+ source_agent_hash: string;
23
+ created_at: string; // ISO-8601
24
+ updated_at: string; // ISO-8601
25
+ decay_rate: number; // 0–1, multiplied per epoch
26
+ }
27
+
28
+ // ── ToolAtom ─────────────────────────────────────────────────
29
+ export interface ToolAtom extends KnowledgeAtom {
30
+ type: 'tool';
31
+ tool_name: string;
32
+ params_hash: string;
33
+ outcome: Outcome;
34
+ error_signature: string | null;
35
+ latency_ms: number | null;
36
+ reliability_score: number; // 0–1
37
+ }
38
+
39
+ // ── NegativeAtom ─────────────────────────────────────────────
40
+ export interface NegativeAtom extends KnowledgeAtom {
41
+ type: 'negative';
42
+ anti_pattern: string;
43
+ failure_cluster_size: number;
44
+ error_type: string;
45
+ severity: Severity;
46
+ }
47
+
48
+ // ── Stub types for future phases ─────────────────────────────
49
+ export interface PatternAtom extends KnowledgeAtom {
50
+ type: 'pattern';
51
+ }
52
+
53
+ export interface SkillAtom extends KnowledgeAtom {
54
+ type: 'skill';
55
+ }
56
+
57
+ export interface ContextAtom extends KnowledgeAtom {
58
+ type: 'context';
59
+ }
60
+
61
+ // ── Union type ───────────────────────────────────────────────
62
+ export type AnyAtom = ToolAtom | NegativeAtom | PatternAtom | SkillAtom | ContextAtom;
63
+
64
+ // ── Outcome signals for captureOutcome ───────────────────────
65
+ export interface OutcomeSignals {
66
+ completed_without_error: boolean;
67
+ revisited_within_1hr: boolean;
68
+ human_accepted: boolean | null;
69
+ convergence_steps: number;
70
+ error_free: boolean;
71
+ }
72
+
73
+ // ── Create helpers (omit auto-generated fields) ──────────────
74
+ export type CreateToolAtom = Omit<ToolAtom, 'id' | 'created_at' | 'updated_at'>;
75
+ export type CreateNegativeAtom = Omit<NegativeAtom, 'id' | 'created_at' | 'updated_at'>;
76
+ export type CreateAtom = Omit<KnowledgeAtom, 'id' | 'created_at' | 'updated_at'>;
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod';
2
+
3
+ // ── Shared enums ─────────────────────────────────────────────
4
+ const TrustTierSchema = z.enum(['quarantine', 'local', 'verified', 'canonical']);
5
+ const AtomTypeSchema = z.enum(['tool', 'negative', 'pattern', 'skill', 'context']);
6
+ const OutcomeSchema = z.enum(['success', 'failure', 'partial']);
7
+ const SeveritySchema = z.enum(['low', 'medium', 'high', 'critical']);
8
+
9
+ const unit = z.number().min(0).max(1);
10
+
11
+ // ── Base CreateAtom ──────────────────────────────────────────
12
+ export const CreateAtomSchema = z.object({
13
+ type: AtomTypeSchema,
14
+ observation: z.string().min(1),
15
+ context: z.string(),
16
+ confidence: unit,
17
+ fitness_score: unit,
18
+ trust_tier: TrustTierSchema,
19
+ source_agent_hash: z.string().min(1),
20
+ decay_rate: unit,
21
+ });
22
+
23
+ // ── CreateToolAtom ───────────────────────────────────────────
24
+ export const CreateToolAtomSchema = CreateAtomSchema.extend({
25
+ type: z.literal('tool'),
26
+ tool_name: z.string().min(1),
27
+ params_hash: z.string(),
28
+ outcome: OutcomeSchema,
29
+ error_signature: z.string().nullable(),
30
+ latency_ms: z.number().nullable(),
31
+ reliability_score: unit,
32
+ });
33
+
34
+ // ── CreateNegativeAtom ───────────────────────────────────────
35
+ export const CreateNegativeAtomSchema = CreateAtomSchema.extend({
36
+ type: z.literal('negative'),
37
+ anti_pattern: z.string().min(1),
38
+ failure_cluster_size: z.number().int().min(0),
39
+ error_type: z.string().min(1),
40
+ severity: SeveritySchema,
41
+ });
42
+
43
+ // ── OutcomeSignals ───────────────────────────────────────────
44
+ export const OutcomeSignalsSchema = z.object({
45
+ completed_without_error: z.boolean(),
46
+ revisited_within_1hr: z.boolean(),
47
+ human_accepted: z.boolean().nullable(),
48
+ convergence_steps: z.number().int().min(0),
49
+ error_free: z.boolean(),
50
+ });
51
+
52
+ /** Validate and return typed data, or throw ZodError */
53
+ export function validateCreateAtom(data: unknown) {
54
+ return CreateAtomSchema.parse(data);
55
+ }
56
+
57
+ export function validateCreateToolAtom(data: unknown) {
58
+ return CreateToolAtomSchema.parse(data);
59
+ }
60
+
61
+ export function validateCreateNegativeAtom(data: unknown) {
62
+ return CreateNegativeAtomSchema.parse(data);
63
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2022"],
7
+ "outDir": "dist",
8
+ "rootDir": "src",
9
+ "declaration": true,
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "forceConsistentCasingInFileNames": true,
14
+ "resolveJsonModule": true,
15
+ "sourceMap": true,
16
+ "composite": true
17
+ },
18
+ "include": ["src"],
19
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
20
+ }