@redaksjon/protokoll-engine 0.1.1-dev.0 → 0.1.3
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/index25.js +5 -5
- package/dist/index31.js +1 -1
- package/dist/index31.js.map +1 -1
- package/dist/index32.js +2 -2
- package/dist/index33.js +4 -4
- package/dist/index34.js +1 -1
- package/dist/index36.js +0 -2
- package/dist/index36.js.map +1 -1
- package/dist/index42.js +43 -143
- package/dist/index42.js.map +1 -1
- package/dist/index43.js +34 -221
- package/dist/index43.js.map +1 -1
- package/dist/index44.js +233 -43
- package/dist/index44.js.map +1 -1
- package/dist/index45.js +156 -38
- package/dist/index45.js.map +1 -1
- package/dist/index46.js +75 -31
- package/dist/index46.js.map +1 -1
- package/dist/index47.js +73 -46
- package/dist/index47.js.map +1 -1
- package/dist/index48.js +5 -36
- package/dist/index48.js.map +1 -1
- package/dist/index49.js +134 -222
- package/dist/index49.js.map +1 -1
- package/dist/index50.js +201 -138
- package/dist/index50.js.map +1 -1
- package/dist/index51.js +42 -74
- package/dist/index51.js.map +1 -1
- package/dist/index52.js +40 -73
- package/dist/index52.js.map +1 -1
- package/dist/index53.js +33 -18
- package/dist/index53.js.map +1 -1
- package/dist/index54.js +2 -2
- package/dist/index55.js +2 -2
- package/dist/index56.js +147 -14
- package/dist/index56.js.map +1 -1
- package/dist/index57.js +15 -2
- package/dist/index57.js.map +1 -1
- package/dist/index58.js +2 -15
- package/dist/index58.js.map +1 -1
- package/dist/index59.js +15 -2
- package/dist/index59.js.map +1 -1
- package/dist/index60.js +2 -4
- package/dist/index60.js.map +1 -1
- package/dist/index61.js +6 -0
- package/dist/index61.js.map +1 -0
- package/dist/out/index.d.ts +23 -0
- package/dist/out/index.d.ts.map +1 -0
- package/dist/out/manager.d.ts +21 -0
- package/dist/out/manager.d.ts.map +1 -0
- package/dist/out/types.d.ts +35 -0
- package/dist/out/types.d.ts.map +1 -0
- package/dist/pipeline/types.d.ts +2 -10
- package/dist/pipeline/types.d.ts.map +1 -1
- package/dist/transcript/operations.d.ts.map +1 -1
- package/package.json +5 -6
- package/dist/output/index.d.ts +0 -15
- package/dist/output/index.d.ts.map +0 -1
package/dist/index49.js
CHANGED
|
@@ -1,239 +1,151 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const findProtokolDirectories = async () => {
|
|
19
|
-
const homeDir = os.homedir();
|
|
20
|
-
const primaryPath = path.join(homeDir, ".protokoll", "context");
|
|
21
|
-
const dirs = [];
|
|
22
|
-
try {
|
|
23
|
-
await fs.access(primaryPath);
|
|
24
|
-
dirs.push(primaryPath);
|
|
25
|
-
logger.debug(`Found protokoll context at: ${primaryPath}`);
|
|
26
|
-
} catch {
|
|
27
|
-
logger.debug(`No protokoll context found at: ${primaryPath}`);
|
|
1
|
+
function extractNameContext(transcript, name) {
|
|
2
|
+
const lowerTranscript = transcript.toLowerCase();
|
|
3
|
+
const lowerName = name.toLowerCase();
|
|
4
|
+
const index = lowerTranscript.indexOf(lowerName);
|
|
5
|
+
if (index === -1) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const sentenceBoundary = /[.!?]/;
|
|
9
|
+
let startIndex = 0;
|
|
10
|
+
let boundariesFound = 0;
|
|
11
|
+
for (let i = index - 1; i >= 0; i--) {
|
|
12
|
+
if (sentenceBoundary.test(transcript[i])) {
|
|
13
|
+
boundariesFound++;
|
|
14
|
+
if (boundariesFound === 2) {
|
|
15
|
+
startIndex = i + 1;
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
28
18
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
19
|
+
}
|
|
20
|
+
let endIndex = transcript.length;
|
|
21
|
+
boundariesFound = 0;
|
|
22
|
+
for (let i = index + name.length; i < transcript.length; i++) {
|
|
23
|
+
if (sentenceBoundary.test(transcript[i])) {
|
|
24
|
+
boundariesFound++;
|
|
25
|
+
if (boundariesFound === 2) {
|
|
26
|
+
endIndex = i + 1;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
37
29
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
logger.debug(`Skipping invalid project file: ${file}`);
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
if (parsed.active === false) {
|
|
53
|
-
logger.debug(`Skipping inactive project: ${parsed.id}`);
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (parsed.sounds_like && parsed.sounds_like.length > 0) {
|
|
57
|
-
for (const soundsLike of parsed.sounds_like) {
|
|
58
|
-
mappings.push({
|
|
59
|
-
soundsLike: soundsLike.toLowerCase(),
|
|
60
|
-
correctText: parsed.name,
|
|
61
|
-
entityType: "project",
|
|
62
|
-
entityId: parsed.id,
|
|
63
|
-
scopedToProjects: null,
|
|
64
|
-
// Will be determined by collision detection
|
|
65
|
-
collisionRisk: "none",
|
|
66
|
-
// Will be determined by collision detection
|
|
67
|
-
tier: 1
|
|
68
|
-
// Will be determined by collision detection
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
logger.debug(`Loaded ${parsed.sounds_like.length} sounds_like entries for project: ${parsed.id}`);
|
|
72
|
-
}
|
|
73
|
-
} catch (error) {
|
|
74
|
-
logger.warn(`Failed to parse project file ${file}: ${error.message}`);
|
|
75
|
-
}
|
|
30
|
+
}
|
|
31
|
+
let context = transcript.substring(startIndex, endIndex).trim();
|
|
32
|
+
if (context.length > 300) {
|
|
33
|
+
const midPoint = context.indexOf(name);
|
|
34
|
+
if (midPoint !== -1) {
|
|
35
|
+
let sentenceStart = midPoint;
|
|
36
|
+
let sentenceEnd = midPoint + name.length;
|
|
37
|
+
for (let i = midPoint - 1; i >= 0; i--) {
|
|
38
|
+
if (sentenceBoundary.test(context[i])) {
|
|
39
|
+
sentenceStart = i + 1;
|
|
40
|
+
break;
|
|
76
41
|
}
|
|
77
|
-
} catch (error) {
|
|
78
|
-
logger.debug(`Could not read projects directory ${projectsDir}: ${error.message}`);
|
|
79
42
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const collisionMap = /* @__PURE__ */ new Map();
|
|
86
|
-
for (const mapping of mappings) {
|
|
87
|
-
const key = mapping.soundsLike.toLowerCase();
|
|
88
|
-
if (!collisionMap.has(key)) {
|
|
89
|
-
collisionMap.set(key, []);
|
|
43
|
+
for (let i = midPoint + name.length; i < context.length; i++) {
|
|
44
|
+
if (sentenceBoundary.test(context[i])) {
|
|
45
|
+
sentenceEnd = i + 1;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
90
48
|
}
|
|
91
|
-
|
|
49
|
+
context = context.substring(sentenceStart, sentenceEnd).trim();
|
|
50
|
+
} else {
|
|
51
|
+
context = context.substring(0, 300) + "...";
|
|
92
52
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
53
|
+
}
|
|
54
|
+
return context;
|
|
55
|
+
}
|
|
56
|
+
const create = (ctx) => ({
|
|
57
|
+
name: "lookup_person",
|
|
58
|
+
description: "Look up information about a person mentioned in the transcript. Use when you encounter a name that might need spelling verification or additional context.",
|
|
59
|
+
parameters: {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
name: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description: "The name to look up (as heard in transcript)"
|
|
65
|
+
},
|
|
66
|
+
phonetic: {
|
|
67
|
+
type: "string",
|
|
68
|
+
description: "How the name sounds (for alias matching)"
|
|
102
69
|
}
|
|
70
|
+
},
|
|
71
|
+
required: ["name"]
|
|
72
|
+
},
|
|
73
|
+
execute: async (args) => {
|
|
74
|
+
const context = ctx.contextInstance;
|
|
75
|
+
if (ctx.resolvedEntities?.has(args.name)) {
|
|
76
|
+
const resolvedName = ctx.resolvedEntities.get(args.name);
|
|
77
|
+
return {
|
|
78
|
+
success: true,
|
|
79
|
+
data: {
|
|
80
|
+
found: true,
|
|
81
|
+
suggestion: `Already resolved: use "${resolvedName}"`,
|
|
82
|
+
cached: true
|
|
83
|
+
}
|
|
84
|
+
};
|
|
103
85
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (database.commonTerms.has(soundsLikeLower)) {
|
|
116
|
-
return 2;
|
|
117
|
-
}
|
|
118
|
-
if (database.collisions.has(soundsLikeLower)) {
|
|
119
|
-
return 2;
|
|
120
|
-
}
|
|
121
|
-
return 1;
|
|
122
|
-
};
|
|
123
|
-
const assignTiersAndCollisions = (mappings) => {
|
|
124
|
-
for (const mapping of mappings) {
|
|
125
|
-
mapping.tier = classifyTier(mapping);
|
|
126
|
-
if (database.collisions.has(mapping.soundsLike.toLowerCase())) {
|
|
127
|
-
mapping.collisionRisk = "high";
|
|
128
|
-
} else if (database.commonTerms.has(mapping.soundsLike.toLowerCase())) {
|
|
129
|
-
mapping.collisionRisk = "medium";
|
|
130
|
-
} else if (mapping.tier === 2) {
|
|
131
|
-
mapping.collisionRisk = "low";
|
|
132
|
-
} else {
|
|
133
|
-
mapping.collisionRisk = "none";
|
|
134
|
-
}
|
|
135
|
-
if (mapping.tier === 2 && mapping.entityType === "project") {
|
|
136
|
-
mapping.scopedToProjects = [mapping.entityId];
|
|
137
|
-
mapping.minConfidence = 0.6;
|
|
138
|
-
}
|
|
139
|
-
logger.debug(
|
|
140
|
-
`Classified "${mapping.soundsLike}" → "${mapping.correctText}" (${mapping.entityType}:${mapping.entityId}) as Tier ${mapping.tier} (risk: ${mapping.collisionRisk})`
|
|
141
|
-
);
|
|
86
|
+
const people = context.search(args.name);
|
|
87
|
+
const personMatches = people.filter((e) => e.type === "person");
|
|
88
|
+
if (personMatches.length > 0) {
|
|
89
|
+
return {
|
|
90
|
+
success: true,
|
|
91
|
+
data: {
|
|
92
|
+
found: true,
|
|
93
|
+
person: personMatches[0],
|
|
94
|
+
suggestion: `Use "${personMatches[0].name}" for correct spelling`
|
|
95
|
+
}
|
|
96
|
+
};
|
|
142
97
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (mapping.entityType === "project") {
|
|
153
|
-
if (!database.tier2.has(mapping.entityId)) {
|
|
154
|
-
database.tier2.set(mapping.entityId, []);
|
|
155
|
-
}
|
|
156
|
-
database.tier2.get(mapping.entityId).push(mapping);
|
|
157
|
-
} else {
|
|
158
|
-
if (!database.tier2.has("_generic")) {
|
|
159
|
-
database.tier2.set("_generic", []);
|
|
98
|
+
if (args.phonetic) {
|
|
99
|
+
const person = context.findBySoundsLike(args.phonetic);
|
|
100
|
+
if (person) {
|
|
101
|
+
return {
|
|
102
|
+
success: true,
|
|
103
|
+
data: {
|
|
104
|
+
found: true,
|
|
105
|
+
person,
|
|
106
|
+
suggestion: `"${args.phonetic}" likely refers to "${person.name}"`
|
|
160
107
|
}
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
} else {
|
|
164
|
-
database.tier3.push(mapping);
|
|
108
|
+
};
|
|
165
109
|
}
|
|
166
110
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
111
|
+
const allProjects = context.getAllProjects();
|
|
112
|
+
const projectOptions = allProjects.filter((p) => p.active !== false).map((p) => `${p.name}${p.description ? ` - ${p.description}` : ""}`);
|
|
113
|
+
const fileName = ctx.sourceFile.split("/").pop() || ctx.sourceFile;
|
|
114
|
+
const fileDate = ctx.audioDate.toLocaleString("en-US", {
|
|
115
|
+
weekday: "short",
|
|
116
|
+
year: "numeric",
|
|
117
|
+
month: "short",
|
|
118
|
+
day: "numeric",
|
|
119
|
+
hour: "2-digit",
|
|
120
|
+
minute: "2-digit"
|
|
121
|
+
});
|
|
122
|
+
const transcriptContext = extractNameContext(ctx.transcriptText, args.name);
|
|
123
|
+
const promptLines = [
|
|
124
|
+
`File: ${fileName}`,
|
|
125
|
+
`Date: ${fileDate}`,
|
|
126
|
+
"",
|
|
127
|
+
`Unknown person mentioned: "${args.name}"`
|
|
178
128
|
];
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
129
|
+
if (transcriptContext) {
|
|
130
|
+
promptLines.push("");
|
|
131
|
+
promptLines.push("Context from transcript:");
|
|
132
|
+
promptLines.push(`"${transcriptContext}"`);
|
|
182
133
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
};
|
|
199
|
-
const getCollision = (soundsLike) => {
|
|
200
|
-
return database.collisions.get(soundsLike.toLowerCase());
|
|
201
|
-
};
|
|
202
|
-
const getAllCollisions = () => {
|
|
203
|
-
return Array.from(database.collisions.values());
|
|
204
|
-
};
|
|
205
|
-
return {
|
|
206
|
-
load,
|
|
207
|
-
getTier1Mappings,
|
|
208
|
-
getTier2MappingsForProject,
|
|
209
|
-
hasCollision,
|
|
210
|
-
getCollision,
|
|
211
|
-
getAllCollisions,
|
|
212
|
-
classifyTier
|
|
213
|
-
};
|
|
214
|
-
};
|
|
215
|
-
const DEFAULT_COMMON_TERMS = [
|
|
216
|
-
"protocol",
|
|
217
|
-
"observation",
|
|
218
|
-
"composition",
|
|
219
|
-
"gateway",
|
|
220
|
-
"service",
|
|
221
|
-
"system",
|
|
222
|
-
"platform"
|
|
223
|
-
];
|
|
224
|
-
const DEFAULT_GENERIC_TERMS = [
|
|
225
|
-
"meeting",
|
|
226
|
-
"update",
|
|
227
|
-
"work",
|
|
228
|
-
"project",
|
|
229
|
-
"task",
|
|
230
|
-
"issue",
|
|
231
|
-
"discussion",
|
|
232
|
-
"review",
|
|
233
|
-
"the",
|
|
234
|
-
"a",
|
|
235
|
-
"an"
|
|
236
|
-
];
|
|
134
|
+
return {
|
|
135
|
+
success: true,
|
|
136
|
+
needsUserInput: true,
|
|
137
|
+
userPrompt: promptLines.join("\n"),
|
|
138
|
+
data: {
|
|
139
|
+
found: false,
|
|
140
|
+
clarificationType: "new_person",
|
|
141
|
+
term: args.name,
|
|
142
|
+
message: `Person "${args.name}" not found. Asking user for details.`,
|
|
143
|
+
knownProjects: allProjects.filter((p) => p.active !== false),
|
|
144
|
+
options: projectOptions
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
});
|
|
237
149
|
|
|
238
150
|
export { create };
|
|
239
151
|
//# sourceMappingURL=index49.js.map
|
package/dist/index49.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index49.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';\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/**\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\n */\n const load = async (): Promise<SoundsLikeDatabase> => {\n logger.info('Loading sounds_like database');\n\n // Load from all sources\n const projectMappings = await loadProjectsFromProtokoll();\n // TODO: Load from people source\n // TODO: Load from terms source\n\n const allMappings = [\n ...projectMappings,\n // ...peopleMappings,\n // ...termMappings,\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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"}
|
|
1
|
+
{"version":3,"file":"index49.js","sources":["../src/agentic/tools/lookup-person.ts"],"sourcesContent":["/**\n * Lookup Person Tool\n * \n * Looks up information about a person mentioned in the transcript.\n */\n\nimport { TranscriptionTool, ToolContext, ToolResult } from '../types';\n\n/**\n * Extract context from transcript around where a name is mentioned.\n * Returns approximately one sentence before and after the name mention.\n */\nfunction extractNameContext(transcript: string, name: string): string | null {\n // Case-insensitive search for the name\n const lowerTranscript = transcript.toLowerCase();\n const lowerName = name.toLowerCase();\n const index = lowerTranscript.indexOf(lowerName);\n \n if (index === -1) {\n return null;\n }\n \n // Define strong sentence boundaries (., !, ?)\n const sentenceBoundary = /[.!?]/;\n \n // Look backwards for the start (find the sentence boundary 1 sentence before)\n let startIndex = 0;\n let boundariesFound = 0;\n for (let i = index - 1; i >= 0; i--) {\n if (sentenceBoundary.test(transcript[i])) {\n boundariesFound++;\n // After finding first boundary (end of current sentence), \n // keep looking for the second (end of previous sentence)\n if (boundariesFound === 2) {\n // Start after this boundary\n startIndex = i + 1;\n break;\n }\n }\n }\n \n // Look forwards for the end (find sentence boundary 1 sentence after)\n let endIndex = transcript.length;\n boundariesFound = 0;\n for (let i = index + name.length; i < transcript.length; i++) {\n if (sentenceBoundary.test(transcript[i])) {\n boundariesFound++;\n // After finding first boundary (end of current sentence),\n // keep looking for the second (end of next sentence)\n if (boundariesFound === 2) {\n // Include this boundary\n endIndex = i + 1;\n break;\n }\n }\n }\n \n // Extract and clean up the context\n let context = transcript.substring(startIndex, endIndex).trim();\n \n // Limit length to avoid overwhelming the prompt (max ~300 chars)\n if (context.length > 300) {\n // Try to cut at a sentence boundary\n const midPoint = context.indexOf(name);\n if (midPoint !== -1) {\n // Keep the sentence with the name, trim around it\n let sentenceStart = midPoint;\n let sentenceEnd = midPoint + name.length;\n \n // Find sentence start\n for (let i = midPoint - 1; i >= 0; i--) {\n if (sentenceBoundary.test(context[i])) {\n sentenceStart = i + 1;\n break;\n }\n }\n \n // Find sentence end\n for (let i = midPoint + name.length; i < context.length; i++) {\n if (sentenceBoundary.test(context[i])) {\n sentenceEnd = i + 1;\n break;\n }\n }\n \n context = context.substring(sentenceStart, sentenceEnd).trim();\n } else {\n // Just truncate if name not found in extracted context\n context = context.substring(0, 300) + '...';\n }\n }\n \n return context;\n}\n\nexport const create = (ctx: ToolContext): TranscriptionTool => ({\n name: 'lookup_person',\n description: 'Look up information about a person mentioned in the transcript. Use when you encounter a name that might need spelling verification or additional context.',\n parameters: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n description: 'The name to look up (as heard in transcript)',\n },\n phonetic: {\n type: 'string',\n description: 'How the name sounds (for alias matching)',\n },\n },\n required: ['name'],\n },\n execute: async (args: { name: string; phonetic?: string }): Promise<ToolResult> => {\n const context = ctx.contextInstance;\n \n // First, check if this person was already resolved in this session\n if (ctx.resolvedEntities?.has(args.name)) {\n const resolvedName = ctx.resolvedEntities.get(args.name);\n return {\n success: true,\n data: {\n found: true,\n suggestion: `Already resolved: use \"${resolvedName}\"`,\n cached: true,\n },\n };\n }\n \n // Try direct name search\n const people = context.search(args.name);\n const personMatches = people.filter(e => e.type === 'person');\n \n if (personMatches.length > 0) {\n return {\n success: true,\n data: {\n found: true,\n person: personMatches[0],\n suggestion: `Use \"${personMatches[0].name}\" for correct spelling`,\n },\n };\n }\n \n // Try phonetic match (sounds_like)\n if (args.phonetic) {\n const person = context.findBySoundsLike(args.phonetic);\n if (person) {\n return {\n success: true,\n data: {\n found: true,\n person,\n suggestion: `\"${args.phonetic}\" likely refers to \"${person.name}\"`,\n },\n };\n }\n }\n \n // Not found - always signal that we need user input\n // The executor will decide whether to actually prompt based on handler availability\n const allProjects = context.getAllProjects();\n const projectOptions = allProjects\n .filter(p => p.active !== false)\n .map(p => `${p.name}${p.description ? ` - ${p.description}` : ''}`);\n \n // Extract filename from sourceFile path for cleaner display\n const fileName = ctx.sourceFile.split('/').pop() || ctx.sourceFile;\n const fileDate = ctx.audioDate.toLocaleString('en-US', {\n weekday: 'short',\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n \n // Find context from transcript where the name is mentioned\n const transcriptContext = extractNameContext(ctx.transcriptText, args.name);\n \n const promptLines = [\n `File: ${fileName}`,\n `Date: ${fileDate}`,\n '',\n `Unknown person mentioned: \"${args.name}\"`,\n ];\n \n if (transcriptContext) {\n promptLines.push('');\n promptLines.push('Context from transcript:');\n promptLines.push(`\"${transcriptContext}\"`);\n }\n \n return {\n success: true,\n needsUserInput: true,\n userPrompt: promptLines.join('\\n'),\n data: {\n found: false,\n clarificationType: 'new_person',\n term: args.name,\n message: `Person \"${args.name}\" not found. Asking user for details.`,\n knownProjects: allProjects.filter(p => p.active !== false),\n options: projectOptions,\n },\n };\n },\n});\n\n"],"names":[],"mappings":"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"}
|