@jungjaehoon/mama-core 1.4.1 → 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 (120) 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/config-loader.d.ts +1 -0
  44. package/dist/config-loader.js +43 -0
  45. package/dist/connectors/event-index.d.ts +44 -0
  46. package/dist/connectors/event-index.js +366 -0
  47. package/dist/connectors/types.d.ts +69 -0
  48. package/dist/connectors/types.js +3 -0
  49. package/dist/db-manager.d.ts +4 -0
  50. package/dist/db-manager.js +10 -3
  51. package/dist/decision-formatter.d.ts +2 -0
  52. package/dist/decision-formatter.js +11 -8
  53. package/dist/embedding-server/mobile/daemon.d.ts +1 -0
  54. package/dist/embedding-server/mobile/daemon.js +11 -1
  55. package/dist/entities/audit-metrics.d.ts +73 -0
  56. package/dist/entities/audit-metrics.js +128 -0
  57. package/dist/entities/candidate-generator.d.ts +9 -0
  58. package/dist/entities/candidate-generator.js +436 -0
  59. package/dist/entities/entity-impact.d.ts +41 -0
  60. package/dist/entities/entity-impact.js +140 -0
  61. package/dist/entities/entity-linked-decision-counts.d.ts +2 -0
  62. package/dist/entities/entity-linked-decision-counts.js +29 -0
  63. package/dist/entities/entity-list.d.ts +23 -0
  64. package/dist/entities/entity-list.js +99 -0
  65. package/dist/entities/entity-orphan-list.d.ts +26 -0
  66. package/dist/entities/entity-orphan-list.js +91 -0
  67. package/dist/entities/entity-search.d.ts +21 -0
  68. package/dist/entities/entity-search.js +150 -0
  69. package/dist/entities/errors.d.ts +65 -0
  70. package/dist/entities/errors.js +124 -0
  71. package/dist/entities/exact-merge-backfill.d.ts +11 -0
  72. package/dist/entities/exact-merge-backfill.js +161 -0
  73. package/dist/entities/lineage-backfill.d.ts +12 -0
  74. package/dist/entities/lineage-backfill.js +101 -0
  75. package/dist/entities/lineage-store.d.ts +51 -0
  76. package/dist/entities/lineage-store.js +308 -0
  77. package/dist/entities/normalization.d.ts +14 -0
  78. package/dist/entities/normalization.js +63 -0
  79. package/dist/entities/policy-store.d.ts +17 -0
  80. package/dist/entities/policy-store.js +263 -0
  81. package/dist/entities/policy-types.d.ts +72 -0
  82. package/dist/entities/policy-types.js +59 -0
  83. package/dist/entities/projection.d.ts +8 -0
  84. package/dist/entities/projection.js +82 -0
  85. package/dist/entities/provenance-query.d.ts +33 -0
  86. package/dist/entities/provenance-query.js +167 -0
  87. package/dist/entities/read-identity.d.ts +31 -0
  88. package/dist/entities/read-identity.js +264 -0
  89. package/dist/entities/recall-bridge.d.ts +5 -0
  90. package/dist/entities/recall-bridge.js +171 -0
  91. package/dist/entities/resolution-engine.d.ts +15 -0
  92. package/dist/entities/resolution-engine.js +69 -0
  93. package/dist/entities/rollback-preview.d.ts +39 -0
  94. package/dist/entities/rollback-preview.js +235 -0
  95. package/dist/entities/source-locator.d.ts +3 -0
  96. package/dist/entities/source-locator.js +32 -0
  97. package/dist/entities/store.d.ts +75 -0
  98. package/dist/entities/store.js +465 -0
  99. package/dist/entities/types.d.ts +162 -0
  100. package/dist/entities/types.js +30 -0
  101. package/dist/index.d.ts +47 -2
  102. package/dist/index.js +66 -25
  103. package/dist/mama-api.d.ts +22 -1
  104. package/dist/mama-api.js +272 -64
  105. package/dist/memory/api.d.ts +28 -1
  106. package/dist/memory/api.js +293 -31
  107. package/dist/memory/types.d.ts +11 -2
  108. package/dist/search/feedback-store.d.ts +59 -0
  109. package/dist/search/feedback-store.js +211 -0
  110. package/dist/search/question-type.d.ts +5 -0
  111. package/dist/search/question-type.js +41 -0
  112. package/dist/search/ranker-features.d.ts +23 -0
  113. package/dist/search/ranker-features.js +104 -0
  114. package/dist/search/ranker-rescore.d.ts +31 -0
  115. package/dist/search/ranker-rescore.js +226 -0
  116. package/dist/search/ranker-trainer.d.ts +94 -0
  117. package/dist/search/ranker-trainer.js +860 -0
  118. package/dist/test-utils.d.ts +14 -0
  119. package/dist/test-utils.js +52 -0
  120. package/package.json +21 -3
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectScript = detectScript;
4
+ exports.normalizeEntityLabel = normalizeEntityLabel;
5
+ exports.extractStructuredIdentifiers = extractStructuredIdentifiers;
6
+ const errors_js_1 = require("./errors.js");
7
+ const LATIN_RE = /\p{Script=Latin}/u;
8
+ const HANGUL_RE = /\p{Script=Hangul}/u;
9
+ const HIRAGANA_RE = /\p{Script=Hiragana}/u;
10
+ const KATAKANA_RE = /\p{Script=Katakana}/u;
11
+ const HAN_RE = /\p{Script=Han}/u;
12
+ function collapseWhitespace(input) {
13
+ return input.normalize('NFKC').trim().replace(/\s+/gu, ' ');
14
+ }
15
+ function detectScript(input) {
16
+ const hasLatin = LATIN_RE.test(input);
17
+ const hasHangul = HANGUL_RE.test(input);
18
+ const hasJapanese = HIRAGANA_RE.test(input) || KATAKANA_RE.test(input) || HAN_RE.test(input);
19
+ const activeScripts = [hasLatin, hasHangul, hasJapanese].filter(Boolean).length;
20
+ if (activeScripts > 1) {
21
+ return 'Mixed';
22
+ }
23
+ if (hasHangul) {
24
+ return 'Hang';
25
+ }
26
+ if (hasJapanese) {
27
+ return 'Jpan';
28
+ }
29
+ if (hasLatin) {
30
+ return 'Latn';
31
+ }
32
+ return 'Unknown';
33
+ }
34
+ function normalizeEntityLabel(input) {
35
+ if (typeof input !== 'string' || input.trim().length === 0) {
36
+ throw new errors_js_1.InvalidEntityLabelError({
37
+ input,
38
+ reason: typeof input === 'string' ? 'empty_string' : 'non_string_input',
39
+ });
40
+ }
41
+ const original = input;
42
+ const collapsed = collapseWhitespace(input);
43
+ const script = detectScript(collapsed);
44
+ const normalized = script === 'Latn' ? collapsed.toLowerCase() : collapsed;
45
+ return {
46
+ original,
47
+ normalized,
48
+ script,
49
+ };
50
+ }
51
+ function extractStructuredIdentifiers(input) {
52
+ const emails = Array.from(new Set((input.match(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/giu) ?? []).map((v) => v.toLowerCase())));
53
+ const handles = Array.from(new Set((input.match(/(?<!\w)@([a-z0-9_][a-z0-9_.-]*)/giu) ?? []).map((v) => v.slice(1).toLowerCase())));
54
+ const domains = Array.from(new Set((input.match(/\b(?:[a-z0-9-]+\.)+[a-z]{2,}\b/giu) ?? [])
55
+ .map((v) => v.toLowerCase())
56
+ .filter((v) => !emails.some((email) => email.endsWith(`@${v}`)))));
57
+ return {
58
+ emails,
59
+ handles,
60
+ domains,
61
+ };
62
+ }
63
+ //# sourceMappingURL=normalization.js.map
@@ -0,0 +1,17 @@
1
+ import type { DatabaseAdapter } from '../db-manager.js';
2
+ import { createDefaultEntityPolicyBootstrap, type EntityPolicyApprovalInput, type EntityPolicyBootstrapDocument, type EntityPolicyKind, type EntityPolicyProposalInput, type EntityPolicyProposalRow, type EntityPolicyRow, type EntityRole, type EntityRoleBinding, type EntityRoleBindingInput } from './policy-types.js';
3
+ type PolicyStoreAdapter = Pick<DatabaseAdapter, 'prepare' | 'transaction'>;
4
+ export declare function ensureEntityPolicyBootstrap(args: {
5
+ bootstrapPath: string;
6
+ now?: () => number;
7
+ }): Promise<void>;
8
+ export declare function listEntityPolicies(adapter?: PolicyStoreAdapter): EntityPolicyRow[];
9
+ export declare function getEntityPolicy(policyKey: string, adapter?: PolicyStoreAdapter): EntityPolicyRow | null;
10
+ export declare function listEntityRoleBindings(adapter?: PolicyStoreAdapter): EntityRoleBinding[];
11
+ export declare function upsertEntityRoleBinding(input: EntityRoleBindingInput, adapter?: PolicyStoreAdapter): void;
12
+ export declare function resolveEntityRoleForActor(actorId: string, adapter?: PolicyStoreAdapter): EntityRole;
13
+ export declare function createEntityPolicyProposal(input: EntityPolicyProposalInput, adapter?: PolicyStoreAdapter): string;
14
+ export declare function getEntityPolicyProposal(proposalId: string, adapter?: PolicyStoreAdapter): EntityPolicyProposalRow | null;
15
+ export declare function approveEntityPolicyProposal(input: EntityPolicyApprovalInput, adapter?: PolicyStoreAdapter): EntityPolicyProposalRow;
16
+ export { createDefaultEntityPolicyBootstrap, type EntityPolicyApprovalInput, type EntityPolicyBootstrapDocument, type EntityPolicyKind, type EntityPolicyProposalInput, type EntityPolicyProposalRow, type EntityPolicyRow, type EntityRole, type EntityRoleBinding, type EntityRoleBindingInput, };
17
+ //# sourceMappingURL=policy-store.d.ts.map
@@ -0,0 +1,263 @@
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.createDefaultEntityPolicyBootstrap = void 0;
7
+ exports.ensureEntityPolicyBootstrap = ensureEntityPolicyBootstrap;
8
+ exports.listEntityPolicies = listEntityPolicies;
9
+ exports.getEntityPolicy = getEntityPolicy;
10
+ exports.listEntityRoleBindings = listEntityRoleBindings;
11
+ exports.upsertEntityRoleBinding = upsertEntityRoleBinding;
12
+ exports.resolveEntityRoleForActor = resolveEntityRoleForActor;
13
+ exports.createEntityPolicyProposal = createEntityPolicyProposal;
14
+ exports.getEntityPolicyProposal = getEntityPolicyProposal;
15
+ exports.approveEntityPolicyProposal = approveEntityPolicyProposal;
16
+ const node_crypto_1 = require("node:crypto");
17
+ const node_fs_1 = __importDefault(require("node:fs"));
18
+ const db_manager_js_1 = require("../db-manager.js");
19
+ const policy_types_js_1 = require("./policy-types.js");
20
+ Object.defineProperty(exports, "createDefaultEntityPolicyBootstrap", { enumerable: true, get: function () { return policy_types_js_1.createDefaultEntityPolicyBootstrap; } });
21
+ function now() {
22
+ return Date.now();
23
+ }
24
+ function parseJsonObject(raw, label) {
25
+ const parsed = JSON.parse(raw);
26
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
27
+ throw new Error(`${label} must be a JSON object`);
28
+ }
29
+ return parsed;
30
+ }
31
+ function parsePolicyRow(row) {
32
+ const policyKind = String(row.policy_kind);
33
+ if (!(0, policy_types_js_1.isEntityPolicyKind)(policyKind)) {
34
+ throw new Error(`Invalid entity policy row: policy_kind=${policyKind}`);
35
+ }
36
+ const valueJson = String(row.value_json);
37
+ return {
38
+ policy_key: String(row.policy_key),
39
+ policy_kind: policyKind,
40
+ value_json: valueJson,
41
+ value: parseJsonObject(valueJson, 'entity_policy.value_json'),
42
+ version: Number(row.version),
43
+ created_at: Number(row.created_at),
44
+ updated_at: Number(row.updated_at),
45
+ };
46
+ }
47
+ function parseRoleBindingRow(row) {
48
+ const role = String(row.role);
49
+ if (!(0, policy_types_js_1.isEntityRole)(role)) {
50
+ throw new Error(`Invalid entity role binding row: role=${role}`);
51
+ }
52
+ return {
53
+ actor_id: String(row.actor_id),
54
+ role,
55
+ created_at: Number(row.created_at),
56
+ updated_at: Number(row.updated_at),
57
+ };
58
+ }
59
+ function parseProposalRow(row) {
60
+ const policyKind = String(row.policy_kind);
61
+ if (!(0, policy_types_js_1.isEntityPolicyKind)(policyKind)) {
62
+ throw new Error(`Invalid entity policy proposal row: policy_kind=${policyKind}`);
63
+ }
64
+ const status = String(row.status);
65
+ if (!(0, policy_types_js_1.isEntityPolicyProposalStatus)(status)) {
66
+ throw new Error(`Invalid entity policy proposal row: status=${status}`);
67
+ }
68
+ const proposedValueJson = String(row.proposed_value_json);
69
+ return {
70
+ proposal_id: String(row.proposal_id),
71
+ policy_key: String(row.policy_key),
72
+ policy_kind: policyKind,
73
+ proposed_value_json: proposedValueJson,
74
+ proposed_value: parseJsonObject(proposedValueJson, 'entity_policy_proposals.proposed_value_json'),
75
+ proposer_actor: String(row.proposer_actor),
76
+ approver_actor: typeof row.approver_actor === 'string' ? row.approver_actor : null,
77
+ reason: String(row.reason),
78
+ status,
79
+ created_at: Number(row.created_at),
80
+ approved_at: typeof row.approved_at === 'number' ? row.approved_at : null,
81
+ };
82
+ }
83
+ function readBootstrapDocument(bootstrapPath) {
84
+ const raw = node_fs_1.default.readFileSync(bootstrapPath, 'utf-8');
85
+ const parsed = JSON.parse(raw);
86
+ if (!parsed || typeof parsed !== 'object') {
87
+ throw new Error('Entity policy bootstrap must be a JSON object');
88
+ }
89
+ if (typeof parsed.version !== 'number') {
90
+ throw new Error('Entity policy bootstrap must include numeric version');
91
+ }
92
+ if (!Array.isArray(parsed.policies)) {
93
+ throw new Error('Entity policy bootstrap must include policies[]');
94
+ }
95
+ if (!Array.isArray(parsed.role_bindings)) {
96
+ throw new Error('Entity policy bootstrap must include role_bindings[]');
97
+ }
98
+ for (const policy of parsed.policies) {
99
+ if (!policy || typeof policy !== 'object' || Array.isArray(policy)) {
100
+ throw new Error('Entity policy bootstrap policies must be objects');
101
+ }
102
+ if (typeof policy.policy_key !== 'string' ||
103
+ typeof policy.policy_kind !== 'string' ||
104
+ !(0, policy_types_js_1.isEntityPolicyKind)(policy.policy_kind) ||
105
+ !policy.value ||
106
+ typeof policy.value !== 'object' ||
107
+ Array.isArray(policy.value)) {
108
+ throw new Error('Entity policy bootstrap policy rows are invalid');
109
+ }
110
+ }
111
+ for (const binding of parsed.role_bindings) {
112
+ if (!binding || typeof binding !== 'object' || Array.isArray(binding)) {
113
+ throw new Error('Entity policy bootstrap role_bindings must be objects');
114
+ }
115
+ if (typeof binding.actor_id !== 'string' ||
116
+ typeof binding.role !== 'string' ||
117
+ !(0, policy_types_js_1.isEntityRole)(binding.role)) {
118
+ throw new Error('Entity policy bootstrap role binding rows are invalid');
119
+ }
120
+ }
121
+ return parsed;
122
+ }
123
+ function countRows(tableName, adapter) {
124
+ const row = adapter.prepare(`SELECT COUNT(*) AS total FROM ${tableName}`).get();
125
+ return Number(row.total ?? 0);
126
+ }
127
+ async function ensureEntityPolicyBootstrap(args) {
128
+ await (0, db_manager_js_1.initDB)();
129
+ const adapter = (0, db_manager_js_1.getAdapter)();
130
+ const document = readBootstrapDocument(args.bootstrapPath);
131
+ const timestamp = args.now?.() ?? now();
132
+ adapter.transaction(() => {
133
+ const policyCount = countRows('entity_policy', adapter);
134
+ const roleBindingCount = countRows('entity_role_bindings', adapter);
135
+ if ((policyCount === 0) !== (roleBindingCount === 0)) {
136
+ throw new Error('Entity policy bootstrap tables are out of sync: entity_policy and entity_role_bindings must be seeded together.');
137
+ }
138
+ if (policyCount === 0 && roleBindingCount === 0) {
139
+ for (const policy of document.policies) {
140
+ adapter
141
+ .prepare(`
142
+ INSERT INTO entity_policy (
143
+ policy_key, policy_kind, value_json, version, created_at, updated_at
144
+ ) VALUES (?, ?, ?, 1, ?, ?)
145
+ `)
146
+ .run(policy.policy_key, policy.policy_kind, JSON.stringify(policy.value), timestamp, timestamp);
147
+ }
148
+ for (const binding of document.role_bindings) {
149
+ adapter
150
+ .prepare(`
151
+ INSERT INTO entity_role_bindings (
152
+ actor_id, role, created_at, updated_at
153
+ ) VALUES (?, ?, ?, ?)
154
+ `)
155
+ .run(binding.actor_id, binding.role, timestamp, timestamp);
156
+ }
157
+ }
158
+ });
159
+ }
160
+ function listEntityPolicies(adapter = (0, db_manager_js_1.getAdapter)()) {
161
+ const rows = adapter
162
+ .prepare('SELECT * FROM entity_policy ORDER BY policy_key ASC')
163
+ .all();
164
+ return rows.map(parsePolicyRow);
165
+ }
166
+ function getEntityPolicy(policyKey, adapter = (0, db_manager_js_1.getAdapter)()) {
167
+ const row = adapter
168
+ .prepare('SELECT * FROM entity_policy WHERE policy_key = ? LIMIT 1')
169
+ .get(policyKey);
170
+ return row ? parsePolicyRow(row) : null;
171
+ }
172
+ function listEntityRoleBindings(adapter = (0, db_manager_js_1.getAdapter)()) {
173
+ const rows = adapter
174
+ .prepare('SELECT * FROM entity_role_bindings ORDER BY actor_id ASC')
175
+ .all();
176
+ return rows.map(parseRoleBindingRow);
177
+ }
178
+ function upsertEntityRoleBinding(input, adapter = (0, db_manager_js_1.getAdapter)()) {
179
+ const timestamp = now();
180
+ adapter
181
+ .prepare(`
182
+ INSERT INTO entity_role_bindings (
183
+ actor_id, role, created_at, updated_at
184
+ ) VALUES (?, ?, ?, ?)
185
+ ON CONFLICT(actor_id) DO UPDATE SET
186
+ role = excluded.role,
187
+ updated_at = excluded.updated_at
188
+ `)
189
+ .run(input.actor_id, input.role, timestamp, timestamp);
190
+ }
191
+ function resolveEntityRoleForActor(actorId, adapter = (0, db_manager_js_1.getAdapter)()) {
192
+ const row = adapter
193
+ .prepare('SELECT role FROM entity_role_bindings WHERE actor_id = ? LIMIT 1')
194
+ .get(actorId);
195
+ return row?.role && (0, policy_types_js_1.isEntityRole)(row.role) ? row.role : 'viewer';
196
+ }
197
+ function createEntityPolicyProposal(input, adapter = (0, db_manager_js_1.getAdapter)()) {
198
+ const proposalId = input.proposal_id ?? `epol_${(0, node_crypto_1.randomUUID)()}`;
199
+ const createdAt = input.created_at ?? now();
200
+ adapter
201
+ .prepare(`
202
+ INSERT INTO entity_policy_proposals (
203
+ proposal_id, policy_key, policy_kind, proposed_value_json, proposer_actor,
204
+ approver_actor, reason, status, created_at, approved_at
205
+ ) VALUES (?, ?, ?, ?, ?, NULL, ?, 'pending', ?, NULL)
206
+ `)
207
+ .run(proposalId, input.policy_key, input.policy_kind, JSON.stringify(input.proposed_value), input.proposer_actor, input.reason, createdAt);
208
+ return proposalId;
209
+ }
210
+ function getEntityPolicyProposal(proposalId, adapter = (0, db_manager_js_1.getAdapter)()) {
211
+ const row = adapter
212
+ .prepare('SELECT * FROM entity_policy_proposals WHERE proposal_id = ? LIMIT 1')
213
+ .get(proposalId);
214
+ return row ? parseProposalRow(row) : null;
215
+ }
216
+ function approveEntityPolicyProposal(input, adapter = (0, db_manager_js_1.getAdapter)()) {
217
+ const approvedAt = input.approved_at ?? now();
218
+ return adapter.transaction(() => {
219
+ const proposal = getEntityPolicyProposal(input.proposal_id, adapter);
220
+ if (!proposal) {
221
+ throw new Error(`Entity policy proposal not found: ${input.proposal_id}`);
222
+ }
223
+ if (proposal.status !== 'pending') {
224
+ throw new Error(`Entity policy proposal is not pending: ${input.proposal_id}`);
225
+ }
226
+ if (proposal.proposer_actor === input.approver_actor) {
227
+ throw new Error('Entity policy proposal requires a different approver');
228
+ }
229
+ const approverRole = resolveEntityRoleForActor(input.approver_actor, adapter);
230
+ if (approverRole !== 'admin') {
231
+ throw new Error('Entity policy proposal approval requires an admin approver');
232
+ }
233
+ const existingPolicy = getEntityPolicy(proposal.policy_key, adapter);
234
+ const version = existingPolicy ? existingPolicy.version + 1 : 1;
235
+ adapter
236
+ .prepare(`
237
+ INSERT INTO entity_policy (
238
+ policy_key, policy_kind, value_json, version, created_at, updated_at
239
+ ) VALUES (?, ?, ?, ?, ?, ?)
240
+ ON CONFLICT(policy_key) DO UPDATE SET
241
+ policy_kind = excluded.policy_kind,
242
+ value_json = excluded.value_json,
243
+ version = excluded.version,
244
+ updated_at = excluded.updated_at
245
+ `)
246
+ .run(proposal.policy_key, proposal.policy_kind, proposal.proposed_value_json, version, approvedAt, approvedAt);
247
+ adapter
248
+ .prepare(`
249
+ UPDATE entity_policy_proposals
250
+ SET approver_actor = ?,
251
+ status = 'approved',
252
+ approved_at = ?
253
+ WHERE proposal_id = ?
254
+ `)
255
+ .run(input.approver_actor, approvedAt, input.proposal_id);
256
+ const approved = getEntityPolicyProposal(input.proposal_id, adapter);
257
+ if (!approved) {
258
+ throw new Error(`Failed to reload approved entity policy proposal: ${input.proposal_id}`);
259
+ }
260
+ return approved;
261
+ });
262
+ }
263
+ //# sourceMappingURL=policy-store.js.map
@@ -0,0 +1,72 @@
1
+ export declare const ENTITY_POLICY_ROLES: readonly ["viewer", "operator", "admin"];
2
+ export type EntityRole = (typeof ENTITY_POLICY_ROLES)[number];
3
+ export declare const ENTITY_POLICY_KINDS: readonly ["entity_kind", "merge_guardrails", "review_thresholds", "connector_policy"];
4
+ export type EntityPolicyKind = (typeof ENTITY_POLICY_KINDS)[number];
5
+ export declare const ENTITY_POLICY_PROPOSAL_STATUSES: readonly ["pending", "approved", "rejected"];
6
+ export type EntityPolicyProposalStatus = (typeof ENTITY_POLICY_PROPOSAL_STATUSES)[number];
7
+ export interface EntityPolicyRow {
8
+ policy_key: string;
9
+ policy_kind: EntityPolicyKind;
10
+ value_json: string;
11
+ value: Record<string, unknown>;
12
+ version: number;
13
+ created_at: number;
14
+ updated_at: number;
15
+ }
16
+ export interface EntityRoleBinding {
17
+ actor_id: string;
18
+ role: EntityRole;
19
+ created_at: number;
20
+ updated_at: number;
21
+ }
22
+ export interface EntityRoleBindingInput {
23
+ actor_id: string;
24
+ role: EntityRole;
25
+ }
26
+ export interface EntityPolicyProposalRow {
27
+ proposal_id: string;
28
+ policy_key: string;
29
+ policy_kind: EntityPolicyKind;
30
+ proposed_value_json: string;
31
+ proposed_value: Record<string, unknown>;
32
+ proposer_actor: string;
33
+ approver_actor: string | null;
34
+ reason: string;
35
+ status: EntityPolicyProposalStatus;
36
+ created_at: number;
37
+ approved_at: number | null;
38
+ }
39
+ export interface EntityPolicyProposalInput {
40
+ policy_key: string;
41
+ policy_kind: EntityPolicyKind;
42
+ proposed_value: Record<string, unknown>;
43
+ proposer_actor: string;
44
+ reason: string;
45
+ proposal_id?: string;
46
+ created_at?: number;
47
+ }
48
+ export interface EntityPolicyApprovalInput {
49
+ proposal_id: string;
50
+ approver_actor: string;
51
+ approved_at?: number;
52
+ }
53
+ export interface EntityPolicyBootstrapPolicy {
54
+ policy_key: string;
55
+ policy_kind: EntityPolicyKind;
56
+ value: Record<string, unknown>;
57
+ }
58
+ export interface EntityPolicyBootstrapRoleBinding {
59
+ actor_id: string;
60
+ role: EntityRole;
61
+ }
62
+ export interface EntityPolicyBootstrapDocument {
63
+ version: number;
64
+ policies: EntityPolicyBootstrapPolicy[];
65
+ role_bindings: EntityPolicyBootstrapRoleBinding[];
66
+ }
67
+ export declare const ENTITY_ROLE_ORDER: Record<EntityRole, number>;
68
+ export declare function isEntityRole(value: string): value is EntityRole;
69
+ export declare function isEntityPolicyKind(value: string): value is EntityPolicyKind;
70
+ export declare function isEntityPolicyProposalStatus(value: string): value is EntityPolicyProposalStatus;
71
+ export declare function createDefaultEntityPolicyBootstrap(adminActor?: string): EntityPolicyBootstrapDocument;
72
+ //# sourceMappingURL=policy-types.d.ts.map
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ENTITY_ROLE_ORDER = exports.ENTITY_POLICY_PROPOSAL_STATUSES = exports.ENTITY_POLICY_KINDS = exports.ENTITY_POLICY_ROLES = void 0;
4
+ exports.isEntityRole = isEntityRole;
5
+ exports.isEntityPolicyKind = isEntityPolicyKind;
6
+ exports.isEntityPolicyProposalStatus = isEntityPolicyProposalStatus;
7
+ exports.createDefaultEntityPolicyBootstrap = createDefaultEntityPolicyBootstrap;
8
+ exports.ENTITY_POLICY_ROLES = ['viewer', 'operator', 'admin'];
9
+ exports.ENTITY_POLICY_KINDS = [
10
+ 'entity_kind',
11
+ 'merge_guardrails',
12
+ 'review_thresholds',
13
+ 'connector_policy',
14
+ ];
15
+ exports.ENTITY_POLICY_PROPOSAL_STATUSES = ['pending', 'approved', 'rejected'];
16
+ exports.ENTITY_ROLE_ORDER = {
17
+ viewer: 0,
18
+ operator: 1,
19
+ admin: 2,
20
+ };
21
+ function isEntityRole(value) {
22
+ return exports.ENTITY_POLICY_ROLES.includes(value);
23
+ }
24
+ function isEntityPolicyKind(value) {
25
+ return exports.ENTITY_POLICY_KINDS.includes(value);
26
+ }
27
+ function isEntityPolicyProposalStatus(value) {
28
+ return exports.ENTITY_POLICY_PROPOSAL_STATUSES.includes(value);
29
+ }
30
+ function createDefaultEntityPolicyBootstrap(adminActor = 'local:viewer') {
31
+ return {
32
+ version: 1,
33
+ policies: [
34
+ {
35
+ policy_key: 'merge_guardrails.default',
36
+ policy_kind: 'merge_guardrails',
37
+ value: {
38
+ max_false_merge_rate: 0.02,
39
+ require_review_for_cross_scope: true,
40
+ },
41
+ },
42
+ {
43
+ policy_key: 'review_thresholds.default',
44
+ policy_kind: 'review_thresholds',
45
+ value: {
46
+ approve_score_min: 0.92,
47
+ defer_score_min: 0.78,
48
+ },
49
+ },
50
+ ],
51
+ role_bindings: [
52
+ {
53
+ actor_id: adminActor,
54
+ role: 'admin',
55
+ },
56
+ ],
57
+ };
58
+ }
59
+ //# sourceMappingURL=policy-types.js.map
@@ -0,0 +1,8 @@
1
+ import type { EntityAlias, EntityNode, EntityTimelineEvent } from './types.js';
2
+ import type { MemoryRecord } from '../memory/types.js';
3
+ interface ProjectionOptions {
4
+ nodeLookup?: Record<string, EntityNode>;
5
+ }
6
+ export declare function projectEntityToRecallSummary(node: EntityNode, aliases: EntityAlias[], latestEvent: EntityTimelineEvent | null, options?: ProjectionOptions): MemoryRecord;
7
+ export {};
8
+ //# sourceMappingURL=projection.d.ts.map
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.projectEntityToRecallSummary = projectEntityToRecallSummary;
4
+ const errors_js_1 = require("./errors.js");
5
+ function formatTimelineEventForRecall(latestEvent) {
6
+ if (!latestEvent) {
7
+ return null;
8
+ }
9
+ const parts = [];
10
+ const eventType = latestEvent.event_type.trim();
11
+ const summary = latestEvent.summary.trim();
12
+ const details = latestEvent.details?.trim() ?? '';
13
+ if (eventType.length > 0) {
14
+ parts.push(eventType);
15
+ }
16
+ if (summary.length > 0) {
17
+ parts.push(summary);
18
+ }
19
+ if (details.length > 0 && details !== summary) {
20
+ try {
21
+ const parsed = JSON.parse(details);
22
+ if (parsed && typeof parsed === 'object') {
23
+ parts.push(JSON.stringify(parsed));
24
+ }
25
+ else {
26
+ parts.push(details);
27
+ }
28
+ }
29
+ catch {
30
+ parts.push(details);
31
+ }
32
+ }
33
+ return parts.length > 0 ? parts.join('\n') : null;
34
+ }
35
+ function assertNoCircularMerge(node, options) {
36
+ if (!options?.nodeLookup) {
37
+ return;
38
+ }
39
+ const seen = new Set();
40
+ let current = options.nodeLookup[node.id] ?? node;
41
+ while (current?.merged_into) {
42
+ if (seen.has(current.id)) {
43
+ throw new errors_js_1.EntityLabelMissingError({
44
+ entity_id: node.id,
45
+ reason: 'circular_merged_into_chain',
46
+ });
47
+ }
48
+ seen.add(current.id);
49
+ current = options.nodeLookup[current.merged_into];
50
+ }
51
+ }
52
+ function projectEntityToRecallSummary(node, aliases, latestEvent, options) {
53
+ if (!node.preferred_label.trim()) {
54
+ throw new errors_js_1.EntityLabelMissingError({
55
+ entity_id: node.id,
56
+ reason: 'missing_preferred_label',
57
+ });
58
+ }
59
+ assertNoCircularMerge(node, options);
60
+ const aliasText = aliases.map((alias) => alias.label).join(', ');
61
+ const detailParts = [formatTimelineEventForRecall(latestEvent), aliasText].filter((value) => Boolean(value));
62
+ const scopeKind = node.scope_kind ?? 'global';
63
+ const scopeId = node.scope_kind === null ? 'global' : (node.scope_id ?? 'global');
64
+ return {
65
+ id: node.id,
66
+ topic: `entity/${node.id}`,
67
+ kind: 'fact',
68
+ summary: node.preferred_label,
69
+ details: detailParts.join('\n'),
70
+ confidence: 0.9,
71
+ status: 'active',
72
+ scopes: [{ kind: scopeKind, id: scopeId }],
73
+ source: {
74
+ package: 'mama-core',
75
+ source_type: 'entity_canonical',
76
+ project_id: node.scope_id ?? undefined,
77
+ },
78
+ created_at: node.created_at,
79
+ updated_at: node.updated_at,
80
+ };
81
+ }
82
+ //# sourceMappingURL=projection.js.map
@@ -0,0 +1,33 @@
1
+ import { type SourceLocatorKind } from './source-locator.js';
2
+ import type { MemoryRecord } from '../memory/types.js';
3
+ export interface ProvenanceObservationSummary {
4
+ id: string;
5
+ observation_type: string;
6
+ entity_kind_hint: string | null;
7
+ surface_form: string;
8
+ normalized_form: string;
9
+ lang: string | null;
10
+ script: string | null;
11
+ context_summary: string | null;
12
+ scope_kind: string;
13
+ scope_id: string | null;
14
+ extractor_version: string;
15
+ embedding_model_version: string | null;
16
+ source_connector: string;
17
+ source_locator: string | null;
18
+ source_locator_kind: SourceLocatorKind;
19
+ source_raw_record_id: string;
20
+ created_at: number;
21
+ }
22
+ export interface MemoryProvenanceResult {
23
+ status: 'legacy' | 'manual' | 'dropped' | 'resolved';
24
+ memory: MemoryRecord;
25
+ observations: ProvenanceObservationSummary[];
26
+ audit: {
27
+ event_type: 'provenance.empty_batch' | 'provenance.link_write_failed';
28
+ reason: string | null;
29
+ created_at: number;
30
+ } | null;
31
+ }
32
+ export declare function queryProvenanceForMemory(memoryId: string): Promise<MemoryProvenanceResult>;
33
+ //# sourceMappingURL=provenance-query.d.ts.map