@stackmemoryai/stackmemory 1.10.5 → 1.14.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 (225) hide show
  1. package/LICENSE +131 -64
  2. package/README.md +107 -24
  3. package/bin/claude-sm +16 -1
  4. package/bin/claude-smd +16 -1
  5. package/bin/codex-smd +16 -1
  6. package/bin/gemini-sm +16 -1
  7. package/bin/hermes-sm +21 -0
  8. package/bin/hermes-smd +21 -0
  9. package/bin/opencode-sm +16 -1
  10. package/dist/src/cli/claude-sm.js +266 -84
  11. package/dist/src/cli/codex-sm.js +225 -33
  12. package/dist/src/cli/commands/bench.js +209 -2
  13. package/dist/src/cli/commands/brain.js +206 -0
  14. package/dist/src/cli/commands/cache.js +126 -0
  15. package/dist/src/cli/commands/company-os.js +184 -0
  16. package/dist/src/cli/commands/context.js +5 -0
  17. package/dist/src/cli/commands/daemon.js +41 -0
  18. package/dist/src/cli/commands/handoff.js +40 -9
  19. package/dist/src/cli/commands/onboard.js +70 -3
  20. package/dist/src/cli/commands/operator.js +127 -0
  21. package/dist/src/cli/commands/optimize.js +117 -0
  22. package/dist/src/cli/commands/orchestrate.js +232 -5
  23. package/dist/src/cli/commands/orchestrator.js +315 -26
  24. package/dist/src/cli/commands/pack.js +322 -0
  25. package/dist/src/cli/commands/patterns.js +254 -0
  26. package/dist/src/cli/commands/portal.js +161 -0
  27. package/dist/src/cli/commands/scaffold.js +92 -0
  28. package/dist/src/cli/commands/search.js +40 -1
  29. package/dist/src/cli/commands/setup.js +178 -11
  30. package/dist/src/cli/commands/skills.js +10 -1
  31. package/dist/src/cli/commands/state.js +265 -0
  32. package/dist/src/cli/commands/sync.js +253 -0
  33. package/dist/src/cli/commands/tasks.js +130 -1
  34. package/dist/src/cli/commands/vision.js +221 -0
  35. package/dist/src/cli/gemini-sm.js +19 -29
  36. package/dist/src/cli/hermes-sm.js +224 -0
  37. package/dist/src/cli/index.js +105 -39
  38. package/dist/src/cli/opencode-sm.js +38 -21
  39. package/dist/src/cli/utils/determinism-watcher.js +66 -0
  40. package/dist/src/cli/utils/real-cli-bin.js +116 -0
  41. package/dist/src/core/brain/brain-store.js +187 -0
  42. package/dist/src/core/brain/brain-sync.js +193 -0
  43. package/dist/src/core/brain/index.js +78 -0
  44. package/dist/src/core/brain/types.js +10 -0
  45. package/dist/src/core/cache/content-cache.js +238 -0
  46. package/dist/src/{integrations/diffmem → core/cache}/index.js +5 -5
  47. package/dist/src/core/cache/token-estimator.js +39 -0
  48. package/dist/src/core/config/feature-flags.js +2 -6
  49. package/dist/src/core/context/frame-database.js +79 -27
  50. package/dist/src/core/context/recursive-context-manager.js +1 -1
  51. package/dist/src/core/context/rehydration.js +2 -1
  52. package/dist/src/core/cross-search/cross-project-search.js +269 -0
  53. package/dist/src/core/cross-search/index.js +10 -0
  54. package/dist/src/core/database/sqlite-adapter.js +14 -84
  55. package/dist/src/core/extensions/provider-adapter.js +5 -0
  56. package/dist/src/core/models/model-router.js +54 -2
  57. package/dist/src/core/models/provider-pricing.js +58 -4
  58. package/dist/src/core/monitoring/logger.js +2 -1
  59. package/dist/src/core/optimization/trace-optimizer.js +413 -0
  60. package/dist/src/core/patterns/index.js +22 -0
  61. package/dist/src/core/patterns/pattern-applier.js +39 -0
  62. package/dist/src/core/patterns/pattern-observer.js +157 -0
  63. package/dist/src/core/patterns/pattern-store.js +259 -0
  64. package/dist/src/core/patterns/types.js +19 -0
  65. package/dist/src/core/provenance/confidence-scorer.js +128 -0
  66. package/dist/src/core/provenance/index.js +40 -0
  67. package/dist/src/core/provenance/provenance-store.js +194 -0
  68. package/dist/src/core/provenance/types.js +82 -0
  69. package/dist/src/core/retrieval/llm-context-retrieval.js +5 -4
  70. package/dist/src/core/retrieval/unified-context-assembler.js +11 -66
  71. package/dist/src/core/session/project-handoff.js +64 -0
  72. package/dist/src/core/session/session-manager.js +28 -0
  73. package/dist/src/core/shared-state/canonical-store.js +564 -0
  74. package/dist/src/core/skill-packs/index.js +18 -0
  75. package/dist/src/core/skill-packs/parser.js +42 -0
  76. package/dist/src/core/skill-packs/registry.js +224 -0
  77. package/dist/src/core/skill-packs/types.js +79 -0
  78. package/dist/src/core/storage/cloud-sync-manager.js +116 -0
  79. package/dist/src/core/storage/cloud-sync.js +574 -0
  80. package/dist/src/core/storage/two-tier-storage.js +5 -1
  81. package/dist/src/core/tasks/master-tasks-template.js +43 -0
  82. package/dist/src/core/tasks/md-task-parser.js +138 -0
  83. package/dist/src/core/trace/trace-event-store.js +282 -0
  84. package/dist/src/core/vision/index.js +27 -0
  85. package/dist/src/core/vision/signals.js +79 -0
  86. package/dist/src/core/vision/types.js +22 -0
  87. package/dist/src/core/vision/vision-file.js +146 -0
  88. package/dist/src/core/vision/vision-loop.js +220 -0
  89. package/dist/src/core/wiki/wiki-compiler.js +103 -1
  90. package/dist/src/daemon/daemon-config.js +52 -0
  91. package/dist/src/daemon/services/desire-path-service.js +566 -0
  92. package/dist/src/daemon/services/github-service.js +126 -0
  93. package/dist/src/daemon/services/research-stream-service.js +320 -0
  94. package/dist/src/daemon/services/telemetry-service.js +192 -0
  95. package/dist/src/daemon/unified-daemon.js +58 -1
  96. package/dist/src/features/browser/cli-browser-agent.js +417 -0
  97. package/dist/src/features/browser/stagehand-workflows.js +578 -0
  98. package/dist/src/features/operator/adapter-factory.js +62 -0
  99. package/dist/src/features/operator/browser-adapter.js +109 -0
  100. package/dist/src/features/operator/desktop-adapter.js +125 -0
  101. package/dist/src/features/operator/index.js +39 -0
  102. package/dist/src/features/operator/llm-decision.js +137 -0
  103. package/dist/src/features/operator/operator-logger.js +92 -0
  104. package/dist/src/features/operator/overnight-runner.js +327 -0
  105. package/dist/src/features/operator/screen-adapter.js +91 -0
  106. package/dist/src/features/operator/session-manager.js +127 -0
  107. package/dist/src/features/operator/state-machine.js +227 -0
  108. package/dist/src/features/operator/task-queue.js +81 -0
  109. package/dist/src/features/portal/index.js +26 -0
  110. package/dist/src/features/portal/server.js +240 -0
  111. package/dist/src/features/portal/types.js +14 -0
  112. package/dist/src/features/portal/ui.js +195 -0
  113. package/dist/src/features/sweep/pty-wrapper.js +13 -5
  114. package/dist/src/features/tasks/task-aware-context.js +2 -1
  115. package/dist/src/features/tui/simple-monitor.js +0 -23
  116. package/dist/src/features/tui/swarm-monitor.js +8 -66
  117. package/dist/src/features/web/client/hooks/use-socket.js +12 -0
  118. package/dist/src/{core/merge/index.js → features/web/client/lib/utils.js} +8 -4
  119. package/dist/src/features/web/client/next-env.d.js +4 -0
  120. package/dist/src/features/web/client/stores/session-store.js +12 -0
  121. package/dist/src/features/web/server/gcp-billing.js +76 -0
  122. package/dist/src/features/web/server/index.js +10 -0
  123. package/dist/src/features/web/server/spend-calculator.js +228 -0
  124. package/dist/src/hooks/schemas.js +6 -1
  125. package/dist/src/integrations/anthropic/client.js +3 -2
  126. package/dist/src/integrations/claude-code/agent-bridge.js +0 -3
  127. package/dist/src/integrations/claude-code/subagent-client.js +307 -11
  128. package/dist/src/integrations/claude-code/task-coordinator.js +2 -1
  129. package/dist/src/integrations/github/pr-state.js +158 -0
  130. package/dist/src/integrations/linear/client.js +4 -1
  131. package/dist/src/integrations/linear/webhook-retry.js +196 -0
  132. package/dist/src/integrations/linear/webhook-server.js +18 -22
  133. package/dist/src/integrations/mcp/handlers/cloud-sync-handlers.js +101 -0
  134. package/dist/src/integrations/mcp/handlers/index.js +40 -84
  135. package/dist/src/integrations/mcp/server.js +542 -641
  136. package/dist/src/integrations/mcp/tool-alias-registry.js +297 -0
  137. package/dist/src/integrations/mcp/tool-definitions.js +152 -682
  138. package/dist/src/mcp/stackmemory-mcp-server.js +571 -231
  139. package/dist/src/orchestrators/multimodal/determinism.js +244 -0
  140. package/dist/src/orchestrators/multimodal/harness.js +149 -78
  141. package/dist/src/orchestrators/multimodal/providers.js +44 -3
  142. package/dist/src/skills/recursive-agent-orchestrator.js +2 -4
  143. package/dist/src/utils/hook-installer.js +0 -8
  144. package/dist/src/utils/process-cleanup.js +1 -7
  145. package/docs/README.md +42 -0
  146. package/docs/guides/README_INSTALL.md +208 -0
  147. package/package.json +27 -9
  148. package/packs/coding/python-fastapi/instructions.md +60 -0
  149. package/packs/coding/python-fastapi/pack.yaml +28 -0
  150. package/packs/coding/typescript-react/instructions.md +47 -0
  151. package/packs/coding/typescript-react/pack.yaml +28 -0
  152. package/packs/core/commands/capture.md +32 -0
  153. package/packs/core/commands/learn.md +73 -0
  154. package/packs/core/commands/next.md +36 -0
  155. package/packs/core/commands/restart.md +58 -0
  156. package/packs/core/commands/restore.md +29 -0
  157. package/packs/core/commands/start.md +57 -0
  158. package/packs/core/commands/stop.md +65 -0
  159. package/packs/core/commands/summary.md +40 -0
  160. package/packs/core/manifest.json +24 -0
  161. package/packs/ops/decision-recovery/instructions.md +65 -0
  162. package/packs/ops/decision-recovery/pack.yaml +89 -0
  163. package/scripts/claude-code-wrapper.sh +11 -0
  164. package/scripts/claude-sm-setup.sh +12 -1
  165. package/scripts/codex-wrapper.sh +11 -0
  166. package/scripts/git-hooks/branch-context-manager.sh +11 -0
  167. package/scripts/git-hooks/post-checkout-stackmemory.sh +11 -0
  168. package/scripts/git-hooks/post-commit-stackmemory.sh +11 -0
  169. package/scripts/git-hooks/pre-commit-stackmemory.sh +11 -0
  170. package/scripts/hooks/cleanup-shell.sh +12 -1
  171. package/scripts/hooks/task-complete.sh +12 -1
  172. package/scripts/install-code-execution-hooks.sh +12 -1
  173. package/scripts/install-sweep-hook.sh +12 -0
  174. package/scripts/install.sh +11 -0
  175. package/scripts/opencode-wrapper.sh +11 -0
  176. package/scripts/portal/cloud-init.yaml +69 -0
  177. package/scripts/portal/setup.sh +69 -0
  178. package/scripts/portal/stackmemory-portal.service +34 -0
  179. package/scripts/setup-claude-integration.sh +12 -1
  180. package/scripts/smoke-init-db.sh +23 -0
  181. package/scripts/stackmemory-daemon.sh +11 -0
  182. package/scripts/verify-dist.cjs +11 -4
  183. package/dist/src/cli/commands/ralph.js +0 -1053
  184. package/dist/src/cli/commands/team.js +0 -168
  185. package/dist/src/core/context/shared-context-layer.js +0 -620
  186. package/dist/src/core/context/stack-merge-resolver.js +0 -748
  187. package/dist/src/core/merge/conflict-detector.js +0 -430
  188. package/dist/src/core/merge/resolution-engine.js +0 -557
  189. package/dist/src/core/merge/stack-diff.js +0 -531
  190. package/dist/src/core/merge/unified-merge-resolver.js +0 -302
  191. package/dist/src/hooks/diffmem-hooks.js +0 -376
  192. package/dist/src/integrations/diffmem/client.js +0 -208
  193. package/dist/src/integrations/diffmem/config.js +0 -14
  194. package/dist/src/integrations/greptile/client.js +0 -101
  195. package/dist/src/integrations/greptile/config.js +0 -14
  196. package/dist/src/integrations/greptile/index.js +0 -11
  197. package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
  198. package/dist/src/integrations/mcp/handlers/diffmem-handlers.js +0 -455
  199. package/dist/src/integrations/mcp/handlers/greptile-handlers.js +0 -456
  200. package/dist/src/integrations/mcp/handlers/provider-handlers.js +0 -227
  201. package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
  202. package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +0 -863
  203. package/dist/src/integrations/ralph/context/context-budget-manager.js +0 -308
  204. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +0 -391
  205. package/dist/src/integrations/ralph/index.js +0 -17
  206. package/dist/src/integrations/ralph/learning/pattern-learner.js +0 -435
  207. package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +0 -448
  208. package/dist/src/integrations/ralph/loopmax.js +0 -488
  209. package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +0 -293
  210. package/dist/src/integrations/ralph/monitoring/swarm-registry.js +0 -107
  211. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +0 -508
  212. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +0 -407
  213. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +0 -495
  214. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +0 -387
  215. package/dist/src/integrations/ralph/performance/performance-optimizer.js +0 -357
  216. package/dist/src/integrations/ralph/recovery/crash-recovery.js +0 -461
  217. package/dist/src/integrations/ralph/state/state-reconciler.js +0 -420
  218. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +0 -444
  219. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -1007
  220. package/dist/src/integrations/ralph/visualization/ralph-debugger.js +0 -635
  221. package/scripts/ralph-loop-implementation.js +0 -404
  222. /package/dist/src/core/{merge → cache}/types.js +0 -0
  223. /package/dist/src/{integrations/diffmem/types.js → core/storage/cloud-sync-types.js} +0 -0
  224. /package/dist/src/{integrations/greptile/types.js → core/trace/trace-event.js} +0 -0
  225. /package/dist/src/{integrations/ralph → features/operator}/types.js +0 -0
