@redaksjon/protokoll-engine 0.1.18 → 0.1.19
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/dist/index33.js +1 -1
- package/dist/index34.js +2 -2
- package/dist/index35.js +4 -4
- package/dist/index36.js +1 -1
- package/dist/index44.js +1 -0
- package/dist/index44.js.map +1 -1
- package/dist/index46.js +2 -0
- package/dist/index46.js.map +1 -1
- package/dist/index53.js +48 -5
- package/dist/index53.js.map +1 -1
- package/dist/index54.js +34 -46
- package/dist/index54.js.map +1 -1
- package/dist/index55.js +280 -35
- package/dist/index55.js.map +1 -1
- package/dist/index56.js +137 -258
- package/dist/index56.js.map +1 -1
- package/dist/index57.js +60 -142
- package/dist/index57.js.map +1 -1
- package/dist/index58.js +70 -73
- package/dist/index58.js.map +1 -1
- package/dist/index59.js +3 -73
- package/dist/index59.js.map +1 -1
- package/dist/index60.js +5 -148
- package/dist/index60.js.map +1 -1
- package/dist/index61.js +4 -4
- package/dist/index62.js +148 -5
- package/dist/index62.js.map +1 -1
- package/package.json +3 -3
package/dist/index55.js
CHANGED
|
@@ -1,39 +1,284 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
1
|
+
import { getLogger } from './index47.js';
|
|
2
|
+
import * as yaml from 'js-yaml';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as os from 'node:os';
|
|
6
|
+
|
|
7
|
+
const create = (config) => {
|
|
8
|
+
const logger = getLogger();
|
|
9
|
+
const database = {
|
|
10
|
+
mappings: [],
|
|
11
|
+
tier1: [],
|
|
12
|
+
tier2: /* @__PURE__ */ new Map(),
|
|
13
|
+
tier3: [],
|
|
14
|
+
collisions: /* @__PURE__ */ new Map(),
|
|
15
|
+
commonTerms: new Set(config?.commonTerms ?? DEFAULT_COMMON_TERMS),
|
|
16
|
+
genericTerms: new Set(config?.genericTerms ?? DEFAULT_GENERIC_TERMS)
|
|
17
|
+
};
|
|
18
|
+
const findProtokolDirectories = async () => {
|
|
19
|
+
if (config?.protokollContextPaths) {
|
|
20
|
+
return config.protokollContextPaths;
|
|
21
|
+
}
|
|
22
|
+
const homeDir = os.homedir();
|
|
23
|
+
const primaryPath = path.join(homeDir, ".protokoll", "context");
|
|
24
|
+
const dirs = [];
|
|
25
|
+
try {
|
|
26
|
+
await fs.access(primaryPath);
|
|
27
|
+
dirs.push(primaryPath);
|
|
28
|
+
logger.debug(`Found protokoll context at: ${primaryPath}`);
|
|
29
|
+
} catch {
|
|
30
|
+
logger.debug(`No protokoll context found at: ${primaryPath}`);
|
|
31
|
+
}
|
|
32
|
+
return dirs;
|
|
33
|
+
};
|
|
34
|
+
const loadProjectsFromProtokoll = async () => {
|
|
35
|
+
logger.debug("Loading projects from protokoll context");
|
|
36
|
+
const contextDirs = await findProtokolDirectories();
|
|
37
|
+
if (contextDirs.length === 0) {
|
|
38
|
+
logger.warn("No protokoll context directories found");
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
const mappings = [];
|
|
42
|
+
for (const contextDir of contextDirs) {
|
|
43
|
+
const projectsDir = path.join(contextDir, "projects");
|
|
44
|
+
try {
|
|
45
|
+
const files = await fs.readdir(projectsDir);
|
|
46
|
+
for (const file of files) {
|
|
47
|
+
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) continue;
|
|
48
|
+
try {
|
|
49
|
+
const content = await fs.readFile(path.join(projectsDir, file), "utf-8");
|
|
50
|
+
const parsed = yaml.load(content);
|
|
51
|
+
if (!parsed || !parsed.id || !parsed.name) {
|
|
52
|
+
logger.debug(`Skipping invalid project file: ${file}`);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (parsed.active === false) {
|
|
56
|
+
logger.debug(`Skipping inactive project: ${parsed.id}`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (parsed.sounds_like && parsed.sounds_like.length > 0) {
|
|
60
|
+
for (const soundsLike of parsed.sounds_like) {
|
|
61
|
+
mappings.push({
|
|
62
|
+
soundsLike: soundsLike.toLowerCase(),
|
|
63
|
+
correctText: parsed.name,
|
|
64
|
+
entityType: "project",
|
|
65
|
+
entityId: parsed.id,
|
|
66
|
+
scopedToProjects: null,
|
|
67
|
+
// Will be determined by collision detection
|
|
68
|
+
collisionRisk: "none",
|
|
69
|
+
// Will be determined by collision detection
|
|
70
|
+
tier: 1
|
|
71
|
+
// Will be determined by collision detection
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
logger.debug(`Loaded ${parsed.sounds_like.length} sounds_like entries for project: ${parsed.id}`);
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
logger.warn(`Failed to parse project file ${file}: ${error.message}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
logger.debug(`Could not read projects directory ${projectsDir}: ${error.message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
logger.info(`Loaded ${mappings.length} sounds_like mappings from protokoll projects`);
|
|
85
|
+
return mappings;
|
|
86
|
+
};
|
|
87
|
+
const detectCollisions = (mappings) => {
|
|
88
|
+
const collisionMap = /* @__PURE__ */ new Map();
|
|
89
|
+
for (const mapping of mappings) {
|
|
90
|
+
const key = mapping.soundsLike.toLowerCase();
|
|
91
|
+
if (!collisionMap.has(key)) {
|
|
92
|
+
collisionMap.set(key, []);
|
|
93
|
+
}
|
|
94
|
+
collisionMap.get(key).push(mapping);
|
|
95
|
+
}
|
|
96
|
+
const collisions = /* @__PURE__ */ new Map();
|
|
97
|
+
for (const [soundsLike, conflictMappings] of collisionMap) {
|
|
98
|
+
if (conflictMappings.length > 1) {
|
|
99
|
+
collisions.set(soundsLike, {
|
|
100
|
+
soundsLike,
|
|
101
|
+
mappings: conflictMappings,
|
|
102
|
+
count: conflictMappings.length
|
|
103
|
+
});
|
|
104
|
+
logger.debug(`Collision detected for "${soundsLike}": ${conflictMappings.length} mappings`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
logger.info(`Detected ${collisions.size} collisions in sounds_like mappings`);
|
|
108
|
+
return collisions;
|
|
109
|
+
};
|
|
110
|
+
const classifyTier = (mapping) => {
|
|
111
|
+
if (!mapping.soundsLike) {
|
|
112
|
+
return 3;
|
|
113
|
+
}
|
|
114
|
+
const soundsLikeLower = mapping.soundsLike.toLowerCase();
|
|
115
|
+
if (database.genericTerms.has(soundsLikeLower)) {
|
|
116
|
+
return 3;
|
|
117
|
+
}
|
|
118
|
+
if (database.commonTerms.has(soundsLikeLower)) {
|
|
119
|
+
return 2;
|
|
120
|
+
}
|
|
121
|
+
if (database.collisions.has(soundsLikeLower)) {
|
|
122
|
+
return 2;
|
|
123
|
+
}
|
|
124
|
+
return 1;
|
|
125
|
+
};
|
|
126
|
+
const assignTiersAndCollisions = (mappings) => {
|
|
127
|
+
for (const mapping of mappings) {
|
|
128
|
+
mapping.tier = classifyTier(mapping);
|
|
129
|
+
if (database.collisions.has(mapping.soundsLike.toLowerCase())) {
|
|
130
|
+
mapping.collisionRisk = "high";
|
|
131
|
+
} else if (database.commonTerms.has(mapping.soundsLike.toLowerCase())) {
|
|
132
|
+
mapping.collisionRisk = "medium";
|
|
133
|
+
} else if (mapping.tier === 2) {
|
|
134
|
+
mapping.collisionRisk = "low";
|
|
135
|
+
} else {
|
|
136
|
+
mapping.collisionRisk = "none";
|
|
137
|
+
}
|
|
138
|
+
if (mapping.tier === 2 && mapping.entityType === "project") {
|
|
139
|
+
mapping.scopedToProjects = [mapping.entityId];
|
|
140
|
+
mapping.minConfidence = config?.tier2Confidence ?? 0.6;
|
|
141
|
+
}
|
|
142
|
+
logger.debug(
|
|
143
|
+
`Classified "${mapping.soundsLike}" → "${mapping.correctText}" (${mapping.entityType}:${mapping.entityId}) as Tier ${mapping.tier} (risk: ${mapping.collisionRisk})`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const organizeMappingsByTier = (mappings) => {
|
|
148
|
+
database.tier1 = [];
|
|
149
|
+
database.tier2 = /* @__PURE__ */ new Map();
|
|
150
|
+
database.tier3 = [];
|
|
151
|
+
for (const mapping of mappings) {
|
|
152
|
+
if (mapping.tier === 1) {
|
|
153
|
+
database.tier1.push(mapping);
|
|
154
|
+
} else if (mapping.tier === 2) {
|
|
155
|
+
if (mapping.entityType === "project") {
|
|
156
|
+
if (!database.tier2.has(mapping.entityId)) {
|
|
157
|
+
database.tier2.set(mapping.entityId, []);
|
|
158
|
+
}
|
|
159
|
+
database.tier2.get(mapping.entityId).push(mapping);
|
|
160
|
+
} else {
|
|
161
|
+
if (!database.tier2.has("_generic")) {
|
|
162
|
+
database.tier2.set("_generic", []);
|
|
163
|
+
}
|
|
164
|
+
database.tier2.get("_generic").push(mapping);
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
database.tier3.push(mapping);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
logger.info(
|
|
171
|
+
`Organized mappings: Tier 1=${database.tier1.length}, Tier 2=${Array.from(database.tier2.values()).reduce((sum, arr) => sum + arr.length, 0)}, Tier 3=${database.tier3.length}`
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
const loadEntitiesFromContext = (ctx) => {
|
|
175
|
+
const mappings = [];
|
|
176
|
+
const addMappings = (name, entityId, entityType, soundsLike, active) => {
|
|
177
|
+
if (active === false) return;
|
|
178
|
+
const seen = /* @__PURE__ */ new Set();
|
|
179
|
+
const push = (raw) => {
|
|
180
|
+
const key = raw.toLowerCase().trim();
|
|
181
|
+
if (!key || seen.has(key)) return;
|
|
182
|
+
seen.add(key);
|
|
183
|
+
mappings.push({
|
|
184
|
+
soundsLike: key,
|
|
185
|
+
correctText: name,
|
|
186
|
+
entityType,
|
|
187
|
+
entityId,
|
|
188
|
+
scopedToProjects: null,
|
|
189
|
+
collisionRisk: "none",
|
|
190
|
+
tier: 1
|
|
191
|
+
});
|
|
192
|
+
};
|
|
193
|
+
push(name);
|
|
194
|
+
for (const sl of soundsLike) {
|
|
195
|
+
push(sl);
|
|
31
196
|
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
197
|
+
};
|
|
198
|
+
for (const project of ctx.getAllProjects()) {
|
|
199
|
+
addMappings(project.name, project.id, "project", project.sounds_like ?? [], project.active);
|
|
200
|
+
}
|
|
201
|
+
for (const person of ctx.getAllPeople()) {
|
|
202
|
+
addMappings(person.name, person.id, "person", person.sounds_like ?? []);
|
|
203
|
+
}
|
|
204
|
+
for (const term of ctx.getAllTerms()) {
|
|
205
|
+
addMappings(term.name, term.id, "term", term.sounds_like ?? []);
|
|
206
|
+
}
|
|
207
|
+
const projectCount = ctx.getAllProjects().filter((p) => p.active !== false).length;
|
|
208
|
+
const peopleCount = ctx.getAllPeople().length;
|
|
209
|
+
const termCount = ctx.getAllTerms().length;
|
|
210
|
+
logger.info(
|
|
211
|
+
`Loaded ${mappings.length} sounds_like mappings from context instance (${projectCount} projects, ${peopleCount} people, ${termCount} terms)`
|
|
212
|
+
);
|
|
213
|
+
return mappings;
|
|
214
|
+
};
|
|
215
|
+
const load = async () => {
|
|
216
|
+
logger.info("Loading sounds_like database");
|
|
217
|
+
let allMappings;
|
|
218
|
+
if (config?.contextInstance) {
|
|
219
|
+
allMappings = loadEntitiesFromContext(config.contextInstance);
|
|
220
|
+
} else {
|
|
221
|
+
const projectMappings = await loadProjectsFromProtokoll();
|
|
222
|
+
allMappings = projectMappings;
|
|
223
|
+
}
|
|
224
|
+
database.mappings = allMappings;
|
|
225
|
+
if (config?.detectCollisions !== false) {
|
|
226
|
+
database.collisions = detectCollisions(allMappings);
|
|
227
|
+
}
|
|
228
|
+
assignTiersAndCollisions(allMappings);
|
|
229
|
+
organizeMappingsByTier(allMappings);
|
|
230
|
+
logger.info(`Sounds_like database loaded: ${allMappings.length} total mappings`);
|
|
231
|
+
return database;
|
|
232
|
+
};
|
|
233
|
+
const getTier1Mappings = () => {
|
|
234
|
+
return database.tier1;
|
|
235
|
+
};
|
|
236
|
+
const getTier2MappingsForProject = (projectId) => {
|
|
237
|
+
const projectMappings = database.tier2.get(projectId) ?? [];
|
|
238
|
+
const genericMappings = database.tier2.get("_generic") ?? [];
|
|
239
|
+
return [...projectMappings, ...genericMappings];
|
|
240
|
+
};
|
|
241
|
+
const hasCollision = (soundsLike) => {
|
|
242
|
+
return database.collisions.has(soundsLike.toLowerCase());
|
|
243
|
+
};
|
|
244
|
+
const getCollision = (soundsLike) => {
|
|
245
|
+
return database.collisions.get(soundsLike.toLowerCase());
|
|
246
|
+
};
|
|
247
|
+
const getAllCollisions = () => {
|
|
248
|
+
return Array.from(database.collisions.values());
|
|
249
|
+
};
|
|
250
|
+
return {
|
|
251
|
+
load,
|
|
252
|
+
getTier1Mappings,
|
|
253
|
+
getTier2MappingsForProject,
|
|
254
|
+
hasCollision,
|
|
255
|
+
getCollision,
|
|
256
|
+
getAllCollisions,
|
|
257
|
+
classifyTier
|
|
258
|
+
};
|
|
36
259
|
};
|
|
260
|
+
const DEFAULT_COMMON_TERMS = [
|
|
261
|
+
"protocol",
|
|
262
|
+
"observation",
|
|
263
|
+
"composition",
|
|
264
|
+
"gateway",
|
|
265
|
+
"service",
|
|
266
|
+
"system",
|
|
267
|
+
"platform"
|
|
268
|
+
];
|
|
269
|
+
const DEFAULT_GENERIC_TERMS = [
|
|
270
|
+
"meeting",
|
|
271
|
+
"update",
|
|
272
|
+
"work",
|
|
273
|
+
"project",
|
|
274
|
+
"task",
|
|
275
|
+
"issue",
|
|
276
|
+
"discussion",
|
|
277
|
+
"review",
|
|
278
|
+
"the",
|
|
279
|
+
"a",
|
|
280
|
+
"an"
|
|
281
|
+
];
|
|
37
282
|
|
|
38
|
-
export {
|
|
283
|
+
export { create };
|
|
39
284
|
//# sourceMappingURL=index55.js.map
|
package/dist/index55.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index55.js","sources":["../src/util/general.ts"],"sourcesContent":["// Utility function for deep merging two objects.\nexport function deepMerge(target: any, source: any): any {\n for (const key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n // Block prototype-polluting keys\n if (key === '__proto__' || key === 'constructor') {\n continue;\n }\n if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {\n if (!target[key]) {\n target[key] = {};\n }\n deepMerge(target[key], source[key]);\n } else {\n target[key] = source[key];\n }\n }\n }\n return target;\n}\n\n//Recursive implementation of jSON.stringify;\nexport const stringifyJSON = function (obj: any): string {\n\n const arrOfKeyVals: string[] = [];\n const arrVals: string[] = [];\n let objKeys: string[] = [];\n\n /*********CHECK FOR PRIMITIVE TYPES**********/\n if (typeof obj === 'number' || typeof obj === 'boolean' || obj === null)\n return '' + obj;\n else if (typeof obj === 'string')\n return '\"' + obj + '\"';\n\n /*********CHECK FOR ARRAY**********/\n else if (Array.isArray(obj)) {\n //check for empty array\n if (obj[0] === undefined)\n return '[]';\n else {\n obj.forEach(function (el) {\n arrVals.push(stringifyJSON(el));\n });\n return '[' + arrVals + ']';\n }\n }\n /*********CHECK FOR OBJECT**********/\n else if (obj instanceof Object) {\n //get object keys\n objKeys = Object.keys(obj);\n //set key output;\n objKeys.forEach(function (key) {\n const keyOut = '\"' + key + '\":';\n const keyValOut = obj[key];\n //skip functions and undefined properties\n if (keyValOut instanceof Function || keyValOut === undefined)\n arrOfKeyVals.push('');\n else if (typeof keyValOut === 'string')\n arrOfKeyVals.push(keyOut + '\"' + keyValOut + '\"');\n else if (typeof keyValOut === 'boolean' || typeof keyValOut === 'number' || keyValOut === null)\n arrOfKeyVals.push(keyOut + keyValOut);\n //check for nested objects, call recursively until no more objects\n else if (keyValOut instanceof Object) {\n arrOfKeyVals.push(keyOut + stringifyJSON(keyValOut));\n }\n });\n return '{' + arrOfKeyVals + '}';\n }\n return '';\n};"],"names":[],"mappings":"AAsBO,MAAM,aAAA,GAAgB,SAAU,GAAA,EAAkB;AAErD,EAAA,MAAM,eAAyB,EAAC;AAChC,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,IAAI,UAAoB,EAAC;AAGzB,EAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,OAAO,GAAA,KAAQ,aAAa,GAAA,KAAQ,IAAA;AAC/D,IAAA,OAAO,EAAA,GAAK,GAAA;AAAA,OAAA,IACP,OAAO,GAAA,KAAQ,QAAA;AACpB,IAAA,OAAO,MAAM,GAAA,GAAM,GAAA;AAAA,OAAA,IAGd,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AAEzB,IAAA,IAAI,GAAA,CAAI,CAAC,CAAA,KAAM,MAAA;AACX,MAAA,OAAO,IAAA;AAAA,SACN;AACD,MAAA,GAAA,CAAI,OAAA,CAAQ,SAAU,EAAA,EAAI;AACtB,QAAA,OAAA,CAAQ,IAAA,CAAK,aAAA,CAAc,EAAE,CAAC,CAAA;AAAA,MAClC,CAAC,CAAA;AACD,MAAA,OAAO,MAAM,OAAA,GAAU,GAAA;AAAA,IAC3B;AAAA,EACJ,CAAA,MAAA,IAES,eAAe,MAAA,EAAQ;AAE5B,IAAA,OAAA,GAAU,MAAA,CAAO,KAAK,GAAG,CAAA;AAEzB,IAAA,OAAA,CAAQ,OAAA,CAAQ,SAAU,GAAA,EAAK;AAC3B,MAAA,MAAM,MAAA,GAAS,MAAM,GAAA,GAAM,IAAA;AAC3B,MAAA,MAAM,SAAA,GAAY,IAAI,GAAG,CAAA;AAEzB,MAAA,IAAI,SAAA,YAAqB,YAAY,SAAA,KAAc,MAAA;AAC/C,QAAA,YAAA,CAAa,KAAK,EAAE,CAAA;AAAA,WAAA,IACf,OAAO,SAAA,KAAc,QAAA;AAC1B,QAAA,YAAA,CAAa,IAAA,CAAK,MAAA,GAAS,GAAA,GAAM,SAAA,GAAY,GAAG,CAAA;AAAA,WAAA,IAC3C,OAAO,SAAA,KAAc,SAAA,IAAa,OAAO,SAAA,KAAc,YAAY,SAAA,KAAc,IAAA;AACtF,QAAA,YAAA,CAAa,IAAA,CAAK,SAAS,SAAS,CAAA;AAAA,WAAA,IAE/B,qBAAqB,MAAA,EAAQ;AAClC,QAAA,YAAA,CAAa,IAAA,CAAK,MAAA,GAAS,aAAA,CAAc,SAAS,CAAC,CAAA;AAAA,MACvD;AAAA,IACJ,CAAC,CAAA;AACD,IAAA,OAAO,MAAM,YAAA,GAAe,GAAA;AAAA,EAChC;AACA,EAAA,OAAO,EAAA;AACX;;;;"}
|
|
1
|
+
{"version":3,"file":"index55.js","sources":["../src/util/sounds-like-database.ts"],"sourcesContent":["/**\n * Sounds-Like Database\n *\n * Aggregates sounds_like mappings from multiple sources (projects, people, terms)\n * and provides efficient lookup and collision detection for entity correction.\n *\n * Part of the simple-replace optimization (Phase 1).\n */\n\nimport * as Logging from '@/logging';\nimport * as yaml from 'js-yaml';\nimport * as fs from 'fs/promises';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport type { ContextInstance } from '@redaksjon/context';\n\n/**\n * Represents a single sounds_like mapping entry\n */\nexport interface SoundsLikeMapping {\n /** What Whisper typically hears (e.g., \"protocol\", \"observation\") */\n soundsLike: string;\n\n /** Correct text to replace with (e.g., \"Protokoll\", \"Observasjon\") */\n correctText: string;\n\n /** Type of entity (project, person, or term) */\n entityType: 'project' | 'person' | 'term';\n\n /** Unique identifier for the entity */\n entityId: string;\n\n /** Only apply replacement in these project contexts (null = apply everywhere) */\n scopedToProjects?: string[] | null;\n\n /** Collision risk level: none (Tier 1), low/medium (Tier 2), high (Tier 3) */\n collisionRisk: 'none' | 'low' | 'medium' | 'high';\n\n /** Tier classification (1 = always safe, 2 = project-scoped, 3 = ambiguous) */\n tier: 1 | 2 | 3;\n\n /** Minimum confidence required for Tier 2 replacements */\n minConfidence?: number;\n}\n\n/**\n * Collision information for a sounds_like value\n */\nexport interface Collision {\n /** The sounds_like value that has collisions */\n soundsLike: string;\n\n /** All mappings that share this sounds_like value */\n mappings: SoundsLikeMapping[];\n\n /** Number of conflicting mappings */\n count: number;\n}\n\n/**\n * Database of sounds_like mappings with collision detection\n */\nexport interface SoundsLikeDatabase {\n /** All loaded mappings */\n mappings: SoundsLikeMapping[];\n\n /** Tier 1 mappings (always safe to apply) */\n tier1: SoundsLikeMapping[];\n\n /** Tier 2 mappings (require project-scoping) */\n tier2: Map<string, SoundsLikeMapping[]>; // Keyed by project ID\n\n /** Tier 3 mappings (too ambiguous, skip) */\n tier3: SoundsLikeMapping[];\n\n /** Detected collisions */\n collisions: Map<string, Collision>;\n\n /** Common terms that should not be replaced */\n commonTerms: Set<string>;\n\n /** Generic terms to always skip (Tier 3) */\n genericTerms: Set<string>;\n}\n\n/**\n * Configuration for the sounds-like database\n */\nexport interface DatabaseConfig {\n /** Protokoll context directories to load from */\n protokollContextPaths?: string[];\n\n /** Confidence threshold for Tier 2 replacements */\n tier2Confidence?: number;\n\n /** Enable collision detection */\n detectCollisions?: boolean;\n\n /** Custom common terms list */\n commonTerms?: string[];\n\n /** Custom generic terms list */\n genericTerms?: string[];\n\n /**\n * An already-loaded context instance. When provided, entity data is read\n * directly from it (covering projects, people, and terms) instead of\n * rescanning the filesystem.\n */\n contextInstance?: ContextInstance;\n}\n\n/**\n * Instance interface for the sounds-like database\n */\nexport interface Instance {\n /** Load all sounds_like mappings from sources */\n load(): Promise<SoundsLikeDatabase>;\n\n /** Get all Tier 1 (always safe) mappings */\n getTier1Mappings(): SoundsLikeMapping[];\n\n /** Get Tier 2 (project-scoped) mappings for a specific project */\n getTier2MappingsForProject(projectId: string): SoundsLikeMapping[];\n\n /** Check if a sounds_like value has collisions */\n hasCollision(soundsLike: string): boolean;\n\n /** Get collision info for a sounds_like value */\n getCollision(soundsLike: string): Collision | undefined;\n\n /** Get all collisions */\n getAllCollisions(): Collision[];\n\n /** Classify a mapping into a tier based on collision risk */\n classifyTier(mapping: Partial<SoundsLikeMapping>): 1 | 2 | 3;\n}\n\ninterface ProtokolProject {\n id: string;\n name: string;\n type: 'project';\n sounds_like?: string[];\n classification?: {\n context_type?: 'work' | 'personal' | 'mixed';\n };\n active?: boolean;\n}\n\n/**\n * Create a sounds-like database instance\n */\nexport const create = (config?: DatabaseConfig): Instance => {\n const logger = Logging.getLogger();\n\n const database: SoundsLikeDatabase = {\n mappings: [],\n tier1: [],\n tier2: new Map(),\n tier3: [],\n collisions: new Map(),\n commonTerms: new Set(config?.commonTerms ?? DEFAULT_COMMON_TERMS),\n genericTerms: new Set(config?.genericTerms ?? DEFAULT_GENERIC_TERMS),\n };\n\n /**\n * Find protokoll context directories\n */\n const findProtokolDirectories = async (): Promise<string[]> => {\n if (config?.protokollContextPaths) {\n return config.protokollContextPaths;\n }\n\n const homeDir = os.homedir();\n const primaryPath = path.join(homeDir, '.protokoll', 'context');\n\n const dirs: string[] = [];\n\n // Check primary protokoll directory\n try {\n await fs.access(primaryPath);\n dirs.push(primaryPath);\n logger.debug(`Found protokoll context at: ${primaryPath}`);\n } catch {\n logger.debug(`No protokoll context found at: ${primaryPath}`);\n }\n\n return dirs;\n };\n\n /**\n * Load projects from protokoll context\n */\n const loadProjectsFromProtokoll = async (): Promise<SoundsLikeMapping[]> => {\n logger.debug('Loading projects from protokoll context');\n\n const contextDirs = await findProtokolDirectories();\n\n if (contextDirs.length === 0) {\n logger.warn('No protokoll context directories found');\n return [];\n }\n\n const mappings: SoundsLikeMapping[] = [];\n\n for (const contextDir of contextDirs) {\n const projectsDir = path.join(contextDir, 'projects');\n\n try {\n const files = await fs.readdir(projectsDir);\n\n for (const file of files) {\n if (!file.endsWith('.yaml') && !file.endsWith('.yml')) continue;\n\n try {\n const content = await fs.readFile(path.join(projectsDir, file), 'utf-8');\n const parsed = yaml.load(content) as Partial<ProtokolProject>;\n\n if (!parsed || !parsed.id || !parsed.name) {\n logger.debug(`Skipping invalid project file: ${file}`);\n continue;\n }\n\n // Skip inactive projects\n if (parsed.active === false) {\n logger.debug(`Skipping inactive project: ${parsed.id}`);\n continue;\n }\n\n // Process sounds_like entries\n if (parsed.sounds_like && parsed.sounds_like.length > 0) {\n for (const soundsLike of parsed.sounds_like) {\n mappings.push({\n soundsLike: soundsLike.toLowerCase(),\n correctText: parsed.name,\n entityType: 'project',\n entityId: parsed.id,\n scopedToProjects: null, // Will be determined by collision detection\n collisionRisk: 'none', // Will be determined by collision detection\n tier: 1, // Will be determined by collision detection\n });\n }\n logger.debug(`Loaded ${parsed.sounds_like.length} sounds_like entries for project: ${parsed.id}`);\n }\n } catch (error: any) {\n logger.warn(`Failed to parse project file ${file}: ${error.message}`);\n }\n }\n } catch (error: any) {\n logger.debug(`Could not read projects directory ${projectsDir}: ${error.message}`);\n }\n }\n\n logger.info(`Loaded ${mappings.length} sounds_like mappings from protokoll projects`);\n return mappings;\n };\n\n /**\n * Detect collisions in mappings\n */\n const detectCollisions = (mappings: SoundsLikeMapping[]): Map<string, Collision> => {\n const collisionMap = new Map<string, SoundsLikeMapping[]>();\n\n // Group by sounds_like value (case-insensitive)\n for (const mapping of mappings) {\n const key = mapping.soundsLike.toLowerCase();\n if (!collisionMap.has(key)) {\n collisionMap.set(key, []);\n }\n collisionMap.get(key)!.push(mapping);\n }\n\n // Identify actual collisions (multiple mappings for same sounds_like)\n const collisions = new Map<string, Collision>();\n for (const [soundsLike, conflictMappings] of collisionMap) {\n if (conflictMappings.length > 1) {\n collisions.set(soundsLike, {\n soundsLike,\n mappings: conflictMappings,\n count: conflictMappings.length,\n });\n logger.debug(`Collision detected for \"${soundsLike}\": ${conflictMappings.length} mappings`);\n }\n }\n\n logger.info(`Detected ${collisions.size} collisions in sounds_like mappings`);\n return collisions;\n };\n\n /**\n * Classify a mapping into a tier based on collision risk\n */\n const classifyTier = (mapping: Partial<SoundsLikeMapping>): 1 | 2 | 3 => {\n if (!mapping.soundsLike) {\n return 3; // Invalid, treat as ambiguous\n }\n\n const soundsLikeLower = mapping.soundsLike.toLowerCase();\n\n // Tier 3: Generic terms (always skip)\n if (database.genericTerms.has(soundsLikeLower)) {\n return 3;\n }\n\n // Tier 2: Common terms (require project-scoping)\n if (database.commonTerms.has(soundsLikeLower)) {\n return 2;\n }\n\n // Tier 2: Has collision with other mappings\n if (database.collisions.has(soundsLikeLower)) {\n return 2;\n }\n\n // Tier 1: Unique, no collisions, not a common term\n return 1;\n };\n\n /**\n * Assign tiers and collision info to all mappings\n */\n const assignTiersAndCollisions = (mappings: SoundsLikeMapping[]): void => {\n for (const mapping of mappings) {\n // Classify tier\n mapping.tier = classifyTier(mapping);\n\n // Determine collision risk\n if (database.collisions.has(mapping.soundsLike.toLowerCase())) {\n mapping.collisionRisk = 'high';\n } else if (database.commonTerms.has(mapping.soundsLike.toLowerCase())) {\n mapping.collisionRisk = 'medium';\n } else if (mapping.tier === 2) {\n mapping.collisionRisk = 'low';\n } else {\n mapping.collisionRisk = 'none';\n }\n\n // Scope to projects for Tier 2\n if (mapping.tier === 2 && mapping.entityType === 'project') {\n mapping.scopedToProjects = [mapping.entityId];\n mapping.minConfidence = config?.tier2Confidence ?? 0.6;\n }\n\n logger.debug(\n `Classified \"${mapping.soundsLike}\" → \"${mapping.correctText}\" ` +\n `(${mapping.entityType}:${mapping.entityId}) as Tier ${mapping.tier} ` +\n `(risk: ${mapping.collisionRisk})`\n );\n }\n };\n\n /**\n * Organize mappings by tier\n */\n const organizeMappingsByTier = (mappings: SoundsLikeMapping[]): void => {\n database.tier1 = [];\n database.tier2 = new Map();\n database.tier3 = [];\n\n for (const mapping of mappings) {\n if (mapping.tier === 1) {\n database.tier1.push(mapping);\n } else if (mapping.tier === 2) {\n // Organize Tier 2 by project ID for efficient lookup\n if (mapping.entityType === 'project') {\n if (!database.tier2.has(mapping.entityId)) {\n database.tier2.set(mapping.entityId, []);\n }\n database.tier2.get(mapping.entityId)!.push(mapping);\n } else {\n // For non-project entities in Tier 2, add to a generic bucket\n if (!database.tier2.has('_generic')) {\n database.tier2.set('_generic', []);\n }\n database.tier2.get('_generic')!.push(mapping);\n }\n } else {\n database.tier3.push(mapping);\n }\n }\n\n logger.info(\n `Organized mappings: Tier 1=${database.tier1.length}, ` +\n `Tier 2=${Array.from(database.tier2.values()).reduce((sum, arr) => sum + arr.length, 0)}, ` +\n `Tier 3=${database.tier3.length}`\n );\n };\n\n /**\n * Load all sounds_like mappings from a ContextInstance (projects, people, terms).\n * Preferred over filesystem scanning when the context is already loaded.\n */\n const loadEntitiesFromContext = (ctx: ContextInstance): SoundsLikeMapping[] => {\n const mappings: SoundsLikeMapping[] = [];\n\n // Helper: emit one mapping per sounds_like value plus the canonical name itself.\n // Including the name means an entity spelled correctly in the transcript is still\n // recognised and associated as an entity reference.\n const addMappings = (\n name: string,\n entityId: string,\n entityType: 'project' | 'person' | 'term',\n soundsLike: string[],\n active?: boolean\n ) => {\n if (active === false) return;\n\n const seen = new Set<string>();\n const push = (raw: string) => {\n const key = raw.toLowerCase().trim();\n if (!key || seen.has(key)) return;\n seen.add(key);\n mappings.push({\n soundsLike: key,\n correctText: name,\n entityType,\n entityId,\n scopedToProjects: null,\n collisionRisk: 'none',\n tier: 1,\n });\n };\n\n // Always include the canonical name so exact spellings are matched\n push(name);\n for (const sl of soundsLike) {\n push(sl);\n }\n };\n\n // Projects\n for (const project of ctx.getAllProjects()) {\n addMappings(project.name, project.id, 'project', project.sounds_like ?? [], project.active);\n }\n\n // People\n for (const person of ctx.getAllPeople()) {\n addMappings(person.name, person.id, 'person', person.sounds_like ?? []);\n }\n\n // Terms\n for (const term of ctx.getAllTerms()) {\n addMappings(term.name, term.id, 'term', term.sounds_like ?? []);\n }\n\n const projectCount = ctx.getAllProjects().filter(p => p.active !== false).length;\n const peopleCount = ctx.getAllPeople().length;\n const termCount = ctx.getAllTerms().length;\n logger.info(\n `Loaded ${mappings.length} sounds_like mappings from context instance ` +\n `(${projectCount} projects, ${peopleCount} people, ${termCount} terms)`\n );\n\n return mappings;\n };\n\n /**\n * Load all sounds_like mappings\n */\n const load = async (): Promise<SoundsLikeDatabase> => {\n logger.info('Loading sounds_like database');\n\n let allMappings: SoundsLikeMapping[];\n\n if (config?.contextInstance) {\n // Prefer context instance - covers projects, people, and terms in one pass\n allMappings = loadEntitiesFromContext(config.contextInstance);\n } else {\n // Fall back to filesystem scanning (projects only)\n const projectMappings = await loadProjectsFromProtokoll();\n allMappings = projectMappings;\n }\n\n database.mappings = allMappings;\n\n // Detect collisions\n if (config?.detectCollisions !== false) {\n database.collisions = detectCollisions(allMappings);\n }\n\n // Assign tiers and collision info\n assignTiersAndCollisions(allMappings);\n\n // Organize by tier for efficient lookup\n organizeMappingsByTier(allMappings);\n\n logger.info(`Sounds_like database loaded: ${allMappings.length} total mappings`);\n\n return database;\n };\n\n /**\n * Get Tier 1 mappings (always safe)\n */\n const getTier1Mappings = (): SoundsLikeMapping[] => {\n return database.tier1;\n };\n\n /**\n * Get Tier 2 mappings for a specific project\n */\n const getTier2MappingsForProject = (projectId: string): SoundsLikeMapping[] => {\n const projectMappings = database.tier2.get(projectId) ?? [];\n const genericMappings = database.tier2.get('_generic') ?? [];\n return [...projectMappings, ...genericMappings];\n };\n\n /**\n * Check if a sounds_like value has collisions\n */\n const hasCollision = (soundsLike: string): boolean => {\n return database.collisions.has(soundsLike.toLowerCase());\n };\n\n /**\n * Get collision info\n */\n const getCollision = (soundsLike: string): Collision | undefined => {\n return database.collisions.get(soundsLike.toLowerCase());\n };\n\n /**\n * Get all collisions\n */\n const getAllCollisions = (): Collision[] => {\n return Array.from(database.collisions.values());\n };\n\n return {\n load,\n getTier1Mappings,\n getTier2MappingsForProject,\n hasCollision,\n getCollision,\n getAllCollisions,\n classifyTier,\n };\n};\n\n/**\n * Default common terms that indicate Tier 2 (project-scoped) replacements\n */\nconst DEFAULT_COMMON_TERMS = [\n 'protocol',\n 'observation',\n 'composition',\n 'gateway',\n 'service',\n 'system',\n 'platform',\n];\n\n/**\n * Default generic terms that should never be replaced (Tier 3)\n */\nconst DEFAULT_GENERIC_TERMS = [\n 'meeting',\n 'update',\n 'work',\n 'project',\n 'task',\n 'issue',\n 'discussion',\n 'review',\n 'the',\n 'a',\n 'an',\n];\n"],"names":["Logging.getLogger"],"mappings":"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"}
|