@jungjaehoon/mama-core 1.4.2 → 1.6.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 (181) hide show
  1. package/README.md +48 -10
  2. package/db/migrations/026-create-canonical-entity-tables.sql +99 -0
  3. package/db/migrations/027-create-entity-review-support.sql +44 -0
  4. package/db/migrations/028-create-entity-audit-tables.sql +45 -0
  5. package/db/migrations/029-extend-entity-observations-observation-type.sql +60 -0
  6. package/db/migrations/030-case-first-memory-substrate.sql +901 -0
  7. package/db/migrations/031-wiki-page-index-provenance-columns.sql +2 -0
  8. package/db/migrations/032-add-memory-provenance-columns.sql +21 -0
  9. package/db/migrations/033-create-model-runs-and-tool-traces.sql +53 -0
  10. package/db/migrations/034-add-connector-event-scope-columns.sql +14 -0
  11. package/db/migrations/035-create-twin-edges.sql +63 -0
  12. package/db/migrations/036-create-agent-situation-packets.sql +61 -0
  13. package/dist/agent-graph/alias-write.d.ts +3 -0
  14. package/dist/agent-graph/alias-write.js +231 -0
  15. package/dist/agent-graph/entity-resolve.d.ts +3 -0
  16. package/dist/agent-graph/entity-resolve.js +275 -0
  17. package/dist/agent-graph/errors.d.ts +4 -0
  18. package/dist/agent-graph/errors.js +11 -0
  19. package/dist/agent-graph/graph-query.d.ts +5 -0
  20. package/dist/agent-graph/graph-query.js +359 -0
  21. package/dist/agent-graph/index.d.ts +6 -0
  22. package/dist/agent-graph/index.js +22 -0
  23. package/dist/agent-graph/types.d.ts +162 -0
  24. package/dist/agent-graph/types.js +3 -0
  25. package/dist/agent-situation/builder.d.ts +8 -0
  26. package/dist/agent-situation/builder.js +239 -0
  27. package/dist/agent-situation/cache-key.d.ts +4 -0
  28. package/dist/agent-situation/cache-key.js +64 -0
  29. package/dist/agent-situation/index.d.ts +7 -0
  30. package/dist/agent-situation/index.js +28 -0
  31. package/dist/agent-situation/packet-store.d.ts +27 -0
  32. package/dist/agent-situation/packet-store.js +228 -0
  33. package/dist/agent-situation/ranking-policy.d.ts +18 -0
  34. package/dist/agent-situation/ranking-policy.js +75 -0
  35. package/dist/agent-situation/source-readers.d.ts +52 -0
  36. package/dist/agent-situation/source-readers.js +300 -0
  37. package/dist/agent-situation/types.d.ts +195 -0
  38. package/dist/agent-situation/types.js +3 -0
  39. package/dist/canonicalize.d.ts +38 -0
  40. package/dist/canonicalize.js +106 -0
  41. package/dist/cases/case-links.d.ts +116 -0
  42. package/dist/cases/case-links.js +794 -0
  43. package/dist/cases/composition-overrides.d.ts +90 -0
  44. package/dist/cases/composition-overrides.js +724 -0
  45. package/dist/cases/corrections.d.ts +156 -0
  46. package/dist/cases/corrections.js +1045 -0
  47. package/dist/cases/freshness.d.ts +57 -0
  48. package/dist/cases/freshness.js +283 -0
  49. package/dist/cases/live-state.d.ts +48 -0
  50. package/dist/cases/live-state.js +256 -0
  51. package/dist/cases/membership-explain.d.ts +56 -0
  52. package/dist/cases/membership-explain.js +140 -0
  53. package/dist/cases/membership-matcher.d.ts +23 -0
  54. package/dist/cases/membership-matcher.js +193 -0
  55. package/dist/cases/merge-split.d.ts +61 -0
  56. package/dist/cases/merge-split.js +368 -0
  57. package/dist/cases/role-inference.d.ts +10 -0
  58. package/dist/cases/role-inference.js +85 -0
  59. package/dist/cases/search-rollup.d.ts +27 -0
  60. package/dist/cases/search-rollup.js +309 -0
  61. package/dist/cases/sqlite-transaction.d.ts +10 -0
  62. package/dist/cases/sqlite-transaction.js +62 -0
  63. package/dist/cases/store.d.ts +74 -0
  64. package/dist/cases/store.js +816 -0
  65. package/dist/cases/target-ref.d.ts +25 -0
  66. package/dist/cases/target-ref.js +22 -0
  67. package/dist/cases/timeline-range.d.ts +6 -0
  68. package/dist/cases/timeline-range.js +322 -0
  69. package/dist/cases/tombstone-sweeper.d.ts +13 -0
  70. package/dist/cases/tombstone-sweeper.js +128 -0
  71. package/dist/cases/types.d.ts +244 -0
  72. package/dist/cases/types.js +47 -0
  73. package/dist/cases/wiki-page-index.d.ts +50 -0
  74. package/dist/cases/wiki-page-index.js +350 -0
  75. package/dist/connectors/event-index.d.ts +45 -0
  76. package/dist/connectors/event-index.js +381 -0
  77. package/dist/connectors/raw-query.d.ts +18 -0
  78. package/dist/connectors/raw-query.js +305 -0
  79. package/dist/connectors/types.d.ts +109 -0
  80. package/dist/connectors/types.js +3 -0
  81. package/dist/db-adapter/node-sqlite-adapter.d.ts +6 -0
  82. package/dist/db-adapter/node-sqlite-adapter.js +131 -0
  83. package/dist/db-manager.d.ts +12 -1
  84. package/dist/db-manager.js +60 -5
  85. package/dist/decision-formatter.d.ts +2 -0
  86. package/dist/decision-formatter.js +11 -8
  87. package/dist/edges/ref-validation.d.ts +8 -0
  88. package/dist/edges/ref-validation.js +262 -0
  89. package/dist/edges/store.d.ts +10 -0
  90. package/dist/edges/store.js +343 -0
  91. package/dist/edges/types.d.ts +86 -0
  92. package/dist/edges/types.js +18 -0
  93. package/dist/embedding-server/mobile/daemon.d.ts +1 -0
  94. package/dist/embedding-server/mobile/daemon.js +11 -1
  95. package/dist/embeddings.js +13 -0
  96. package/dist/entities/audit-metrics.d.ts +73 -0
  97. package/dist/entities/audit-metrics.js +128 -0
  98. package/dist/entities/candidate-generator.d.ts +9 -0
  99. package/dist/entities/candidate-generator.js +436 -0
  100. package/dist/entities/entity-impact.d.ts +41 -0
  101. package/dist/entities/entity-impact.js +140 -0
  102. package/dist/entities/entity-linked-decision-counts.d.ts +2 -0
  103. package/dist/entities/entity-linked-decision-counts.js +29 -0
  104. package/dist/entities/entity-list.d.ts +23 -0
  105. package/dist/entities/entity-list.js +99 -0
  106. package/dist/entities/entity-orphan-list.d.ts +26 -0
  107. package/dist/entities/entity-orphan-list.js +91 -0
  108. package/dist/entities/entity-search.d.ts +21 -0
  109. package/dist/entities/entity-search.js +150 -0
  110. package/dist/entities/errors.d.ts +65 -0
  111. package/dist/entities/errors.js +124 -0
  112. package/dist/entities/exact-merge-backfill.d.ts +11 -0
  113. package/dist/entities/exact-merge-backfill.js +161 -0
  114. package/dist/entities/lineage-backfill.d.ts +12 -0
  115. package/dist/entities/lineage-backfill.js +101 -0
  116. package/dist/entities/lineage-store.d.ts +51 -0
  117. package/dist/entities/lineage-store.js +308 -0
  118. package/dist/entities/normalization.d.ts +14 -0
  119. package/dist/entities/normalization.js +63 -0
  120. package/dist/entities/policy-store.d.ts +17 -0
  121. package/dist/entities/policy-store.js +263 -0
  122. package/dist/entities/policy-types.d.ts +72 -0
  123. package/dist/entities/policy-types.js +59 -0
  124. package/dist/entities/projection.d.ts +8 -0
  125. package/dist/entities/projection.js +82 -0
  126. package/dist/entities/provenance-query.d.ts +33 -0
  127. package/dist/entities/provenance-query.js +167 -0
  128. package/dist/entities/read-identity.d.ts +31 -0
  129. package/dist/entities/read-identity.js +264 -0
  130. package/dist/entities/recall-bridge.d.ts +5 -0
  131. package/dist/entities/recall-bridge.js +171 -0
  132. package/dist/entities/resolution-engine.d.ts +15 -0
  133. package/dist/entities/resolution-engine.js +69 -0
  134. package/dist/entities/rollback-preview.d.ts +39 -0
  135. package/dist/entities/rollback-preview.js +235 -0
  136. package/dist/entities/source-locator.d.ts +3 -0
  137. package/dist/entities/source-locator.js +32 -0
  138. package/dist/entities/store.d.ts +75 -0
  139. package/dist/entities/store.js +465 -0
  140. package/dist/entities/types.d.ts +162 -0
  141. package/dist/entities/types.js +30 -0
  142. package/dist/index.d.ts +62 -3
  143. package/dist/index.js +116 -26
  144. package/dist/mama-api.d.ts +53 -35
  145. package/dist/mama-api.js +437 -78
  146. package/dist/memory/api.d.ts +26 -38
  147. package/dist/memory/api.js +772 -68
  148. package/dist/memory/event-store.d.ts +3 -0
  149. package/dist/memory/event-store.js +21 -2
  150. package/dist/memory/provenance-audit.d.ts +22 -0
  151. package/dist/memory/provenance-audit.js +112 -0
  152. package/dist/memory/provenance-query.d.ts +11 -0
  153. package/dist/memory/provenance-query.js +193 -0
  154. package/dist/memory/provenance.d.ts +29 -0
  155. package/dist/memory/provenance.js +136 -0
  156. package/dist/memory/scope-backfill.d.ts +17 -0
  157. package/dist/memory/scope-backfill.js +77 -0
  158. package/dist/memory/types.d.ts +97 -16
  159. package/dist/model-runs/store.d.ts +13 -0
  160. package/dist/model-runs/store.js +457 -0
  161. package/dist/model-runs/tool-trace-store.d.ts +4 -0
  162. package/dist/model-runs/tool-trace-store.js +114 -0
  163. package/dist/model-runs/types.d.ts +69 -0
  164. package/dist/model-runs/types.js +5 -0
  165. package/dist/runtime/trusted-provenance.d.ts +3 -0
  166. package/dist/runtime/trusted-provenance.js +6 -0
  167. package/dist/search/feedback-store.d.ts +59 -0
  168. package/dist/search/feedback-store.js +211 -0
  169. package/dist/search/question-type.d.ts +5 -0
  170. package/dist/search/question-type.js +41 -0
  171. package/dist/search/ranker-features.d.ts +23 -0
  172. package/dist/search/ranker-features.js +104 -0
  173. package/dist/search/ranker-rescore.d.ts +31 -0
  174. package/dist/search/ranker-rescore.js +226 -0
  175. package/dist/search/ranker-trainer.d.ts +94 -0
  176. package/dist/search/ranker-trainer.js +860 -0
  177. package/dist/search/search-quality.d.ts +36 -0
  178. package/dist/search/search-quality.js +29 -0
  179. package/dist/test-utils.d.ts +14 -0
  180. package/dist/test-utils.js +52 -0
  181. package/package.json +28 -3
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.appendToolTrace = appendToolTrace;
7
+ exports.listToolTracesForRun = listToolTracesForRun;
8
+ const node_crypto_1 = __importDefault(require("node:crypto"));
9
+ const db_manager_js_1 = require("../db-manager.js");
10
+ function traceId() {
11
+ return `tr_${node_crypto_1.default.randomUUID().replace(/-/g, '')}`;
12
+ }
13
+ function requiredString(value, field) {
14
+ if (typeof value !== 'string' || value.length === 0) {
15
+ throw new Error(`tool_traces.${field} must be a non-empty string: ${formatInvalidValue(value)}`);
16
+ }
17
+ return value;
18
+ }
19
+ function formatInvalidValue(value) {
20
+ return typeof value === 'string' ? JSON.stringify(value) : String(value);
21
+ }
22
+ function nullableString(value) {
23
+ return typeof value === 'string' && value.length > 0 ? value : null;
24
+ }
25
+ function normalizeTimestamp(value) {
26
+ if (value === undefined || value === null) {
27
+ return Date.now();
28
+ }
29
+ return requiredInteger(value, 'created_at');
30
+ }
31
+ function normalizeDuration(value) {
32
+ if (value === undefined || value === null) {
33
+ return 0;
34
+ }
35
+ return requiredNonNegativeInteger(value, 'duration_ms');
36
+ }
37
+ function requiredInteger(value, field) {
38
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
39
+ throw new Error(`tool_traces.${field} must be a finite number: ${formatInvalidValue(value)}`);
40
+ }
41
+ return Math.floor(value);
42
+ }
43
+ function requiredNonNegativeInteger(value, field) {
44
+ const normalized = requiredInteger(value, field);
45
+ if (normalized < 0) {
46
+ throw new Error(`tool_traces.${field} must be non-negative: ${formatInvalidValue(value)}`);
47
+ }
48
+ return normalized;
49
+ }
50
+ function mapToolTraceRow(row) {
51
+ return {
52
+ trace_id: requiredString(row.trace_id, 'trace_id'),
53
+ model_run_id: requiredString(row.model_run_id, 'model_run_id'),
54
+ gateway_call_id: nullableString(row.gateway_call_id),
55
+ tool_name: requiredString(row.tool_name, 'tool_name'),
56
+ input_summary: nullableString(row.input_summary),
57
+ output_summary: nullableString(row.output_summary),
58
+ execution_status: nullableString(row.execution_status),
59
+ duration_ms: requiredNonNegativeInteger(row.duration_ms, 'duration_ms'),
60
+ envelope_hash: nullableString(row.envelope_hash),
61
+ created_at: requiredInteger(row.created_at, 'created_at'),
62
+ };
63
+ }
64
+ async function initializedAdapter() {
65
+ await (0, db_manager_js_1.initDB)();
66
+ return (0, db_manager_js_1.getAdapter)();
67
+ }
68
+ function selectToolTrace(adapter, id) {
69
+ const row = adapter
70
+ .prepare(`
71
+ SELECT
72
+ trace_id, model_run_id, gateway_call_id, tool_name, input_summary,
73
+ output_summary, execution_status, duration_ms, envelope_hash, created_at
74
+ FROM tool_traces
75
+ WHERE trace_id = ?
76
+ `)
77
+ .get(id);
78
+ if (!row) {
79
+ throw new Error(`Tool trace not found: ${id}`);
80
+ }
81
+ return mapToolTraceRow(row);
82
+ }
83
+ async function appendToolTrace(input) {
84
+ const adapter = await initializedAdapter();
85
+ const id = nullableString(input.trace_id) ?? traceId();
86
+ const modelRunId = requiredString(input.model_run_id, 'model_run_id');
87
+ const toolName = requiredString(input.tool_name, 'tool_name');
88
+ adapter
89
+ .prepare(`
90
+ INSERT INTO tool_traces (
91
+ trace_id, model_run_id, gateway_call_id, tool_name, input_summary,
92
+ output_summary, execution_status, duration_ms, envelope_hash, created_at
93
+ )
94
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
95
+ `)
96
+ .run(id, modelRunId, nullableString(input.gateway_call_id), toolName, nullableString(input.input_summary), nullableString(input.output_summary), nullableString(input.execution_status), normalizeDuration(input.duration_ms), nullableString(input.envelope_hash), normalizeTimestamp(input.created_at));
97
+ return selectToolTrace(adapter, id);
98
+ }
99
+ async function listToolTracesForRun(modelRunId) {
100
+ const adapter = await initializedAdapter();
101
+ const id = requiredString(modelRunId, 'model_run_id');
102
+ const rows = adapter
103
+ .prepare(`
104
+ SELECT
105
+ trace_id, model_run_id, gateway_call_id, tool_name, input_summary,
106
+ output_summary, execution_status, duration_ms, envelope_hash, created_at
107
+ FROM tool_traces
108
+ WHERE model_run_id = ?
109
+ ORDER BY created_at DESC, rowid DESC
110
+ `)
111
+ .all(id);
112
+ return rows.map(mapToolTraceRow);
113
+ }
114
+ //# sourceMappingURL=tool-trace-store.js.map
@@ -0,0 +1,69 @@
1
+ export declare const MODEL_RUN_STATUSES: readonly ["running", "committed", "failed", "legacy"];
2
+ export type ModelRunStatus = (typeof MODEL_RUN_STATUSES)[number];
3
+ export interface BeginModelRunInput {
4
+ model_run_id?: string;
5
+ model_id?: string | null;
6
+ model_provider?: string | null;
7
+ prompt_version?: string | null;
8
+ tool_manifest_version?: string | null;
9
+ output_schema_version?: string | null;
10
+ agent_id?: string | null;
11
+ instance_id?: string | null;
12
+ envelope_hash?: string | null;
13
+ parent_model_run_id?: string | null;
14
+ input_snapshot_ref?: string | null;
15
+ input_refs?: Record<string, unknown> | null;
16
+ input_refs_json?: string | null;
17
+ status?: ModelRunStatus;
18
+ error_summary?: string | null;
19
+ token_count?: number | null;
20
+ cost_estimate?: number | null;
21
+ created_at?: number;
22
+ }
23
+ export interface ModelRunRecord {
24
+ model_run_id: string;
25
+ model_id: string | null;
26
+ model_provider: string | null;
27
+ prompt_version: string | null;
28
+ tool_manifest_version: string | null;
29
+ output_schema_version: string | null;
30
+ agent_id: string | null;
31
+ instance_id: string | null;
32
+ envelope_hash: string | null;
33
+ parent_model_run_id: string | null;
34
+ input_snapshot_ref: string | null;
35
+ input_refs_json: string | null;
36
+ input_refs: Record<string, unknown> | null;
37
+ completion_summary: string | null;
38
+ status: ModelRunStatus;
39
+ error_summary: string | null;
40
+ token_count: number;
41
+ cost_estimate: number | null;
42
+ created_at: number;
43
+ completed_at: number | null;
44
+ }
45
+ export interface AppendToolTraceInput {
46
+ trace_id?: string;
47
+ model_run_id: string;
48
+ gateway_call_id?: string | null;
49
+ tool_name: string;
50
+ input_summary?: string | null;
51
+ output_summary?: string | null;
52
+ execution_status?: string | null;
53
+ duration_ms?: number | null;
54
+ envelope_hash?: string | null;
55
+ created_at?: number;
56
+ }
57
+ export interface ToolTraceRecord {
58
+ trace_id: string;
59
+ model_run_id: string;
60
+ gateway_call_id: string | null;
61
+ tool_name: string;
62
+ input_summary: string | null;
63
+ output_summary: string | null;
64
+ execution_status: string | null;
65
+ duration_ms: number;
66
+ envelope_hash: string | null;
67
+ created_at: number;
68
+ }
69
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MODEL_RUN_STATUSES = void 0;
4
+ exports.MODEL_RUN_STATUSES = ['running', 'committed', 'failed', 'legacy'];
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,3 @@
1
+ export { createTrustedProvenanceCapability } from '../memory/provenance.js';
2
+ export type { TrustedMemoryWriteOptions, TrustedProvenanceCapability, } from '../memory/provenance.js';
3
+ //# sourceMappingURL=trusted-provenance.d.ts.map
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createTrustedProvenanceCapability = void 0;
4
+ var provenance_js_1 = require("../memory/provenance.js");
5
+ Object.defineProperty(exports, "createTrustedProvenanceCapability", { enumerable: true, get: function () { return provenance_js_1.createTrustedProvenanceCapability; } });
6
+ //# sourceMappingURL=trusted-provenance.js.map
@@ -0,0 +1,59 @@
1
+ import type { RunResult } from '../db-adapter/statement.js';
2
+ import { type QuestionType } from './question-type.js';
3
+ export type SearchFeedbackKind = 'shown' | 'accept' | 'reject' | 'hide' | 'click';
4
+ export type SearchFeedbackSourceType = 'decision' | 'checkpoint' | 'wiki_page' | 'case' | 'connector_event';
5
+ export interface SearchFeedbackInput {
6
+ result_id: string;
7
+ session_id?: string | null;
8
+ query: string;
9
+ result_source_type: SearchFeedbackSourceType;
10
+ result_source_id: string;
11
+ feedback_kind: SearchFeedbackKind;
12
+ question_type?: QuestionType | null;
13
+ shown_index: number;
14
+ result_case_id?: string | null;
15
+ clicked_result_rank?: number | null;
16
+ created_at?: string;
17
+ }
18
+ export interface ListSearchFeedbackOptions {
19
+ since?: string;
20
+ until?: string;
21
+ limit?: number;
22
+ }
23
+ export interface SearchFeedbackRow {
24
+ feedback_id: string;
25
+ query: string;
26
+ query_hash_hex: string;
27
+ question_type: QuestionType;
28
+ result_source_type: SearchFeedbackSourceType;
29
+ result_source_id: string;
30
+ case_id: string | null;
31
+ feedback_kind: SearchFeedbackKind;
32
+ rank_position: number;
33
+ score_before: number | null;
34
+ score_after: number | null;
35
+ session_id: string | null;
36
+ actor: string;
37
+ metadata_json: string | null;
38
+ created_at: string;
39
+ updated_at: string | null;
40
+ }
41
+ export interface RecordSearchFeedbackResult {
42
+ feedback_id: string;
43
+ query_hash: string;
44
+ deduped: boolean;
45
+ }
46
+ export interface FeedbackStoreAdapter {
47
+ prepare(sql: string): {
48
+ run(...params: unknown[]): RunResult;
49
+ get(...params: unknown[]): unknown;
50
+ all(...params: unknown[]): unknown[];
51
+ };
52
+ transaction?<T>(fn: () => T): T | (() => T);
53
+ }
54
+ export declare function hashQuery(query: string): string;
55
+ export declare function getFeedbackRetentionDays(adapter: FeedbackStoreAdapter): number;
56
+ export declare function compactShownSearchFeedback(adapter: FeedbackStoreAdapter, retentionDays?: number): number;
57
+ export declare function recordSearchFeedback(adapter: FeedbackStoreAdapter, input: SearchFeedbackInput): RecordSearchFeedbackResult;
58
+ export declare function listSearchFeedback(adapter: FeedbackStoreAdapter, options?: ListSearchFeedbackOptions): SearchFeedbackRow[];
59
+ //# sourceMappingURL=feedback-store.d.ts.map
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hashQuery = hashQuery;
4
+ exports.getFeedbackRetentionDays = getFeedbackRetentionDays;
5
+ exports.compactShownSearchFeedback = compactShownSearchFeedback;
6
+ exports.recordSearchFeedback = recordSearchFeedback;
7
+ exports.listSearchFeedback = listSearchFeedback;
8
+ const node_crypto_1 = require("node:crypto");
9
+ const question_type_js_1 = require("./question-type.js");
10
+ const DEFAULT_RETENTION_DAYS = 180;
11
+ const MS_PER_DAY = 24 * 60 * 60 * 1000;
12
+ function normalizedQuery(query) {
13
+ return query.trim().toLowerCase();
14
+ }
15
+ function fullQueryHashHex(query) {
16
+ return (0, node_crypto_1.createHash)('sha256').update(normalizedQuery(query)).digest('hex');
17
+ }
18
+ function fullQueryHashBuffer(query) {
19
+ return Buffer.from(fullQueryHashHex(query), 'hex');
20
+ }
21
+ function hashQuery(query) {
22
+ return fullQueryHashHex(query).slice(0, 16);
23
+ }
24
+ function assertNonEmpty(value, field) {
25
+ if (!value || value.trim().length === 0) {
26
+ throw new Error(`${field} is required`);
27
+ }
28
+ }
29
+ function assertRank(value, field) {
30
+ if (!Number.isInteger(value) || value < 0) {
31
+ throw new Error(`${field} must be a non-negative integer`);
32
+ }
33
+ }
34
+ function nowIso() {
35
+ return new Date().toISOString();
36
+ }
37
+ function cutoffIso(retentionDays, now = new Date()) {
38
+ return new Date(now.getTime() - retentionDays * MS_PER_DAY).toISOString();
39
+ }
40
+ function stableObject(value) {
41
+ if (Array.isArray(value)) {
42
+ return value.map((item) => stableObject(item));
43
+ }
44
+ if (typeof value !== 'object' || value === null) {
45
+ return value;
46
+ }
47
+ const input = value;
48
+ const output = {};
49
+ for (const key of Object.keys(input).sort((left, right) => left.localeCompare(right))) {
50
+ const item = input[key];
51
+ if (item !== undefined) {
52
+ output[key] = stableObject(item);
53
+ }
54
+ }
55
+ return output;
56
+ }
57
+ function canonicalJson(value) {
58
+ return JSON.stringify(stableObject(value));
59
+ }
60
+ function runTransaction(adapter, fn) {
61
+ if (!adapter.transaction) {
62
+ throw new Error('recordSearchFeedback requires adapter.transaction for atomic writes.');
63
+ }
64
+ const result = adapter.transaction(fn);
65
+ if (typeof result === 'function') {
66
+ return result();
67
+ }
68
+ return result;
69
+ }
70
+ function parseRetentionValue(value) {
71
+ const parsed = JSON.parse(value);
72
+ const numeric = typeof parsed === 'number' ? parsed : Number(parsed);
73
+ if (!Number.isFinite(numeric) || numeric <= 0) {
74
+ throw new Error(`Invalid search_feedback_retention_days value: ${value}`);
75
+ }
76
+ return Math.floor(numeric);
77
+ }
78
+ function getFeedbackRetentionDays(adapter) {
79
+ const row = adapter
80
+ .prepare(`
81
+ SELECT value_json
82
+ FROM search_ranker_settings
83
+ WHERE key = 'search_feedback_retention_days'
84
+ `)
85
+ .get();
86
+ if (!row?.value_json) {
87
+ return DEFAULT_RETENTION_DAYS;
88
+ }
89
+ return parseRetentionValue(row.value_json);
90
+ }
91
+ function compactShownSearchFeedback(adapter, retentionDays = getFeedbackRetentionDays(adapter)) {
92
+ const result = adapter
93
+ .prepare(`
94
+ DELETE FROM search_feedback
95
+ WHERE feedback_kind = 'shown'
96
+ AND created_at < ?
97
+ `)
98
+ .run(cutoffIso(retentionDays));
99
+ return result.changes;
100
+ }
101
+ function sessionForInput(input, queryHash) {
102
+ if (input.session_id && input.session_id.trim().length > 0) {
103
+ return input.session_id;
104
+ }
105
+ if (input.feedback_kind === 'shown') {
106
+ return `anonymous:${queryHash}`;
107
+ }
108
+ return input.session_id ?? null;
109
+ }
110
+ function rankPositionForInput(input) {
111
+ if (input.feedback_kind === 'click' && input.clicked_result_rank !== undefined) {
112
+ const rank = input.clicked_result_rank ?? input.shown_index;
113
+ assertRank(rank, 'clicked_result_rank');
114
+ return rank;
115
+ }
116
+ assertRank(input.shown_index, 'shown_index');
117
+ return input.shown_index;
118
+ }
119
+ function questionTypeForInput(input) {
120
+ if (input.question_type && (0, question_type_js_1.isQuestionType)(input.question_type)) {
121
+ return input.question_type;
122
+ }
123
+ return (0, question_type_js_1.classifyQuestionType)(input.query);
124
+ }
125
+ function recordSearchFeedback(adapter, input) {
126
+ assertNonEmpty(input.result_id, 'result_id');
127
+ assertNonEmpty(input.query, 'query');
128
+ assertNonEmpty(input.result_source_type, 'result_source_type');
129
+ assertNonEmpty(input.result_source_id, 'result_source_id');
130
+ const queryHashHex = fullQueryHashHex(input.query);
131
+ const queryHash = fullQueryHashBuffer(input.query);
132
+ const queryHashShort = queryHashHex.slice(0, 16);
133
+ const sessionId = sessionForInput(input, queryHashShort);
134
+ const questionType = questionTypeForInput(input);
135
+ const rankPosition = rankPositionForInput(input);
136
+ const createdAt = input.created_at ?? nowIso();
137
+ const feedbackId = `search_feedback_${(0, node_crypto_1.randomUUID)()}`;
138
+ const metadataJson = canonicalJson({
139
+ clicked_result_rank: input.clicked_result_rank ?? null,
140
+ input_result_id: input.result_id,
141
+ });
142
+ return runTransaction(adapter, () => {
143
+ let deduped = false;
144
+ if (input.feedback_kind === 'shown') {
145
+ compactShownSearchFeedback(adapter);
146
+ if (sessionId) {
147
+ const deleted = adapter
148
+ .prepare(`
149
+ DELETE FROM search_feedback
150
+ WHERE feedback_kind = 'shown'
151
+ AND session_id = ?
152
+ AND query_hash = ?
153
+ AND result_source_type = ?
154
+ AND result_source_id = ?
155
+ `)
156
+ .run(sessionId, queryHash, input.result_source_type, input.result_source_id);
157
+ deduped = deleted.changes > 0;
158
+ }
159
+ }
160
+ adapter
161
+ .prepare(`
162
+ INSERT INTO search_feedback (
163
+ feedback_id, query, query_hash, question_type, result_source_type, result_source_id,
164
+ case_id, feedback_kind, rank_position, score_before, score_after, session_id,
165
+ actor, metadata_json, created_at, updated_at
166
+ )
167
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, ?, 'system:search-feedback', ?, ?, NULL)
168
+ `)
169
+ .run(feedbackId, input.query, queryHash, questionType, input.result_source_type, input.result_source_id, input.result_case_id ?? null, input.feedback_kind, rankPosition, sessionId, metadataJson, createdAt);
170
+ return {
171
+ feedback_id: feedbackId,
172
+ query_hash: queryHashShort,
173
+ deduped,
174
+ };
175
+ });
176
+ }
177
+ function listSearchFeedback(adapter, options = {}) {
178
+ const retentionDays = getFeedbackRetentionDays(adapter);
179
+ const until = options.until ?? nowIso();
180
+ const since = options.since ?? cutoffIso(retentionDays, new Date(until));
181
+ const limit = options.limit ?? 10_000;
182
+ assertRank(limit, 'limit');
183
+ const rows = adapter
184
+ .prepare(`
185
+ SELECT
186
+ feedback_id,
187
+ query,
188
+ lower(hex(query_hash)) AS query_hash_hex,
189
+ question_type,
190
+ result_source_type,
191
+ result_source_id,
192
+ case_id,
193
+ feedback_kind,
194
+ rank_position,
195
+ score_before,
196
+ score_after,
197
+ session_id,
198
+ actor,
199
+ metadata_json,
200
+ created_at,
201
+ updated_at
202
+ FROM search_feedback
203
+ WHERE created_at >= ?
204
+ AND created_at <= ?
205
+ ORDER BY created_at ASC, feedback_id ASC
206
+ LIMIT ?
207
+ `)
208
+ .all(since, until, limit);
209
+ return rows;
210
+ }
211
+ //# sourceMappingURL=feedback-store.js.map
@@ -0,0 +1,5 @@
1
+ export type QuestionType = 'correction' | 'artifact' | 'timeline' | 'status' | 'decision_reason' | 'how_to' | 'unknown';
2
+ export declare const QUESTION_TYPES: readonly QuestionType[];
3
+ export declare function classifyQuestionType(query: string): QuestionType;
4
+ export declare function isQuestionType(value: string): value is QuestionType;
5
+ //# sourceMappingURL=question-type.d.ts.map
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QUESTION_TYPES = void 0;
4
+ exports.classifyQuestionType = classifyQuestionType;
5
+ exports.isQuestionType = isQuestionType;
6
+ exports.QUESTION_TYPES = [
7
+ 'correction',
8
+ 'artifact',
9
+ 'timeline',
10
+ 'status',
11
+ 'decision_reason',
12
+ 'how_to',
13
+ 'unknown',
14
+ ];
15
+ // Korean: patterns for classifyQuestionType() multilingual query routing.
16
+ const CORRECTION_PATTERN = /\b(fix|revert|correct|correction|supersede|superseded|revise|revision|수정|되돌|정정|교정)\b/i; // Korean: keywords
17
+ const ARTIFACT_PATTERN = /\b(file|doc|docs|document|image|video|pdf|drive|obsidian|attachment|artifact|파일|문서|이미지|영상|첨부|드라이브|옵시디언)\b/i; // Korean: keywords
18
+ const TIMELINE_PATTERN = /\b(when|history|before|after|around|timeline|chronology|언제|이력|히스토리|전|후|즈음)\b|\bon\s+\d{4}(?:-\d{1,2})?(?:-\d{1,2})?\b|\b\d{4}-\d{1,2}-\d{1,2}\b/i; // Korean: keywords
19
+ const STATUS_PATTERN = /\b(status|state|current|currently|now|latest|progress|blocked|done|상태|현재|최신|진행|진척)\b/i; // Korean: keywords
20
+ const DECISION_REASON_PATTERN = /\b(why|reason|because|rationale|근거|이유|왜|때문)\b/i; // Korean: keywords
21
+ const HOW_TO_PATTERN = /\b(how\s+to|how\s+do|setup|set\s+up|configure|configuration|install|설정|구성|어떻게)\b/i; // Korean: keywords
22
+ function classifyQuestionType(query) {
23
+ const normalized = query.trim();
24
+ if (CORRECTION_PATTERN.test(normalized))
25
+ return 'correction';
26
+ if (ARTIFACT_PATTERN.test(normalized))
27
+ return 'artifact';
28
+ if (TIMELINE_PATTERN.test(normalized))
29
+ return 'timeline';
30
+ if (STATUS_PATTERN.test(normalized))
31
+ return 'status';
32
+ if (DECISION_REASON_PATTERN.test(normalized))
33
+ return 'decision_reason';
34
+ if (HOW_TO_PATTERN.test(normalized))
35
+ return 'how_to';
36
+ return 'unknown';
37
+ }
38
+ function isQuestionType(value) {
39
+ return exports.QUESTION_TYPES.includes(value);
40
+ }
41
+ //# sourceMappingURL=question-type.js.map
@@ -0,0 +1,23 @@
1
+ import { type QuestionType } from './question-type.js';
2
+ export declare const SEARCH_RANKER_FEATURE_SET_VERSION = "v1.0.0";
3
+ export type RankerSourceType = 'decision' | 'event' | 'observation' | 'artifact' | 'connector_event';
4
+ export declare const RANKER_SOURCE_TYPES: readonly RankerSourceType[];
5
+ export interface RankerFeatureVector {
6
+ bm25_score: number;
7
+ vector_score: number;
8
+ case_rollup_score: number;
9
+ recency_score: number;
10
+ same_case_boost: number;
11
+ source_type_onehot: Record<RankerSourceType, number>;
12
+ question_type_onehot: Record<QuestionType, number>;
13
+ }
14
+ export interface RankerQueryMeta {
15
+ question_type?: QuestionType;
16
+ case_id?: string | null;
17
+ }
18
+ type FeatureRow = Record<string, unknown>;
19
+ export declare function extractFeatures(row: FeatureRow, query: string, queryMeta?: RankerQueryMeta): RankerFeatureVector;
20
+ export declare function serializeFeatures(vec: RankerFeatureVector): number[];
21
+ export declare function featureSetVersion(): string;
22
+ export {};
23
+ //# sourceMappingURL=ranker-features.d.ts.map
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RANKER_SOURCE_TYPES = exports.SEARCH_RANKER_FEATURE_SET_VERSION = void 0;
4
+ exports.extractFeatures = extractFeatures;
5
+ exports.serializeFeatures = serializeFeatures;
6
+ exports.featureSetVersion = featureSetVersion;
7
+ const question_type_js_1 = require("./question-type.js");
8
+ exports.SEARCH_RANKER_FEATURE_SET_VERSION = 'v1.0.0';
9
+ exports.RANKER_SOURCE_TYPES = [
10
+ 'decision',
11
+ 'event',
12
+ 'observation',
13
+ 'artifact',
14
+ 'connector_event',
15
+ ];
16
+ function numberValue(value, fallback = 0) {
17
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
18
+ return fallback;
19
+ }
20
+ return value;
21
+ }
22
+ function stringValue(value) {
23
+ if (value === null || value === undefined) {
24
+ return null;
25
+ }
26
+ return String(value);
27
+ }
28
+ function sourceTypeKey(row) {
29
+ const sourceType = stringValue(row.source_type ?? row.result_source_type ?? row.kind ?? row.page_type);
30
+ switch (sourceType) {
31
+ case 'decision':
32
+ return 'decision';
33
+ case 'event':
34
+ case 'checkpoint':
35
+ return 'event';
36
+ case 'observation':
37
+ case 'case':
38
+ return 'observation';
39
+ case 'artifact':
40
+ case 'wiki_page':
41
+ return 'artifact';
42
+ case 'connector_event':
43
+ case 'standalone_connector_hit':
44
+ return 'connector_event';
45
+ default:
46
+ return null;
47
+ }
48
+ }
49
+ function zeroSourceOnehot() {
50
+ return {
51
+ decision: 0,
52
+ event: 0,
53
+ observation: 0,
54
+ artifact: 0,
55
+ connector_event: 0,
56
+ };
57
+ }
58
+ function zeroQuestionOnehot() {
59
+ return {
60
+ correction: 0,
61
+ artifact: 0,
62
+ timeline: 0,
63
+ status: 0,
64
+ decision_reason: 0,
65
+ how_to: 0,
66
+ unknown: 0,
67
+ };
68
+ }
69
+ function extractFeatures(row, query, queryMeta = {}) {
70
+ const sourceOnehot = zeroSourceOnehot();
71
+ const sourceType = sourceTypeKey(row);
72
+ if (sourceType) {
73
+ sourceOnehot[sourceType] = 1;
74
+ }
75
+ const questionType = queryMeta.question_type ?? (0, question_type_js_1.classifyQuestionType)(query);
76
+ const questionOnehot = zeroQuestionOnehot();
77
+ questionOnehot[questionType] = 1;
78
+ const rowCaseId = stringValue(row.case_id ?? row.result_case_id);
79
+ const queryCaseId = queryMeta.case_id ?? null;
80
+ return {
81
+ bm25_score: numberValue(row.bm25_score ?? row.bm25 ?? row.fts_score),
82
+ vector_score: numberValue(row.vector_score ?? row.similarity),
83
+ case_rollup_score: numberValue(row.case_rollup_score ?? row.retrieval_score ?? row.final_score ?? row.score),
84
+ recency_score: numberValue(row.recency_score),
85
+ same_case_boost: queryCaseId && rowCaseId && queryCaseId === rowCaseId ? 1 : 0,
86
+ source_type_onehot: sourceOnehot,
87
+ question_type_onehot: questionOnehot,
88
+ };
89
+ }
90
+ function serializeFeatures(vec) {
91
+ return [
92
+ vec.bm25_score,
93
+ vec.vector_score,
94
+ vec.case_rollup_score,
95
+ vec.recency_score,
96
+ vec.same_case_boost,
97
+ ...exports.RANKER_SOURCE_TYPES.map((sourceType) => vec.source_type_onehot[sourceType]),
98
+ ...question_type_js_1.QUESTION_TYPES.map((questionType) => vec.question_type_onehot[questionType]),
99
+ ];
100
+ }
101
+ function featureSetVersion() {
102
+ return exports.SEARCH_RANKER_FEATURE_SET_VERSION;
103
+ }
104
+ //# sourceMappingURL=ranker-features.js.map
@@ -0,0 +1,31 @@
1
+ import type { RunResult } from '../db-adapter/statement.js';
2
+ import { type SearchRankerModel } from './ranker-trainer.js';
3
+ export type SearchRankerSkippedReason = 'feature_disabled' | 'no_active_model' | 'feature_set_mismatch' | 'quality_gate_failed' | 'insufficient_result_count' | 'llm_reranking_requested';
4
+ export interface RankerRescoreAdapter {
5
+ prepare(sql: string): {
6
+ run(...params: unknown[]): RunResult;
7
+ get(...params: unknown[]): unknown;
8
+ all(...params: unknown[]): unknown[];
9
+ };
10
+ }
11
+ export interface RankerScoredFields {
12
+ ranker_score?: number;
13
+ score_before_ranker?: number;
14
+ final_score?: number;
15
+ }
16
+ export interface RescoreSearchResultsOptions<T> {
17
+ query: string;
18
+ results: T[];
19
+ featureEnabled?: boolean;
20
+ useReranking?: boolean;
21
+ }
22
+ export interface RescoreSearchResultsResult<T> {
23
+ results: Array<T & RankerScoredFields>;
24
+ model_id: string | null;
25
+ skipped_reason?: SearchRankerSkippedReason;
26
+ }
27
+ export declare function loadActiveSearchRankerModel(adapter: RankerRescoreAdapter): SearchRankerModel | null;
28
+ export declare function isSearchRankerEnabled(adapter: RankerRescoreAdapter): boolean;
29
+ export declare function rescoreSearchResults<T extends Record<string, unknown>>(adapter: RankerRescoreAdapter, options: RescoreSearchResultsOptions<T>): RescoreSearchResultsResult<T>;
30
+ export declare function logisticScoreForRow(model: SearchRankerModel, row: Record<string, unknown>, query: string): number;
31
+ //# sourceMappingURL=ranker-rescore.d.ts.map