@@ -0,0 +1,157 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ class PatternObserver {
6
+ constructor(store) {
7
+ this.store = store;
8
+ }
9
+ /**
10
+ * Analyze a session's trace events and extract patterns.
11
+ * Call at session_end with the session's trace events.
12
+ */
13
+ observe(events, projectId) {
14
+ if (events.length < 3) return [];
15
+ const learned = [];
16
+ const sequences = this.findRepeatedSequences(events);
17
+ for (const seq of sequences) {
18
+ const id = this.sequenceId(seq.operations);
19
+ const existing = this.store.get(id);
20
+ if (existing) {
21
+ this.store.reinforce(id, `Session observation (${seq.count}x)`);
22
+ } else {
23
+ this.store.create({
24
+ id,
25
+ domain: this.inferDomain(seq.operations),
26
+ trigger: `When performing a ${this.describeIntent(seq.operations)} workflow`,
27
+ action: `Follow sequence: ${seq.operations.join(" \u2192 ")}`,
28
+ evidence: [`Observed ${seq.count}x in session`],
29
+ scope: projectId ? "project" : "global",
30
+ projectId,
31
+ source: "observed"
32
+ });
33
+ }
34
+ learned.push(id);
35
+ }
36
+ const errorFixes = this.findErrorFixPairs(events);
37
+ for (const { error, fix } of errorFixes) {
38
+ const id = `fix-${this.slugify(error.operation)}-${this.slugify(fix.operation)}`;
39
+ const existing = this.store.get(id);
40
+ if (existing) {
41
+ this.store.reinforce(
42
+ id,
43
+ `Error\u2192fix: ${error.error} \u2192 ${fix.operation}`
44
+ );
45
+ } else {
46
+ this.store.create({
47
+ id,
48
+ domain: "debugging",
49
+ trigger: `When encountering error in ${error.operation}`,
50
+ action: `Resolve with ${fix.operation}: ${this.summarizeInputs(fix)}`,
51
+ evidence: [`Error: ${error.error?.slice(0, 200)}`],
52
+ scope: projectId ? "project" : "global",
53
+ projectId,
54
+ source: "observed"
55
+ });
56
+ }
57
+ learned.push(id);
58
+ }
59
+ const preferences = this.findToolPreferences(events);
60
+ for (const { before, after, count } of preferences) {
61
+ const id = `prefer-${this.slugify(before)}-before-${this.slugify(after)}`;
62
+ const existing = this.store.get(id);
63
+ if (existing) {
64
+ this.store.reinforce(id, `${before}\u2192${after} (${count}x)`);
65
+ } else if (count >= 3) {
66
+ this.store.create({
67
+ id,
68
+ domain: "workflow",
69
+ trigger: `When about to use ${after}`,
70
+ action: `Use ${before} first`,
71
+ evidence: [`Observed ${count}x: ${before} always precedes ${after}`],
72
+ scope: "global",
73
+ source: "observed"
74
+ });
75
+ learned.push(id);
76
+ }
77
+ }
78
+ return learned;
79
+ }
80
+ // ── Sequence Detection ────────────────────────────────
81
+ findRepeatedSequences(events) {
82
+ const ops = events.map((e) => e.operation);
83
+ const sequences = /* @__PURE__ */ new Map();
84
+ for (let windowSize = 2; windowSize <= 4; windowSize++) {
85
+ for (let i = 0; i <= ops.length - windowSize; i++) {
86
+ const seq = ops.slice(i, i + windowSize);
87
+ const key = seq.join("\u2192");
88
+ const existing = sequences.get(key);
89
+ if (existing) {
90
+ existing.count++;
91
+ } else {
92
+ sequences.set(key, { operations: seq, count: 1 });
93
+ }
94
+ }
95
+ }
96
+ return Array.from(sequences.values()).filter((s) => s.count >= 3);
97
+ }
98
+ // ── Error→Fix Detection ───────────────────────────────
99
+ findErrorFixPairs(events) {
100
+ const pairs = [];
101
+ for (let i = 0; i < events.length - 1; i++) {
102
+ const event = events[i];
103
+ if (!event.error) continue;
104
+ for (let j = i + 1; j < Math.min(i + 4, events.length); j++) {
105
+ const next = events[j];
106
+ if (!next.error) {
107
+ pairs.push({ error: event, fix: next });
108
+ break;
109
+ }
110
+ }
111
+ }
112
+ return pairs;
113
+ }
114
+ // ── Tool Preference Detection ─────────────────────────
115
+ findToolPreferences(events) {
116
+ const pairs = /* @__PURE__ */ new Map();
117
+ const ops = events.map((e) => e.operation);
118
+ for (let i = 0; i < ops.length - 1; i++) {
119
+ const key = `${ops[i]}\u2192${ops[i + 1]}`;
120
+ pairs.set(key, (pairs.get(key) ?? 0) + 1);
121
+ }
122
+ return Array.from(pairs.entries()).filter(([, count]) => count >= 3).map(([key, count]) => {
123
+ const [before, after] = key.split("\u2192");
124
+ return { before, after, count };
125
+ });
126
+ }
127
+ // ── Helpers ───────────────────────────────────────────
128
+ sequenceId(ops) {
129
+ return `seq-${ops.map((o) => this.slugify(o)).join("-")}`;
130
+ }
131
+ slugify(s) {
132
+ return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 30);
133
+ }
134
+ inferDomain(ops) {
135
+ const joined = ops.join(" ").toLowerCase();
136
+ if (/test|vitest|jest/.test(joined)) return "testing";
137
+ if (/git|commit|branch|push/.test(joined)) return "git";
138
+ if (/grep|glob|read|search/.test(joined)) return "workflow";
139
+ if (/edit|write/.test(joined)) return "code-style";
140
+ if (/security|auth|token/.test(joined)) return "security";
141
+ return "general";
142
+ }
143
+ describeIntent(ops) {
144
+ const unique = [...new Set(ops)];
145
+ if (unique.length <= 2) return unique.join(" + ");
146
+ return `${unique[0]} \u2192 ${unique[unique.length - 1]}`;
147
+ }
148
+ summarizeInputs(event) {
149
+ const inputs = event.inputs;
150
+ if (!inputs || typeof inputs !== "object") return "";
151
+ const keys = Object.keys(inputs).slice(0, 3);
152
+ return keys.join(", ");
153
+ }
154
+ }
155
+ export {
156
+ PatternObserver
157
+ };
@@ -0,0 +1,259 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import {
6
+ computeConfidence,
7
+ CONFIDENCE_DECAY_PER_WEEK,
8
+ CONFIDENCE_BOOST_PER_OBSERVATION
9
+ } from "./types.js";
10
+ class PatternStore {
11
+ constructor(db) {
12
+ this.db = db;
13
+ }
14
+ /** Create a new pattern */
15
+ create(input) {
16
+ const now = Date.now();
17
+ const confidence = input.confidence ?? computeConfidence(1);
18
+ this.db.prepare(
19
+ `
20
+ INSERT INTO patterns (id, domain, trigger, action, evidence, confidence, observation_count, scope, project_id, status, source, created_at, updated_at)
21
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
22
+ `
23
+ ).run(
24
+ input.id,
25
+ input.domain,
26
+ input.trigger,
27
+ input.action,
28
+ JSON.stringify(input.evidence ?? []),
29
+ confidence,
30
+ 1,
31
+ input.scope ?? "project",
32
+ input.projectId ?? null,
33
+ "pending",
34
+ input.source ?? "manual",
35
+ now,
36
+ now
37
+ );
38
+ return this.get(input.id);
39
+ }
40
+ /** Get a pattern by ID */
41
+ get(id) {
42
+ const row = this.db.prepare("SELECT * FROM patterns WHERE id = ?").get(id);
43
+ return row ? this.toPattern(row) : void 0;
44
+ }
45
+ /** List patterns with filtering */
46
+ list(query = {}) {
47
+ const conditions = [];
48
+ const params = [];
49
+ if (query.domain) {
50
+ conditions.push("domain = ?");
51
+ params.push(query.domain);
52
+ }
53
+ if (query.status) {
54
+ conditions.push("status = ?");
55
+ params.push(query.status);
56
+ }
57
+ if (query.scope) {
58
+ conditions.push("scope = ?");
59
+ params.push(query.scope);
60
+ }
61
+ if (query.projectId) {
62
+ conditions.push("(project_id = ? OR scope = 'global')");
63
+ params.push(query.projectId);
64
+ }
65
+ if (query.minConfidence !== void 0) {
66
+ conditions.push("confidence >= ?");
67
+ params.push(query.minConfidence);
68
+ }
69
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
70
+ const limit = query.limit ?? 100;
71
+ const rows = this.db.prepare(
72
+ `SELECT * FROM patterns ${where} ORDER BY confidence DESC, updated_at DESC LIMIT ?`
73
+ ).all(...params, limit);
74
+ return rows.map((r) => this.toPattern(r));
75
+ }
76
+ /** Record an observation that reinforces a pattern */
77
+ reinforce(id, evidence) {
78
+ const pattern = this.get(id);
79
+ if (!pattern) return;
80
+ const newCount = pattern.observationCount + 1;
81
+ const baseConfidence = computeConfidence(newCount);
82
+ const boosted = Math.min(
83
+ 1,
84
+ baseConfidence + CONFIDENCE_BOOST_PER_OBSERVATION
85
+ );
86
+ const newEvidence = [...pattern.evidence, evidence].slice(-20);
87
+ this.db.prepare(
88
+ `
89
+ UPDATE patterns
90
+ SET observation_count = ?, confidence = ?, evidence = ?, updated_at = ?, status = CASE WHEN status = 'pending' AND ? >= 0.5 THEN 'active' ELSE status END
91
+ WHERE id = ?
92
+ `
93
+ ).run(
94
+ newCount,
95
+ boosted,
96
+ JSON.stringify(newEvidence),
97
+ Date.now(),
98
+ boosted,
99
+ id
100
+ );
101
+ }
102
+ /** Apply weekly confidence decay to all patterns */
103
+ applyDecay() {
104
+ const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1e3;
105
+ const result = this.db.prepare(
106
+ `
107
+ UPDATE patterns
108
+ SET confidence = MAX(0.05, confidence - ?),
109
+ updated_at = ?
110
+ WHERE updated_at < ? AND status != 'archived'
111
+ `
112
+ ).run(CONFIDENCE_DECAY_PER_WEEK, Date.now(), oneWeekAgo);
113
+ return result.changes;
114
+ }
115
+ /** Mark a pattern as matched during retrieval */
116
+ recordMatch(id) {
117
+ this.db.prepare("UPDATE patterns SET last_matched_at = ? WHERE id = ?").run(Date.now(), id);
118
+ }
119
+ /** Activate a pending pattern */
120
+ activate(id) {
121
+ this.db.prepare(
122
+ "UPDATE patterns SET status = 'active', updated_at = ? WHERE id = ?"
123
+ ).run(Date.now(), id);
124
+ }
125
+ /** Archive a pattern */
126
+ archive(id, supersededBy) {
127
+ this.db.prepare(
128
+ "UPDATE patterns SET status = 'archived', superseded_by = ?, updated_at = ? WHERE id = ?"
129
+ ).run(supersededBy ?? null, Date.now(), id);
130
+ }
131
+ /** Prune old pending patterns that never got promoted */
132
+ prune(maxAgeDays = 30) {
133
+ const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1e3;
134
+ const result = this.db.prepare(
135
+ "DELETE FROM patterns WHERE status = 'pending' AND created_at < ?"
136
+ ).run(cutoff);
137
+ return result.changes;
138
+ }
139
+ /** Get aggregate stats */
140
+ stats() {
141
+ const all = this.list({ limit: 1e3 });
142
+ const byDomain = {};
143
+ const byStatus = {};
144
+ let totalConfidence = 0;
145
+ for (const p of all) {
146
+ byDomain[p.domain] = (byDomain[p.domain] ?? 0) + 1;
147
+ byStatus[p.status] = (byStatus[p.status] ?? 0) + 1;
148
+ totalConfidence += p.confidence;
149
+ }
150
+ const topPatterns = all.filter((p) => p.status === "active").slice(0, 10).map((p) => ({ id: p.id, confidence: p.confidence, trigger: p.trigger }));
151
+ return {
152
+ total: all.length,
153
+ byDomain,
154
+ byStatus,
155
+ avgConfidence: all.length > 0 ? totalConfidence / all.length : 0,
156
+ topPatterns
157
+ };
158
+ }
159
+ /** Promote a project-scoped pattern to global */
160
+ promote(id) {
161
+ this.db.prepare(
162
+ "UPDATE patterns SET scope = 'global', project_id = NULL, updated_at = ? WHERE id = ?"
163
+ ).run(Date.now(), id);
164
+ }
165
+ /** List distinct project IDs with pattern counts */
166
+ projects() {
167
+ const rows = this.db.prepare(
168
+ `
169
+ SELECT project_id, COUNT(*) as count, AVG(confidence) as avg_confidence
170
+ FROM patterns
171
+ WHERE project_id IS NOT NULL AND status != 'archived'
172
+ GROUP BY project_id
173
+ ORDER BY count DESC
174
+ `
175
+ ).all();
176
+ return rows.map((r) => ({
177
+ projectId: r.project_id,
178
+ count: r.count,
179
+ avgConfidence: r.avg_confidence
180
+ }));
181
+ }
182
+ /** Find patterns that appear in 2+ projects (promotion candidates) */
183
+ promotionCandidates(minConfidence = 0.7) {
184
+ const rows = this.db.prepare(
185
+ `
186
+ SELECT p1.* FROM patterns p1
187
+ WHERE p1.scope = 'project'
188
+ AND p1.confidence >= ?
189
+ AND p1.status = 'active'
190
+ AND EXISTS (
191
+ SELECT 1 FROM patterns p2
192
+ WHERE p2.trigger = p1.trigger
193
+ AND p2.action = p1.action
194
+ AND p2.project_id != p1.project_id
195
+ AND p2.status = 'active'
196
+ )
197
+ ORDER BY p1.confidence DESC
198
+ `
199
+ ).all(minConfidence);
200
+ return rows.map((r) => this.toPattern(r));
201
+ }
202
+ /** Find clusters of related patterns (for evolve) */
203
+ findClusters(minSize = 2) {
204
+ const active = this.list({ status: "active", limit: 500 });
205
+ const byDomain = /* @__PURE__ */ new Map();
206
+ for (const p of active) {
207
+ const list = byDomain.get(p.domain) ?? [];
208
+ list.push(p);
209
+ byDomain.set(p.domain, list);
210
+ }
211
+ return Array.from(byDomain.entries()).filter(([, patterns]) => patterns.length >= minSize).map(([domain, patterns]) => ({
212
+ domain,
213
+ patterns: patterns.sort((a, b) => b.confidence - a.confidence)
214
+ }));
215
+ }
216
+ /** Find patterns relevant to a query string */
217
+ search(query, projectId) {
218
+ const words = query.toLowerCase().split(/\s+/).filter(Boolean);
219
+ if (words.length === 0) return [];
220
+ const candidates = this.list({
221
+ status: "active",
222
+ projectId,
223
+ minConfidence: 0.3,
224
+ limit: 200
225
+ });
226
+ const scored = candidates.map((p) => {
227
+ const text = `${p.trigger} ${p.action} ${p.domain}`.toLowerCase();
228
+ const hits = words.filter((w) => text.includes(w)).length;
229
+ const score = hits / words.length;
230
+ return { pattern: p, score };
231
+ });
232
+ return scored.filter((s) => s.score > 0.2).sort(
233
+ (a, b) => b.score * b.pattern.confidence - a.score * a.pattern.confidence
234
+ ).slice(0, 10).map((s) => s.pattern);
235
+ }
236
+ // ── Private ───────────────────────────────────────────
237
+ toPattern(row) {
238
+ return {
239
+ id: row.id,
240
+ domain: row.domain,
241
+ trigger: row.trigger,
242
+ action: row.action,
243
+ evidence: JSON.parse(row.evidence || "[]"),
244
+ confidence: row.confidence,
245
+ observationCount: row.observation_count,
246
+ scope: row.scope,
247
+ projectId: row.project_id,
248
+ status: row.status,
249
+ source: row.source,
250
+ createdAt: row.created_at,
251
+ updatedAt: row.updated_at,
252
+ lastMatchedAt: row.last_matched_at,
253
+ supersededBy: row.superseded_by
254
+ };
255
+ }
256
+ }
257
+ export {
258
+ PatternStore
259
+ };
@@ -0,0 +1,19 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ function computeConfidence(observationCount) {
6
+ if (observationCount >= 11) return 0.85;
7
+ if (observationCount >= 6) return 0.7;
8
+ if (observationCount >= 3) return 0.5;
9
+ return 0.3;
10
+ }
11
+ const CONFIDENCE_DECAY_PER_WEEK = 0.02;
12
+ const CONFIDENCE_BOOST_PER_OBSERVATION = 0.05;
13
+ const CONFIDENCE_PENALTY_PER_CONTRADICTION = 0.1;
14
+ export {
15
+ CONFIDENCE_BOOST_PER_OBSERVATION,
16
+ CONFIDENCE_DECAY_PER_WEEK,
17
+ CONFIDENCE_PENALTY_PER_CONTRADICTION,
18
+ computeConfidence
19
+ };
@@ -0,0 +1,128 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ const TRIGGER_PHRASES = [
6
+ "we decided",
7
+ "the plan is",
8
+ "going forward",
9
+ "action item",
10
+ "let's go with",
11
+ "agreed to",
12
+ "we're doing",
13
+ "approved",
14
+ "confirmed",
15
+ "the approach is",
16
+ "final answer",
17
+ "ship it",
18
+ "green light",
19
+ "sign off",
20
+ "consensus is"
21
+ ];
22
+ const HEDGE_PHRASES = [
23
+ "maybe",
24
+ "might",
25
+ "not sure",
26
+ "i think",
27
+ "possibly",
28
+ "perhaps",
29
+ "could be",
30
+ "uncertain",
31
+ "don't know",
32
+ "unclear"
33
+ ];
34
+ const IMPERATIVE_VERBS = [
35
+ "use",
36
+ "deploy",
37
+ "migrate",
38
+ "switch",
39
+ "remove",
40
+ "add",
41
+ "implement",
42
+ "create",
43
+ "delete",
44
+ "update",
45
+ "replace",
46
+ "refactor",
47
+ "integrate",
48
+ "configure",
49
+ "enable",
50
+ "disable"
51
+ ];
52
+ const WEIGHTS = {
53
+ triggerPhrase: 0.3,
54
+ triggerPhraseCap: 0.6,
55
+ imperativeVerb: 0.15,
56
+ actorAttribution: 0.1,
57
+ recencyBonus: 0.1,
58
+ replyCountBonus: 0.05,
59
+ questionPenalty: -0.2,
60
+ hedgePenalty: -0.15
61
+ };
62
+ const THRESHOLDS = {
63
+ accept: 0.7,
64
+ review: 0.4
65
+ };
66
+ const RECENCY_WINDOW_MS = 48 * 60 * 60 * 1e3;
67
+ function scoreConfidence(text, context = {}) {
68
+ const lower = (text || "").toLowerCase();
69
+ const signals = {};
70
+ let score = 0;
71
+ const matchedTriggers = TRIGGER_PHRASES.filter((p) => lower.includes(p));
72
+ const triggerScore = Math.min(
73
+ matchedTriggers.length * WEIGHTS.triggerPhrase,
74
+ WEIGHTS.triggerPhraseCap
75
+ );
76
+ if (triggerScore > 0) {
77
+ signals["triggerPhrases"] = matchedTriggers;
78
+ score += triggerScore;
79
+ }
80
+ const sentences = lower.split(/[.!?\n]+/).map((s) => s.trim()).filter(Boolean);
81
+ const hasImperative = sentences.some((s) => {
82
+ const firstWord = s.split(/\s+/)[0];
83
+ return IMPERATIVE_VERBS.includes(firstWord ?? "");
84
+ });
85
+ if (hasImperative) {
86
+ signals["imperativeVerb"] = true;
87
+ score += WEIGHTS.imperativeVerb;
88
+ }
89
+ if (context.actor) {
90
+ signals["actorAttribution"] = context.actor;
91
+ score += WEIGHTS.actorAttribution;
92
+ }
93
+ if (context.relatedTicketDate && context.messageDate) {
94
+ const ticketTime = new Date(context.relatedTicketDate).getTime();
95
+ const msgTime = new Date(context.messageDate).getTime();
96
+ const diff = Math.abs(msgTime - ticketTime);
97
+ if (diff <= RECENCY_WINDOW_MS) {
98
+ signals["recencyBonus"] = true;
99
+ score += WEIGHTS.recencyBonus;
100
+ }
101
+ }
102
+ if (context.replyCount !== void 0 && context.replyCount > 2) {
103
+ signals["replyCountBonus"] = context.replyCount;
104
+ score += WEIGHTS.replyCountBonus;
105
+ }
106
+ const isQuestion = /\?\s*$/.test(text.trim()) || lower.startsWith("should we") || lower.startsWith("what if");
107
+ if (isQuestion) {
108
+ signals["questionPenalty"] = true;
109
+ score += WEIGHTS.questionPenalty;
110
+ }
111
+ const matchedHedges = HEDGE_PHRASES.filter((p) => lower.includes(p));
112
+ if (matchedHedges.length > 0) {
113
+ signals["hedgePhrases"] = matchedHedges;
114
+ score += WEIGHTS.hedgePenalty;
115
+ }
116
+ const confidence = Math.max(0, Math.min(1, score));
117
+ const classification = confidence >= THRESHOLDS.accept ? "accept" : confidence >= THRESHOLDS.review ? "review" : "discard";
118
+ return { confidence, signals, classification };
119
+ }
120
+ export {
121
+ HEDGE_PHRASES,
122
+ IMPERATIVE_VERBS,
123
+ RECENCY_WINDOW_MS,
124
+ THRESHOLDS,
125
+ TRIGGER_PHRASES,
126
+ WEIGHTS,
127
+ scoreConfidence
128
+ };
@@ -0,0 +1,40 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import {
6
+ SourceRefSchema,
7
+ ProvenanceRecordSchema,
8
+ ActorSchema,
9
+ TraceEventSchema,
10
+ ConfidenceClassificationSchema,
11
+ ConfidenceScoreSchema,
12
+ ConfidenceConfigSchema
13
+ } from "./types.js";
14
+ import {
15
+ scoreConfidence,
16
+ TRIGGER_PHRASES,
17
+ HEDGE_PHRASES,
18
+ IMPERATIVE_VERBS,
19
+ WEIGHTS,
20
+ THRESHOLDS,
21
+ RECENCY_WINDOW_MS
22
+ } from "./confidence-scorer.js";
23
+ import { ProvenanceStore } from "./provenance-store.js";
24
+ export {
25
+ ActorSchema,
26
+ ConfidenceClassificationSchema,
27
+ ConfidenceConfigSchema,
28
+ ConfidenceScoreSchema,
29
+ HEDGE_PHRASES,
30
+ IMPERATIVE_VERBS,
31
+ ProvenanceRecordSchema,
32
+ ProvenanceStore,
33
+ RECENCY_WINDOW_MS,
34
+ SourceRefSchema,
35
+ THRESHOLDS,
36
+ TRIGGER_PHRASES,
37
+ TraceEventSchema,
38
+ WEIGHTS,
39
+ scoreConfidence
40
+ };