@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.
- package/db/migrations/026-create-canonical-entity-tables.sql +99 -0
- package/db/migrations/027-create-entity-review-support.sql +44 -0
- package/db/migrations/028-create-entity-audit-tables.sql +45 -0
- package/db/migrations/029-extend-entity-observations-observation-type.sql +60 -0
- package/db/migrations/030-case-first-memory-substrate.sql +901 -0
- package/db/migrations/031-wiki-page-index-provenance-columns.sql +2 -0
- package/dist/canonicalize.d.ts +38 -0
- package/dist/canonicalize.js +106 -0
- package/dist/cases/case-links.d.ts +116 -0
- package/dist/cases/case-links.js +794 -0
- package/dist/cases/composition-overrides.d.ts +90 -0
- package/dist/cases/composition-overrides.js +724 -0
- package/dist/cases/corrections.d.ts +156 -0
- package/dist/cases/corrections.js +1045 -0
- package/dist/cases/freshness.d.ts +57 -0
- package/dist/cases/freshness.js +283 -0
- package/dist/cases/live-state.d.ts +48 -0
- package/dist/cases/live-state.js +256 -0
- package/dist/cases/membership-explain.d.ts +56 -0
- package/dist/cases/membership-explain.js +140 -0
- package/dist/cases/membership-matcher.d.ts +23 -0
- package/dist/cases/membership-matcher.js +193 -0
- package/dist/cases/merge-split.d.ts +61 -0
- package/dist/cases/merge-split.js +368 -0
- package/dist/cases/role-inference.d.ts +10 -0
- package/dist/cases/role-inference.js +85 -0
- package/dist/cases/search-rollup.d.ts +23 -0
- package/dist/cases/search-rollup.js +225 -0
- package/dist/cases/sqlite-transaction.d.ts +10 -0
- package/dist/cases/sqlite-transaction.js +62 -0
- package/dist/cases/store.d.ts +74 -0
- package/dist/cases/store.js +816 -0
- package/dist/cases/target-ref.d.ts +25 -0
- package/dist/cases/target-ref.js +22 -0
- package/dist/cases/timeline-range.d.ts +6 -0
- package/dist/cases/timeline-range.js +322 -0
- package/dist/cases/tombstone-sweeper.d.ts +13 -0
- package/dist/cases/tombstone-sweeper.js +128 -0
- package/dist/cases/types.d.ts +244 -0
- package/dist/cases/types.js +47 -0
- package/dist/cases/wiki-page-index.d.ts +50 -0
- package/dist/cases/wiki-page-index.js +350 -0
- package/dist/config-loader.d.ts +1 -0
- package/dist/config-loader.js +43 -0
- package/dist/connectors/event-index.d.ts +44 -0
- package/dist/connectors/event-index.js +366 -0
- package/dist/connectors/types.d.ts +69 -0
- package/dist/connectors/types.js +3 -0
- package/dist/db-manager.d.ts +4 -0
- package/dist/db-manager.js +10 -3
- package/dist/decision-formatter.d.ts +2 -0
- package/dist/decision-formatter.js +11 -8
- package/dist/embedding-server/mobile/daemon.d.ts +1 -0
- package/dist/embedding-server/mobile/daemon.js +11 -1
- package/dist/entities/audit-metrics.d.ts +73 -0
- package/dist/entities/audit-metrics.js +128 -0
- package/dist/entities/candidate-generator.d.ts +9 -0
- package/dist/entities/candidate-generator.js +436 -0
- package/dist/entities/entity-impact.d.ts +41 -0
- package/dist/entities/entity-impact.js +140 -0
- package/dist/entities/entity-linked-decision-counts.d.ts +2 -0
- package/dist/entities/entity-linked-decision-counts.js +29 -0
- package/dist/entities/entity-list.d.ts +23 -0
- package/dist/entities/entity-list.js +99 -0
- package/dist/entities/entity-orphan-list.d.ts +26 -0
- package/dist/entities/entity-orphan-list.js +91 -0
- package/dist/entities/entity-search.d.ts +21 -0
- package/dist/entities/entity-search.js +150 -0
- package/dist/entities/errors.d.ts +65 -0
- package/dist/entities/errors.js +124 -0
- package/dist/entities/exact-merge-backfill.d.ts +11 -0
- package/dist/entities/exact-merge-backfill.js +161 -0
- package/dist/entities/lineage-backfill.d.ts +12 -0
- package/dist/entities/lineage-backfill.js +101 -0
- package/dist/entities/lineage-store.d.ts +51 -0
- package/dist/entities/lineage-store.js +308 -0
- package/dist/entities/normalization.d.ts +14 -0
- package/dist/entities/normalization.js +63 -0
- package/dist/entities/policy-store.d.ts +17 -0
- package/dist/entities/policy-store.js +263 -0
- package/dist/entities/policy-types.d.ts +72 -0
- package/dist/entities/policy-types.js +59 -0
- package/dist/entities/projection.d.ts +8 -0
- package/dist/entities/projection.js +82 -0
- package/dist/entities/provenance-query.d.ts +33 -0
- package/dist/entities/provenance-query.js +167 -0
- package/dist/entities/read-identity.d.ts +31 -0
- package/dist/entities/read-identity.js +264 -0
- package/dist/entities/recall-bridge.d.ts +5 -0
- package/dist/entities/recall-bridge.js +171 -0
- package/dist/entities/resolution-engine.d.ts +15 -0
- package/dist/entities/resolution-engine.js +69 -0
- package/dist/entities/rollback-preview.d.ts +39 -0
- package/dist/entities/rollback-preview.js +235 -0
- package/dist/entities/source-locator.d.ts +3 -0
- package/dist/entities/source-locator.js +32 -0
- package/dist/entities/store.d.ts +75 -0
- package/dist/entities/store.js +465 -0
- package/dist/entities/types.d.ts +162 -0
- package/dist/entities/types.js +30 -0
- package/dist/index.d.ts +47 -2
- package/dist/index.js +66 -25
- package/dist/mama-api.d.ts +22 -1
- package/dist/mama-api.js +272 -64
- package/dist/memory/api.d.ts +28 -1
- package/dist/memory/api.js +293 -31
- package/dist/memory/types.d.ts +11 -2
- package/dist/search/feedback-store.d.ts +59 -0
- package/dist/search/feedback-store.js +211 -0
- package/dist/search/question-type.d.ts +5 -0
- package/dist/search/question-type.js +41 -0
- package/dist/search/ranker-features.d.ts +23 -0
- package/dist/search/ranker-features.js +104 -0
- package/dist/search/ranker-rescore.d.ts +31 -0
- package/dist/search/ranker-rescore.js +226 -0
- package/dist/search/ranker-trainer.d.ts +94 -0
- package/dist/search/ranker-trainer.js +860 -0
- package/dist/test-utils.d.ts +14 -0
- package/dist/test-utils.js +52 -0
- 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
|