@jungjaehoon/mama-core 1.4.2 → 1.5.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 (118) hide show
  1. package/db/migrations/026-create-canonical-entity-tables.sql +99 -0
  2. package/db/migrations/027-create-entity-review-support.sql +44 -0
  3. package/db/migrations/028-create-entity-audit-tables.sql +45 -0
  4. package/db/migrations/029-extend-entity-observations-observation-type.sql +60 -0
  5. package/db/migrations/030-case-first-memory-substrate.sql +901 -0
  6. package/db/migrations/031-wiki-page-index-provenance-columns.sql +2 -0
  7. package/dist/canonicalize.d.ts +38 -0
  8. package/dist/canonicalize.js +106 -0
  9. package/dist/cases/case-links.d.ts +116 -0
  10. package/dist/cases/case-links.js +794 -0
  11. package/dist/cases/composition-overrides.d.ts +90 -0
  12. package/dist/cases/composition-overrides.js +724 -0
  13. package/dist/cases/corrections.d.ts +156 -0
  14. package/dist/cases/corrections.js +1045 -0
  15. package/dist/cases/freshness.d.ts +57 -0
  16. package/dist/cases/freshness.js +283 -0
  17. package/dist/cases/live-state.d.ts +48 -0
  18. package/dist/cases/live-state.js +256 -0
  19. package/dist/cases/membership-explain.d.ts +56 -0
  20. package/dist/cases/membership-explain.js +140 -0
  21. package/dist/cases/membership-matcher.d.ts +23 -0
  22. package/dist/cases/membership-matcher.js +193 -0
  23. package/dist/cases/merge-split.d.ts +61 -0
  24. package/dist/cases/merge-split.js +368 -0
  25. package/dist/cases/role-inference.d.ts +10 -0
  26. package/dist/cases/role-inference.js +85 -0
  27. package/dist/cases/search-rollup.d.ts +23 -0
  28. package/dist/cases/search-rollup.js +225 -0
  29. package/dist/cases/sqlite-transaction.d.ts +10 -0
  30. package/dist/cases/sqlite-transaction.js +62 -0
  31. package/dist/cases/store.d.ts +74 -0
  32. package/dist/cases/store.js +816 -0
  33. package/dist/cases/target-ref.d.ts +25 -0
  34. package/dist/cases/target-ref.js +22 -0
  35. package/dist/cases/timeline-range.d.ts +6 -0
  36. package/dist/cases/timeline-range.js +322 -0
  37. package/dist/cases/tombstone-sweeper.d.ts +13 -0
  38. package/dist/cases/tombstone-sweeper.js +128 -0
  39. package/dist/cases/types.d.ts +244 -0
  40. package/dist/cases/types.js +47 -0
  41. package/dist/cases/wiki-page-index.d.ts +50 -0
  42. package/dist/cases/wiki-page-index.js +350 -0
  43. package/dist/connectors/event-index.d.ts +44 -0
  44. package/dist/connectors/event-index.js +366 -0
  45. package/dist/connectors/types.d.ts +69 -0
  46. package/dist/connectors/types.js +3 -0
  47. package/dist/db-manager.d.ts +4 -0
  48. package/dist/db-manager.js +10 -3
  49. package/dist/decision-formatter.d.ts +2 -0
  50. package/dist/decision-formatter.js +11 -8
  51. package/dist/embedding-server/mobile/daemon.d.ts +1 -0
  52. package/dist/embedding-server/mobile/daemon.js +11 -1
  53. package/dist/entities/audit-metrics.d.ts +73 -0
  54. package/dist/entities/audit-metrics.js +128 -0
  55. package/dist/entities/candidate-generator.d.ts +9 -0
  56. package/dist/entities/candidate-generator.js +436 -0
  57. package/dist/entities/entity-impact.d.ts +41 -0
  58. package/dist/entities/entity-impact.js +140 -0
  59. package/dist/entities/entity-linked-decision-counts.d.ts +2 -0
  60. package/dist/entities/entity-linked-decision-counts.js +29 -0
  61. package/dist/entities/entity-list.d.ts +23 -0
  62. package/dist/entities/entity-list.js +99 -0
  63. package/dist/entities/entity-orphan-list.d.ts +26 -0
  64. package/dist/entities/entity-orphan-list.js +91 -0
  65. package/dist/entities/entity-search.d.ts +21 -0
  66. package/dist/entities/entity-search.js +150 -0
  67. package/dist/entities/errors.d.ts +65 -0
  68. package/dist/entities/errors.js +124 -0
  69. package/dist/entities/exact-merge-backfill.d.ts +11 -0
  70. package/dist/entities/exact-merge-backfill.js +161 -0
  71. package/dist/entities/lineage-backfill.d.ts +12 -0
  72. package/dist/entities/lineage-backfill.js +101 -0
  73. package/dist/entities/lineage-store.d.ts +51 -0
  74. package/dist/entities/lineage-store.js +308 -0
  75. package/dist/entities/normalization.d.ts +14 -0
  76. package/dist/entities/normalization.js +63 -0
  77. package/dist/entities/policy-store.d.ts +17 -0
  78. package/dist/entities/policy-store.js +263 -0
  79. package/dist/entities/policy-types.d.ts +72 -0
  80. package/dist/entities/policy-types.js +59 -0
  81. package/dist/entities/projection.d.ts +8 -0
  82. package/dist/entities/projection.js +82 -0
  83. package/dist/entities/provenance-query.d.ts +33 -0
  84. package/dist/entities/provenance-query.js +167 -0
  85. package/dist/entities/read-identity.d.ts +31 -0
  86. package/dist/entities/read-identity.js +264 -0
  87. package/dist/entities/recall-bridge.d.ts +5 -0
  88. package/dist/entities/recall-bridge.js +171 -0
  89. package/dist/entities/resolution-engine.d.ts +15 -0
  90. package/dist/entities/resolution-engine.js +69 -0
  91. package/dist/entities/rollback-preview.d.ts +39 -0
  92. package/dist/entities/rollback-preview.js +235 -0
  93. package/dist/entities/source-locator.d.ts +3 -0
  94. package/dist/entities/source-locator.js +32 -0
  95. package/dist/entities/store.d.ts +75 -0
  96. package/dist/entities/store.js +465 -0
  97. package/dist/entities/types.d.ts +162 -0
  98. package/dist/entities/types.js +30 -0
  99. package/dist/index.d.ts +47 -2
  100. package/dist/index.js +66 -25
  101. package/dist/mama-api.d.ts +22 -1
  102. package/dist/mama-api.js +272 -64
  103. package/dist/memory/api.d.ts +28 -1
  104. package/dist/memory/api.js +293 -31
  105. package/dist/memory/types.d.ts +11 -2
  106. package/dist/search/feedback-store.d.ts +59 -0
  107. package/dist/search/feedback-store.js +211 -0
  108. package/dist/search/question-type.d.ts +5 -0
  109. package/dist/search/question-type.js +41 -0
  110. package/dist/search/ranker-features.d.ts +23 -0
  111. package/dist/search/ranker-features.js +104 -0
  112. package/dist/search/ranker-rescore.d.ts +31 -0
  113. package/dist/search/ranker-rescore.js +226 -0
  114. package/dist/search/ranker-trainer.d.ts +94 -0
  115. package/dist/search/ranker-trainer.js +860 -0
  116. package/dist/test-utils.d.ts +14 -0
  117. package/dist/test-utils.js +52 -0
  118. package/package.json +21 -3
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rollUpSearchHits = rollUpSearchHits;
4
+ const store_js_1 = require("./store.js");
5
+ function sourceKey(sourceType, sourceId) {
6
+ return `${sourceType}:${sourceId}`;
7
+ }
8
+ function objectRecord(value) {
9
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
10
+ return value;
11
+ }
12
+ return null;
13
+ }
14
+ function stringOrNull(value) {
15
+ if (value === null || value === undefined) {
16
+ return null;
17
+ }
18
+ return String(value);
19
+ }
20
+ function placeholders(values) {
21
+ if (values.length === 0) {
22
+ throw new Error('Cannot build SQL IN clause for an empty value list.');
23
+ }
24
+ return values.map(() => '?').join(', ');
25
+ }
26
+ function withCaseId(record, caseId) {
27
+ const object = objectRecord(record);
28
+ if (!object) {
29
+ return { case_id: caseId };
30
+ }
31
+ return { ...object, case_id: caseId };
32
+ }
33
+ function loadCaseRecord(adapter, caseId) {
34
+ const row = adapter.prepare('SELECT * FROM case_truth WHERE case_id = ?').get(caseId);
35
+ return row ?? { case_id: caseId };
36
+ }
37
+ function canonicalCaseId(adapter, caseId) {
38
+ return (0, store_js_1.resolveCanonicalCaseChain)(adapter, caseId).terminal_case_id;
39
+ }
40
+ function listActiveMembershipCaseIds(adapter, sourceType, sourceId) {
41
+ const rows = adapter
42
+ .prepare(`
43
+ SELECT case_id
44
+ FROM case_memberships
45
+ WHERE source_type = ?
46
+ AND source_id = ?
47
+ AND status = 'active'
48
+ `)
49
+ .all(sourceType, sourceId);
50
+ return rows.map((row) => row.case_id);
51
+ }
52
+ function uniqueStrings(values) {
53
+ return Array.from(new Set(values.filter((value) => Boolean(value))));
54
+ }
55
+ function listConnectorEventMembershipCaseIds(adapter, leaf) {
56
+ const record = objectRecord(leaf.record) ?? {};
57
+ const sourceConnector = stringOrNull(record.source_connector);
58
+ const sourceLocator = stringOrNull(record.source_locator);
59
+ const artifactLocator = stringOrNull(record.artifact_locator);
60
+ const candidates = uniqueStrings([sourceLocator, artifactLocator]);
61
+ const caseIds = new Set();
62
+ if (sourceConnector && sourceLocator) {
63
+ const observationRows = adapter
64
+ .prepare(`
65
+ SELECT cm.case_id
66
+ FROM entity_observations eo
67
+ JOIN case_memberships cm
68
+ ON cm.source_type = 'observation'
69
+ AND cm.source_id = eo.id
70
+ AND cm.status = 'active'
71
+ WHERE eo.source_connector = ?
72
+ AND eo.source_locator = ?
73
+ `)
74
+ .all(sourceConnector, sourceLocator);
75
+ for (const row of observationRows) {
76
+ caseIds.add(row.case_id);
77
+ }
78
+ }
79
+ if (candidates.length > 0) {
80
+ const artifactRows = adapter
81
+ .prepare(`
82
+ SELECT case_id
83
+ FROM case_memberships
84
+ WHERE source_type = 'artifact'
85
+ AND status = 'active'
86
+ AND source_id IN (${placeholders(candidates)})
87
+ `)
88
+ .all(...candidates);
89
+ for (const row of artifactRows) {
90
+ caseIds.add(row.case_id);
91
+ }
92
+ }
93
+ return Array.from(caseIds).sort((left, right) => left.localeCompare(right));
94
+ }
95
+ function membershipCaseIdsForLeaf(adapter, leaf) {
96
+ if (leaf.source_type === 'connector_event') {
97
+ return listConnectorEventMembershipCaseIds(adapter, leaf);
98
+ }
99
+ return listActiveMembershipCaseIds(adapter, leaf.source_type, leaf.source_id);
100
+ }
101
+ function upsertOrphan(orphans, leaf) {
102
+ const key = sourceKey(leaf.source_type, leaf.source_id);
103
+ const existing = orphans.get(key);
104
+ if (existing && existing.score >= leaf.fused_rank_score) {
105
+ return;
106
+ }
107
+ orphans.set(key, {
108
+ source_type: leaf.source_type === 'connector_event' ? 'standalone_connector_hit' : leaf.source_type,
109
+ source_id: leaf.source_id,
110
+ case_id: null,
111
+ score: leaf.fused_rank_score,
112
+ record: leaf.record,
113
+ });
114
+ }
115
+ function upsertDirectCase(directCases, adapter, leaf, caseId) {
116
+ const survivorCaseId = canonicalCaseId(adapter, caseId);
117
+ const existing = directCases.get(survivorCaseId);
118
+ if (!existing || leaf.fused_rank_score > existing.score) {
119
+ directCases.set(survivorCaseId, {
120
+ case_id: survivorCaseId,
121
+ score: leaf.fused_rank_score,
122
+ max_leaf_score: leaf.fused_rank_score,
123
+ contributing_leaves: new Set([leaf.source_id]),
124
+ dedupe_keys: new Set([leaf.source_id]),
125
+ record: withCaseId(leaf.record, survivorCaseId),
126
+ });
127
+ }
128
+ return survivorCaseId;
129
+ }
130
+ function upsertCaseGroup(groups, adapter, leaf, survivorCaseId) {
131
+ const dedupeKey = `${survivorCaseId}\0${leaf.source_type}\0${leaf.source_id}`;
132
+ let group = groups.get(survivorCaseId);
133
+ if (!group) {
134
+ group = {
135
+ case_id: survivorCaseId,
136
+ score: 0,
137
+ max_leaf_score: 0,
138
+ contributing_leaves: new Set(),
139
+ dedupe_keys: new Set(),
140
+ record: loadCaseRecord(adapter, survivorCaseId),
141
+ };
142
+ groups.set(survivorCaseId, group);
143
+ }
144
+ if (group.dedupe_keys.has(dedupeKey)) {
145
+ return;
146
+ }
147
+ group.dedupe_keys.add(dedupeKey);
148
+ group.contributing_leaves.add(leaf.source_id);
149
+ group.score += leaf.fused_rank_score;
150
+ group.max_leaf_score = Math.max(group.max_leaf_score, leaf.fused_rank_score);
151
+ }
152
+ function groupToResult(group) {
153
+ const baseRecord = objectRecord(withCaseId(group.record, group.case_id)) ?? {
154
+ case_id: group.case_id,
155
+ };
156
+ const record = { ...baseRecord, max_leaf_score: group.max_leaf_score };
157
+ return {
158
+ source_type: 'case',
159
+ source_id: group.case_id,
160
+ case_id: group.case_id,
161
+ score: group.score,
162
+ contributing_leaves: Array.from(group.contributing_leaves).sort((left, right) => left.localeCompare(right)),
163
+ record,
164
+ };
165
+ }
166
+ function maxLeafScore(result) {
167
+ const object = objectRecord(result.record);
168
+ const value = object?.max_leaf_score;
169
+ return typeof value === 'number' && Number.isFinite(value) ? value : result.score;
170
+ }
171
+ function compareResults(left, right) {
172
+ if (right.score !== left.score) {
173
+ return right.score - left.score;
174
+ }
175
+ const leafDelta = maxLeafScore(right) - maxLeafScore(left);
176
+ if (leafDelta !== 0) {
177
+ return leafDelta;
178
+ }
179
+ if (left.source_type === 'case' && right.source_type !== 'case') {
180
+ return -1;
181
+ }
182
+ if (left.source_type !== 'case' && right.source_type === 'case') {
183
+ return 1;
184
+ }
185
+ return left.source_id.localeCompare(right.source_id);
186
+ }
187
+ function rollUpSearchHits(input) {
188
+ const directCases = new Map();
189
+ const directCaseIds = new Set();
190
+ const groupedCases = new Map();
191
+ const orphans = new Map();
192
+ for (const leaf of input.fusedHits) {
193
+ if (leaf.source_type === 'wiki_page' && leaf.page_type === 'case') {
194
+ if (leaf.case_id) {
195
+ const survivorCaseId = upsertDirectCase(directCases, input.adapter, leaf, leaf.case_id);
196
+ directCaseIds.add(survivorCaseId);
197
+ groupedCases.delete(survivorCaseId);
198
+ }
199
+ else {
200
+ upsertOrphan(orphans, leaf);
201
+ }
202
+ continue;
203
+ }
204
+ const membershipCaseIds = membershipCaseIdsForLeaf(input.adapter, leaf);
205
+ if (membershipCaseIds.length === 0) {
206
+ upsertOrphan(orphans, leaf);
207
+ continue;
208
+ }
209
+ for (const membershipCaseId of membershipCaseIds) {
210
+ const survivorCaseId = canonicalCaseId(input.adapter, membershipCaseId);
211
+ if (directCaseIds.has(survivorCaseId)) {
212
+ continue;
213
+ }
214
+ upsertCaseGroup(groupedCases, input.adapter, leaf, survivorCaseId);
215
+ }
216
+ }
217
+ return [
218
+ ...Array.from(directCases.values()).map(groupToResult),
219
+ ...Array.from(groupedCases.values())
220
+ .filter((group) => !directCaseIds.has(group.case_id))
221
+ .map(groupToResult),
222
+ ...Array.from(orphans.values()),
223
+ ].sort(compareResults);
224
+ }
225
+ //# sourceMappingURL=search-rollup.js.map
@@ -0,0 +1,10 @@
1
+ export interface ImmediateTransactionAdapter {
2
+ exec?: (sql: string) => void;
3
+ transaction: <T>(fn: () => T) => T;
4
+ }
5
+ export interface ImmediateTransactionOptions {
6
+ maxBusyRetries?: number;
7
+ busyRetryDelayMs?: number;
8
+ }
9
+ export declare function runImmediateTransaction<T>(adapter: ImmediateTransactionAdapter, fn: () => T, options?: ImmediateTransactionOptions): T;
10
+ //# sourceMappingURL=sqlite-transaction.d.ts.map
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runImmediateTransaction = runImmediateTransaction;
4
+ function isThenable(value) {
5
+ return (((typeof value === 'object' && value !== null) || typeof value === 'function') &&
6
+ typeof value.then === 'function');
7
+ }
8
+ function isBusyError(error) {
9
+ if (!(error instanceof Error)) {
10
+ return false;
11
+ }
12
+ const code = error.code;
13
+ return code === 'SQLITE_BUSY' || /SQLITE_BUSY|database is locked/i.test(error.message);
14
+ }
15
+ function requireSynchronousResult(result) {
16
+ if (isThenable(result)) {
17
+ throw new Error('runImmediateTransaction() callbacks must be synchronous');
18
+ }
19
+ return result;
20
+ }
21
+ function runImmediateTransaction(adapter, fn, options) {
22
+ const maxBusyRetries = options?.maxBusyRetries ?? 3;
23
+ const busyRetryDelayMs = Math.max(0, options?.busyRetryDelayMs ?? 20);
24
+ if (!adapter.exec) {
25
+ // Fake test adapters without exec can only provide deferred transaction
26
+ // semantics; production SQLite adapters should expose exec for BEGIN IMMEDIATE.
27
+ return adapter.transaction(() => requireSynchronousResult(fn()));
28
+ }
29
+ let attempt = 0;
30
+ for (;;) {
31
+ try {
32
+ adapter.exec('BEGIN IMMEDIATE');
33
+ try {
34
+ const result = requireSynchronousResult(fn());
35
+ adapter.exec('COMMIT');
36
+ return result;
37
+ }
38
+ catch (error) {
39
+ try {
40
+ adapter.exec('ROLLBACK');
41
+ }
42
+ catch {
43
+ // Preserve the original transaction failure when rollback also fails.
44
+ }
45
+ throw error;
46
+ }
47
+ }
48
+ catch (error) {
49
+ if (isBusyError(error) && attempt < maxBusyRetries) {
50
+ attempt += 1;
51
+ if (busyRetryDelayMs > 0) {
52
+ // Preserve the retry budget contract without blocking the event loop
53
+ // inside shared core code. Callers that need delayed retries should
54
+ // wrap this synchronous helper at a higher async boundary.
55
+ }
56
+ continue;
57
+ }
58
+ throw error;
59
+ }
60
+ }
61
+ }
62
+ //# sourceMappingURL=sqlite-transaction.js.map
@@ -0,0 +1,74 @@
1
+ import { type DatabaseAdapter } from '../db-manager.js';
2
+ import type { CanonicalCaseResolution, CaseAssembly, CaseAssemblyMembership, CaseBlocker, CaseConfidence, CaseCorrectionRecord, CaseMembershipRecord, CaseMembershipSourceType, CasePrimaryActor, CaseProposalKind, CaseProposalQueueRecord, CaseTruthRecord } from './types.js';
3
+ type CaseStoreAdapter = Pick<DatabaseAdapter, 'prepare' | 'transaction'>;
4
+ export interface UpsertCaseTruthSlowFieldsInput {
5
+ case_id: string;
6
+ current_wiki_path?: string | null;
7
+ title: string;
8
+ status_reason?: string | null;
9
+ primary_actors?: CasePrimaryActor[] | string | null;
10
+ blockers?: CaseBlocker[] | string | null;
11
+ confidence?: CaseConfidence | null;
12
+ scope_refs?: Array<{
13
+ kind: string;
14
+ id: string;
15
+ }> | string | null;
16
+ wiki_path_history?: Array<{
17
+ path: string;
18
+ valid_from: string;
19
+ valid_to: string | null;
20
+ }> | string | null;
21
+ compiled_at: string;
22
+ }
23
+ export interface UpsertExplicitCaseMembershipsInput {
24
+ case_id: string;
25
+ rows: Array<{
26
+ source_type: CaseMembershipSourceType;
27
+ source_id: string;
28
+ role?: string | null;
29
+ confidence?: number | null;
30
+ reason?: string | null;
31
+ }>;
32
+ }
33
+ export interface EnqueueCaseProposalInput {
34
+ project: string;
35
+ proposal_kind: CaseProposalKind;
36
+ proposed_payload: string;
37
+ stable_fingerprint_input: unknown;
38
+ conflicting_case_id?: string | null;
39
+ }
40
+ export interface AssembleCaseOptions {
41
+ since?: string;
42
+ limit?: number;
43
+ }
44
+ export declare function resolveCanonicalCaseChain(adapter: CaseStoreAdapter, caseId: string, maxDepth?: number): CanonicalCaseResolution;
45
+ export declare function expandCaseChainForAssembly(adapter: Pick<DatabaseAdapter, 'prepare'>, terminalCaseId: string, resolvedChain: string[]): string[];
46
+ export declare function upsertCaseTruthSlowFields(input: UpsertCaseTruthSlowFieldsInput): CaseTruthRecord;
47
+ export declare function upsertCaseTruthSlowFields(adapter: CaseStoreAdapter, input: UpsertCaseTruthSlowFieldsInput): CaseTruthRecord;
48
+ export declare function upsertExplicitCaseMemberships(input: UpsertExplicitCaseMembershipsInput): CaseMembershipRecord[];
49
+ export declare function upsertExplicitCaseMemberships(adapter: CaseStoreAdapter, input: UpsertExplicitCaseMembershipsInput): CaseMembershipRecord[];
50
+ export declare function listActiveMembershipsForCaseChain(adapter: CaseStoreAdapter, caseIds: string[], limit?: number): CaseAssemblyMembership[];
51
+ export interface ListActiveCorrectionsForCaseResult {
52
+ terminal_case_id: string;
53
+ resolved_via_case_id: string | null;
54
+ chain: string[];
55
+ corrections: CaseCorrectionRecord[];
56
+ }
57
+ /**
58
+ * Resolves the canonical merge chain for caseId (both up to terminal and
59
+ * down to merged losers) and returns every active, unreverted correction
60
+ * across the entire chain. HITL surfaces must use this rather than a
61
+ * direct `WHERE case_id = ?` query so post-merge corrections on loser
62
+ * cases remain visible and actionable from the survivor's view.
63
+ */
64
+ export declare function listActiveCorrectionsForCase(adapter: CaseStoreAdapter, caseId: string): ListActiveCorrectionsForCaseResult;
65
+ export declare function listActiveCorrectionsForCaseChain(adapter: CaseStoreAdapter, caseIds: string[]): CaseCorrectionRecord[];
66
+ export declare function enqueueCaseProposal(adapter: CaseStoreAdapter, input: EnqueueCaseProposalInput): {
67
+ proposal_id: string;
68
+ inserted: boolean;
69
+ };
70
+ export declare function listUnresolvedCaseProposals(adapter: CaseStoreAdapter, project: string): CaseProposalQueueRecord[];
71
+ export declare function assembleCase(caseId: string, options?: AssembleCaseOptions): CaseAssembly;
72
+ export declare function assembleCase(adapter: CaseStoreAdapter, caseId: string, options?: AssembleCaseOptions): CaseAssembly;
73
+ export {};
74
+ //# sourceMappingURL=store.d.ts.map