opencode-swarm-plugin 0.44.0 → 0.44.1
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/bin/swarm.serve.test.ts +6 -4
- package/bin/swarm.ts +16 -10
- package/dist/compaction-prompt-scoring.js +139 -0
- package/dist/eval-capture.js +12811 -0
- package/dist/hive.d.ts.map +1 -1
- package/dist/index.js +7644 -62599
- package/dist/plugin.js +23766 -78721
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm-review.d.ts.map +1 -1
- package/package.json +17 -5
- package/.changeset/swarm-insights-data-layer.md +0 -63
- package/.hive/analysis/eval-failure-analysis-2025-12-25.md +0 -331
- package/.hive/analysis/session-data-quality-audit.md +0 -320
- package/.hive/eval-results.json +0 -483
- package/.hive/issues.jsonl +0 -138
- package/.hive/memories.jsonl +0 -729
- package/.opencode/eval-history.jsonl +0 -327
- package/.turbo/turbo-build.log +0 -9
- package/CHANGELOG.md +0 -2286
- package/SCORER-ANALYSIS.md +0 -598
- package/docs/analysis/subagent-coordination-patterns.md +0 -902
- package/docs/analysis-socratic-planner-pattern.md +0 -504
- package/docs/planning/ADR-001-monorepo-structure.md +0 -171
- package/docs/planning/ADR-002-package-extraction.md +0 -393
- package/docs/planning/ADR-003-performance-improvements.md +0 -451
- package/docs/planning/ADR-004-message-queue-features.md +0 -187
- package/docs/planning/ADR-005-devtools-observability.md +0 -202
- package/docs/planning/ADR-007-swarm-enhancements-worktree-review.md +0 -168
- package/docs/planning/ADR-008-worker-handoff-protocol.md +0 -293
- package/docs/planning/ADR-009-oh-my-opencode-patterns.md +0 -353
- package/docs/planning/ADR-010-cass-inhousing.md +0 -1215
- package/docs/planning/ROADMAP.md +0 -368
- package/docs/semantic-memory-cli-syntax.md +0 -123
- package/docs/swarm-mail-architecture.md +0 -1147
- package/docs/testing/context-recovery-test.md +0 -470
- package/evals/ARCHITECTURE.md +0 -1189
- package/evals/README.md +0 -768
- package/evals/compaction-prompt.eval.ts +0 -149
- package/evals/compaction-resumption.eval.ts +0 -289
- package/evals/coordinator-behavior.eval.ts +0 -307
- package/evals/coordinator-session.eval.ts +0 -154
- package/evals/evalite.config.ts.bak +0 -15
- package/evals/example.eval.ts +0 -31
- package/evals/fixtures/cass-baseline.ts +0 -217
- package/evals/fixtures/compaction-cases.ts +0 -350
- package/evals/fixtures/compaction-prompt-cases.ts +0 -311
- package/evals/fixtures/coordinator-sessions.ts +0 -328
- package/evals/fixtures/decomposition-cases.ts +0 -105
- package/evals/lib/compaction-loader.test.ts +0 -248
- package/evals/lib/compaction-loader.ts +0 -320
- package/evals/lib/data-loader.evalite-test.ts +0 -289
- package/evals/lib/data-loader.test.ts +0 -345
- package/evals/lib/data-loader.ts +0 -281
- package/evals/lib/llm.ts +0 -115
- package/evals/scorers/compaction-prompt-scorers.ts +0 -145
- package/evals/scorers/compaction-scorers.ts +0 -305
- package/evals/scorers/coordinator-discipline.evalite-test.ts +0 -539
- package/evals/scorers/coordinator-discipline.ts +0 -325
- package/evals/scorers/index.test.ts +0 -146
- package/evals/scorers/index.ts +0 -328
- package/evals/scorers/outcome-scorers.evalite-test.ts +0 -27
- package/evals/scorers/outcome-scorers.ts +0 -349
- package/evals/swarm-decomposition.eval.ts +0 -121
- package/examples/commands/swarm.md +0 -745
- package/examples/plugin-wrapper-template.ts +0 -2515
- package/examples/skills/hive-workflow/SKILL.md +0 -212
- package/examples/skills/skill-creator/SKILL.md +0 -223
- package/examples/skills/swarm-coordination/SKILL.md +0 -292
- package/global-skills/cli-builder/SKILL.md +0 -344
- package/global-skills/cli-builder/references/advanced-patterns.md +0 -244
- package/global-skills/learning-systems/SKILL.md +0 -644
- package/global-skills/skill-creator/LICENSE.txt +0 -202
- package/global-skills/skill-creator/SKILL.md +0 -352
- package/global-skills/skill-creator/references/output-patterns.md +0 -82
- package/global-skills/skill-creator/references/workflows.md +0 -28
- package/global-skills/swarm-coordination/SKILL.md +0 -995
- package/global-skills/swarm-coordination/references/coordinator-patterns.md +0 -235
- package/global-skills/swarm-coordination/references/strategies.md +0 -138
- package/global-skills/system-design/SKILL.md +0 -213
- package/global-skills/testing-patterns/SKILL.md +0 -430
- package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +0 -586
- package/opencode-swarm-plugin-0.30.7.tgz +0 -0
- package/opencode-swarm-plugin-0.31.0.tgz +0 -0
- package/scripts/cleanup-test-memories.ts +0 -346
- package/scripts/init-skill.ts +0 -222
- package/scripts/migrate-unknown-sessions.ts +0 -349
- package/scripts/validate-skill.ts +0 -204
- package/src/agent-mail.ts +0 -1724
- package/src/anti-patterns.test.ts +0 -1167
- package/src/anti-patterns.ts +0 -448
- package/src/compaction-capture.integration.test.ts +0 -257
- package/src/compaction-hook.test.ts +0 -838
- package/src/compaction-hook.ts +0 -1204
- package/src/compaction-observability.integration.test.ts +0 -139
- package/src/compaction-observability.test.ts +0 -187
- package/src/compaction-observability.ts +0 -324
- package/src/compaction-prompt-scorers.test.ts +0 -475
- package/src/compaction-prompt-scoring.ts +0 -300
- package/src/contributor-tools.test.ts +0 -133
- package/src/contributor-tools.ts +0 -201
- package/src/dashboard.test.ts +0 -611
- package/src/dashboard.ts +0 -462
- package/src/error-enrichment.test.ts +0 -403
- package/src/error-enrichment.ts +0 -219
- package/src/eval-capture.test.ts +0 -1015
- package/src/eval-capture.ts +0 -929
- package/src/eval-gates.test.ts +0 -306
- package/src/eval-gates.ts +0 -218
- package/src/eval-history.test.ts +0 -508
- package/src/eval-history.ts +0 -214
- package/src/eval-learning.test.ts +0 -378
- package/src/eval-learning.ts +0 -360
- package/src/eval-runner.test.ts +0 -223
- package/src/eval-runner.ts +0 -402
- package/src/export-tools.test.ts +0 -476
- package/src/export-tools.ts +0 -257
- package/src/hive.integration.test.ts +0 -2241
- package/src/hive.ts +0 -1628
- package/src/index.ts +0 -940
- package/src/learning.integration.test.ts +0 -1815
- package/src/learning.ts +0 -1079
- package/src/logger.test.ts +0 -189
- package/src/logger.ts +0 -135
- package/src/mandate-promotion.test.ts +0 -473
- package/src/mandate-promotion.ts +0 -239
- package/src/mandate-storage.integration.test.ts +0 -601
- package/src/mandate-storage.test.ts +0 -578
- package/src/mandate-storage.ts +0 -794
- package/src/mandates.ts +0 -540
- package/src/memory-tools.test.ts +0 -195
- package/src/memory-tools.ts +0 -344
- package/src/memory.integration.test.ts +0 -334
- package/src/memory.test.ts +0 -158
- package/src/memory.ts +0 -527
- package/src/model-selection.test.ts +0 -188
- package/src/model-selection.ts +0 -68
- package/src/observability-tools.test.ts +0 -359
- package/src/observability-tools.ts +0 -871
- package/src/output-guardrails.test.ts +0 -438
- package/src/output-guardrails.ts +0 -381
- package/src/pattern-maturity.test.ts +0 -1160
- package/src/pattern-maturity.ts +0 -525
- package/src/planning-guardrails.test.ts +0 -491
- package/src/planning-guardrails.ts +0 -438
- package/src/plugin.ts +0 -23
- package/src/post-compaction-tracker.test.ts +0 -251
- package/src/post-compaction-tracker.ts +0 -237
- package/src/query-tools.test.ts +0 -636
- package/src/query-tools.ts +0 -324
- package/src/rate-limiter.integration.test.ts +0 -466
- package/src/rate-limiter.ts +0 -774
- package/src/replay-tools.test.ts +0 -496
- package/src/replay-tools.ts +0 -240
- package/src/repo-crawl.integration.test.ts +0 -441
- package/src/repo-crawl.ts +0 -610
- package/src/schemas/cell-events.test.ts +0 -347
- package/src/schemas/cell-events.ts +0 -807
- package/src/schemas/cell.ts +0 -257
- package/src/schemas/evaluation.ts +0 -166
- package/src/schemas/index.test.ts +0 -199
- package/src/schemas/index.ts +0 -286
- package/src/schemas/mandate.ts +0 -232
- package/src/schemas/swarm-context.ts +0 -115
- package/src/schemas/task.ts +0 -161
- package/src/schemas/worker-handoff.test.ts +0 -302
- package/src/schemas/worker-handoff.ts +0 -131
- package/src/sessions/agent-discovery.test.ts +0 -137
- package/src/sessions/agent-discovery.ts +0 -112
- package/src/sessions/index.ts +0 -15
- package/src/skills.integration.test.ts +0 -1192
- package/src/skills.test.ts +0 -643
- package/src/skills.ts +0 -1549
- package/src/storage.integration.test.ts +0 -341
- package/src/storage.ts +0 -884
- package/src/structured.integration.test.ts +0 -817
- package/src/structured.test.ts +0 -1046
- package/src/structured.ts +0 -762
- package/src/swarm-decompose.test.ts +0 -188
- package/src/swarm-decompose.ts +0 -1302
- package/src/swarm-deferred.integration.test.ts +0 -157
- package/src/swarm-deferred.test.ts +0 -38
- package/src/swarm-insights.test.ts +0 -214
- package/src/swarm-insights.ts +0 -459
- package/src/swarm-mail.integration.test.ts +0 -970
- package/src/swarm-mail.ts +0 -739
- package/src/swarm-orchestrate.integration.test.ts +0 -282
- package/src/swarm-orchestrate.test.ts +0 -548
- package/src/swarm-orchestrate.ts +0 -3084
- package/src/swarm-prompts.test.ts +0 -1270
- package/src/swarm-prompts.ts +0 -2077
- package/src/swarm-research.integration.test.ts +0 -701
- package/src/swarm-research.test.ts +0 -698
- package/src/swarm-research.ts +0 -472
- package/src/swarm-review.integration.test.ts +0 -285
- package/src/swarm-review.test.ts +0 -879
- package/src/swarm-review.ts +0 -709
- package/src/swarm-strategies.ts +0 -407
- package/src/swarm-worktree.test.ts +0 -501
- package/src/swarm-worktree.ts +0 -575
- package/src/swarm.integration.test.ts +0 -2377
- package/src/swarm.ts +0 -38
- package/src/tool-adapter.integration.test.ts +0 -1221
- package/src/tool-availability.ts +0 -461
- package/tsconfig.json +0 -28
package/src/storage.ts
DELETED
|
@@ -1,884 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Storage Module - Pluggable persistence for learning data
|
|
3
|
-
*
|
|
4
|
-
* Provides a unified storage interface with multiple backends:
|
|
5
|
-
* - semantic-memory (default) - Persistent with semantic search
|
|
6
|
-
* - in-memory - For testing and ephemeral sessions
|
|
7
|
-
*
|
|
8
|
-
* The semantic-memory backend uses collections:
|
|
9
|
-
* - `swarm-feedback` - Criterion feedback events
|
|
10
|
-
* - `swarm-patterns` - Decomposition patterns and anti-patterns
|
|
11
|
-
* - `swarm-maturity` - Pattern maturity tracking
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```typescript
|
|
15
|
-
* // Use default semantic-memory storage
|
|
16
|
-
* const storage = createStorage();
|
|
17
|
-
*
|
|
18
|
-
* // Or configure explicitly
|
|
19
|
-
* const storage = createStorage({
|
|
20
|
-
* backend: "semantic-memory",
|
|
21
|
-
* collections: {
|
|
22
|
-
* feedback: "my-feedback",
|
|
23
|
-
* patterns: "my-patterns",
|
|
24
|
-
* maturity: "my-maturity",
|
|
25
|
-
* },
|
|
26
|
-
* });
|
|
27
|
-
*
|
|
28
|
-
* // Or use in-memory for testing
|
|
29
|
-
* const storage = createStorage({ backend: "memory" });
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
import type { FeedbackEvent } from "./learning";
|
|
34
|
-
import type { DecompositionPattern } from "./anti-patterns";
|
|
35
|
-
import type { PatternMaturity, MaturityFeedback } from "./pattern-maturity";
|
|
36
|
-
import { InMemoryFeedbackStorage } from "./learning";
|
|
37
|
-
import { InMemoryPatternStorage } from "./anti-patterns";
|
|
38
|
-
import { InMemoryMaturityStorage } from "./pattern-maturity";
|
|
39
|
-
|
|
40
|
-
// ============================================================================
|
|
41
|
-
// Command Resolution
|
|
42
|
-
// ============================================================================
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Cached semantic-memory command (native or bunx fallback)
|
|
46
|
-
*/
|
|
47
|
-
let cachedCommand: string[] | null = null;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Resolve the semantic-memory command
|
|
51
|
-
*
|
|
52
|
-
* Checks for native install first, falls back to bunx.
|
|
53
|
-
* Result is cached for the session.
|
|
54
|
-
*/
|
|
55
|
-
async function resolveSemanticMemoryCommand(): Promise<string[]> {
|
|
56
|
-
if (cachedCommand) return cachedCommand;
|
|
57
|
-
|
|
58
|
-
// Try native install first
|
|
59
|
-
const nativeResult = await Bun.$`which semantic-memory`.quiet().nothrow();
|
|
60
|
-
if (nativeResult.exitCode === 0) {
|
|
61
|
-
cachedCommand = ["semantic-memory"];
|
|
62
|
-
return cachedCommand;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Fall back to bunx
|
|
66
|
-
cachedCommand = ["bunx", "semantic-memory"];
|
|
67
|
-
return cachedCommand;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Execute semantic-memory command with args
|
|
72
|
-
*/
|
|
73
|
-
async function execSemanticMemory(
|
|
74
|
-
args: string[],
|
|
75
|
-
): Promise<{ exitCode: number; stdout: Buffer; stderr: Buffer }> {
|
|
76
|
-
try {
|
|
77
|
-
const cmd = await resolveSemanticMemoryCommand();
|
|
78
|
-
const fullCmd = [...cmd, ...args];
|
|
79
|
-
|
|
80
|
-
// Use Bun.spawn for dynamic command arrays
|
|
81
|
-
const proc = Bun.spawn(fullCmd, {
|
|
82
|
-
stdout: "pipe",
|
|
83
|
-
stderr: "pipe",
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
try {
|
|
87
|
-
const stdout = Buffer.from(await new Response(proc.stdout).arrayBuffer());
|
|
88
|
-
const stderr = Buffer.from(await new Response(proc.stderr).arrayBuffer());
|
|
89
|
-
const exitCode = await proc.exited;
|
|
90
|
-
|
|
91
|
-
return { exitCode, stdout, stderr };
|
|
92
|
-
} finally {
|
|
93
|
-
// Ensure process cleanup
|
|
94
|
-
proc.kill();
|
|
95
|
-
}
|
|
96
|
-
} catch (error) {
|
|
97
|
-
// Return structured error result on exceptions
|
|
98
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
99
|
-
return {
|
|
100
|
-
exitCode: 1,
|
|
101
|
-
stdout: Buffer.from(""),
|
|
102
|
-
stderr: Buffer.from(`Error executing semantic-memory: ${errorMessage}`),
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Reset the cached command (for testing)
|
|
109
|
-
*/
|
|
110
|
-
export function resetCommandCache(): void {
|
|
111
|
-
cachedCommand = null;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// ============================================================================
|
|
115
|
-
// Configuration
|
|
116
|
-
// ============================================================================
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Storage backend type
|
|
120
|
-
*/
|
|
121
|
-
export type StorageBackend = "semantic-memory" | "memory";
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Collection names for semantic-memory
|
|
125
|
-
*/
|
|
126
|
-
export interface StorageCollections {
|
|
127
|
-
feedback: string;
|
|
128
|
-
patterns: string;
|
|
129
|
-
maturity: string;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Storage configuration
|
|
134
|
-
*/
|
|
135
|
-
export interface StorageConfig {
|
|
136
|
-
/** Backend to use (default: "semantic-memory") */
|
|
137
|
-
backend: StorageBackend;
|
|
138
|
-
/** Collection names for semantic-memory backend */
|
|
139
|
-
collections: StorageCollections;
|
|
140
|
-
/** Whether to use semantic search for queries (default: true) */
|
|
141
|
-
useSemanticSearch: boolean;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Generate unique test collection name
|
|
146
|
-
*
|
|
147
|
-
* Creates a timestamp-based suffix for test collections to ensure complete isolation.
|
|
148
|
-
* Each test run gets its own collections that don't pollute production semantic-memory.
|
|
149
|
-
*
|
|
150
|
-
* @returns Unique suffix like "test-1734567890123"
|
|
151
|
-
*
|
|
152
|
-
* @example
|
|
153
|
-
* ```typescript
|
|
154
|
-
* // In test setup:
|
|
155
|
-
* process.env.TEST_SEMANTIC_MEMORY_COLLECTION = getTestCollectionName();
|
|
156
|
-
* // Results in collections like: swarm-feedback-test-1734567890123
|
|
157
|
-
* ```
|
|
158
|
-
*/
|
|
159
|
-
export function getTestCollectionName(): string {
|
|
160
|
-
return `test-${Date.now()}`;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Get collection names with optional test suffix
|
|
165
|
-
*
|
|
166
|
-
* Supports two test isolation modes:
|
|
167
|
-
* 1. TEST_MEMORY_COLLECTIONS=true - appends "-test" (shared across test run)
|
|
168
|
-
* 2. TEST_SEMANTIC_MEMORY_COLLECTION=<suffix> - appends custom suffix (unique per test run)
|
|
169
|
-
*
|
|
170
|
-
* Mode 2 is preferred for full isolation - prevents test pollution of production
|
|
171
|
-
* semantic-memory collections.
|
|
172
|
-
*
|
|
173
|
-
* @example
|
|
174
|
-
* ```typescript
|
|
175
|
-
* // Production
|
|
176
|
-
* getCollectionNames()
|
|
177
|
-
* // => { feedback: "swarm-feedback", patterns: "swarm-patterns", maturity: "swarm-maturity" }
|
|
178
|
-
*
|
|
179
|
-
* // Test mode 1 (legacy)
|
|
180
|
-
* process.env.TEST_MEMORY_COLLECTIONS = "true"
|
|
181
|
-
* getCollectionNames()
|
|
182
|
-
* // => { feedback: "swarm-feedback-test", patterns: "swarm-patterns-test", ... }
|
|
183
|
-
*
|
|
184
|
-
* // Test mode 2 (preferred - full isolation)
|
|
185
|
-
* process.env.TEST_SEMANTIC_MEMORY_COLLECTION = "test-1734567890123"
|
|
186
|
-
* getCollectionNames()
|
|
187
|
-
* // => { feedback: "swarm-feedback-test-1734567890123", patterns: "swarm-patterns-test-1734567890123", ... }
|
|
188
|
-
* ```
|
|
189
|
-
*/
|
|
190
|
-
function getCollectionNames(): StorageCollections {
|
|
191
|
-
const base = {
|
|
192
|
-
feedback: "swarm-feedback",
|
|
193
|
-
patterns: "swarm-patterns",
|
|
194
|
-
maturity: "swarm-maturity",
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
// Test isolation mode 2 (preferred): unique suffix per test run
|
|
198
|
-
const testSuffix = process.env.TEST_SEMANTIC_MEMORY_COLLECTION;
|
|
199
|
-
if (testSuffix) {
|
|
200
|
-
return {
|
|
201
|
-
feedback: `${base.feedback}-${testSuffix}`,
|
|
202
|
-
patterns: `${base.patterns}-${testSuffix}`,
|
|
203
|
-
maturity: `${base.maturity}-${testSuffix}`,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Test isolation mode 1 (legacy): shared "-test" suffix
|
|
208
|
-
if (process.env.TEST_MEMORY_COLLECTIONS === "true") {
|
|
209
|
-
return {
|
|
210
|
-
feedback: `${base.feedback}-test`,
|
|
211
|
-
patterns: `${base.patterns}-test`,
|
|
212
|
-
maturity: `${base.maturity}-test`,
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return base;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Get default storage configuration
|
|
221
|
-
*
|
|
222
|
-
* Returns a fresh config object on each call to ensure env vars (like
|
|
223
|
-
* TEST_SEMANTIC_MEMORY_COLLECTION) are read at runtime, not module load time.
|
|
224
|
-
*
|
|
225
|
-
* @returns Default storage configuration
|
|
226
|
-
*/
|
|
227
|
-
export function getDefaultStorageConfig(): StorageConfig {
|
|
228
|
-
return {
|
|
229
|
-
backend: "semantic-memory",
|
|
230
|
-
collections: getCollectionNames(),
|
|
231
|
-
useSemanticSearch: true,
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* @deprecated Use getDefaultStorageConfig() instead. This static export
|
|
237
|
-
* captures collections at module load time, breaking test isolation.
|
|
238
|
-
*/
|
|
239
|
-
export const DEFAULT_STORAGE_CONFIG: StorageConfig = getDefaultStorageConfig();
|
|
240
|
-
|
|
241
|
-
// ============================================================================
|
|
242
|
-
// Unified Storage Interface
|
|
243
|
-
// ============================================================================
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Unified storage interface for all learning data
|
|
247
|
-
*/
|
|
248
|
-
export interface LearningStorage {
|
|
249
|
-
// Feedback operations
|
|
250
|
-
storeFeedback(event: FeedbackEvent): Promise<void>;
|
|
251
|
-
getFeedbackByCriterion(criterion: string): Promise<FeedbackEvent[]>;
|
|
252
|
-
getFeedbackByBead(beadId: string): Promise<FeedbackEvent[]>;
|
|
253
|
-
getAllFeedback(): Promise<FeedbackEvent[]>;
|
|
254
|
-
findSimilarFeedback(query: string, limit?: number): Promise<FeedbackEvent[]>;
|
|
255
|
-
|
|
256
|
-
// Pattern operations
|
|
257
|
-
storePattern(pattern: DecompositionPattern): Promise<void>;
|
|
258
|
-
getPattern(id: string): Promise<DecompositionPattern | null>;
|
|
259
|
-
getAllPatterns(): Promise<DecompositionPattern[]>;
|
|
260
|
-
getAntiPatterns(): Promise<DecompositionPattern[]>;
|
|
261
|
-
getPatternsByTag(tag: string): Promise<DecompositionPattern[]>;
|
|
262
|
-
findSimilarPatterns(
|
|
263
|
-
query: string,
|
|
264
|
-
limit?: number,
|
|
265
|
-
): Promise<DecompositionPattern[]>;
|
|
266
|
-
|
|
267
|
-
// Maturity operations
|
|
268
|
-
storeMaturity(maturity: PatternMaturity): Promise<void>;
|
|
269
|
-
getMaturity(patternId: string): Promise<PatternMaturity | null>;
|
|
270
|
-
getAllMaturity(): Promise<PatternMaturity[]>;
|
|
271
|
-
getMaturityByState(state: string): Promise<PatternMaturity[]>;
|
|
272
|
-
storeMaturityFeedback(feedback: MaturityFeedback): Promise<void>;
|
|
273
|
-
getMaturityFeedback(patternId: string): Promise<MaturityFeedback[]>;
|
|
274
|
-
|
|
275
|
-
// Lifecycle
|
|
276
|
-
close(): Promise<void>;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// ============================================================================
|
|
280
|
-
// Session Stats Tracking
|
|
281
|
-
// ============================================================================
|
|
282
|
-
|
|
283
|
-
interface SessionStats {
|
|
284
|
-
storesCount: number;
|
|
285
|
-
queriesCount: number;
|
|
286
|
-
sessionStart: number;
|
|
287
|
-
lastAlertCheck: number;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
let sessionStats: SessionStats = {
|
|
291
|
-
storesCount: 0,
|
|
292
|
-
queriesCount: 0,
|
|
293
|
-
sessionStart: Date.now(),
|
|
294
|
-
lastAlertCheck: Date.now(),
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Reset session stats (for testing)
|
|
299
|
-
*/
|
|
300
|
-
export function resetSessionStats(): void {
|
|
301
|
-
sessionStats = {
|
|
302
|
-
storesCount: 0,
|
|
303
|
-
queriesCount: 0,
|
|
304
|
-
sessionStart: Date.now(),
|
|
305
|
-
lastAlertCheck: Date.now(),
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Get current session stats
|
|
311
|
-
*/
|
|
312
|
-
export function getSessionStats(): Readonly<SessionStats> {
|
|
313
|
-
return { ...sessionStats };
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// ============================================================================
|
|
317
|
-
// Semantic Memory Storage Implementation
|
|
318
|
-
// ============================================================================
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Semantic-memory backed storage
|
|
322
|
-
*
|
|
323
|
-
* Uses the semantic-memory CLI for persistence with semantic search.
|
|
324
|
-
* Data survives across sessions and can be searched by meaning.
|
|
325
|
-
*/
|
|
326
|
-
export class SemanticMemoryStorage implements LearningStorage {
|
|
327
|
-
private config: StorageConfig;
|
|
328
|
-
|
|
329
|
-
constructor(config: Partial<StorageConfig> = {}) {
|
|
330
|
-
// Use getDefaultStorageConfig() to ensure env vars are read at runtime
|
|
331
|
-
this.config = { ...getDefaultStorageConfig(), ...config };
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// -------------------------------------------------------------------------
|
|
335
|
-
// Helpers
|
|
336
|
-
// -------------------------------------------------------------------------
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Check if low usage alert should be sent
|
|
340
|
-
*
|
|
341
|
-
* Sends alert via agentmail if:
|
|
342
|
-
* - More than 10 minutes have elapsed since session start
|
|
343
|
-
* - Less than 1 store operation has occurred
|
|
344
|
-
* - Alert hasn't been sent in the last 10 minutes
|
|
345
|
-
*/
|
|
346
|
-
private async checkLowUsageAlert(): Promise<void> {
|
|
347
|
-
const TEN_MINUTES = 10 * 60 * 1000;
|
|
348
|
-
const now = Date.now();
|
|
349
|
-
const sessionDuration = now - sessionStats.sessionStart;
|
|
350
|
-
const timeSinceLastAlert = now - sessionStats.lastAlertCheck;
|
|
351
|
-
|
|
352
|
-
if (
|
|
353
|
-
sessionDuration >= TEN_MINUTES &&
|
|
354
|
-
sessionStats.storesCount < 1 &&
|
|
355
|
-
timeSinceLastAlert >= TEN_MINUTES
|
|
356
|
-
) {
|
|
357
|
-
console.warn(
|
|
358
|
-
`[storage] LOW USAGE ALERT: ${sessionStats.storesCount} stores after ${Math.floor(sessionDuration / 60000)} minutes`,
|
|
359
|
-
);
|
|
360
|
-
sessionStats.lastAlertCheck = now;
|
|
361
|
-
|
|
362
|
-
// Send alert via Agent Mail if available
|
|
363
|
-
// Note: This requires agentmail to be initialized, which may not always be the case
|
|
364
|
-
// We'll log the alert and let the coordinator detect it in logs
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
private async store(
|
|
369
|
-
collection: string,
|
|
370
|
-
data: unknown,
|
|
371
|
-
metadata?: Record<string, unknown>,
|
|
372
|
-
): Promise<void> {
|
|
373
|
-
const content = typeof data === "string" ? data : JSON.stringify(data);
|
|
374
|
-
const args = ["store", content, "--collection", collection];
|
|
375
|
-
|
|
376
|
-
if (metadata) {
|
|
377
|
-
args.push("--metadata", JSON.stringify(metadata));
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
sessionStats.storesCount++;
|
|
381
|
-
|
|
382
|
-
const result = await execSemanticMemory(args);
|
|
383
|
-
|
|
384
|
-
if (result.exitCode !== 0) {
|
|
385
|
-
console.warn(
|
|
386
|
-
`[storage] semantic-memory store() failed with exit code ${result.exitCode}: ${result.stderr.toString().trim()}`,
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Alert check: if 10+ minutes elapsed with < 1 store, send alert
|
|
391
|
-
await this.checkLowUsageAlert();
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
private async find<T>(
|
|
395
|
-
collection: string,
|
|
396
|
-
query: string,
|
|
397
|
-
limit: number = 10,
|
|
398
|
-
useFts: boolean = false,
|
|
399
|
-
): Promise<T[]> {
|
|
400
|
-
const args = [
|
|
401
|
-
"find",
|
|
402
|
-
query,
|
|
403
|
-
"--collection",
|
|
404
|
-
collection,
|
|
405
|
-
"--limit",
|
|
406
|
-
String(limit),
|
|
407
|
-
"--json",
|
|
408
|
-
];
|
|
409
|
-
|
|
410
|
-
if (useFts) {
|
|
411
|
-
args.push("--fts");
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
sessionStats.queriesCount++;
|
|
415
|
-
|
|
416
|
-
const result = await execSemanticMemory(args);
|
|
417
|
-
|
|
418
|
-
if (result.exitCode !== 0) {
|
|
419
|
-
console.warn(
|
|
420
|
-
`[storage] semantic-memory find() failed with exit code ${result.exitCode}: ${result.stderr.toString().trim()}`,
|
|
421
|
-
);
|
|
422
|
-
return [];
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
try {
|
|
426
|
-
const output = result.stdout.toString().trim();
|
|
427
|
-
if (!output) return [];
|
|
428
|
-
|
|
429
|
-
const parsed = JSON.parse(output);
|
|
430
|
-
// semantic-memory returns { results: [...] } or just [...]
|
|
431
|
-
const results = Array.isArray(parsed) ? parsed : parsed.results || [];
|
|
432
|
-
|
|
433
|
-
// Extract the stored content from each result
|
|
434
|
-
return results.map((r: { content?: string; information?: string }) => {
|
|
435
|
-
const content = r.content || r.information || "";
|
|
436
|
-
try {
|
|
437
|
-
return JSON.parse(content);
|
|
438
|
-
} catch {
|
|
439
|
-
return content;
|
|
440
|
-
}
|
|
441
|
-
});
|
|
442
|
-
} catch (error) {
|
|
443
|
-
console.warn(
|
|
444
|
-
`[storage] Failed to parse semantic-memory find() output: ${error instanceof Error ? error.message : String(error)}`,
|
|
445
|
-
);
|
|
446
|
-
return [];
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
private async list<T>(collection: string): Promise<T[]> {
|
|
451
|
-
sessionStats.queriesCount++;
|
|
452
|
-
|
|
453
|
-
const result = await execSemanticMemory([
|
|
454
|
-
"list",
|
|
455
|
-
"--collection",
|
|
456
|
-
collection,
|
|
457
|
-
"--json",
|
|
458
|
-
]);
|
|
459
|
-
|
|
460
|
-
if (result.exitCode !== 0) {
|
|
461
|
-
console.warn(
|
|
462
|
-
`[storage] semantic-memory list() failed with exit code ${result.exitCode}: ${result.stderr.toString().trim()}`,
|
|
463
|
-
);
|
|
464
|
-
return [];
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
try {
|
|
468
|
-
const output = result.stdout.toString().trim();
|
|
469
|
-
if (!output) return [];
|
|
470
|
-
|
|
471
|
-
const parsed = JSON.parse(output);
|
|
472
|
-
const items = Array.isArray(parsed) ? parsed : parsed.items || [];
|
|
473
|
-
|
|
474
|
-
return items.map((item: { content?: string; information?: string }) => {
|
|
475
|
-
const content = item.content || item.information || "";
|
|
476
|
-
try {
|
|
477
|
-
return JSON.parse(content);
|
|
478
|
-
} catch {
|
|
479
|
-
return content;
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
} catch (error) {
|
|
483
|
-
console.warn(
|
|
484
|
-
`[storage] Failed to parse semantic-memory list() output: ${error instanceof Error ? error.message : String(error)}`,
|
|
485
|
-
);
|
|
486
|
-
return [];
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
// -------------------------------------------------------------------------
|
|
491
|
-
// Feedback Operations
|
|
492
|
-
// -------------------------------------------------------------------------
|
|
493
|
-
|
|
494
|
-
async storeFeedback(event: FeedbackEvent): Promise<void> {
|
|
495
|
-
await this.store(this.config.collections.feedback, event, {
|
|
496
|
-
criterion: event.criterion,
|
|
497
|
-
type: event.type,
|
|
498
|
-
bead_id: event.bead_id || "",
|
|
499
|
-
timestamp: event.timestamp,
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
async getFeedbackByCriterion(criterion: string): Promise<FeedbackEvent[]> {
|
|
504
|
-
// Use FTS for exact criterion match
|
|
505
|
-
return this.find<FeedbackEvent>(
|
|
506
|
-
this.config.collections.feedback,
|
|
507
|
-
criterion,
|
|
508
|
-
100,
|
|
509
|
-
true, // FTS for exact match
|
|
510
|
-
);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
async getFeedbackByBead(beadId: string): Promise<FeedbackEvent[]> {
|
|
514
|
-
return this.find<FeedbackEvent>(
|
|
515
|
-
this.config.collections.feedback,
|
|
516
|
-
beadId,
|
|
517
|
-
100,
|
|
518
|
-
true,
|
|
519
|
-
);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
async getAllFeedback(): Promise<FeedbackEvent[]> {
|
|
523
|
-
return this.list<FeedbackEvent>(this.config.collections.feedback);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
async findSimilarFeedback(
|
|
527
|
-
query: string,
|
|
528
|
-
limit: number = 10,
|
|
529
|
-
): Promise<FeedbackEvent[]> {
|
|
530
|
-
return this.find<FeedbackEvent>(
|
|
531
|
-
this.config.collections.feedback,
|
|
532
|
-
query,
|
|
533
|
-
limit,
|
|
534
|
-
!this.config.useSemanticSearch,
|
|
535
|
-
);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// -------------------------------------------------------------------------
|
|
539
|
-
// Pattern Operations
|
|
540
|
-
// -------------------------------------------------------------------------
|
|
541
|
-
|
|
542
|
-
async storePattern(pattern: DecompositionPattern): Promise<void> {
|
|
543
|
-
await this.store(this.config.collections.patterns, pattern, {
|
|
544
|
-
id: pattern.id,
|
|
545
|
-
kind: pattern.kind,
|
|
546
|
-
is_negative: pattern.is_negative,
|
|
547
|
-
tags: pattern.tags.join(","),
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
async getPattern(id: string): Promise<DecompositionPattern | null> {
|
|
552
|
-
// List all and filter by ID - FTS search by ID is unreliable
|
|
553
|
-
const all = await this.list<DecompositionPattern>(
|
|
554
|
-
this.config.collections.patterns,
|
|
555
|
-
);
|
|
556
|
-
return all.find((p) => p.id === id) || null;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
async getAllPatterns(): Promise<DecompositionPattern[]> {
|
|
560
|
-
return this.list<DecompositionPattern>(this.config.collections.patterns);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
async getAntiPatterns(): Promise<DecompositionPattern[]> {
|
|
564
|
-
const all = await this.getAllPatterns();
|
|
565
|
-
return all.filter((p) => p.kind === "anti_pattern");
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
async getPatternsByTag(tag: string): Promise<DecompositionPattern[]> {
|
|
569
|
-
const results = await this.find<DecompositionPattern>(
|
|
570
|
-
this.config.collections.patterns,
|
|
571
|
-
tag,
|
|
572
|
-
100,
|
|
573
|
-
true,
|
|
574
|
-
);
|
|
575
|
-
return results.filter((p) => p.tags.includes(tag));
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
async findSimilarPatterns(
|
|
579
|
-
query: string,
|
|
580
|
-
limit: number = 10,
|
|
581
|
-
): Promise<DecompositionPattern[]> {
|
|
582
|
-
return this.find<DecompositionPattern>(
|
|
583
|
-
this.config.collections.patterns,
|
|
584
|
-
query,
|
|
585
|
-
limit,
|
|
586
|
-
!this.config.useSemanticSearch,
|
|
587
|
-
);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// -------------------------------------------------------------------------
|
|
591
|
-
// Maturity Operations
|
|
592
|
-
// -------------------------------------------------------------------------
|
|
593
|
-
|
|
594
|
-
async storeMaturity(maturity: PatternMaturity): Promise<void> {
|
|
595
|
-
await this.store(this.config.collections.maturity, maturity, {
|
|
596
|
-
pattern_id: maturity.pattern_id,
|
|
597
|
-
state: maturity.state,
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
async getMaturity(patternId: string): Promise<PatternMaturity | null> {
|
|
602
|
-
// List all and filter by pattern_id - FTS search by ID is unreliable
|
|
603
|
-
const all = await this.list<PatternMaturity>(
|
|
604
|
-
this.config.collections.maturity,
|
|
605
|
-
);
|
|
606
|
-
return all.find((m) => m.pattern_id === patternId) || null;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
async getAllMaturity(): Promise<PatternMaturity[]> {
|
|
610
|
-
return this.list<PatternMaturity>(this.config.collections.maturity);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
async getMaturityByState(state: string): Promise<PatternMaturity[]> {
|
|
614
|
-
const all = await this.getAllMaturity();
|
|
615
|
-
return all.filter((m) => m.state === state);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
async storeMaturityFeedback(feedback: MaturityFeedback): Promise<void> {
|
|
619
|
-
await this.store(this.config.collections.maturity + "-feedback", feedback, {
|
|
620
|
-
pattern_id: feedback.pattern_id,
|
|
621
|
-
type: feedback.type,
|
|
622
|
-
timestamp: feedback.timestamp,
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
async getMaturityFeedback(patternId: string): Promise<MaturityFeedback[]> {
|
|
627
|
-
// List all and filter by pattern_id - FTS search by ID is unreliable
|
|
628
|
-
const all = await this.list<MaturityFeedback>(
|
|
629
|
-
this.config.collections.maturity + "-feedback",
|
|
630
|
-
);
|
|
631
|
-
return all.filter((f) => f.pattern_id === patternId);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
async close(): Promise<void> {
|
|
635
|
-
// No cleanup needed for CLI-based storage
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// ============================================================================
|
|
640
|
-
// In-Memory Storage Implementation
|
|
641
|
-
// ============================================================================
|
|
642
|
-
|
|
643
|
-
/**
|
|
644
|
-
* In-memory storage adapter
|
|
645
|
-
*
|
|
646
|
-
* Wraps the existing in-memory implementations into the unified interface.
|
|
647
|
-
* Useful for testing and ephemeral sessions.
|
|
648
|
-
*/
|
|
649
|
-
export class InMemoryStorage implements LearningStorage {
|
|
650
|
-
private feedback: InMemoryFeedbackStorage;
|
|
651
|
-
private patterns: InMemoryPatternStorage;
|
|
652
|
-
private maturity: InMemoryMaturityStorage;
|
|
653
|
-
|
|
654
|
-
constructor() {
|
|
655
|
-
this.feedback = new InMemoryFeedbackStorage();
|
|
656
|
-
this.patterns = new InMemoryPatternStorage();
|
|
657
|
-
this.maturity = new InMemoryMaturityStorage();
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
// Feedback
|
|
661
|
-
async storeFeedback(event: FeedbackEvent): Promise<void> {
|
|
662
|
-
return this.feedback.store(event);
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
async getFeedbackByCriterion(criterion: string): Promise<FeedbackEvent[]> {
|
|
666
|
-
return this.feedback.getByCriterion(criterion);
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
async getFeedbackByBead(beadId: string): Promise<FeedbackEvent[]> {
|
|
670
|
-
return this.feedback.getByBead(beadId);
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
async getAllFeedback(): Promise<FeedbackEvent[]> {
|
|
674
|
-
return this.feedback.getAll();
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
async findSimilarFeedback(
|
|
678
|
-
query: string,
|
|
679
|
-
limit: number = 10,
|
|
680
|
-
): Promise<FeedbackEvent[]> {
|
|
681
|
-
// In-memory doesn't support semantic search, filter by query string match
|
|
682
|
-
const all = await this.feedback.getAll();
|
|
683
|
-
const lowerQuery = query.toLowerCase();
|
|
684
|
-
const filtered = all.filter(
|
|
685
|
-
(event) =>
|
|
686
|
-
event.criterion.toLowerCase().includes(lowerQuery) ||
|
|
687
|
-
(event.bead_id && event.bead_id.toLowerCase().includes(lowerQuery)) ||
|
|
688
|
-
(event.context && event.context.toLowerCase().includes(lowerQuery)),
|
|
689
|
-
);
|
|
690
|
-
return filtered.slice(0, limit);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
// Patterns
|
|
694
|
-
async storePattern(pattern: DecompositionPattern): Promise<void> {
|
|
695
|
-
return this.patterns.store(pattern);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
async getPattern(id: string): Promise<DecompositionPattern | null> {
|
|
699
|
-
return this.patterns.get(id);
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
async getAllPatterns(): Promise<DecompositionPattern[]> {
|
|
703
|
-
return this.patterns.getAll();
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
async getAntiPatterns(): Promise<DecompositionPattern[]> {
|
|
707
|
-
return this.patterns.getAntiPatterns();
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
async getPatternsByTag(tag: string): Promise<DecompositionPattern[]> {
|
|
711
|
-
return this.patterns.getByTag(tag);
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
async findSimilarPatterns(
|
|
715
|
-
query: string,
|
|
716
|
-
limit: number = 10,
|
|
717
|
-
): Promise<DecompositionPattern[]> {
|
|
718
|
-
const results = await this.patterns.findByContent(query);
|
|
719
|
-
return results.slice(0, limit);
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
// Maturity
|
|
723
|
-
async storeMaturity(maturity: PatternMaturity): Promise<void> {
|
|
724
|
-
return this.maturity.store(maturity);
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
async getMaturity(patternId: string): Promise<PatternMaturity | null> {
|
|
728
|
-
return this.maturity.get(patternId);
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
async getAllMaturity(): Promise<PatternMaturity[]> {
|
|
732
|
-
return this.maturity.getAll();
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
async getMaturityByState(state: string): Promise<PatternMaturity[]> {
|
|
736
|
-
return this.maturity.getByState(state as any);
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
async storeMaturityFeedback(feedback: MaturityFeedback): Promise<void> {
|
|
740
|
-
return this.maturity.storeFeedback(feedback);
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
async getMaturityFeedback(patternId: string): Promise<MaturityFeedback[]> {
|
|
744
|
-
return this.maturity.getFeedback(patternId);
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
async close(): Promise<void> {
|
|
748
|
-
// No cleanup needed
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
// ============================================================================
|
|
753
|
-
// Factory
|
|
754
|
-
// ============================================================================
|
|
755
|
-
|
|
756
|
-
/**
|
|
757
|
-
* Create a storage instance
|
|
758
|
-
*
|
|
759
|
-
* @param config - Storage configuration (default: semantic-memory)
|
|
760
|
-
* @returns Configured storage instance
|
|
761
|
-
*
|
|
762
|
-
* @example
|
|
763
|
-
* ```typescript
|
|
764
|
-
* // Default semantic-memory storage
|
|
765
|
-
* const storage = createStorage();
|
|
766
|
-
*
|
|
767
|
-
* // In-memory for testing
|
|
768
|
-
* const storage = createStorage({ backend: "memory" });
|
|
769
|
-
*
|
|
770
|
-
* // Custom collections
|
|
771
|
-
* const storage = createStorage({
|
|
772
|
-
* backend: "semantic-memory",
|
|
773
|
-
* collections: {
|
|
774
|
-
* feedback: "my-project-feedback",
|
|
775
|
-
* patterns: "my-project-patterns",
|
|
776
|
-
* maturity: "my-project-maturity",
|
|
777
|
-
* },
|
|
778
|
-
* });
|
|
779
|
-
* ```
|
|
780
|
-
*/
|
|
781
|
-
export function createStorage(
|
|
782
|
-
config: Partial<StorageConfig> = {},
|
|
783
|
-
): LearningStorage {
|
|
784
|
-
// Use getDefaultStorageConfig() to ensure env vars are read at runtime
|
|
785
|
-
const fullConfig = { ...getDefaultStorageConfig(), ...config };
|
|
786
|
-
|
|
787
|
-
switch (fullConfig.backend) {
|
|
788
|
-
case "semantic-memory":
|
|
789
|
-
return new SemanticMemoryStorage(fullConfig);
|
|
790
|
-
case "memory":
|
|
791
|
-
return new InMemoryStorage();
|
|
792
|
-
default:
|
|
793
|
-
throw new Error(`Unknown storage backend: ${fullConfig.backend}`);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
/**
|
|
798
|
-
* Check if semantic-memory is available (native or via bunx)
|
|
799
|
-
*/
|
|
800
|
-
export async function isSemanticMemoryAvailable(): Promise<boolean> {
|
|
801
|
-
try {
|
|
802
|
-
const result = await execSemanticMemory(["stats"]);
|
|
803
|
-
return result.exitCode === 0;
|
|
804
|
-
} catch {
|
|
805
|
-
return false;
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
/**
|
|
810
|
-
* Get the resolved semantic-memory command (for debugging/logging)
|
|
811
|
-
*/
|
|
812
|
-
export async function getResolvedCommand(): Promise<string[]> {
|
|
813
|
-
return resolveSemanticMemoryCommand();
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
/**
|
|
817
|
-
* Create storage with automatic fallback
|
|
818
|
-
*
|
|
819
|
-
* Uses semantic-memory if available, otherwise falls back to in-memory.
|
|
820
|
-
*
|
|
821
|
-
* @param config - Storage configuration
|
|
822
|
-
* @returns Storage instance
|
|
823
|
-
*/
|
|
824
|
-
export async function createStorageWithFallback(
|
|
825
|
-
config: Partial<StorageConfig> = {},
|
|
826
|
-
): Promise<LearningStorage> {
|
|
827
|
-
if (config.backend === "memory") {
|
|
828
|
-
return new InMemoryStorage();
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
const available = await isSemanticMemoryAvailable();
|
|
832
|
-
if (available) {
|
|
833
|
-
return new SemanticMemoryStorage(config);
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
console.warn(
|
|
837
|
-
"semantic-memory not available, falling back to in-memory storage",
|
|
838
|
-
);
|
|
839
|
-
return new InMemoryStorage();
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
// ============================================================================
|
|
843
|
-
// Global Storage Instance
|
|
844
|
-
// ============================================================================
|
|
845
|
-
|
|
846
|
-
let globalStorage: LearningStorage | null = null;
|
|
847
|
-
let globalStoragePromise: Promise<LearningStorage> | null = null;
|
|
848
|
-
|
|
849
|
-
/**
|
|
850
|
-
* Get or create the global storage instance
|
|
851
|
-
*
|
|
852
|
-
* Uses semantic-memory by default, with automatic fallback to in-memory.
|
|
853
|
-
* Prevents race conditions by storing the initialization promise.
|
|
854
|
-
*/
|
|
855
|
-
export async function getStorage(): Promise<LearningStorage> {
|
|
856
|
-
if (!globalStoragePromise) {
|
|
857
|
-
globalStoragePromise = createStorageWithFallback().then((storage) => {
|
|
858
|
-
globalStorage = storage;
|
|
859
|
-
return storage;
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
return globalStoragePromise;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
/**
|
|
866
|
-
* Set the global storage instance
|
|
867
|
-
*
|
|
868
|
-
* Useful for testing or custom configurations.
|
|
869
|
-
*/
|
|
870
|
-
export function setStorage(storage: LearningStorage): void {
|
|
871
|
-
globalStorage = storage;
|
|
872
|
-
globalStoragePromise = Promise.resolve(storage);
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
/**
|
|
876
|
-
* Reset the global storage instance
|
|
877
|
-
*/
|
|
878
|
-
export async function resetStorage(): Promise<void> {
|
|
879
|
-
if (globalStorage) {
|
|
880
|
-
await globalStorage.close();
|
|
881
|
-
globalStorage = null;
|
|
882
|
-
}
|
|
883
|
-
globalStoragePromise = null;
|
|
884
|
-
}
|