@stackmemoryai/stackmemory 1.10.5 → 1.12.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 (92) hide show
  1. package/README.md +104 -23
  2. package/dist/src/cli/claude-sm.js +266 -84
  3. package/dist/src/cli/codex-sm.js +185 -33
  4. package/dist/src/cli/commands/bench.js +209 -2
  5. package/dist/src/cli/commands/cache.js +126 -0
  6. package/dist/src/cli/commands/daemon.js +41 -0
  7. package/dist/src/cli/commands/handoff.js +40 -9
  8. package/dist/src/cli/commands/onboard.js +70 -3
  9. package/dist/src/cli/commands/optimize.js +117 -0
  10. package/dist/src/cli/commands/orchestrate.js +230 -5
  11. package/dist/src/cli/commands/orchestrator.js +312 -24
  12. package/dist/src/cli/commands/pack.js +322 -0
  13. package/dist/src/cli/commands/search.js +40 -1
  14. package/dist/src/cli/commands/setup.js +177 -7
  15. package/dist/src/cli/commands/skills.js +10 -1
  16. package/dist/src/cli/commands/state.js +265 -0
  17. package/dist/src/cli/gemini-sm.js +19 -29
  18. package/dist/src/cli/index.js +90 -29
  19. package/dist/src/cli/opencode-sm.js +38 -21
  20. package/dist/src/cli/utils/determinism-watcher.js +66 -0
  21. package/dist/src/cli/utils/real-cli-bin.js +44 -0
  22. package/dist/src/core/cache/content-cache.js +238 -0
  23. package/dist/src/core/cache/index.js +11 -0
  24. package/dist/src/core/cache/token-estimator.js +16 -0
  25. package/dist/src/core/context/frame-database.js +38 -30
  26. package/dist/src/core/cross-search/cross-project-search.js +269 -0
  27. package/dist/src/core/{merge → cross-search}/index.js +6 -4
  28. package/dist/src/core/database/sqlite-adapter.js +0 -83
  29. package/dist/src/core/extensions/provider-adapter.js +5 -0
  30. package/dist/src/core/models/model-router.js +22 -2
  31. package/dist/src/core/monitoring/logger.js +2 -1
  32. package/dist/src/core/optimization/trace-optimizer.js +413 -0
  33. package/dist/src/core/provenance/confidence-scorer.js +128 -0
  34. package/dist/src/core/provenance/index.js +40 -0
  35. package/dist/src/core/provenance/provenance-store.js +194 -0
  36. package/dist/src/core/provenance/types.js +82 -0
  37. package/dist/src/core/session/project-handoff.js +64 -0
  38. package/dist/src/core/session/session-manager.js +28 -0
  39. package/dist/src/core/shared-state/canonical-store.js +564 -0
  40. package/dist/src/core/skill-packs/index.js +18 -0
  41. package/dist/src/core/skill-packs/parser.js +42 -0
  42. package/dist/src/core/skill-packs/registry.js +224 -0
  43. package/dist/src/core/skill-packs/types.js +66 -0
  44. package/dist/src/core/trace/trace-event-store.js +282 -0
  45. package/dist/src/core/trace/trace-event.js +4 -0
  46. package/dist/src/daemon/daemon-config.js +7 -0
  47. package/dist/src/daemon/services/github-service.js +126 -0
  48. package/dist/src/daemon/unified-daemon.js +30 -0
  49. package/dist/src/features/sweep/pty-wrapper.js +13 -5
  50. package/dist/src/hooks/schemas.js +2 -0
  51. package/dist/src/integrations/claude-code/subagent-client.js +89 -0
  52. package/dist/src/integrations/github/pr-state.js +158 -0
  53. package/dist/src/integrations/linear/client.js +4 -1
  54. package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +188 -0
  55. package/dist/src/integrations/mcp/handlers/index.js +40 -59
  56. package/dist/src/integrations/mcp/server.js +425 -311
  57. package/dist/src/integrations/mcp/tool-alias-registry.js +370 -0
  58. package/dist/src/integrations/mcp/tool-definitions.js +98 -229
  59. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +3 -40
  60. package/dist/src/integrations/ralph/learning/pattern-learner.js +1 -20
  61. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -2
  62. package/dist/src/mcp/stackmemory-mcp-server.js +315 -0
  63. package/dist/src/orchestrators/multimodal/determinism.js +243 -0
  64. package/dist/src/orchestrators/multimodal/harness.js +147 -77
  65. package/dist/src/orchestrators/multimodal/providers.js +44 -3
  66. package/dist/src/utils/hook-installer.js +0 -8
  67. package/package.json +10 -1
  68. package/packs/coding/python-fastapi/instructions.md +60 -0
  69. package/packs/coding/python-fastapi/pack.yaml +28 -0
  70. package/packs/coding/typescript-react/instructions.md +47 -0
  71. package/packs/coding/typescript-react/pack.yaml +28 -0
  72. package/packs/core/commands/capture.md +32 -0
  73. package/packs/core/commands/learn.md +73 -0
  74. package/packs/core/commands/next.md +36 -0
  75. package/packs/core/commands/restart.md +58 -0
  76. package/packs/core/commands/restore.md +29 -0
  77. package/packs/core/commands/start.md +57 -0
  78. package/packs/core/commands/stop.md +65 -0
  79. package/packs/core/commands/summary.md +40 -0
  80. package/packs/core/manifest.json +24 -0
  81. package/packs/ops/decision-recovery/instructions.md +65 -0
  82. package/packs/ops/decision-recovery/pack.yaml +89 -0
  83. package/dist/src/cli/commands/team.js +0 -168
  84. package/dist/src/core/context/shared-context-layer.js +0 -620
  85. package/dist/src/core/context/stack-merge-resolver.js +0 -748
  86. package/dist/src/core/merge/conflict-detector.js +0 -430
  87. package/dist/src/core/merge/resolution-engine.js +0 -557
  88. package/dist/src/core/merge/stack-diff.js +0 -531
  89. package/dist/src/core/merge/unified-merge-resolver.js +0 -302
  90. package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
  91. package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
  92. /package/dist/src/core/{merge → cache}/types.js +0 -0
