@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,244 @@
1
+ export declare const CASE_TRUTH_STATUSES: readonly ["active", "blocked", "resolved", "stale", "archived", "merged", "split"];
2
+ export type CaseTruthStatus = (typeof CASE_TRUTH_STATUSES)[number];
3
+ export declare const CASE_FAST_WRITE_STATUSES: readonly ["active", "blocked", "resolved", "stale"];
4
+ export type CaseFastWriteStatus = (typeof CASE_FAST_WRITE_STATUSES)[number];
5
+ export declare const CASE_MEMBERSHIP_SOURCE_TYPES: readonly ["decision", "event", "observation", "artifact"];
6
+ export type CaseMembershipSourceType = (typeof CASE_MEMBERSHIP_SOURCE_TYPES)[number];
7
+ export declare const CASE_MEMBERSHIP_STATUSES: readonly ["active", "candidate", "removed", "excluded", "stale"];
8
+ export type CaseMembershipStatus = (typeof CASE_MEMBERSHIP_STATUSES)[number];
9
+ export declare const CASE_MEMBERSHIP_ADDED_BY: readonly ["wiki-compiler", "memory-agent", "user-correction"];
10
+ export type CaseMembershipAddedBy = (typeof CASE_MEMBERSHIP_ADDED_BY)[number];
11
+ export declare const CASE_CORRECTION_TARGET_KINDS: readonly ["case_field", "membership", "wiki_section"];
12
+ export type CaseCorrectionTargetKind = (typeof CASE_CORRECTION_TARGET_KINDS)[number];
13
+ export declare const CASE_PROPOSAL_KINDS: readonly ["ambiguous_slug", "duplicate_frontmatter", "missing_frontmatter", "unknown_case_id", "stale_case_id", "merged_target", "archived_target", "lock_conflict", "corrupt_frontmatter", "quarantined_accepted_case"];
14
+ export type CaseProposalKind = (typeof CASE_PROPOSAL_KINDS)[number];
15
+ export declare const CASE_PROPOSAL_RESOLUTIONS: readonly ["accepted", "rejected", "modified", "merged"];
16
+ export type CaseProposalResolution = (typeof CASE_PROPOSAL_RESOLUTIONS)[number];
17
+ export declare const CASE_CONFIDENCE_LEVELS: readonly ["high", "medium", "low"];
18
+ export type CaseConfidence = (typeof CASE_CONFIDENCE_LEVELS)[number];
19
+ export interface CaseTruthRecord {
20
+ case_id: string;
21
+ current_wiki_path: string | null;
22
+ title: string;
23
+ status: CaseTruthStatus;
24
+ status_reason: string | null;
25
+ primary_actors: string | null;
26
+ blockers: string | null;
27
+ last_activity_at: string | null;
28
+ canonical_case_id?: string | null;
29
+ split_from_case_id?: string | null;
30
+ wiki_path_history: string | null;
31
+ scope_refs: string | null;
32
+ confidence: CaseConfidence | null;
33
+ compiled_at: string | null;
34
+ state_updated_at: string | null;
35
+ created_at: string;
36
+ updated_at: string;
37
+ }
38
+ export interface CaseMembershipRecord {
39
+ case_id: string;
40
+ source_type: CaseMembershipSourceType;
41
+ source_id: string;
42
+ role: string | null;
43
+ confidence: number | null;
44
+ reason: string | null;
45
+ status: CaseMembershipStatus;
46
+ added_by: CaseMembershipAddedBy;
47
+ added_at: string;
48
+ updated_at: string;
49
+ user_locked: 0 | 1;
50
+ }
51
+ export interface CaseCorrectionRecord {
52
+ correction_id: string;
53
+ case_id: string;
54
+ target_kind: CaseCorrectionTargetKind;
55
+ target_ref_json: string;
56
+ target_ref_hash: Buffer;
57
+ field_name: string | null;
58
+ old_value_json: string | null;
59
+ new_value_json: string;
60
+ reason: string;
61
+ is_lock_active: 0 | 1;
62
+ superseded_by: string | null;
63
+ reverted_at: string | null;
64
+ applied_by: string;
65
+ applied_at: string;
66
+ }
67
+ export interface CaseCorrectionRequiresReconfirmResult {
68
+ kind: 'requires_reconfirm';
69
+ code: 'case.correction_requires_reconfirm';
70
+ case_id: string;
71
+ target_kind: CaseCorrectionTargetKind;
72
+ target_ref_json: string;
73
+ current_value_json: string;
74
+ old_value_json: string | null;
75
+ proposed_new_value_json: string;
76
+ reconfirm_token: string;
77
+ reconfirm_token_expires_at: string;
78
+ message: string;
79
+ }
80
+ export interface CaseProposalQueueRecord {
81
+ proposal_id: string;
82
+ project: string;
83
+ proposal_kind: CaseProposalKind;
84
+ proposed_payload: string;
85
+ payload_fingerprint: Buffer;
86
+ conflicting_case_id: string | null;
87
+ detected_at: string;
88
+ resolved_at: string | null;
89
+ resolution: CaseProposalResolution | null;
90
+ resolution_note: string | null;
91
+ }
92
+ export interface CasePrimaryActor {
93
+ entity_id: string;
94
+ role: string;
95
+ }
96
+ export interface CaseBlocker {
97
+ text: string;
98
+ source_decision_id?: string;
99
+ source_event_id?: string;
100
+ }
101
+ export interface CaseAssemblyMembership {
102
+ source_type: CaseMembershipSourceType;
103
+ source_id: string;
104
+ role: string | null;
105
+ confidence: number | null;
106
+ reason: string | null;
107
+ user_locked: boolean;
108
+ }
109
+ export interface CaseAssemblyDecision {
110
+ id: string;
111
+ topic: string;
112
+ decision: string;
113
+ reasoning: string | null;
114
+ confidence: number | null;
115
+ event_date: string | null;
116
+ }
117
+ export interface CaseAssemblyTimelineEvent {
118
+ event_id: string;
119
+ event_type: string;
120
+ entity_id: string;
121
+ role: string | null;
122
+ observed_at: string;
123
+ summary: string;
124
+ details: string | null;
125
+ }
126
+ export interface CaseAssemblyObservation {
127
+ observation_id: string;
128
+ surface_form: string;
129
+ source_locator: string;
130
+ timestamp_observed: string;
131
+ }
132
+ export interface CaseAssemblyCorrection {
133
+ correction_id: string;
134
+ target_kind: CaseCorrectionTargetKind;
135
+ target_ref: Record<string, unknown>;
136
+ new_value_json: string;
137
+ reason: string;
138
+ applied_at: string;
139
+ }
140
+ export interface CaseAssemblyLinkedCase {
141
+ case_id: string;
142
+ relation: 'merged_from' | 'split_into' | 'wikilink';
143
+ }
144
+ export interface WikiPageAssemblySnapshot {
145
+ title: string | null;
146
+ content: string | null;
147
+ confidence: CaseConfidence | null;
148
+ compiled_at: string | null;
149
+ }
150
+ export interface CaseAssembly {
151
+ case_id: string;
152
+ current_wiki_path: string | null;
153
+ wiki_page: WikiPageAssemblySnapshot | null;
154
+ case_truth: {
155
+ title: string;
156
+ status: CaseTruthStatus;
157
+ status_reason: string | null;
158
+ primary_actors: CasePrimaryActor[];
159
+ blockers: CaseBlocker[];
160
+ last_activity_at: string | null;
161
+ confidence: CaseConfidence | null;
162
+ canonical_case_id: string | null;
163
+ split_from_case_id: string | null;
164
+ } | null;
165
+ memberships: CaseAssemblyMembership[];
166
+ timeline_events: CaseAssemblyTimelineEvent[];
167
+ decisions: CaseAssemblyDecision[];
168
+ recent_evidence: CaseAssemblyObservation[];
169
+ active_corrections: CaseAssemblyCorrection[];
170
+ linked_cases: CaseAssemblyLinkedCase[];
171
+ resolved_via_case_id?: string;
172
+ case_links?: Array<{
173
+ link_id: string;
174
+ case_id_from: string;
175
+ case_id_to: string;
176
+ link_type: string;
177
+ created_at: string;
178
+ created_by: string;
179
+ confidence: number | null;
180
+ reason_json: string | null;
181
+ source_kind: string;
182
+ source_ref: string | null;
183
+ }>;
184
+ promoted_sources?: {
185
+ canonical_decision_id: string | null;
186
+ canonical_event_id: string | null;
187
+ promoted_at: string | null;
188
+ promoted_by: string | null;
189
+ promotion_reason: string | null;
190
+ } | null;
191
+ freshness?: {
192
+ freshness_score: number | null;
193
+ freshness_state: string | null;
194
+ freshness_score_is_drifted: 0 | 1;
195
+ freshness_drift_threshold: number | null;
196
+ freshness_checked_at: string | null;
197
+ freshness_reason_json: string | null;
198
+ } | null;
199
+ membership_explanations?: Record<string, Record<string, unknown>>;
200
+ }
201
+ export interface CanonicalCaseResolution {
202
+ terminal_case_id: string;
203
+ chain: string[];
204
+ resolved_via_case_id: string | null;
205
+ }
206
+ export interface CaseTimelineRangeInput {
207
+ case_id: string;
208
+ from?: string | number;
209
+ to?: string | number;
210
+ order?: 'asc' | 'desc';
211
+ limit?: number;
212
+ include_connector_enrichments?: boolean;
213
+ }
214
+ export interface CaseTimelineRangeItem {
215
+ item_type: CaseMembershipSourceType;
216
+ source_type: CaseMembershipSourceType;
217
+ source_id: string;
218
+ source_locator: string | null;
219
+ event_datetime: number | null;
220
+ event_date: string | null;
221
+ title: string | null;
222
+ summary: string | null;
223
+ role: string | null;
224
+ confidence: number | null;
225
+ membership_reason: string | null;
226
+ connector_event?: {
227
+ event_index_id: string;
228
+ source_connector: string;
229
+ source_id: string;
230
+ source_locator: string | null;
231
+ artifact_locator: string | null;
232
+ title: string | null;
233
+ content: string;
234
+ event_datetime: number | null;
235
+ event_date: string | null;
236
+ } | null;
237
+ }
238
+ export interface CaseTimelineRangeResult {
239
+ terminal_case_id: string;
240
+ resolved_via_case_id: string | null;
241
+ chain: string[];
242
+ items: CaseTimelineRangeItem[];
243
+ }
244
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CASE_CONFIDENCE_LEVELS = exports.CASE_PROPOSAL_RESOLUTIONS = exports.CASE_PROPOSAL_KINDS = exports.CASE_CORRECTION_TARGET_KINDS = exports.CASE_MEMBERSHIP_ADDED_BY = exports.CASE_MEMBERSHIP_STATUSES = exports.CASE_MEMBERSHIP_SOURCE_TYPES = exports.CASE_FAST_WRITE_STATUSES = exports.CASE_TRUTH_STATUSES = void 0;
4
+ exports.CASE_TRUTH_STATUSES = [
5
+ 'active',
6
+ 'blocked',
7
+ 'resolved',
8
+ 'stale',
9
+ 'archived',
10
+ 'merged',
11
+ 'split',
12
+ ];
13
+ exports.CASE_FAST_WRITE_STATUSES = ['active', 'blocked', 'resolved', 'stale'];
14
+ exports.CASE_MEMBERSHIP_SOURCE_TYPES = [
15
+ 'decision',
16
+ 'event',
17
+ 'observation',
18
+ 'artifact',
19
+ ];
20
+ exports.CASE_MEMBERSHIP_STATUSES = [
21
+ 'active',
22
+ 'candidate',
23
+ 'removed',
24
+ 'excluded',
25
+ 'stale',
26
+ ];
27
+ exports.CASE_MEMBERSHIP_ADDED_BY = [
28
+ 'wiki-compiler',
29
+ 'memory-agent',
30
+ 'user-correction',
31
+ ];
32
+ exports.CASE_CORRECTION_TARGET_KINDS = ['case_field', 'membership', 'wiki_section'];
33
+ exports.CASE_PROPOSAL_KINDS = [
34
+ 'ambiguous_slug',
35
+ 'duplicate_frontmatter',
36
+ 'missing_frontmatter',
37
+ 'unknown_case_id',
38
+ 'stale_case_id',
39
+ 'merged_target',
40
+ 'archived_target',
41
+ 'lock_conflict',
42
+ 'corrupt_frontmatter',
43
+ 'quarantined_accepted_case',
44
+ ];
45
+ exports.CASE_PROPOSAL_RESOLUTIONS = ['accepted', 'rejected', 'modified', 'merged'];
46
+ exports.CASE_CONFIDENCE_LEVELS = ['high', 'medium', 'low'];
47
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,50 @@
1
+ import type { DatabaseAdapter as DBManagerAdapter } from '../db-manager.js';
2
+ export type AdapterLike = Pick<DBManagerAdapter, 'prepare' | 'transaction'>;
3
+ export interface WikiPageIndexRecord {
4
+ id: number;
5
+ source_locator: string;
6
+ page_type: 'entity' | 'lesson' | 'synthesis' | 'process' | 'case';
7
+ title: string;
8
+ content: string;
9
+ case_id: string | null;
10
+ source_ids: string[];
11
+ entity_refs: string[];
12
+ confidence: 'high' | 'medium' | 'low' | null;
13
+ compiled_at: string;
14
+ created_at: string;
15
+ updated_at: string;
16
+ }
17
+ export interface WikiPageSearchHit {
18
+ record: WikiPageIndexRecord;
19
+ rank: number;
20
+ raw_score: number;
21
+ }
22
+ export interface UpsertWikiPageIndexInput {
23
+ source_locator: string;
24
+ page_type: WikiPageIndexRecord['page_type'];
25
+ title: string;
26
+ content: string;
27
+ case_id?: string | null;
28
+ source_ids: string[];
29
+ entity_refs: string[];
30
+ confidence: WikiPageIndexRecord['confidence'];
31
+ compiled_at: string;
32
+ embedding?: Float32Array | null;
33
+ embedding_model?: string;
34
+ }
35
+ export declare function upsertWikiPageIndexEntry(adapter: AdapterLike, input: UpsertWikiPageIndexInput): {
36
+ id: number;
37
+ created: boolean;
38
+ };
39
+ export declare function deleteWikiPageIndexEntry(adapter: AdapterLike, source_locator: string): {
40
+ deleted: boolean;
41
+ };
42
+ export declare function ftsSearchWikiPages(adapter: AdapterLike, query: string, limit: number): WikiPageSearchHit[];
43
+ export declare function vectorSearchWikiPages(adapter: AdapterLike, queryEmbedding: Float32Array, limit: number): WikiPageSearchHit[];
44
+ export declare function searchWikiPages(input: {
45
+ adapter: AdapterLike;
46
+ query: string;
47
+ queryEmbedding?: Float32Array | null;
48
+ limit: number;
49
+ }): WikiPageSearchHit[];
50
+ //# sourceMappingURL=wiki-page-index.d.ts.map
@@ -0,0 +1,350 @@
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.upsertWikiPageIndexEntry = upsertWikiPageIndexEntry;
7
+ exports.deleteWikiPageIndexEntry = deleteWikiPageIndexEntry;
8
+ exports.ftsSearchWikiPages = ftsSearchWikiPages;
9
+ exports.vectorSearchWikiPages = vectorSearchWikiPages;
10
+ exports.searchWikiPages = searchWikiPages;
11
+ const node_crypto_1 = __importDefault(require("node:crypto"));
12
+ const embeddings_js_1 = require("../embeddings.js");
13
+ function tableColumns(adapter, tableName) {
14
+ const rows = adapter.prepare(`PRAGMA table_info(${tableName})`).all();
15
+ return new Set(rows.map((row) => row.name));
16
+ }
17
+ function readSchema(adapter) {
18
+ const indexColumns = tableColumns(adapter, 'wiki_page_index');
19
+ const embeddingColumns = tableColumns(adapter, 'wiki_page_embeddings');
20
+ if (!indexColumns.has('id') && !indexColumns.has('page_id')) {
21
+ throw new Error('wiki_page_index schema is missing both id and page_id columns.');
22
+ }
23
+ return {
24
+ hasIdColumn: indexColumns.has('id'),
25
+ hasPageIdColumn: indexColumns.has('page_id'),
26
+ hasSourceTypeColumn: indexColumns.has('source_type'),
27
+ hasSourceIdsColumn: indexColumns.has('source_ids'),
28
+ hasEntityRefsColumn: indexColumns.has('entity_refs'),
29
+ hasCreatedAtColumn: indexColumns.has('created_at'),
30
+ embeddingUsesWikiPageId: embeddingColumns.has('wiki_page_id'),
31
+ embeddingUsesVector: embeddingColumns.has('vector'),
32
+ embeddingHasModel: embeddingColumns.has('model'),
33
+ embeddingHasDim: embeddingColumns.has('dim'),
34
+ embeddingHasCreatedAt: embeddingColumns.has('created_at'),
35
+ };
36
+ }
37
+ function nowIso() {
38
+ return new Date().toISOString();
39
+ }
40
+ function normalizeLimit(limit) {
41
+ if (!Number.isFinite(limit)) {
42
+ return 10;
43
+ }
44
+ return Math.max(0, Math.floor(limit));
45
+ }
46
+ function pageIdForSourceLocator(sourceLocator) {
47
+ const digest = node_crypto_1.default.createHash('sha256').update(sourceLocator).digest('hex').slice(0, 24);
48
+ return `wiki_page_${digest}`;
49
+ }
50
+ function parseJsonArray(value) {
51
+ if (value === null || value === undefined || value === '') {
52
+ return [];
53
+ }
54
+ if (Array.isArray(value)) {
55
+ return value.map(String);
56
+ }
57
+ if (typeof value !== 'string') {
58
+ return [];
59
+ }
60
+ const parsed = JSON.parse(value);
61
+ if (!Array.isArray(parsed)) {
62
+ throw new Error('wiki_page_index JSON array column decoded to a non-array value.');
63
+ }
64
+ return parsed.map(String);
65
+ }
66
+ function normalizeConfidence(value) {
67
+ if (value === 'high' || value === 'medium' || value === 'low') {
68
+ return value;
69
+ }
70
+ return null;
71
+ }
72
+ function rowToRecord(row) {
73
+ const id = Number(row.id);
74
+ if (!Number.isFinite(id)) {
75
+ throw new Error('wiki_page_index row is missing a numeric id/rowid.');
76
+ }
77
+ return {
78
+ id,
79
+ source_locator: String(row.source_locator ?? ''),
80
+ page_type: row.page_type,
81
+ title: String(row.title ?? ''),
82
+ content: String(row.content ?? ''),
83
+ case_id: row.case_id === null || row.case_id === undefined ? null : String(row.case_id),
84
+ source_ids: parseJsonArray(row.source_ids),
85
+ entity_refs: parseJsonArray(row.entity_refs),
86
+ confidence: normalizeConfidence(row.confidence),
87
+ compiled_at: String(row.compiled_at ?? ''),
88
+ created_at: String(row.created_at ?? row.updated_at ?? row.compiled_at ?? ''),
89
+ updated_at: String(row.updated_at ?? row.created_at ?? row.compiled_at ?? ''),
90
+ };
91
+ }
92
+ function selectByLocator(adapter, schema, sourceLocator) {
93
+ const sql = schema.hasIdColumn
94
+ ? 'SELECT * FROM wiki_page_index WHERE source_locator = ?'
95
+ : 'SELECT rowid AS id, * FROM wiki_page_index WHERE source_locator = ?';
96
+ const row = adapter.prepare(sql).get(sourceLocator);
97
+ return row ? rowToRecord(row) : null;
98
+ }
99
+ function selectByFtsPageId(adapter, schema, pageId) {
100
+ const sql = schema.hasIdColumn
101
+ ? 'SELECT * FROM wiki_page_index WHERE id = ?'
102
+ : 'SELECT rowid AS id, * FROM wiki_page_index WHERE page_id = ?';
103
+ const lookup = schema.hasIdColumn ? Number(pageId) : String(pageId);
104
+ const row = adapter.prepare(sql).get(lookup);
105
+ return row ? rowToRecord(row) : null;
106
+ }
107
+ function vectorToBuffer(embedding) {
108
+ return Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
109
+ }
110
+ function blobToVector(value) {
111
+ if (!(value instanceof Uint8Array)) {
112
+ return null;
113
+ }
114
+ if (value.byteLength % 4 !== 0) {
115
+ return null;
116
+ }
117
+ const buffer = Buffer.isBuffer(value) ? value : Buffer.from(value);
118
+ const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
119
+ return new Float32Array(arrayBuffer);
120
+ }
121
+ function upsertEmbedding(adapter, schema, record, input) {
122
+ if (!input.embedding) {
123
+ return;
124
+ }
125
+ const vector = vectorToBuffer(input.embedding);
126
+ const model = input.embedding_model ?? embeddings_js_1.MODEL_NAME;
127
+ const dim = input.embedding.length;
128
+ if (schema.embeddingUsesWikiPageId && schema.embeddingUsesVector) {
129
+ adapter
130
+ .prepare(`
131
+ INSERT INTO wiki_page_embeddings(wiki_page_id, vector, model, dim, created_at)
132
+ VALUES (?, ?, ?, ?, ?)
133
+ ON CONFLICT(wiki_page_id) DO UPDATE SET
134
+ vector = excluded.vector,
135
+ model = excluded.model,
136
+ dim = excluded.dim
137
+ `)
138
+ .run(record.id, vector, model, dim, nowIso());
139
+ return;
140
+ }
141
+ adapter
142
+ .prepare(`
143
+ INSERT INTO wiki_page_embeddings(page_id, embedding)
144
+ VALUES (?, ?)
145
+ ON CONFLICT(page_id) DO UPDATE SET
146
+ embedding = excluded.embedding
147
+ `)
148
+ .run(pageIdForSourceLocator(record.source_locator), vector);
149
+ }
150
+ function buildFtsQuery(query) {
151
+ const tokens = query
152
+ .toLowerCase()
153
+ .match(/[\p{L}\p{N}_]+/gu)
154
+ ?.filter((token) => token.length > 0);
155
+ if (!tokens || tokens.length === 0) {
156
+ return query;
157
+ }
158
+ return tokens.map((token) => `"${token.replace(/"/g, '""')}"`).join(' OR ');
159
+ }
160
+ function normalizeBm25Score(raw) {
161
+ const value = Number(raw);
162
+ if (!Number.isFinite(value)) {
163
+ return 0;
164
+ }
165
+ if (value < 0) {
166
+ return 1 + Math.abs(value);
167
+ }
168
+ return 1 / (1 + value);
169
+ }
170
+ function searchTableExists(adapter, tableName) {
171
+ const row = adapter
172
+ .prepare("SELECT name FROM sqlite_master WHERE name = ? AND type IN ('table','virtual table')")
173
+ .get(tableName);
174
+ return row !== undefined;
175
+ }
176
+ function upsertWikiPageIndexEntry(adapter, input) {
177
+ const schema = readSchema(adapter);
178
+ const existing = selectByLocator(adapter, schema, input.source_locator);
179
+ const updatedAt = nowIso();
180
+ return adapter.transaction(() => {
181
+ if (schema.hasIdColumn) {
182
+ adapter
183
+ .prepare(`
184
+ INSERT INTO wiki_page_index (
185
+ source_locator, page_type, title, content, case_id, source_ids,
186
+ confidence, entity_refs, compiled_at, created_at, updated_at
187
+ )
188
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
189
+ ON CONFLICT(source_locator) DO UPDATE SET
190
+ page_type = excluded.page_type,
191
+ title = excluded.title,
192
+ content = excluded.content,
193
+ case_id = excluded.case_id,
194
+ source_ids = excluded.source_ids,
195
+ confidence = excluded.confidence,
196
+ entity_refs = excluded.entity_refs,
197
+ compiled_at = excluded.compiled_at,
198
+ updated_at = excluded.updated_at
199
+ `)
200
+ .run(input.source_locator, input.page_type, input.title, input.content, input.case_id ?? null, JSON.stringify(input.source_ids), input.confidence, JSON.stringify(input.entity_refs), input.compiled_at, updatedAt, updatedAt);
201
+ }
202
+ else if (schema.hasSourceIdsColumn && schema.hasEntityRefsColumn) {
203
+ adapter
204
+ .prepare(`
205
+ INSERT INTO wiki_page_index (
206
+ page_id, source_type, source_locator, case_id, title, page_type,
207
+ content, confidence, source_ids, entity_refs, compiled_at, updated_at
208
+ )
209
+ VALUES (?, 'wiki_page', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
210
+ ON CONFLICT(source_type, source_locator) DO UPDATE SET
211
+ case_id = excluded.case_id,
212
+ title = excluded.title,
213
+ page_type = excluded.page_type,
214
+ content = excluded.content,
215
+ confidence = excluded.confidence,
216
+ source_ids = excluded.source_ids,
217
+ entity_refs = excluded.entity_refs,
218
+ compiled_at = excluded.compiled_at,
219
+ updated_at = excluded.updated_at
220
+ `)
221
+ .run(pageIdForSourceLocator(input.source_locator), input.source_locator, input.case_id ?? null, input.title, input.page_type, input.content, input.confidence, JSON.stringify(input.source_ids), JSON.stringify(input.entity_refs), input.compiled_at, updatedAt);
222
+ }
223
+ else {
224
+ adapter
225
+ .prepare(`
226
+ INSERT INTO wiki_page_index (
227
+ page_id, source_type, source_locator, case_id, title, page_type,
228
+ content, confidence, compiled_at, updated_at
229
+ )
230
+ VALUES (?, 'wiki_page', ?, ?, ?, ?, ?, ?, ?, ?)
231
+ ON CONFLICT(source_type, source_locator) DO UPDATE SET
232
+ case_id = excluded.case_id,
233
+ title = excluded.title,
234
+ page_type = excluded.page_type,
235
+ content = excluded.content,
236
+ confidence = excluded.confidence,
237
+ compiled_at = excluded.compiled_at,
238
+ updated_at = excluded.updated_at
239
+ `)
240
+ .run(pageIdForSourceLocator(input.source_locator), input.source_locator, input.case_id ?? null, input.title, input.page_type, input.content, input.confidence, input.compiled_at, updatedAt);
241
+ }
242
+ const saved = selectByLocator(adapter, schema, input.source_locator);
243
+ if (!saved) {
244
+ throw new Error(`Failed to load wiki_page_index row for ${input.source_locator}.`);
245
+ }
246
+ upsertEmbedding(adapter, schema, saved, input);
247
+ return {
248
+ id: saved.id,
249
+ created: existing === null,
250
+ };
251
+ });
252
+ }
253
+ function deleteWikiPageIndexEntry(adapter, source_locator) {
254
+ const result = adapter
255
+ .prepare('DELETE FROM wiki_page_index WHERE source_locator = ?')
256
+ .run(source_locator);
257
+ return { deleted: result.changes > 0 };
258
+ }
259
+ function ftsSearchWikiPages(adapter, query, limit) {
260
+ const boundedLimit = normalizeLimit(limit);
261
+ if (boundedLimit === 0 || !query.trim()) {
262
+ return [];
263
+ }
264
+ if (!searchTableExists(adapter, 'wiki_pages_fts')) {
265
+ return [];
266
+ }
267
+ const schema = readSchema(adapter);
268
+ const rows = adapter
269
+ .prepare(`
270
+ SELECT page_id, bm25(wiki_pages_fts) AS raw
271
+ FROM wiki_pages_fts
272
+ WHERE wiki_pages_fts MATCH ?
273
+ ORDER BY raw
274
+ LIMIT ?
275
+ `)
276
+ .all(buildFtsQuery(query), boundedLimit);
277
+ const hits = [];
278
+ for (const row of rows) {
279
+ const record = selectByFtsPageId(adapter, schema, row.page_id);
280
+ if (!record) {
281
+ continue;
282
+ }
283
+ hits.push({
284
+ record,
285
+ rank: hits.length,
286
+ raw_score: normalizeBm25Score(row.raw),
287
+ });
288
+ }
289
+ return hits;
290
+ }
291
+ function vectorSearchWikiPages(adapter, queryEmbedding, limit) {
292
+ const boundedLimit = normalizeLimit(limit);
293
+ if (boundedLimit === 0) {
294
+ return [];
295
+ }
296
+ const schema = readSchema(adapter);
297
+ const rows = schema.embeddingUsesWikiPageId
298
+ ? adapter
299
+ .prepare(`
300
+ SELECT i.*, e.vector
301
+ FROM wiki_page_embeddings e
302
+ JOIN wiki_page_index i ON i.id = e.wiki_page_id
303
+ `)
304
+ .all()
305
+ : adapter
306
+ .prepare(`
307
+ SELECT i.rowid AS id, i.*, e.embedding
308
+ FROM wiki_page_embeddings e
309
+ JOIN wiki_page_index i ON i.page_id = e.page_id
310
+ `)
311
+ .all();
312
+ const scored = [];
313
+ for (const row of rows) {
314
+ const candidate = blobToVector(schema.embeddingUsesVector ? row.vector : row.embedding);
315
+ if (!candidate || candidate.length !== queryEmbedding.length) {
316
+ continue;
317
+ }
318
+ const score = (0, embeddings_js_1.cosineSimilarity)(candidate, queryEmbedding);
319
+ if (!Number.isFinite(score)) {
320
+ continue;
321
+ }
322
+ scored.push({ record: rowToRecord(row), score });
323
+ }
324
+ return scored
325
+ .sort((left, right) => right.score - left.score)
326
+ .slice(0, boundedLimit)
327
+ .map((entry, rank) => ({
328
+ record: entry.record,
329
+ rank,
330
+ raw_score: entry.score,
331
+ }));
332
+ }
333
+ function searchWikiPages(input) {
334
+ const ftsHits = ftsSearchWikiPages(input.adapter, input.query, input.limit);
335
+ const vectorHits = input.queryEmbedding
336
+ ? vectorSearchWikiPages(input.adapter, input.queryEmbedding, input.limit)
337
+ : [];
338
+ const merged = new Map();
339
+ for (const hit of [...ftsHits, ...vectorHits]) {
340
+ const existing = merged.get(hit.record.id);
341
+ if (!existing || hit.raw_score > existing.raw_score) {
342
+ merged.set(hit.record.id, hit);
343
+ }
344
+ }
345
+ return Array.from(merged.values())
346
+ .sort((left, right) => right.raw_score - left.raw_score)
347
+ .slice(0, normalizeLimit(input.limit))
348
+ .map((hit, rank) => ({ ...hit, rank }));
349
+ }
350
+ //# sourceMappingURL=wiki-page-index.js.map