@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.
- package/LICENSE +131 -64
- package/README.md +107 -24
- package/bin/claude-sm +16 -1
- package/bin/claude-smd +16 -1
- package/bin/codex-smd +16 -1
- package/bin/gemini-sm +16 -1
- package/bin/hermes-sm +21 -0
- package/bin/hermes-smd +21 -0
- package/bin/opencode-sm +16 -1
- package/dist/src/cli/claude-sm.js +266 -84
- package/dist/src/cli/codex-sm.js +225 -33
- package/dist/src/cli/commands/bench.js +209 -2
- package/dist/src/cli/commands/brain.js +206 -0
- package/dist/src/cli/commands/cache.js +126 -0
- package/dist/src/cli/commands/company-os.js +184 -0
- package/dist/src/cli/commands/context.js +5 -0
- package/dist/src/cli/commands/daemon.js +41 -0
- package/dist/src/cli/commands/handoff.js +40 -9
- package/dist/src/cli/commands/onboard.js +70 -3
- package/dist/src/cli/commands/operator.js +127 -0
- package/dist/src/cli/commands/optimize.js +117 -0
- package/dist/src/cli/commands/orchestrate.js +232 -5
- package/dist/src/cli/commands/orchestrator.js +315 -26
- package/dist/src/cli/commands/pack.js +322 -0
- package/dist/src/cli/commands/patterns.js +254 -0
- package/dist/src/cli/commands/portal.js +161 -0
- package/dist/src/cli/commands/scaffold.js +92 -0
- package/dist/src/cli/commands/search.js +40 -1
- package/dist/src/cli/commands/setup.js +178 -11
- package/dist/src/cli/commands/skills.js +10 -1
- package/dist/src/cli/commands/state.js +265 -0
- package/dist/src/cli/commands/sync.js +253 -0
- package/dist/src/cli/commands/tasks.js +130 -1
- package/dist/src/cli/commands/vision.js +221 -0
- package/dist/src/cli/gemini-sm.js +19 -29
- package/dist/src/cli/hermes-sm.js +224 -0
- package/dist/src/cli/index.js +105 -39
- package/dist/src/cli/opencode-sm.js +38 -21
- package/dist/src/cli/utils/determinism-watcher.js +66 -0
- package/dist/src/cli/utils/real-cli-bin.js +116 -0
- package/dist/src/core/brain/brain-store.js +187 -0
- package/dist/src/core/brain/brain-sync.js +193 -0
- package/dist/src/core/brain/index.js +78 -0
- package/dist/src/core/brain/types.js +10 -0
- package/dist/src/core/cache/content-cache.js +238 -0
- package/dist/src/{integrations/diffmem → core/cache}/index.js +5 -5
- package/dist/src/core/cache/token-estimator.js +39 -0
- package/dist/src/core/config/feature-flags.js +2 -6
- package/dist/src/core/context/frame-database.js +79 -27
- package/dist/src/core/context/recursive-context-manager.js +1 -1
- package/dist/src/core/context/rehydration.js +2 -1
- package/dist/src/core/cross-search/cross-project-search.js +269 -0
- package/dist/src/core/cross-search/index.js +10 -0
- package/dist/src/core/database/sqlite-adapter.js +14 -84
- package/dist/src/core/extensions/provider-adapter.js +5 -0
- package/dist/src/core/models/model-router.js +54 -2
- package/dist/src/core/models/provider-pricing.js +58 -4
- package/dist/src/core/monitoring/logger.js +2 -1
- package/dist/src/core/optimization/trace-optimizer.js +413 -0
- package/dist/src/core/patterns/index.js +22 -0
- package/dist/src/core/patterns/pattern-applier.js +39 -0
- package/dist/src/core/patterns/pattern-observer.js +157 -0
- package/dist/src/core/patterns/pattern-store.js +259 -0
- package/dist/src/core/patterns/types.js +19 -0
- package/dist/src/core/provenance/confidence-scorer.js +128 -0
- package/dist/src/core/provenance/index.js +40 -0
- package/dist/src/core/provenance/provenance-store.js +194 -0
- package/dist/src/core/provenance/types.js +82 -0
- package/dist/src/core/retrieval/llm-context-retrieval.js +5 -4
- package/dist/src/core/retrieval/unified-context-assembler.js +11 -66
- package/dist/src/core/session/project-handoff.js +64 -0
- package/dist/src/core/session/session-manager.js +28 -0
- package/dist/src/core/shared-state/canonical-store.js +564 -0
- package/dist/src/core/skill-packs/index.js +18 -0
- package/dist/src/core/skill-packs/parser.js +42 -0
- package/dist/src/core/skill-packs/registry.js +224 -0
- package/dist/src/core/skill-packs/types.js +79 -0
- package/dist/src/core/storage/cloud-sync-manager.js +116 -0
- package/dist/src/core/storage/cloud-sync.js +574 -0
- package/dist/src/core/storage/two-tier-storage.js +5 -1
- package/dist/src/core/tasks/master-tasks-template.js +43 -0
- package/dist/src/core/tasks/md-task-parser.js +138 -0
- package/dist/src/core/trace/trace-event-store.js +282 -0
- package/dist/src/core/vision/index.js +27 -0
- package/dist/src/core/vision/signals.js +79 -0
- package/dist/src/core/vision/types.js +22 -0
- package/dist/src/core/vision/vision-file.js +146 -0
- package/dist/src/core/vision/vision-loop.js +220 -0
- package/dist/src/core/wiki/wiki-compiler.js +103 -1
- package/dist/src/daemon/daemon-config.js +52 -0
- package/dist/src/daemon/services/desire-path-service.js +566 -0
- package/dist/src/daemon/services/github-service.js +126 -0
- package/dist/src/daemon/services/research-stream-service.js +320 -0
- package/dist/src/daemon/services/telemetry-service.js +192 -0
- package/dist/src/daemon/unified-daemon.js +58 -1
- package/dist/src/features/browser/cli-browser-agent.js +417 -0
- package/dist/src/features/browser/stagehand-workflows.js +578 -0
- package/dist/src/features/operator/adapter-factory.js +62 -0
- package/dist/src/features/operator/browser-adapter.js +109 -0
- package/dist/src/features/operator/desktop-adapter.js +125 -0
- package/dist/src/features/operator/index.js +39 -0
- package/dist/src/features/operator/llm-decision.js +137 -0
- package/dist/src/features/operator/operator-logger.js +92 -0
- package/dist/src/features/operator/overnight-runner.js +327 -0
- package/dist/src/features/operator/screen-adapter.js +91 -0
- package/dist/src/features/operator/session-manager.js +127 -0
- package/dist/src/features/operator/state-machine.js +227 -0
- package/dist/src/features/operator/task-queue.js +81 -0
- package/dist/src/features/portal/index.js +26 -0
- package/dist/src/features/portal/server.js +240 -0
- package/dist/src/features/portal/types.js +14 -0
- package/dist/src/features/portal/ui.js +195 -0
- package/dist/src/features/sweep/pty-wrapper.js +13 -5
- package/dist/src/features/tasks/task-aware-context.js +2 -1
- package/dist/src/features/tui/simple-monitor.js +0 -23
- package/dist/src/features/tui/swarm-monitor.js +8 -66
- package/dist/src/features/web/client/hooks/use-socket.js +12 -0
- package/dist/src/{core/merge/index.js → features/web/client/lib/utils.js} +8 -4
- package/dist/src/features/web/client/next-env.d.js +4 -0
- package/dist/src/features/web/client/stores/session-store.js +12 -0
- package/dist/src/features/web/server/gcp-billing.js +76 -0
- package/dist/src/features/web/server/index.js +10 -0
- package/dist/src/features/web/server/spend-calculator.js +228 -0
- package/dist/src/hooks/schemas.js +6 -1
- package/dist/src/integrations/anthropic/client.js +3 -2
- package/dist/src/integrations/claude-code/agent-bridge.js +0 -3
- package/dist/src/integrations/claude-code/subagent-client.js +307 -11
- package/dist/src/integrations/claude-code/task-coordinator.js +2 -1
- package/dist/src/integrations/github/pr-state.js +158 -0
- package/dist/src/integrations/linear/client.js +4 -1
- package/dist/src/integrations/linear/webhook-retry.js +196 -0
- package/dist/src/integrations/linear/webhook-server.js +18 -22
- package/dist/src/integrations/mcp/handlers/cloud-sync-handlers.js +101 -0
- package/dist/src/integrations/mcp/handlers/index.js +40 -84
- package/dist/src/integrations/mcp/server.js +542 -641
- package/dist/src/integrations/mcp/tool-alias-registry.js +297 -0
- package/dist/src/integrations/mcp/tool-definitions.js +152 -682
- package/dist/src/mcp/stackmemory-mcp-server.js +571 -231
- package/dist/src/orchestrators/multimodal/determinism.js +244 -0
- package/dist/src/orchestrators/multimodal/harness.js +149 -78
- package/dist/src/orchestrators/multimodal/providers.js +44 -3
- package/dist/src/skills/recursive-agent-orchestrator.js +2 -4
- package/dist/src/utils/hook-installer.js +0 -8
- package/dist/src/utils/process-cleanup.js +1 -7
- package/docs/README.md +42 -0
- package/docs/guides/README_INSTALL.md +208 -0
- package/package.json +27 -9
- package/packs/coding/python-fastapi/instructions.md +60 -0
- package/packs/coding/python-fastapi/pack.yaml +28 -0
- package/packs/coding/typescript-react/instructions.md +47 -0
- package/packs/coding/typescript-react/pack.yaml +28 -0
- package/packs/core/commands/capture.md +32 -0
- package/packs/core/commands/learn.md +73 -0
- package/packs/core/commands/next.md +36 -0
- package/packs/core/commands/restart.md +58 -0
- package/packs/core/commands/restore.md +29 -0
- package/packs/core/commands/start.md +57 -0
- package/packs/core/commands/stop.md +65 -0
- package/packs/core/commands/summary.md +40 -0
- package/packs/core/manifest.json +24 -0
- package/packs/ops/decision-recovery/instructions.md +65 -0
- package/packs/ops/decision-recovery/pack.yaml +89 -0
- package/scripts/claude-code-wrapper.sh +11 -0
- package/scripts/claude-sm-setup.sh +12 -1
- package/scripts/codex-wrapper.sh +11 -0
- package/scripts/git-hooks/branch-context-manager.sh +11 -0
- package/scripts/git-hooks/post-checkout-stackmemory.sh +11 -0
- package/scripts/git-hooks/post-commit-stackmemory.sh +11 -0
- package/scripts/git-hooks/pre-commit-stackmemory.sh +11 -0
- package/scripts/hooks/cleanup-shell.sh +12 -1
- package/scripts/hooks/task-complete.sh +12 -1
- package/scripts/install-code-execution-hooks.sh +12 -1
- package/scripts/install-sweep-hook.sh +12 -0
- package/scripts/install.sh +11 -0
- package/scripts/opencode-wrapper.sh +11 -0
- package/scripts/portal/cloud-init.yaml +69 -0
- package/scripts/portal/setup.sh +69 -0
- package/scripts/portal/stackmemory-portal.service +34 -0
- package/scripts/setup-claude-integration.sh +12 -1
- package/scripts/smoke-init-db.sh +23 -0
- package/scripts/stackmemory-daemon.sh +11 -0
- package/scripts/verify-dist.cjs +11 -4
- package/dist/src/cli/commands/ralph.js +0 -1053
- package/dist/src/cli/commands/team.js +0 -168
- package/dist/src/core/context/shared-context-layer.js +0 -620
- package/dist/src/core/context/stack-merge-resolver.js +0 -748
- package/dist/src/core/merge/conflict-detector.js +0 -430
- package/dist/src/core/merge/resolution-engine.js +0 -557
- package/dist/src/core/merge/stack-diff.js +0 -531
- package/dist/src/core/merge/unified-merge-resolver.js +0 -302
- package/dist/src/hooks/diffmem-hooks.js +0 -376
- package/dist/src/integrations/diffmem/client.js +0 -208
- package/dist/src/integrations/diffmem/config.js +0 -14
- package/dist/src/integrations/greptile/client.js +0 -101
- package/dist/src/integrations/greptile/config.js +0 -14
- package/dist/src/integrations/greptile/index.js +0 -11
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
- package/dist/src/integrations/mcp/handlers/diffmem-handlers.js +0 -455
- package/dist/src/integrations/mcp/handlers/greptile-handlers.js +0 -456
- package/dist/src/integrations/mcp/handlers/provider-handlers.js +0 -227
- package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
- package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +0 -863
- package/dist/src/integrations/ralph/context/context-budget-manager.js +0 -308
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +0 -391
- package/dist/src/integrations/ralph/index.js +0 -17
- package/dist/src/integrations/ralph/learning/pattern-learner.js +0 -435
- package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +0 -448
- package/dist/src/integrations/ralph/loopmax.js +0 -488
- package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +0 -293
- package/dist/src/integrations/ralph/monitoring/swarm-registry.js +0 -107
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +0 -508
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +0 -407
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +0 -495
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +0 -387
- package/dist/src/integrations/ralph/performance/performance-optimizer.js +0 -357
- package/dist/src/integrations/ralph/recovery/crash-recovery.js +0 -461
- package/dist/src/integrations/ralph/state/state-reconciler.js +0 -420
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +0 -444
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -1007
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +0 -635
- package/scripts/ralph-loop-implementation.js +0 -404
- /package/dist/src/core/{merge → cache}/types.js +0 -0
- /package/dist/src/{integrations/diffmem/types.js → core/storage/cloud-sync-types.js} +0 -0
- /package/dist/src/{integrations/greptile/types.js → core/trace/trace-event.js} +0 -0
- /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
|
+
};
|