@@ -0,0 +1,413 @@
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 { existsSync, mkdirSync, writeFileSync } from "fs";
6
+ import { join } from "path";
7
+ const DEFAULT_OPTIONS = {
8
+ lookbackDays: 30,
9
+ minOccurrences: 2,
10
+ maxExamples: 3
11
+ };
12
+ const MUTATING_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "multi_edit"]);
13
+ const SEARCH_TOOLS = /* @__PURE__ */ new Set(["search", "grep", "read", "glob", "find"]);
14
+ const VERIFICATION_TOOLS = /* @__PURE__ */ new Set([
15
+ "test",
16
+ "bash",
17
+ "lint",
18
+ "build",
19
+ "npm",
20
+ "pytest",
21
+ "vitest",
22
+ "jest"
23
+ ]);
24
+ function classifyErrorText(text) {
25
+ const lower = text.toLowerCase();
26
+ if (lower.includes("lint") || lower.includes("eslint") || lower.includes("prettier")) {
27
+ return "lint_failure";
28
+ }
29
+ if (lower.includes("test") && (lower.includes("fail") || lower.includes("error"))) {
30
+ return "test_failure";
31
+ }
32
+ if (lower.includes("timeout") || lower.includes("timed out")) {
33
+ return "timeout";
34
+ }
35
+ if (lower.includes("rate limit") || lower.includes("429")) {
36
+ return "rate_limit";
37
+ }
38
+ if (lower.includes("permission") || lower.includes("eacces")) {
39
+ return "permission_failure";
40
+ }
41
+ if (lower.includes("build") && lower.includes("error")) {
42
+ return "build_failure";
43
+ }
44
+ return "unknown_failure";
45
+ }
46
+ function uniq(items) {
47
+ return [...new Set(items)];
48
+ }
49
+ function truncate(value, max = 120) {
50
+ return value.length > max ? `${value.slice(0, max - 1)}\u2026` : value;
51
+ }
52
+ function toolPattern(trace) {
53
+ return trace.tools.map((tool) => tool.tool).join("\u2192");
54
+ }
55
+ function hasVerification(trace) {
56
+ return trace.tools.some((tool) => VERIFICATION_TOOLS.has(tool.tool));
57
+ }
58
+ function hasMutation(trace) {
59
+ return trace.tools.some((tool) => MUTATING_TOOLS.has(tool.tool));
60
+ }
61
+ function countSearchTools(trace) {
62
+ return trace.tools.filter((tool) => SEARCH_TOOLS.has(tool.tool)).length;
63
+ }
64
+ function countRepeatedFailingTools(trace) {
65
+ const counts = /* @__PURE__ */ new Map();
66
+ for (const tool of trace.tools) {
67
+ if (!tool.error) continue;
68
+ counts.set(tool.tool, (counts.get(tool.tool) || 0) + 1);
69
+ }
70
+ let max = 0;
71
+ for (const value of counts.values()) {
72
+ if (value > max) max = value;
73
+ }
74
+ return max;
75
+ }
76
+ function createAccumulator(kind, label, targetAreas, actions, validations) {
77
+ return {
78
+ kind,
79
+ label,
80
+ traceIds: /* @__PURE__ */ new Set(),
81
+ summaries: [],
82
+ affectedFiles: /* @__PURE__ */ new Set(),
83
+ toolPatterns: /* @__PURE__ */ new Set(),
84
+ targetAreas: new Set(targetAreas),
85
+ actions,
86
+ validations
87
+ };
88
+ }
89
+ function pushTraceEvidence(bucket, trace, maxExamples) {
90
+ bucket.traceIds.add(trace.id);
91
+ if (bucket.summaries.length < maxExamples) {
92
+ bucket.summaries.push(truncate(trace.summary));
93
+ }
94
+ for (const file of trace.metadata.filesModified) {
95
+ bucket.affectedFiles.add(file);
96
+ }
97
+ bucket.toolPatterns.add(toolPattern(trace));
98
+ }
99
+ function buildCluster(id, bucket) {
100
+ return {
101
+ id,
102
+ kind: bucket.kind,
103
+ label: bucket.label,
104
+ occurrences: bucket.traceIds.size,
105
+ traceIds: [...bucket.traceIds],
106
+ sampleSummaries: bucket.summaries,
107
+ affectedFiles: [...bucket.affectedFiles].sort(),
108
+ toolPatterns: [...bucket.toolPatterns].sort(),
109
+ targetAreas: [...bucket.targetAreas],
110
+ actions: bucket.actions,
111
+ validations: bucket.validations
112
+ };
113
+ }
114
+ function recommendationForCluster(cluster) {
115
+ const highPriority = cluster.kind === "error_pattern" || cluster.occurrences >= 3;
116
+ return {
117
+ id: `rec-${cluster.id}`,
118
+ title: cluster.label,
119
+ priority: highPriority ? "high" : "medium",
120
+ confidence: Math.min(0.45 + cluster.occurrences * 0.12, 0.95),
121
+ summary: `${cluster.label} appeared in ${cluster.occurrences} trace${cluster.occurrences === 1 ? "" : "s"}. Focus on ${cluster.targetAreas.join(", ")} first.`,
122
+ targetAreas: cluster.targetAreas,
123
+ actions: cluster.actions,
124
+ validations: cluster.validations,
125
+ supportingClusters: [cluster.id]
126
+ };
127
+ }
128
+ class TraceOptimizer {
129
+ constructor(traceStore) {
130
+ this.traceStore = traceStore;
131
+ }
132
+ analyze(options = {}) {
133
+ const config = { ...DEFAULT_OPTIONS, ...options };
134
+ const cutoff = Date.now() - config.lookbackDays * 24 * 60 * 60 * 1e3;
135
+ const traces = this.traceStore.getAllTraces().filter((trace) => trace.metadata.startTime >= cutoff);
136
+ const clusters = this.buildClusters(traces, config);
137
+ const recommendations = clusters.map(recommendationForCluster);
138
+ const tracesByType = {};
139
+ for (const trace of traces) {
140
+ tracesByType[trace.type] = (tracesByType[trace.type] || 0) + 1;
141
+ }
142
+ const averageToolsPerTrace = traces.length > 0 ? traces.reduce((sum, trace) => sum + trace.tools.length, 0) / traces.length : 0;
143
+ const averageTraceScore = traces.length > 0 ? traces.reduce((sum, trace) => sum + trace.score, 0) / traces.length : 0;
144
+ return {
145
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
146
+ lookbackDays: config.lookbackDays,
147
+ totalTracesAnalyzed: traces.length,
148
+ tracesWithErrors: traces.filter(hasErrors).length,
149
+ causalTraces: traces.filter((trace) => trace.metadata.causalChain).length,
150
+ averageToolsPerTrace,
151
+ averageTraceScore,
152
+ tracesByType,
153
+ clusters,
154
+ recommendations
155
+ };
156
+ }
157
+ persistReport(projectRoot, report) {
158
+ const outputDir = join(projectRoot, ".stackmemory", "build");
159
+ if (!existsSync(outputDir)) {
160
+ mkdirSync(outputDir, { recursive: true });
161
+ }
162
+ const jsonPath = join(outputDir, "trace-optimizer-latest.json");
163
+ const markdownPath = join(outputDir, "trace-optimizer-latest.md");
164
+ writeFileSync(jsonPath, `${JSON.stringify(report, null, 2)}
165
+ `, "utf8");
166
+ writeFileSync(markdownPath, renderMarkdownReport(report), "utf8");
167
+ return { jsonPath, markdownPath };
168
+ }
169
+ buildClusters(traces, options) {
170
+ const buckets = /* @__PURE__ */ new Map();
171
+ for (const trace of traces) {
172
+ const errors = extractErrors(trace);
173
+ for (const error of errors) {
174
+ const code = classifyErrorText(error);
175
+ const key = `error:${code}`;
176
+ if (!buckets.has(key)) {
177
+ buckets.set(
178
+ key,
179
+ createAccumulator(
180
+ "error_pattern",
181
+ labelForError(code),
182
+ targetAreasForError(code),
183
+ actionsForError(code),
184
+ validationsForError(code)
185
+ )
186
+ );
187
+ }
188
+ pushTraceEvidence(buckets.get(key), trace, options.maxExamples);
189
+ }
190
+ if (hasMutation(trace) && !hasVerification(trace)) {
191
+ const key = "verification_gap";
192
+ if (!buckets.has(key)) {
193
+ buckets.set(
194
+ key,
195
+ createAccumulator(
196
+ "verification_gap",
197
+ "Mutating traces often finish without an explicit verification step",
198
+ ["hooks", "wrappers", "orchestrator"],
199
+ [
200
+ "Add a post-edit verification policy that requires targeted test, lint, or build execution before a task is considered complete.",
201
+ "Teach wrappers and agent prompts to prefer the smallest validating command after edits instead of stopping at file changes."
202
+ ],
203
+ [
204
+ "npm run test:run",
205
+ "npm run lint",
206
+ "stackmemory bench determinism --latest --json"
207
+ ]
208
+ )
209
+ );
210
+ }
211
+ pushTraceEvidence(buckets.get(key), trace, options.maxExamples);
212
+ }
213
+ if (countRepeatedFailingTools(trace) >= 2) {
214
+ const key = "retry_loop";
215
+ if (!buckets.has(key)) {
216
+ buckets.set(
217
+ key,
218
+ createAccumulator(
219
+ "retry_loop",
220
+ "Failing tools are being retried in loops instead of changing strategy",
221
+ ["orchestrator", "hooks", "prompts"],
222
+ [
223
+ "Add retry guards that trigger diagnosis or fallback prompts after the second failing attempt of the same tool.",
224
+ "Capture the first failure reason and inject it into the next planning step so the harness pivots instead of repeating."
225
+ ],
226
+ ["npm run determinism:test", "stackmemory conductor trace-stats"]
227
+ )
228
+ );
229
+ }
230
+ pushTraceEvidence(buckets.get(key), trace, options.maxExamples);
231
+ }
232
+ if (countSearchTools(trace) >= 4 && !hasMutation(trace) && trace.tools.length >= 5) {
233
+ const key = "context_thrash";
234
+ if (!buckets.has(key)) {
235
+ buckets.set(
236
+ key,
237
+ createAccumulator(
238
+ "context_thrash",
239
+ "Search-heavy traces suggest context assembly or retrieval is too weak",
240
+ ["retrieval", "hooks", "context bundling"],
241
+ [
242
+ "Promote recurring search\u2192read loops into explicit retrieval bundles or preloaded context packets.",
243
+ "Use trace summaries to precompute likely files, anchors, or commands for similar future tasks."
244
+ ],
245
+ ["python scripts/dspy/eval.py", "stackmemory retrieval stats"]
246
+ )
247
+ );
248
+ }
249
+ pushTraceEvidence(buckets.get(key), trace, options.maxExamples);
250
+ }
251
+ }
252
+ return [...buckets.entries()].map(([id, bucket]) => buildCluster(id, bucket)).filter((cluster) => cluster.occurrences >= options.minOccurrences).sort((a, b) => {
253
+ if (b.occurrences !== a.occurrences) {
254
+ return b.occurrences - a.occurrences;
255
+ }
256
+ return a.label.localeCompare(b.label);
257
+ });
258
+ }
259
+ }
260
+ function hasErrors(trace) {
261
+ return extractErrors(trace).length > 0;
262
+ }
263
+ function extractErrors(trace) {
264
+ const toolErrors = trace.tools.map((tool) => tool.error).filter((value) => Boolean(value));
265
+ return uniq([...trace.metadata.errorsEncountered, ...toolErrors]);
266
+ }
267
+ function labelForError(code) {
268
+ switch (code) {
269
+ case "lint_failure":
270
+ return "Lint failures recur across traces and should be gated earlier";
271
+ case "test_failure":
272
+ return "Test failures recur and need tighter edit-time validation";
273
+ case "timeout":
274
+ return "Timeouts recur and need fallback or budget-aware orchestration";
275
+ case "rate_limit":
276
+ return "Rate-limit failures recur and need backoff-aware retry policies";
277
+ case "permission_failure":
278
+ return "Permission or missing-file failures recur and need environment preflight checks";
279
+ case "build_failure":
280
+ return "Build failures recur and should be surfaced before finalization";
281
+ default:
282
+ return "Unclassified failures recur and need structured diagnosis";
283
+ }
284
+ }
285
+ function targetAreasForError(code) {
286
+ switch (code) {
287
+ case "lint_failure":
288
+ case "test_failure":
289
+ case "build_failure":
290
+ return ["hooks", "wrappers", "verification"];
291
+ case "timeout":
292
+ case "rate_limit":
293
+ return ["orchestrator", "fallbacks", "retry policy"];
294
+ case "permission_failure":
295
+ return ["setup", "hooks", "environment checks"];
296
+ default:
297
+ return ["orchestrator", "prompts", "diagnostics"];
298
+ }
299
+ }
300
+ function actionsForError(code) {
301
+ switch (code) {
302
+ case "lint_failure":
303
+ return [
304
+ "Insert a fast lint or formatting guard after edits when touched files match configured source globs.",
305
+ "Surface the exact lint failure in the next planning turn so the harness repairs before moving on."
306
+ ];
307
+ case "test_failure":
308
+ return [
309
+ "Require targeted test execution after code edits in code paths that already have tests.",
310
+ "Teach the orchestrator to stop on the second failing test loop and switch to diagnosis mode."
311
+ ];
312
+ case "timeout":
313
+ return [
314
+ "Add time-budget-aware fallbacks and cut off repeated long-running commands sooner.",
315
+ "Persist timeout causes so later attempts can shorten prompts, narrow file scopes, or switch models."
316
+ ];
317
+ case "rate_limit":
318
+ return [
319
+ "Back off and downgrade to a cheaper model or cached context path after a rate-limit event.",
320
+ "Record rate-limit state in session context so retry attempts do not immediately hit the same ceiling."
321
+ ];
322
+ case "permission_failure":
323
+ return [
324
+ "Run environment and path preflight checks before invoking tools that assume local binaries or files exist.",
325
+ "Convert common permission failures into actionable setup hints instead of opaque retries."
326
+ ];
327
+ case "build_failure":
328
+ return [
329
+ "Add a build gate for changes that touch runtime entrypoints, package manifests, or bundler config.",
330
+ "Capture compiler diagnostics into the next repair prompt instead of relying on the model to infer them."
331
+ ];
332
+ default:
333
+ return [
334
+ "Capture richer error metadata and turn repeated failures into a structured diagnosis step.",
335
+ "Route repeated unknown failures through a narrower repair prompt instead of repeating the same harness path."
336
+ ];
337
+ }
338
+ }
339
+ function validationsForError(code) {
340
+ switch (code) {
341
+ case "lint_failure":
342
+ return ["npm run lint", "npm run test:run"];
343
+ case "test_failure":
344
+ return ["npm run test:run", "npm run determinism:test"];
345
+ case "build_failure":
346
+ return ["npm run build", "npm run test:run"];
347
+ case "timeout":
348
+ return [
349
+ "stackmemory conductor trace-stats",
350
+ "npm run determinism:latest"
351
+ ];
352
+ case "rate_limit":
353
+ return ["stackmemory conductor trace-stats"];
354
+ case "permission_failure":
355
+ return ["stackmemory doctor", "npm run test:smoke-db"];
356
+ default:
357
+ return ["npm run test:run"];
358
+ }
359
+ }
360
+ function renderMarkdownReport(report) {
361
+ const lines = [];
362
+ lines.push("# Trace Optimizer Report");
363
+ lines.push("");
364
+ lines.push(`Generated: ${report.generatedAt}`);
365
+ lines.push(`Lookback: ${report.lookbackDays} day(s)`);
366
+ lines.push("");
367
+ lines.push("## Summary");
368
+ lines.push(`- Traces analyzed: ${report.totalTracesAnalyzed}`);
369
+ lines.push(`- Traces with errors: ${report.tracesWithErrors}`);
370
+ lines.push(`- Causal traces: ${report.causalTraces}`);
371
+ lines.push(`- Avg tools/trace: ${report.averageToolsPerTrace.toFixed(2)}`);
372
+ lines.push(`- Avg trace score: ${report.averageTraceScore.toFixed(2)}`);
373
+ lines.push("");
374
+ lines.push("## Recommendations");
375
+ if (report.recommendations.length === 0) {
376
+ lines.push("- No repeated failure patterns crossed the current threshold.");
377
+ } else {
378
+ for (const recommendation of report.recommendations) {
379
+ lines.push(
380
+ `- ${recommendation.title} (${recommendation.priority}, confidence ${recommendation.confidence.toFixed(2)})`
381
+ );
382
+ lines.push(` Summary: ${recommendation.summary}`);
383
+ lines.push(` Targets: ${recommendation.targetAreas.join(", ")}`);
384
+ lines.push(` Actions: ${recommendation.actions.join(" | ")}`);
385
+ lines.push(` Validate: ${recommendation.validations.join(" | ")}`);
386
+ }
387
+ }
388
+ lines.push("");
389
+ lines.push("## Clusters");
390
+ if (report.clusters.length === 0) {
391
+ lines.push("- No clusters found.");
392
+ } else {
393
+ for (const cluster of report.clusters) {
394
+ lines.push(`### ${cluster.label}`);
395
+ lines.push(`- Occurrences: ${cluster.occurrences}`);
396
+ lines.push(`- Kind: ${cluster.kind}`);
397
+ lines.push(
398
+ `- Tool patterns: ${cluster.toolPatterns.join(", ") || "n/a"}`
399
+ );
400
+ lines.push(`- Files: ${cluster.affectedFiles.join(", ") || "n/a"}`);
401
+ lines.push(
402
+ `- Sample traces: ${cluster.sampleSummaries.join(" | ") || "n/a"}`
403
+ );
404
+ lines.push("");
405
+ }
406
+ }
407
+ return `${lines.join("\n")}
408
+ `;
409
+ }
410
+ export {
411
+ TraceOptimizer,
412
+ renderMarkdownReport
413
+ };
@@ -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
+ };