@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/index50.js
CHANGED
|
@@ -1,163 +1,226 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
function extractTermContext(transcript, term) {
|
|
2
|
+
const lowerTranscript = transcript.toLowerCase();
|
|
3
|
+
const lowerTerm = term.toLowerCase();
|
|
4
|
+
const index = lowerTranscript.indexOf(lowerTerm);
|
|
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
|
+
}
|
|
10
18
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
}
|
|
20
|
+
let endIndex = transcript.length;
|
|
21
|
+
boundariesFound = 0;
|
|
22
|
+
for (let i = index + term.length; i < transcript.length; i++) {
|
|
23
|
+
if (sentenceBoundary.test(transcript[i])) {
|
|
24
|
+
boundariesFound++;
|
|
25
|
+
if (boundariesFound === 2) {
|
|
26
|
+
endIndex = i + 1;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
18
29
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
}
|
|
31
|
+
let context = transcript.substring(startIndex, endIndex).trim();
|
|
32
|
+
if (context.length > 300) {
|
|
33
|
+
const midPoint = context.indexOf(term);
|
|
34
|
+
if (midPoint !== -1) {
|
|
35
|
+
let sentenceStart = midPoint;
|
|
36
|
+
let sentenceEnd = midPoint + term.length;
|
|
37
|
+
for (let i = midPoint - 1; i >= 0; i--) {
|
|
38
|
+
if (sentenceBoundary.test(context[i])) {
|
|
39
|
+
sentenceStart = i + 1;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
26
42
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
43
|
+
for (let i = midPoint + term.length; i < context.length; i++) {
|
|
44
|
+
if (sentenceBoundary.test(context[i])) {
|
|
45
|
+
sentenceEnd = i + 1;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
32
48
|
}
|
|
49
|
+
context = context.substring(sentenceStart, sentenceEnd).trim();
|
|
50
|
+
} else {
|
|
51
|
+
context = context.substring(0, 300) + "...";
|
|
33
52
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
return
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
const regex = new RegExp(`\\b${soundsLike}\\b`, "i");
|
|
68
|
-
const match = surroundingText.match(regex);
|
|
69
|
-
if (!match) {
|
|
70
|
-
return "unknown";
|
|
53
|
+
}
|
|
54
|
+
return context;
|
|
55
|
+
}
|
|
56
|
+
const create = (ctx) => ({
|
|
57
|
+
name: "lookup_project",
|
|
58
|
+
description: "Look up project information for routing and context. Use when you need to determine where this note should be filed.",
|
|
59
|
+
parameters: {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
name: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description: "The project name or identifier"
|
|
65
|
+
},
|
|
66
|
+
triggerPhrase: {
|
|
67
|
+
type: "string",
|
|
68
|
+
description: "A phrase from the transcript that might indicate the project"
|
|
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
|
+
};
|
|
71
85
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
86
|
+
if (context.isIgnored(args.name)) {
|
|
87
|
+
return {
|
|
88
|
+
success: true,
|
|
89
|
+
data: {
|
|
90
|
+
found: false,
|
|
91
|
+
ignored: true,
|
|
92
|
+
message: `"${args.name}" is on the ignore list - skipping without prompting`
|
|
93
|
+
}
|
|
94
|
+
};
|
|
76
95
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
let contextProjectId;
|
|
97
|
+
if (ctx.routingInstance) {
|
|
98
|
+
const allProjects2 = context.getAllProjects();
|
|
99
|
+
const activeProject = allProjects2.find((p) => p.active !== false);
|
|
100
|
+
contextProjectId = activeProject?.id;
|
|
82
101
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
102
|
+
const searchResults = context.searchWithContext(args.name, contextProjectId);
|
|
103
|
+
const projectMatches = searchResults.filter((e) => e.type === "project");
|
|
104
|
+
const termMatches = searchResults.filter((e) => e.type === "term");
|
|
105
|
+
if (projectMatches.length > 0) {
|
|
106
|
+
const project = projectMatches[0];
|
|
88
107
|
return {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
108
|
+
success: true,
|
|
109
|
+
data: {
|
|
110
|
+
found: true,
|
|
111
|
+
project,
|
|
112
|
+
matchedVia: "context_aware_search"
|
|
113
|
+
}
|
|
92
114
|
};
|
|
93
115
|
}
|
|
94
|
-
if (
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
confidence: 1
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
if (mapping.tier === 2) {
|
|
105
|
-
if (shouldApplyTier2(mapping, classification)) {
|
|
106
|
-
return {
|
|
107
|
-
shouldReplace: true,
|
|
108
|
-
mapping,
|
|
109
|
-
reason: `Tier 2 mapping (project: ${classification.project}, confidence: ${classification.confidence})`,
|
|
110
|
-
confidence: classification.confidence ?? 0.5
|
|
111
|
-
};
|
|
112
|
-
} else {
|
|
116
|
+
if (termMatches.length > 0) {
|
|
117
|
+
const term = termMatches[0];
|
|
118
|
+
const termProjects = term.projects || [];
|
|
119
|
+
if (termProjects.length > 0) {
|
|
120
|
+
const allProjects2 = context.getAllProjects();
|
|
121
|
+
const associatedProject = allProjects2.find((p) => p.id === termProjects[0]);
|
|
122
|
+
if (associatedProject) {
|
|
113
123
|
return {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
124
|
+
success: true,
|
|
125
|
+
data: {
|
|
126
|
+
found: true,
|
|
127
|
+
project: associatedProject,
|
|
128
|
+
matchedVia: "term",
|
|
129
|
+
termName: term.name
|
|
130
|
+
}
|
|
117
131
|
};
|
|
118
132
|
}
|
|
119
133
|
}
|
|
120
|
-
return {
|
|
121
|
-
shouldReplace: false,
|
|
122
|
-
reason: "Tier 3 mapping (too ambiguous)",
|
|
123
|
-
confidence: 1
|
|
124
|
-
};
|
|
125
134
|
}
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
);
|
|
130
|
-
if (resolvedMapping) {
|
|
131
|
-
return {
|
|
132
|
-
shouldReplace: true,
|
|
133
|
-
mapping: resolvedMapping,
|
|
134
|
-
reason: `Collision resolved (${availableMappings.length} candidates)`,
|
|
135
|
-
confidence: classification.confidence ?? 0.5
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
if (surroundingText && useCapitalizationHints) {
|
|
139
|
-
const hint = detectCapitalizationHint(soundsLike, surroundingText);
|
|
140
|
-
if (hint === "common-term") {
|
|
135
|
+
const soundsLikeMatch = context.findBySoundsLike(args.name);
|
|
136
|
+
if (soundsLikeMatch) {
|
|
137
|
+
if (soundsLikeMatch.type === "project") {
|
|
141
138
|
return {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
139
|
+
success: true,
|
|
140
|
+
data: {
|
|
141
|
+
found: true,
|
|
142
|
+
project: soundsLikeMatch,
|
|
143
|
+
matchedVia: "sounds_like"
|
|
144
|
+
}
|
|
145
145
|
};
|
|
146
|
+
} else if (soundsLikeMatch.type === "term") {
|
|
147
|
+
const termProjects = soundsLikeMatch.projects || [];
|
|
148
|
+
if (termProjects.length > 0) {
|
|
149
|
+
const allProjects2 = context.getAllProjects();
|
|
150
|
+
const associatedProject = allProjects2.find((p) => p.id === termProjects[0]);
|
|
151
|
+
if (associatedProject) {
|
|
152
|
+
return {
|
|
153
|
+
success: true,
|
|
154
|
+
data: {
|
|
155
|
+
found: true,
|
|
156
|
+
project: associatedProject,
|
|
157
|
+
matchedVia: "term_sounds_like",
|
|
158
|
+
termName: soundsLikeMatch.name
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
146
163
|
}
|
|
147
164
|
}
|
|
165
|
+
if (args.triggerPhrase) {
|
|
166
|
+
const allProjects2 = context.getAllProjects();
|
|
167
|
+
for (const project of allProjects2) {
|
|
168
|
+
const phrases = project.classification?.explicit_phrases ?? [];
|
|
169
|
+
if (phrases.some((p) => args.triggerPhrase?.toLowerCase().includes(p.toLowerCase()))) {
|
|
170
|
+
return {
|
|
171
|
+
success: true,
|
|
172
|
+
data: {
|
|
173
|
+
found: true,
|
|
174
|
+
project,
|
|
175
|
+
matchedTrigger: args.triggerPhrase
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const allProjects = context.getAllProjects();
|
|
182
|
+
const projectOptions = allProjects.filter((p) => p.active !== false).map((p) => `${p.name}${p.description ? ` - ${p.description}` : ""}`);
|
|
183
|
+
const fileName = ctx.sourceFile.split("/").pop() || ctx.sourceFile;
|
|
184
|
+
const fileDate = ctx.audioDate.toLocaleString("en-US", {
|
|
185
|
+
weekday: "short",
|
|
186
|
+
year: "numeric",
|
|
187
|
+
month: "short",
|
|
188
|
+
day: "numeric",
|
|
189
|
+
hour: "2-digit",
|
|
190
|
+
minute: "2-digit"
|
|
191
|
+
});
|
|
192
|
+
const transcriptContext = extractTermContext(ctx.transcriptText, args.name);
|
|
193
|
+
const contextLines = [
|
|
194
|
+
`File: ${fileName}`,
|
|
195
|
+
`Date: ${fileDate}`,
|
|
196
|
+
"",
|
|
197
|
+
`Unknown project/term: "${args.name}"`
|
|
198
|
+
];
|
|
199
|
+
if (transcriptContext) {
|
|
200
|
+
contextLines.push("");
|
|
201
|
+
contextLines.push("Context from transcript:");
|
|
202
|
+
contextLines.push(`"${transcriptContext}"`);
|
|
203
|
+
} else if (args.triggerPhrase) {
|
|
204
|
+
contextLines.push("");
|
|
205
|
+
contextLines.push("Context from transcript:");
|
|
206
|
+
contextLines.push(`"${args.triggerPhrase}"`);
|
|
207
|
+
}
|
|
148
208
|
return {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
209
|
+
success: true,
|
|
210
|
+
needsUserInput: true,
|
|
211
|
+
userPrompt: contextLines.join("\n"),
|
|
212
|
+
data: {
|
|
213
|
+
found: false,
|
|
214
|
+
clarificationType: "new_project",
|
|
215
|
+
term: args.name,
|
|
216
|
+
triggerPhrase: args.triggerPhrase,
|
|
217
|
+
message: `Project "${args.name}" not found. Asking user if this is a new project.`,
|
|
218
|
+
knownProjects: allProjects.filter((p) => p.active !== false),
|
|
219
|
+
options: projectOptions
|
|
220
|
+
}
|
|
152
221
|
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
decideReplacement,
|
|
156
|
-
shouldApplyTier2,
|
|
157
|
-
resolveCollision,
|
|
158
|
-
detectCapitalizationHint
|
|
159
|
-
};
|
|
160
|
-
};
|
|
222
|
+
}
|
|
223
|
+
});
|
|
161
224
|
|
|
162
225
|
export { create };
|
|
163
226
|
//# sourceMappingURL=index50.js.map
|
package/dist/index50.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index50.js","sources":["../src/util/collision-detector.ts"],"sourcesContent":["/**\n * Collision Detector\n *\n * Provides utilities for detecting and resolving collisions in sounds_like mappings.\n * Helps determine when it's safe to apply a replacement and when context is needed.\n *\n * Part of the simple-replace optimization (Phase 1).\n */\n\nimport * as Logging from '@/logging';\nimport { SoundsLikeMapping, Collision } from './sounds-like-database';\n\n/**\n * Classification result for an entity\n */\nexport interface Classification {\n /** Identified project ID */\n project?: string;\n\n /** Classification confidence (0-1) */\n confidence?: number;\n\n /** Additional classification metadata */\n [key: string]: any;\n}\n\n/**\n * Replacement decision\n */\nexport interface ReplacementDecision {\n /** Whether to apply the replacement */\n shouldReplace: boolean;\n\n /** The mapping to use (if shouldReplace is true) */\n mapping?: SoundsLikeMapping;\n\n /** Reason for the decision */\n reason: string;\n\n /** Confidence in this decision (0-1) */\n confidence: number;\n}\n\n/**\n * Context for collision resolution\n */\nexport interface CollisionContext {\n /** Classification of the transcription */\n classification: Classification;\n\n /** The sounds_like value being considered */\n soundsLike: string;\n\n /** Available mappings for this sounds_like */\n availableMappings: SoundsLikeMapping[];\n\n /** Surrounding text context (optional) */\n surroundingText?: string;\n}\n\n/**\n * Instance interface for collision detector\n */\nexport interface Instance {\n /**\n * Decide whether to apply a replacement given a collision context\n */\n decideReplacement(context: CollisionContext): ReplacementDecision;\n\n /**\n * Check if a Tier 2 mapping should be applied given classification\n */\n shouldApplyTier2(mapping: SoundsLikeMapping, classification: Classification): boolean;\n\n /**\n * Resolve a collision by selecting the best mapping\n */\n resolveCollision(collision: Collision, classification: Classification): SoundsLikeMapping | null;\n\n /**\n * Detect capitalization hints in context\n */\n detectCapitalizationHint(soundsLike: string, surroundingText: string): 'proper-noun' | 'common-term' | 'unknown';\n}\n\n/**\n * Configuration for collision detector\n */\nexport interface CollisionDetectorConfig {\n /** Minimum confidence for Tier 2 replacements (default: 0.6) */\n tier2MinConfidence?: number;\n\n /** High confidence threshold for aggressive replacement (default: 0.8) */\n tier2HighConfidence?: number;\n\n /** Enable capitalization hints (default: true) */\n useCapitalizationHints?: boolean;\n\n /** Enable surrounding text analysis (default: false, future feature) */\n useSurroundingText?: boolean;\n}\n\n/**\n * Create a collision detector instance\n */\nexport const create = (config?: CollisionDetectorConfig): Instance => {\n const logger = Logging.getLogger();\n\n const tier2MinConfidence = config?.tier2MinConfidence ?? 0.6;\n // const tier2HighConfidence = config?.tier2HighConfidence ?? 0.8; // Reserved for future use\n const useCapitalizationHints = config?.useCapitalizationHints ?? true;\n\n /**\n * Check if a Tier 2 mapping should be applied\n */\n const shouldApplyTier2 = (\n mapping: SoundsLikeMapping,\n classification: Classification\n ): boolean => {\n // Must be Tier 2\n if (mapping.tier !== 2) {\n return false;\n }\n\n // Check confidence threshold\n const confidence = classification.confidence ?? 0;\n const minConfidence = mapping.minConfidence ?? tier2MinConfidence;\n\n if (confidence < minConfidence) {\n logger.debug(\n `Skipping Tier 2 replacement for \"${mapping.soundsLike}\": ` +\n `confidence ${confidence} < ${minConfidence}`\n );\n return false;\n }\n\n // For project-scoped mappings, check if project matches\n if (mapping.scopedToProjects && mapping.scopedToProjects.length > 0) {\n const classifiedProject = classification.project;\n\n if (!classifiedProject) {\n logger.debug(\n `Skipping Tier 2 replacement for \"${mapping.soundsLike}\": ` +\n `no project in classification`\n );\n return false;\n }\n\n if (!mapping.scopedToProjects.includes(classifiedProject)) {\n logger.debug(\n `Skipping Tier 2 replacement for \"${mapping.soundsLike}\": ` +\n `project \"${classifiedProject}\" not in scope [${mapping.scopedToProjects.join(', ')}]`\n );\n return false;\n }\n }\n\n logger.debug(\n `Applying Tier 2 replacement for \"${mapping.soundsLike}\" → \"${mapping.correctText}\" ` +\n `(project: ${classification.project}, confidence: ${confidence})`\n );\n return true;\n };\n\n /**\n * Resolve a collision by selecting the best mapping\n */\n const resolveCollision = (\n collision: Collision,\n classification: Classification\n ): SoundsLikeMapping | null => {\n const { soundsLike, mappings } = collision;\n\n logger.debug(`Resolving collision for \"${soundsLike}\" (${mappings.length} candidates)`);\n\n // Filter to Tier 1 and applicable Tier 2 mappings\n const tier1Mappings = mappings.filter(m => m.tier === 1);\n const tier2Mappings = mappings.filter(m => m.tier === 2 && shouldApplyTier2(m, classification));\n\n // Prefer Tier 1 if available (always safe)\n if (tier1Mappings.length === 1) {\n logger.debug(`Resolved collision: using Tier 1 mapping \"${tier1Mappings[0].correctText}\"`);\n return tier1Mappings[0];\n }\n\n // If multiple Tier 1 mappings, this is ambiguous (shouldn't happen in practice)\n if (tier1Mappings.length > 1) {\n logger.warn(`Multiple Tier 1 mappings for \"${soundsLike}\", skipping replacement`);\n return null;\n }\n\n // Try Tier 2 mappings that match classification\n if (tier2Mappings.length === 1) {\n logger.debug(`Resolved collision: using Tier 2 mapping \"${tier2Mappings[0].correctText}\"`);\n return tier2Mappings[0];\n }\n\n // If multiple Tier 2 mappings match, this is ambiguous\n if (tier2Mappings.length > 1) {\n logger.debug(`Multiple Tier 2 mappings match for \"${soundsLike}\", skipping replacement`);\n return null;\n }\n\n // No applicable mappings\n logger.debug(`No applicable mappings for collision \"${soundsLike}\"`);\n return null;\n };\n\n /**\n * Detect capitalization hints in surrounding text\n */\n const detectCapitalizationHint = (\n soundsLike: string,\n surroundingText: string\n ): 'proper-noun' | 'common-term' | 'unknown' => {\n if (!useCapitalizationHints || !surroundingText) {\n return 'unknown';\n }\n\n // Find the sounds_like in the surrounding text\n const regex = new RegExp(`\\\\b${soundsLike}\\\\b`, 'i');\n const match = surroundingText.match(regex);\n\n if (!match) {\n return 'unknown';\n }\n\n const matchedText = match[0];\n\n // Check if it's capitalized\n const isCapitalized = matchedText[0] === matchedText[0].toUpperCase();\n\n if (!isCapitalized) {\n // Lowercase in text → likely common term\n return 'common-term';\n }\n\n // Capitalized - check if it's at sentence start\n const indexInText = surroundingText.indexOf(matchedText);\n\n // Look back for sentence boundaries\n const beforeText = surroundingText.substring(0, indexInText).trimEnd();\n const isAtSentenceStart = beforeText.length === 0 || /[.!?]\\s*$/.test(beforeText);\n\n if (isAtSentenceStart) {\n // Capitalized at sentence start → ambiguous\n return 'unknown';\n }\n\n // Capitalized mid-sentence → likely proper noun\n return 'proper-noun';\n };\n\n /**\n * Decide whether to apply a replacement\n */\n const decideReplacement = (context: CollisionContext): ReplacementDecision => {\n const { classification, soundsLike, availableMappings, surroundingText } = context;\n\n // No mappings available\n if (availableMappings.length === 0) {\n return {\n shouldReplace: false,\n reason: 'No mappings available',\n confidence: 1.0,\n };\n }\n\n // Single mapping - straightforward\n if (availableMappings.length === 1) {\n const mapping = availableMappings[0];\n\n // Tier 1: Always apply\n if (mapping.tier === 1) {\n return {\n shouldReplace: true,\n mapping,\n reason: 'Tier 1 mapping (always safe)',\n confidence: 1.0,\n };\n }\n\n // Tier 2: Check conditions\n if (mapping.tier === 2) {\n if (shouldApplyTier2(mapping, classification)) {\n return {\n shouldReplace: true,\n mapping,\n reason: `Tier 2 mapping (project: ${classification.project}, confidence: ${classification.confidence})`,\n confidence: classification.confidence ?? 0.5,\n };\n } else {\n return {\n shouldReplace: false,\n reason: 'Tier 2 conditions not met',\n confidence: 0.5,\n };\n }\n }\n\n // Tier 3: Skip\n return {\n shouldReplace: false,\n reason: 'Tier 3 mapping (too ambiguous)',\n confidence: 1.0,\n };\n }\n\n // Multiple mappings - collision scenario\n const resolvedMapping = resolveCollision(\n { soundsLike, mappings: availableMappings, count: availableMappings.length },\n classification\n );\n\n if (resolvedMapping) {\n return {\n shouldReplace: true,\n mapping: resolvedMapping,\n reason: `Collision resolved (${availableMappings.length} candidates)`,\n confidence: classification.confidence ?? 0.5,\n };\n }\n\n // Use capitalization hint as fallback (future enhancement)\n if (surroundingText && useCapitalizationHints) {\n const hint = detectCapitalizationHint(soundsLike, surroundingText);\n\n if (hint === 'common-term') {\n return {\n shouldReplace: false,\n reason: 'Capitalization hint suggests common term',\n confidence: 0.7,\n };\n }\n }\n\n // Could not resolve collision\n return {\n shouldReplace: false,\n reason: 'Collision could not be resolved',\n confidence: 0.5,\n };\n };\n\n return {\n decideReplacement,\n shouldApplyTier2,\n resolveCollision,\n detectCapitalizationHint,\n };\n};\n"],"names":["Logging.getLogger"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index50.js","sources":["../src/agentic/tools/lookup-project.ts"],"sourcesContent":["/**\n * Lookup Project Tool\n * \n * Looks up project information for routing and context.\n * Prompts to create unknown projects when user input is available.\n */\n\nimport { TranscriptionTool, ToolContext, ToolResult } from '../types';\n\n/**\n * Extract context from transcript around where a term is mentioned.\n * Returns approximately one sentence before and after the term mention.\n */\nfunction extractTermContext(transcript: string, term: string): string | null {\n // Case-insensitive search for the term\n const lowerTranscript = transcript.toLowerCase();\n const lowerTerm = term.toLowerCase();\n const index = lowerTranscript.indexOf(lowerTerm);\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 + term.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(term);\n if (midPoint !== -1) {\n // Keep the sentence with the term, trim around it\n let sentenceStart = midPoint;\n let sentenceEnd = midPoint + term.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 + term.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 term 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_project',\n description: 'Look up project information for routing and context. Use when you need to determine where this note should be filed.',\n parameters: {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n description: 'The project name or identifier',\n },\n triggerPhrase: {\n type: 'string',\n description: 'A phrase from the transcript that might indicate the project',\n },\n },\n required: ['name'],\n },\n execute: async (args: { name: string; triggerPhrase?: string }): Promise<ToolResult> => {\n const context = ctx.contextInstance;\n \n // First, check if this project/term 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 // Check if this term is on the ignore list\n if (context.isIgnored(args.name)) {\n return {\n success: true,\n data: {\n found: false,\n ignored: true,\n message: `\"${args.name}\" is on the ignore list - skipping without prompting`,\n },\n };\n }\n \n // Try to determine context from routing instance if available\n let contextProjectId: string | undefined;\n if (ctx.routingInstance) {\n const allProjects = context.getAllProjects();\n // Use the first active project as a context hint (could be improved)\n const activeProject = allProjects.find(p => p.active !== false);\n contextProjectId = activeProject?.id;\n }\n \n // Use context-aware search (prefers related projects)\n const searchResults = context.searchWithContext(args.name, contextProjectId);\n const projectMatches = searchResults.filter(e => e.type === 'project');\n const termMatches = searchResults.filter(e => e.type === 'term');\n \n if (projectMatches.length > 0) {\n const project = projectMatches[0];\n return {\n success: true,\n data: {\n found: true,\n project,\n matchedVia: 'context_aware_search',\n },\n };\n }\n \n // Check if we found a term that's associated with projects\n if (termMatches.length > 0) {\n const term = termMatches[0];\n const termProjects = term.projects || [];\n \n if (termProjects.length > 0) {\n // Get the first associated project\n const allProjects = context.getAllProjects();\n const associatedProject = allProjects.find(p => p.id === termProjects[0]);\n \n if (associatedProject) {\n return {\n success: true,\n data: {\n found: true,\n project: associatedProject,\n matchedVia: 'term',\n termName: term.name,\n },\n };\n }\n }\n }\n \n // Try findBySoundsLike as a fallback for exact phonetic matches\n const soundsLikeMatch = context.findBySoundsLike(args.name);\n if (soundsLikeMatch) {\n if (soundsLikeMatch.type === 'project') {\n return {\n success: true,\n data: {\n found: true,\n project: soundsLikeMatch,\n matchedVia: 'sounds_like',\n },\n };\n } else if (soundsLikeMatch.type === 'term') {\n const termProjects = soundsLikeMatch.projects || [];\n \n if (termProjects.length > 0) {\n const allProjects = context.getAllProjects();\n const associatedProject = allProjects.find(p => p.id === termProjects[0]);\n \n if (associatedProject) {\n return {\n success: true,\n data: {\n found: true,\n project: associatedProject,\n matchedVia: 'term_sounds_like',\n termName: soundsLikeMatch.name,\n },\n };\n }\n }\n }\n }\n \n // Try getting all projects and matching trigger phrases\n if (args.triggerPhrase) {\n const allProjects = context.getAllProjects();\n for (const project of allProjects) {\n const phrases = project.classification?.explicit_phrases ?? [];\n if (phrases.some(p => args.triggerPhrase?.toLowerCase().includes(p.toLowerCase()))) {\n return {\n success: true,\n data: {\n found: true,\n project,\n matchedTrigger: args.triggerPhrase,\n },\n };\n }\n }\n }\n \n // Project 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 project/term is mentioned\n const transcriptContext = extractTermContext(ctx.transcriptText, args.name);\n \n const contextLines = [\n `File: ${fileName}`,\n `Date: ${fileDate}`,\n '',\n `Unknown project/term: \"${args.name}\"`,\n ];\n \n if (transcriptContext) {\n contextLines.push('');\n contextLines.push('Context from transcript:');\n contextLines.push(`\"${transcriptContext}\"`);\n } else if (args.triggerPhrase) {\n contextLines.push('');\n contextLines.push('Context from transcript:');\n contextLines.push(`\"${args.triggerPhrase}\"`);\n }\n \n return {\n success: true,\n needsUserInput: true,\n userPrompt: contextLines.join('\\n'),\n data: {\n found: false,\n clarificationType: 'new_project',\n term: args.name,\n triggerPhrase: args.triggerPhrase,\n message: `Project \"${args.name}\" not found. Asking user if this is a new project.`,\n knownProjects: allProjects.filter(p => p.active !== false),\n options: projectOptions,\n },\n };\n },\n});\n\n"],"names":["allProjects"],"mappings":"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"}
|
package/dist/index51.js
CHANGED
|
@@ -1,81 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const original = match[0];
|
|
19
|
-
let replacement = mapping.correctText;
|
|
20
|
-
replacements.push({
|
|
21
|
-
index: match.index,
|
|
22
|
-
length: original.length,
|
|
23
|
-
replacement
|
|
24
|
-
});
|
|
25
|
-
occurrences.push({
|
|
26
|
-
original,
|
|
27
|
-
replacement,
|
|
28
|
-
position: match.index,
|
|
29
|
-
mapping
|
|
30
|
-
});
|
|
31
|
-
count++;
|
|
32
|
-
}
|
|
33
|
-
let resultText = text;
|
|
34
|
-
for (let i = replacements.length - 1; i >= 0; i--) {
|
|
35
|
-
const { index, length, replacement } = replacements[i];
|
|
36
|
-
resultText = resultText.slice(0, index) + replacement + resultText.slice(index + length);
|
|
37
|
-
}
|
|
38
|
-
if (count > 0) {
|
|
39
|
-
logger.debug(
|
|
40
|
-
`Replaced "${mapping.soundsLike}" → "${mapping.correctText}" (${count} occurrence${count === 1 ? "" : "s"})`
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
text: resultText,
|
|
45
|
-
count,
|
|
46
|
-
occurrences,
|
|
47
|
-
appliedMappings: count > 0 ? [mapping] : []
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
const applyReplacements = (text, mappings) => {
|
|
51
|
-
let resultText = text;
|
|
52
|
-
let totalCount = 0;
|
|
53
|
-
const allOccurrences = [];
|
|
54
|
-
const appliedMappings = [];
|
|
55
|
-
for (const mapping of mappings) {
|
|
56
|
-
const result = applySingleReplacement(resultText, mapping);
|
|
57
|
-
if (result.count > 0) {
|
|
58
|
-
resultText = result.text;
|
|
59
|
-
totalCount += result.count;
|
|
60
|
-
allOccurrences.push(...result.occurrences);
|
|
61
|
-
appliedMappings.push(mapping);
|
|
1
|
+
const create = (ctx) => ({
|
|
2
|
+
name: "verify_spelling",
|
|
3
|
+
description: "Request user verification for an unknown name or term. Use when you encounter something that needs human confirmation.",
|
|
4
|
+
parameters: {
|
|
5
|
+
type: "object",
|
|
6
|
+
properties: {
|
|
7
|
+
term: {
|
|
8
|
+
type: "string",
|
|
9
|
+
description: "The term that needs verification"
|
|
10
|
+
},
|
|
11
|
+
context: {
|
|
12
|
+
type: "string",
|
|
13
|
+
description: "Context around where this term appears"
|
|
14
|
+
},
|
|
15
|
+
suggestedSpelling: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "Your best guess at the correct spelling"
|
|
62
18
|
}
|
|
19
|
+
},
|
|
20
|
+
required: ["term"]
|
|
21
|
+
},
|
|
22
|
+
execute: async (args) => {
|
|
23
|
+
if (!ctx.interactiveMode) {
|
|
24
|
+
return {
|
|
25
|
+
success: true,
|
|
26
|
+
data: {
|
|
27
|
+
verified: false,
|
|
28
|
+
useSuggestion: true,
|
|
29
|
+
spelling: args.suggestedSpelling || args.term,
|
|
30
|
+
message: "Non-interactive mode: using best guess"
|
|
31
|
+
}
|
|
32
|
+
};
|
|
63
33
|
}
|
|
64
|
-
logger.debug(
|
|
65
|
-
`Applied ${mappings.length} mappings, made ${totalCount} replacements (${appliedMappings.length} mappings had matches)`
|
|
66
|
-
);
|
|
67
34
|
return {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
35
|
+
success: true,
|
|
36
|
+
needsUserInput: true,
|
|
37
|
+
userPrompt: `Unknown term: "${args.term}"${args.context ? ` (context: "${args.context}")` : ""}
|
|
38
|
+
${args.suggestedSpelling ? `Suggested spelling: "${args.suggestedSpelling}"` : ""}
|
|
39
|
+
Please provide the correct spelling:`,
|
|
40
|
+
data: {
|
|
41
|
+
term: args.term,
|
|
42
|
+
suggestedSpelling: args.suggestedSpelling
|
|
43
|
+
}
|
|
72
44
|
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
applyReplacements,
|
|
76
|
-
applySingleReplacement
|
|
77
|
-
};
|
|
78
|
-
};
|
|
45
|
+
}
|
|
46
|
+
});
|
|
79
47
|
|
|
80
48
|
export { create };
|
|
81
49
|
//# sourceMappingURL=index51.js.map
|
package/dist/index51.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index51.js","sources":["../src/util/text-replacer.ts"],"sourcesContent":["/**\n * Text Replacer\n *\n * Performs intelligent text replacements with case preservation and word boundary matching.\n * Core utility for the simple-replace phase.\n *\n * Part of the simple-replace optimization (Phase 2).\n */\n\nimport * as Logging from '@/logging';\nimport { SoundsLikeMapping } from './sounds-like-database';\n\n/**\n * Replacement result for a single occurrence\n */\nexport interface ReplacementOccurrence {\n /** Original text that was replaced */\n original: string;\n\n /** Replacement text */\n replacement: string;\n\n /** Position in the text where replacement occurred */\n position: number;\n\n /** The mapping that was used */\n mapping: SoundsLikeMapping;\n}\n\n/**\n * Result of applying replacements to text\n */\nexport interface ReplacementResult {\n /** The text after replacements */\n text: string;\n\n /** Number of replacements made */\n count: number;\n\n /** Detailed information about each replacement */\n occurrences: ReplacementOccurrence[];\n\n /** Mappings that were applied */\n appliedMappings: SoundsLikeMapping[];\n}\n\n/**\n * Configuration for text replacer\n */\nexport interface TextReplacerConfig {\n /** Preserve case of original text when replacing (default: true) */\n preserveCase?: boolean;\n\n /** Use word boundaries for matching (default: true) */\n useWordBoundaries?: boolean;\n\n /** Case-insensitive matching (default: true) */\n caseInsensitive?: boolean;\n}\n\n/**\n * Instance interface for text replacer\n */\nexport interface Instance {\n /**\n * Apply a set of replacements to text\n */\n applyReplacements(text: string, mappings: SoundsLikeMapping[]): ReplacementResult;\n\n /**\n * Apply a single replacement to text\n */\n applySingleReplacement(text: string, mapping: SoundsLikeMapping): ReplacementResult;\n}\n\n/**\n * Create a text replacer instance\n */\nexport const create = (config?: TextReplacerConfig): Instance => {\n const logger = Logging.getLogger();\n\n const preserveCase = config?.preserveCase ?? true;\n const useWordBoundaries = config?.useWordBoundaries ?? true;\n const caseInsensitive = config?.caseInsensitive ?? true;\n\n /**\n * Determine the case style of a string\n */\n const getCaseStyle = (text: string): 'upper' | 'lower' | 'title' | 'mixed' => {\n // Check for all uppercase first (includes single chars)\n if (text.length > 0 && text === text.toUpperCase() && text.toUpperCase() !== text.toLowerCase()) {\n return 'upper';\n }\n // Check for all lowercase\n if (text === text.toLowerCase()) {\n return 'lower';\n }\n // Check for title case (first char upper, or mixed case starting with upper)\n if (text[0] === text[0].toUpperCase()) {\n return 'title';\n }\n return 'mixed';\n };\n\n /**\n * Apply case style from original to replacement\n *\n * Case preservation means: make the replacement match the case pattern of the original\n * - \"protocol\" (lowercase) → \"protokoll\" (lowercase)\n * - \"Protocol\" (title) → \"Protokoll\" (title)\n * - \"PROTOCOL\" (upper) → \"PROTOKOLL\" (upper)\n */\n const applyCaseStyle = (replacement: string, originalCase: 'upper' | 'lower' | 'title' | 'mixed'): string => {\n switch (originalCase) {\n case 'upper':\n // ALL CAPS in original → ALL CAPS in replacement\n return replacement.toUpperCase();\n case 'lower':\n // all lowercase in original → all lowercase in replacement\n return replacement.toLowerCase();\n case 'title':\n // Title Case in original → Title Case in replacement (first char upper, rest as-is)\n return replacement.charAt(0).toUpperCase() + replacement.slice(1);\n case 'mixed':\n default:\n // Mixed case → preserve replacement's original case\n return replacement;\n }\n };\n\n /**\n * Create a regex pattern for matching\n */\n const createPattern = (soundsLike: string): RegExp => {\n // Escape special regex characters\n const escaped = soundsLike.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n // Add word boundaries if enabled\n const pattern = useWordBoundaries ? `\\\\b${escaped}\\\\b` : escaped;\n\n // Create regex with appropriate flags\n const flags = caseInsensitive ? 'gi' : 'g';\n return new RegExp(pattern, flags);\n };\n\n /**\n * Apply a single replacement to text\n */\n const applySingleReplacement = (text: string, mapping: SoundsLikeMapping): ReplacementResult => {\n const pattern = createPattern(mapping.soundsLike);\n const occurrences: ReplacementOccurrence[] = [];\n let count = 0;\n\n // Track positions to avoid double-replacement issues\n const replacements: { index: number; length: number; replacement: string }[] = [];\n\n // Find all matches\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(text)) !== null) {\n const original = match[0];\n let replacement = mapping.correctText;\n\n // Preserve case if enabled\n if (preserveCase) {\n const caseStyle = getCaseStyle(original);\n replacement = applyCaseStyle(replacement, caseStyle);\n }\n\n replacements.push({\n index: match.index,\n length: original.length,\n replacement,\n });\n\n occurrences.push({\n original,\n replacement,\n position: match.index,\n mapping,\n });\n\n count++;\n }\n\n // Apply replacements in reverse order to preserve positions\n let resultText = text;\n for (let i = replacements.length - 1; i >= 0; i--) {\n const { index, length, replacement } = replacements[i];\n resultText = resultText.slice(0, index) + replacement + resultText.slice(index + length);\n }\n\n if (count > 0) {\n logger.debug(\n `Replaced \"${mapping.soundsLike}\" → \"${mapping.correctText}\" ` +\n `(${count} occurrence${count === 1 ? '' : 's'})`\n );\n }\n\n return {\n text: resultText,\n count,\n occurrences,\n appliedMappings: count > 0 ? [mapping] : [],\n };\n };\n\n /**\n * Apply multiple replacements to text\n */\n const applyReplacements = (text: string, mappings: SoundsLikeMapping[]): ReplacementResult => {\n let resultText = text;\n let totalCount = 0;\n const allOccurrences: ReplacementOccurrence[] = [];\n const appliedMappings: SoundsLikeMapping[] = [];\n\n // Apply each mapping sequentially\n for (const mapping of mappings) {\n const result = applySingleReplacement(resultText, mapping);\n\n if (result.count > 0) {\n resultText = result.text;\n totalCount += result.count;\n allOccurrences.push(...result.occurrences);\n appliedMappings.push(mapping);\n }\n }\n\n logger.debug(\n `Applied ${mappings.length} mappings, made ${totalCount} replacements ` +\n `(${appliedMappings.length} mappings had matches)`\n );\n\n return {\n text: resultText,\n count: totalCount,\n occurrences: allOccurrences,\n appliedMappings,\n };\n };\n\n return {\n applyReplacements,\n applySingleReplacement,\n };\n};\n"],"names":["Logging.getLogger"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index51.js","sources":["../src/agentic/tools/verify-spelling.ts"],"sourcesContent":["/**\n * Verify Spelling Tool\n * \n * Requests user verification for an unknown name or term.\n */\n\nimport { TranscriptionTool, ToolContext, ToolResult } from '../types';\n\nexport const create = (ctx: ToolContext): TranscriptionTool => ({\n name: 'verify_spelling',\n description: 'Request user verification for an unknown name or term. Use when you encounter something that needs human confirmation.',\n parameters: {\n type: 'object',\n properties: {\n term: {\n type: 'string',\n description: 'The term that needs verification',\n },\n context: {\n type: 'string',\n description: 'Context around where this term appears',\n },\n suggestedSpelling: {\n type: 'string',\n description: 'Your best guess at the correct spelling',\n },\n },\n required: ['term'],\n },\n execute: async (args: { term: string; context?: string; suggestedSpelling?: string }): Promise<ToolResult> => {\n if (!ctx.interactiveMode) {\n // In batch mode, return best guess\n return {\n success: true,\n data: {\n verified: false,\n useSuggestion: true,\n spelling: args.suggestedSpelling || args.term,\n message: 'Non-interactive mode: using best guess',\n },\n };\n }\n \n // In interactive mode, mark for user input\n return {\n success: true,\n needsUserInput: true,\n userPrompt: `Unknown term: \"${args.term}\"${args.context ? ` (context: \"${args.context}\")` : ''}\n${args.suggestedSpelling ? `Suggested spelling: \"${args.suggestedSpelling}\"` : ''}\nPlease provide the correct spelling:`,\n data: {\n term: args.term,\n suggestedSpelling: args.suggestedSpelling,\n },\n };\n },\n});\n\n"],"names":[],"mappings":"AAQO,MAAM,MAAA,GAAS,CAAC,GAAA,MAAyC;AAAA,EAC5D,IAAA,EAAM,iBAAA;AAAA,EACN,WAAA,EAAa,wHAAA;AAAA,EACb,UAAA,EAAY;AAAA,IACR,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACR,IAAA,EAAM;AAAA,QACF,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACjB;AAAA,MACA,OAAA,EAAS;AAAA,QACL,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACjB;AAAA,MACA,iBAAA,EAAmB;AAAA,QACf,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA;AACjB,KACJ;AAAA,IACA,QAAA,EAAU,CAAC,MAAM;AAAA,GACrB;AAAA,EACA,OAAA,EAAS,OAAO,IAAA,KAA8F;AAC1G,IAAA,IAAI,CAAC,IAAI,eAAA,EAAiB;AAEtB,MAAA,OAAO;AAAA,QACH,OAAA,EAAS,IAAA;AAAA,QACT,IAAA,EAAM;AAAA,UACF,QAAA,EAAU,KAAA;AAAA,UACV,aAAA,EAAe,IAAA;AAAA,UACf,QAAA,EAAU,IAAA,CAAK,iBAAA,IAAqB,IAAA,CAAK,IAAA;AAAA,UACzC,OAAA,EAAS;AAAA;AACb,OACJ;AAAA,IACJ;AAGA,IAAA,OAAO;AAAA,MACH,OAAA,EAAS,IAAA;AAAA,MACT,cAAA,EAAgB,IAAA;AAAA,MAChB,UAAA,EAAY,CAAA,eAAA,EAAkB,IAAA,CAAK,IAAI,CAAA,CAAA,EAAI,IAAA,CAAK,OAAA,GAAU,CAAA,YAAA,EAAe,IAAA,CAAK,OAAO,CAAA,EAAA,CAAA,GAAO,EAAE;AAAA,EACxG,KAAK,iBAAA,GAAoB,CAAA,qBAAA,EAAwB,IAAA,CAAK,iBAAiB,MAAM,EAAE;AAAA,oCAAA,CAAA;AAAA,MAErE,IAAA,EAAM;AAAA,QACF,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,mBAAmB,IAAA,CAAK;AAAA;AAC5B,KACJ;AAAA,EACJ;AACJ,CAAA;;;;"}
|