@redaksjon/protokoll 1.0.20 → 1.0.21-dev.20260225190148.00bb9be
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/configDiscovery.js +978 -9
- package/dist/configDiscovery.js.map +1 -1
- package/dist/mcp/server.js +1 -0
- package/dist/mcp/server.js.map +1 -1
- package/package.json +1 -1
- package/vitest.config.ts +1 -1
package/dist/configDiscovery.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { resolve, isAbsolute, relative, extname, join, dirname, basename } from 'node:path';
|
|
2
2
|
import * as Metadata from '@redaksjon/protokoll-engine';
|
|
3
|
-
import { Transcript, Media, Util, Pipeline, findIgnoredResilient, findCompanyResilient, findTermResilient, findPersonResilient, findProjectResilient, Reasoning, VALID_STATUSES, isValidStatus } from '@redaksjon/protokoll-engine';
|
|
3
|
+
import { Transcript, Media, Util, Pipeline, findIgnoredResilient, findCompanyResilient, findTermResilient, findPersonResilient, findProjectResilient, Routing, Phases, Reasoning, Agentic, VALID_STATUSES, isValidStatus } from '@redaksjon/protokoll-engine';
|
|
4
4
|
import * as yaml from 'js-yaml';
|
|
5
|
-
import { stat, readdir, mkdir, unlink } from 'node:fs/promises';
|
|
5
|
+
import { stat, readdir, mkdir, mkdtemp, rm, unlink } from 'node:fs/promises';
|
|
6
6
|
import { readFileSync } from 'node:fs';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { create as create$1, parseEntityUri as parseEntityUri$1, createRelationship, createDocumentContent, createCodeContent, createMarkdownContent, createTextContent, createUrlContent } from '@redaksjon/context';
|
|
9
|
-
import os from 'node:os';
|
|
9
|
+
import os, { tmpdir } from 'node:os';
|
|
10
10
|
import winston from 'winston';
|
|
11
11
|
import { glob } from 'glob';
|
|
12
12
|
import { randomUUID } from 'crypto';
|
|
13
|
-
import {
|
|
13
|
+
import { randomUUID as randomUUID$1 } from 'node:crypto';
|
|
14
|
+
import { PklTranscript, readTranscript, listTranscripts as listTranscripts$1 } from '@redaksjon/protokoll-format';
|
|
14
15
|
import * as Cardigantime from '@utilarium/cardigantime';
|
|
15
16
|
|
|
16
17
|
const SCHEME = "protokoll";
|
|
@@ -204,7 +205,7 @@ function isProtokolUri(uri) {
|
|
|
204
205
|
return uri.startsWith(`${SCHEME}://`);
|
|
205
206
|
}
|
|
206
207
|
|
|
207
|
-
const VERSION = "1.0.
|
|
208
|
+
const VERSION = "1.0.21-dev.20260225190148.00bb9be (working/00bb9be 2026-02-25 11:00:08 -0800) linux arm64 v24.14.0";
|
|
208
209
|
const PROGRAM_NAME = "protokoll";
|
|
209
210
|
const DATE_FORMAT_YEAR_MONTH_DAY_HOURS_MINUTES_SECONDS = "YYYY-M-D-HHmmss";
|
|
210
211
|
const DEFAULT_AUDIO_EXTENSIONS = ["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm", "qta"];
|
|
@@ -224,6 +225,7 @@ const DEFAULT_TERM_SOUNDS_LIKE_ON_ADD = true;
|
|
|
224
225
|
const DEFAULT_TERM_DESCRIPTION_ON_ADD = true;
|
|
225
226
|
const DEFAULT_TERM_TOPICS_ON_ADD = true;
|
|
226
227
|
const DEFAULT_TERM_PROJECT_SUGGESTIONS = true;
|
|
228
|
+
const MAX_CONTENT_LENGTH = 15e3;
|
|
227
229
|
const ASSIST_TIMEOUT_MS = 3e4;
|
|
228
230
|
const DEFAULT_MAX_AUDIO_SIZE = 26214400;
|
|
229
231
|
const DEFAULT_TEMP_DIRECTORY = os.tmpdir();
|
|
@@ -912,6 +914,37 @@ const shared = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
|
912
914
|
}, Symbol.toStringTag, { value: 'Module' }));
|
|
913
915
|
|
|
914
916
|
const { listTranscripts, resolveTranscriptPath, readTranscriptContent, stripTranscriptExtension } = Transcript;
|
|
917
|
+
function parseStoredSummaries$1(raw) {
|
|
918
|
+
try {
|
|
919
|
+
const parsed = JSON.parse(raw);
|
|
920
|
+
if (!Array.isArray(parsed)) {
|
|
921
|
+
return [];
|
|
922
|
+
}
|
|
923
|
+
return parsed.map((item) => {
|
|
924
|
+
if (!item || typeof item !== "object") {
|
|
925
|
+
return null;
|
|
926
|
+
}
|
|
927
|
+
const record = item;
|
|
928
|
+
const id = String(record.id || "").trim();
|
|
929
|
+
const content = String(record.content || "").trim();
|
|
930
|
+
if (!id || !content) {
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
return {
|
|
934
|
+
id,
|
|
935
|
+
title: String(record.title || "").trim(),
|
|
936
|
+
audience: String(record.audience || "").trim(),
|
|
937
|
+
guidance: String(record.guidance || "").trim(),
|
|
938
|
+
stylePreset: String(record.stylePreset || "detailed").trim() || "detailed",
|
|
939
|
+
styleLabel: String(record.styleLabel || "Detailed summary").trim() || "Detailed summary",
|
|
940
|
+
content,
|
|
941
|
+
generatedAt: String(record.generatedAt || "").trim() || (/* @__PURE__ */ new Date()).toISOString()
|
|
942
|
+
};
|
|
943
|
+
}).filter((summary) => summary !== null).sort((a, b) => b.generatedAt.localeCompare(a.generatedAt));
|
|
944
|
+
} catch {
|
|
945
|
+
return [];
|
|
946
|
+
}
|
|
947
|
+
}
|
|
915
948
|
async function readTranscriptResource(transcriptPath) {
|
|
916
949
|
if (!transcriptPath || typeof transcriptPath !== "string") {
|
|
917
950
|
throw new Error(`Invalid transcript path: ${transcriptPath}`);
|
|
@@ -927,6 +960,7 @@ async function readTranscriptResource(transcriptPath) {
|
|
|
927
960
|
const { PklTranscript } = await import('@redaksjon/protokoll-format');
|
|
928
961
|
const pklTranscript = PklTranscript.open(resolved.path, { readOnly: true });
|
|
929
962
|
let rawTranscript = void 0;
|
|
963
|
+
let summaries = [];
|
|
930
964
|
try {
|
|
931
965
|
if (pklTranscript.hasRawTranscript) {
|
|
932
966
|
const rawData = pklTranscript.rawTranscript;
|
|
@@ -939,6 +973,8 @@ async function readTranscriptResource(transcriptPath) {
|
|
|
939
973
|
};
|
|
940
974
|
}
|
|
941
975
|
}
|
|
976
|
+
const historyArtifact = pklTranscript.getArtifact("summary_history");
|
|
977
|
+
summaries = parseStoredSummaries$1(historyArtifact?.data?.toString("utf8") || "[]");
|
|
942
978
|
} finally {
|
|
943
979
|
pklTranscript.close();
|
|
944
980
|
}
|
|
@@ -965,7 +1001,8 @@ async function readTranscriptResource(transcriptPath) {
|
|
|
965
1001
|
} : void 0
|
|
966
1002
|
},
|
|
967
1003
|
content,
|
|
968
|
-
rawTranscript
|
|
1004
|
+
rawTranscript,
|
|
1005
|
+
summaries
|
|
969
1006
|
};
|
|
970
1007
|
return {
|
|
971
1008
|
uri: buildTranscriptUri(identifierPath),
|
|
@@ -1625,6 +1662,58 @@ function getPrompts() {
|
|
|
1625
1662
|
}
|
|
1626
1663
|
]
|
|
1627
1664
|
},
|
|
1665
|
+
{
|
|
1666
|
+
name: "identify_tasks_from_transcript",
|
|
1667
|
+
description: "Identify task candidates from a transcript with a review-first flow. Instructs the assistant to identify first, present candidates with none preselected, and only create tasks after explicit user approval.",
|
|
1668
|
+
arguments: [
|
|
1669
|
+
{
|
|
1670
|
+
name: "transcriptPath",
|
|
1671
|
+
description: "Path or URI to the transcript to analyze",
|
|
1672
|
+
required: true
|
|
1673
|
+
},
|
|
1674
|
+
{
|
|
1675
|
+
name: "maxCandidates",
|
|
1676
|
+
description: "Optional maximum number of task candidates to identify (default: 25)",
|
|
1677
|
+
required: false
|
|
1678
|
+
},
|
|
1679
|
+
{
|
|
1680
|
+
name: "includeTagSuggestions",
|
|
1681
|
+
description: "Optional flag to include suggested tags (default: true)",
|
|
1682
|
+
required: false
|
|
1683
|
+
}
|
|
1684
|
+
]
|
|
1685
|
+
},
|
|
1686
|
+
{
|
|
1687
|
+
name: "summarize_transcript",
|
|
1688
|
+
description: "Create an audience-aware transcript summary with privacy guardrails and style presets.",
|
|
1689
|
+
arguments: [
|
|
1690
|
+
{
|
|
1691
|
+
name: "transcriptPath",
|
|
1692
|
+
description: "Path to the transcript to summarize",
|
|
1693
|
+
required: true
|
|
1694
|
+
},
|
|
1695
|
+
{
|
|
1696
|
+
name: "audience",
|
|
1697
|
+
description: "Target audience for the summary (e.g. internal team, external attendee)",
|
|
1698
|
+
required: true
|
|
1699
|
+
},
|
|
1700
|
+
{
|
|
1701
|
+
name: "stylePreset",
|
|
1702
|
+
description: "Summary style: quick_bullets, detailed, attendee_facing (default: detailed)",
|
|
1703
|
+
required: false
|
|
1704
|
+
},
|
|
1705
|
+
{
|
|
1706
|
+
name: "guidance",
|
|
1707
|
+
description: "Additional instructions (especially privacy/sensitivity constraints)",
|
|
1708
|
+
required: false
|
|
1709
|
+
},
|
|
1710
|
+
{
|
|
1711
|
+
name: "summaryTitle",
|
|
1712
|
+
description: "Optional title to use for the summary output",
|
|
1713
|
+
required: false
|
|
1714
|
+
}
|
|
1715
|
+
]
|
|
1716
|
+
},
|
|
1628
1717
|
{
|
|
1629
1718
|
name: "enrich_entity",
|
|
1630
1719
|
description: "Add or update an entity with smart assistance for generating metadata.",
|
|
@@ -1731,6 +1820,10 @@ async function getPrompt(name, args) {
|
|
|
1731
1820
|
return generateSetupProjectPrompt(args);
|
|
1732
1821
|
case "review_transcript":
|
|
1733
1822
|
return generateReviewTranscriptPrompt(args);
|
|
1823
|
+
case "identify_tasks_from_transcript":
|
|
1824
|
+
return generateIdentifyTasksFromTranscriptPrompt(args);
|
|
1825
|
+
case "summarize_transcript":
|
|
1826
|
+
return generateSummarizeTranscriptPrompt(args);
|
|
1734
1827
|
case "enrich_entity":
|
|
1735
1828
|
return generateEnrichEntityPrompt(args);
|
|
1736
1829
|
case "batch_transcription":
|
|
@@ -1852,6 +1945,75 @@ async function generateReviewTranscriptPrompt(args) {
|
|
|
1852
1945
|
}
|
|
1853
1946
|
];
|
|
1854
1947
|
}
|
|
1948
|
+
async function generateIdentifyTasksFromTranscriptPrompt(args) {
|
|
1949
|
+
const transcriptPath = args.transcriptPath;
|
|
1950
|
+
if (!transcriptPath) {
|
|
1951
|
+
throw new Error("transcriptPath is required");
|
|
1952
|
+
}
|
|
1953
|
+
const maxCandidates = args.maxCandidates?.trim() || "25";
|
|
1954
|
+
const includeTagSuggestions = args.includeTagSuggestions?.trim() || "true";
|
|
1955
|
+
const template = loadTemplate("identify_tasks_from_transcript");
|
|
1956
|
+
const content = fillTemplate(template, {
|
|
1957
|
+
transcriptPath,
|
|
1958
|
+
maxCandidates,
|
|
1959
|
+
includeTagSuggestions
|
|
1960
|
+
});
|
|
1961
|
+
return [
|
|
1962
|
+
{
|
|
1963
|
+
role: "user",
|
|
1964
|
+
content: {
|
|
1965
|
+
type: "text",
|
|
1966
|
+
text: content
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
];
|
|
1970
|
+
}
|
|
1971
|
+
async function generateSummarizeTranscriptPrompt(args) {
|
|
1972
|
+
const transcriptPath = args.transcriptPath;
|
|
1973
|
+
const audience = args.audience;
|
|
1974
|
+
if (!transcriptPath) {
|
|
1975
|
+
throw new Error("transcriptPath is required");
|
|
1976
|
+
}
|
|
1977
|
+
if (!audience) {
|
|
1978
|
+
throw new Error("audience is required");
|
|
1979
|
+
}
|
|
1980
|
+
const presetMap = {
|
|
1981
|
+
quick_bullets: {
|
|
1982
|
+
label: "Quick paragraph + bullet points",
|
|
1983
|
+
instructions: "Write one concise paragraph followed by 4-8 bullets covering decisions, actions, and risks."
|
|
1984
|
+
},
|
|
1985
|
+
detailed: {
|
|
1986
|
+
label: "Detailed summary",
|
|
1987
|
+
instructions: "Write a structured summary with context, key discussion points, decisions, open questions, and next steps."
|
|
1988
|
+
},
|
|
1989
|
+
attendee_facing: {
|
|
1990
|
+
label: "Attendee-facing summary",
|
|
1991
|
+
instructions: "Write a professional external-facing summary suitable for attendees; avoid private/internal reflections unless explicitly approved."
|
|
1992
|
+
}
|
|
1993
|
+
};
|
|
1994
|
+
const presetKey = (args.stylePreset || "detailed").trim();
|
|
1995
|
+
const selectedPreset = presetMap[presetKey] ?? presetMap.detailed;
|
|
1996
|
+
const summaryTitleLine = args.summaryTitle?.trim() ? `- Target title: "${args.summaryTitle.trim()}"` : "- Target title: (auto-generate from transcript title and date)";
|
|
1997
|
+
const guidanceBlock = args.guidance?.trim() ? args.guidance.trim() : "No extra guidance provided.";
|
|
1998
|
+
const template = loadTemplate("summarize_transcript");
|
|
1999
|
+
const content = fillTemplate(template, {
|
|
2000
|
+
transcriptPath,
|
|
2001
|
+
audience: audience.trim(),
|
|
2002
|
+
styleLabel: selectedPreset.label,
|
|
2003
|
+
styleInstructions: selectedPreset.instructions,
|
|
2004
|
+
summaryTitleLine,
|
|
2005
|
+
guidanceBlock
|
|
2006
|
+
});
|
|
2007
|
+
return [
|
|
2008
|
+
{
|
|
2009
|
+
role: "user",
|
|
2010
|
+
content: {
|
|
2011
|
+
type: "text",
|
|
2012
|
+
text: content
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
];
|
|
2016
|
+
}
|
|
1855
2017
|
async function generateEnrichEntityPrompt(args) {
|
|
1856
2018
|
const template = loadTemplate("enrich_entity");
|
|
1857
2019
|
const content = fillTemplate(template, args);
|
|
@@ -4026,6 +4188,125 @@ async function handleSuggestTermMetadata(args) {
|
|
|
4026
4188
|
}
|
|
4027
4189
|
|
|
4028
4190
|
const { ensurePklExtension, transcriptExists } = Transcript;
|
|
4191
|
+
function parseStoredSummaries(raw) {
|
|
4192
|
+
try {
|
|
4193
|
+
const parsed = JSON.parse(raw);
|
|
4194
|
+
if (!Array.isArray(parsed)) {
|
|
4195
|
+
return [];
|
|
4196
|
+
}
|
|
4197
|
+
return parsed.map((item) => {
|
|
4198
|
+
if (!item || typeof item !== "object") {
|
|
4199
|
+
return null;
|
|
4200
|
+
}
|
|
4201
|
+
const record = item;
|
|
4202
|
+
const id = String(record.id || "").trim();
|
|
4203
|
+
const content = String(record.content || "").trim();
|
|
4204
|
+
if (!id || !content) {
|
|
4205
|
+
return null;
|
|
4206
|
+
}
|
|
4207
|
+
return {
|
|
4208
|
+
id,
|
|
4209
|
+
title: String(record.title || "").trim(),
|
|
4210
|
+
audience: String(record.audience || "").trim(),
|
|
4211
|
+
guidance: String(record.guidance || "").trim(),
|
|
4212
|
+
stylePreset: String(record.stylePreset || "detailed").trim() || "detailed",
|
|
4213
|
+
styleLabel: String(record.styleLabel || "Detailed summary").trim() || "Detailed summary",
|
|
4214
|
+
content,
|
|
4215
|
+
generatedAt: String(record.generatedAt || "").trim() || (/* @__PURE__ */ new Date()).toISOString()
|
|
4216
|
+
};
|
|
4217
|
+
}).filter((summary) => summary !== null).sort((a, b) => b.generatedAt.localeCompare(a.generatedAt));
|
|
4218
|
+
} catch {
|
|
4219
|
+
return [];
|
|
4220
|
+
}
|
|
4221
|
+
}
|
|
4222
|
+
const SUMMARY_STYLE_PRESETS = {
|
|
4223
|
+
quick_bullets: {
|
|
4224
|
+
label: "Quick paragraph + bullet points",
|
|
4225
|
+
instructions: "Write one concise paragraph followed by 4-8 bullets covering decisions, actions, and risks."
|
|
4226
|
+
},
|
|
4227
|
+
detailed: {
|
|
4228
|
+
label: "Detailed summary",
|
|
4229
|
+
instructions: "Write a structured summary with context, key discussion points, decisions, open questions, and next steps."
|
|
4230
|
+
},
|
|
4231
|
+
attendee_facing: {
|
|
4232
|
+
label: "Attendee-facing summary",
|
|
4233
|
+
instructions: "Write a professional external-facing summary suitable for attendees; avoid private/internal reflections unless explicitly approved."
|
|
4234
|
+
}
|
|
4235
|
+
};
|
|
4236
|
+
function splitIntoCandidateSentences(content) {
|
|
4237
|
+
return content.split(/\n+|(?<=[.!?])\s+/g).map((sentence) => sentence.trim()).filter((sentence) => sentence.length >= 8);
|
|
4238
|
+
}
|
|
4239
|
+
function normalizeTaskText(sentence) {
|
|
4240
|
+
const normalized = sentence.replace(/^(?:i need to|we need to|i should|we should|let'?s|remember to|todo:|action item:)\s+/i, "").replace(/\s+/g, " ").trim();
|
|
4241
|
+
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
4242
|
+
}
|
|
4243
|
+
function inferDueDate(sentence) {
|
|
4244
|
+
const lower = sentence.toLowerCase();
|
|
4245
|
+
if (lower.includes("today")) {
|
|
4246
|
+
return "today";
|
|
4247
|
+
}
|
|
4248
|
+
if (lower.includes("tomorrow")) {
|
|
4249
|
+
return "tomorrow";
|
|
4250
|
+
}
|
|
4251
|
+
if (lower.includes("next week")) {
|
|
4252
|
+
return "next week";
|
|
4253
|
+
}
|
|
4254
|
+
if (lower.includes("this week")) {
|
|
4255
|
+
return "this week";
|
|
4256
|
+
}
|
|
4257
|
+
if (lower.includes("by friday")) {
|
|
4258
|
+
return "by friday";
|
|
4259
|
+
}
|
|
4260
|
+
return null;
|
|
4261
|
+
}
|
|
4262
|
+
function extractHashtagTags(sentence) {
|
|
4263
|
+
const matches = sentence.match(/#[a-z0-9_-]+/gi) || [];
|
|
4264
|
+
return Array.from(new Set(matches.map((tag) => tag.slice(1).toLowerCase())));
|
|
4265
|
+
}
|
|
4266
|
+
function toConfidenceBucket(score) {
|
|
4267
|
+
if (score >= 0.75) {
|
|
4268
|
+
return "high";
|
|
4269
|
+
}
|
|
4270
|
+
if (score >= 0.5) {
|
|
4271
|
+
return "medium";
|
|
4272
|
+
}
|
|
4273
|
+
return "low";
|
|
4274
|
+
}
|
|
4275
|
+
function scoreTaskCandidate(sentence) {
|
|
4276
|
+
const explicitActionPattern = /\b(i need to|we need to|i should|we should|let'?s|remember to|todo|action item|i will|i'll|must)\b/i;
|
|
4277
|
+
const inferredIntentPattern = /\b(follow up|check|review|investigate|confirm|decide|plan|schedule|reach out|send|draft|prepare|update|fix|create|write|call|email|look into|figure out)\b/i;
|
|
4278
|
+
let score = 0;
|
|
4279
|
+
const rationaleParts = [];
|
|
4280
|
+
if (explicitActionPattern.test(sentence)) {
|
|
4281
|
+
score += 0.55;
|
|
4282
|
+
rationaleParts.push("explicit action language");
|
|
4283
|
+
}
|
|
4284
|
+
if (inferredIntentPattern.test(sentence)) {
|
|
4285
|
+
score += 0.35;
|
|
4286
|
+
rationaleParts.push("inferred follow-up intent");
|
|
4287
|
+
}
|
|
4288
|
+
if (inferDueDate(sentence)) {
|
|
4289
|
+
score += 0.1;
|
|
4290
|
+
rationaleParts.push("time cue detected");
|
|
4291
|
+
}
|
|
4292
|
+
if (score < 0.3) {
|
|
4293
|
+
return null;
|
|
4294
|
+
}
|
|
4295
|
+
return {
|
|
4296
|
+
score: Math.min(1, Number(score.toFixed(2))),
|
|
4297
|
+
rationale: rationaleParts.join("; ")
|
|
4298
|
+
};
|
|
4299
|
+
}
|
|
4300
|
+
function getSuggestedEntities(entities) {
|
|
4301
|
+
if (!entities) {
|
|
4302
|
+
return [];
|
|
4303
|
+
}
|
|
4304
|
+
const people = (entities.people || []).map((entity) => ({ ...entity, type: "person" }));
|
|
4305
|
+
const projects = (entities.projects || []).map((entity) => ({ ...entity, type: "project" }));
|
|
4306
|
+
const terms = (entities.terms || []).map((entity) => ({ ...entity, type: "term" }));
|
|
4307
|
+
const companies = (entities.companies || []).map((entity) => ({ ...entity, type: "company" }));
|
|
4308
|
+
return [...people, ...projects, ...terms, ...companies];
|
|
4309
|
+
}
|
|
4029
4310
|
const readTranscriptTool = {
|
|
4030
4311
|
name: "protokoll_read_transcript",
|
|
4031
4312
|
description: "Read a transcript file and parse its metadata and content. Path is relative to the configured output directory. Returns structured data including title, metadata, routing info, and content.",
|
|
@@ -4099,6 +4380,34 @@ const listTranscriptsTool = {
|
|
|
4099
4380
|
required: []
|
|
4100
4381
|
}
|
|
4101
4382
|
};
|
|
4383
|
+
const identifyTasksFromTranscriptTool = {
|
|
4384
|
+
name: "protokoll_identify_tasks_from_transcript",
|
|
4385
|
+
description: "Identify task candidates from transcript or note content without creating tasks. Returns structured candidates with confidence buckets, rationale, and metadata suggestions so users can review and choose which tasks to create.",
|
|
4386
|
+
inputSchema: {
|
|
4387
|
+
type: "object",
|
|
4388
|
+
properties: {
|
|
4389
|
+
transcriptPath: {
|
|
4390
|
+
type: "string",
|
|
4391
|
+
description: 'Transcript URI (preferred) or relative path from output directory. URI format: "protokoll://transcript/2026/2/12-1606-meeting" (no file extension). Path format: "2026/2/12-1606-meeting" or "2026/2/12-1606-meeting.pkl"'
|
|
4392
|
+
},
|
|
4393
|
+
maxCandidates: {
|
|
4394
|
+
type: "number",
|
|
4395
|
+
description: "Maximum number of candidates to return (default: 25, max: 50)",
|
|
4396
|
+
default: 25
|
|
4397
|
+
},
|
|
4398
|
+
includeTagSuggestions: {
|
|
4399
|
+
type: "boolean",
|
|
4400
|
+
description: "Whether to include suggested tags based on transcript metadata and hashtags (default: true)",
|
|
4401
|
+
default: true
|
|
4402
|
+
},
|
|
4403
|
+
contextDirectory: {
|
|
4404
|
+
type: "string",
|
|
4405
|
+
description: "Optional: Path to the .protokoll context directory"
|
|
4406
|
+
}
|
|
4407
|
+
},
|
|
4408
|
+
required: ["transcriptPath"]
|
|
4409
|
+
}
|
|
4410
|
+
};
|
|
4102
4411
|
const editTranscriptTool = {
|
|
4103
4412
|
name: "protokoll_edit_transcript",
|
|
4104
4413
|
description: "Edit an existing transcript's title, project assignment, tags, and/or status. Path is relative to the configured output directory. IMPORTANT: When you change the title, this tool RENAMES THE FILE to match the new title (slugified). Always use this tool instead of directly editing transcript files when changing titles. Changing the project will update metadata and may move the file to a new location based on the project's routing configuration.",
|
|
@@ -4140,6 +4449,68 @@ const editTranscriptTool = {
|
|
|
4140
4449
|
required: ["transcriptPath"]
|
|
4141
4450
|
}
|
|
4142
4451
|
};
|
|
4452
|
+
const summarizeTranscriptTool = {
|
|
4453
|
+
name: "protokoll_summarize_transcript",
|
|
4454
|
+
description: "Generate an audience-aware summary for a transcript using privacy/sensitivity guardrails. Returns markdown summary text and does not modify transcript content.",
|
|
4455
|
+
inputSchema: {
|
|
4456
|
+
type: "object",
|
|
4457
|
+
properties: {
|
|
4458
|
+
transcriptPath: {
|
|
4459
|
+
type: "string",
|
|
4460
|
+
description: 'Transcript URI (preferred) or relative path from output directory. URI format: "protokoll://transcript/2026/2/12-1606-meeting" (no file extension). Path format: "2026/2/12-1606-meeting" or "2026/2/12-1606-meeting.pkl"'
|
|
4461
|
+
},
|
|
4462
|
+
audience: {
|
|
4463
|
+
type: "string",
|
|
4464
|
+
description: "Optional audience label (e.g. internal team, project attendees, external partner)"
|
|
4465
|
+
},
|
|
4466
|
+
stylePreset: {
|
|
4467
|
+
type: "string",
|
|
4468
|
+
enum: ["quick_bullets", "detailed", "attendee_facing"],
|
|
4469
|
+
description: "Summary style preset (default: detailed)",
|
|
4470
|
+
default: "detailed"
|
|
4471
|
+
},
|
|
4472
|
+
guidance: {
|
|
4473
|
+
type: "string",
|
|
4474
|
+
description: "Optional extra instructions, especially for privacy/sensitivity constraints"
|
|
4475
|
+
},
|
|
4476
|
+
summaryTitle: {
|
|
4477
|
+
type: "string",
|
|
4478
|
+
description: "Optional title to use in the generated summary"
|
|
4479
|
+
},
|
|
4480
|
+
model: {
|
|
4481
|
+
type: "string",
|
|
4482
|
+
description: `LLM model for summary generation (default: ${DEFAULT_MODEL})`
|
|
4483
|
+
},
|
|
4484
|
+
contextDirectory: {
|
|
4485
|
+
type: "string",
|
|
4486
|
+
description: "Optional: Path to the .protokoll context directory"
|
|
4487
|
+
}
|
|
4488
|
+
},
|
|
4489
|
+
required: ["transcriptPath"]
|
|
4490
|
+
}
|
|
4491
|
+
};
|
|
4492
|
+
const deleteTranscriptSummaryTool = {
|
|
4493
|
+
name: "protokoll_delete_transcript_summary",
|
|
4494
|
+
description: "Delete a previously generated summary from transcript artifact storage by summary ID. Path is relative to the configured output directory.",
|
|
4495
|
+
inputSchema: {
|
|
4496
|
+
type: "object",
|
|
4497
|
+
properties: {
|
|
4498
|
+
transcriptPath: {
|
|
4499
|
+
type: "string",
|
|
4500
|
+
description: 'Transcript URI (preferred) or relative path from output directory. URI format: "protokoll://transcript/2026/2/12-1606-meeting" (no file extension). Path format: "2026/2/12-1606-meeting" or "2026/2/12-1606-meeting.pkl"'
|
|
4501
|
+
},
|
|
4502
|
+
summaryId: {
|
|
4503
|
+
type: "string",
|
|
4504
|
+
description: 'Summary ID to remove (for example: "summary-174..." )'
|
|
4505
|
+
},
|
|
4506
|
+
contextDirectory: {
|
|
4507
|
+
type: "string",
|
|
4508
|
+
description: "Optional: Path to the .protokoll context directory"
|
|
4509
|
+
}
|
|
4510
|
+
},
|
|
4511
|
+
required: ["transcriptPath", "summaryId"]
|
|
4512
|
+
}
|
|
4513
|
+
};
|
|
4143
4514
|
const changeTranscriptDateTool = {
|
|
4144
4515
|
name: "protokoll_change_transcript_date",
|
|
4145
4516
|
description: "Change the date of an existing transcript. This will move the transcript file to a new location based on the new date and the project's routing configuration. The file will be moved to the appropriate YYYY/MM/ directory structure. Path is relative to the configured output directory. WARNING: This may remove the transcript from the current view if it moves to a different date folder.",
|
|
@@ -4215,6 +4586,32 @@ const provideFeedbackTool = {
|
|
|
4215
4586
|
required: ["transcriptPath", "feedback"]
|
|
4216
4587
|
}
|
|
4217
4588
|
};
|
|
4589
|
+
const enhanceTranscriptTool = {
|
|
4590
|
+
name: "protokoll_enhance_transcript",
|
|
4591
|
+
description: "Enhance an existing transcript using the same post-transcription pipeline flow (simple-replace + agentic tool-based enhancement) used after Whisper completes. Reads from originalText when provided, otherwise uses raw transcript text if available, falling back to current transcript content. Writes enhanced content and updates metadata/status.",
|
|
4592
|
+
inputSchema: {
|
|
4593
|
+
type: "object",
|
|
4594
|
+
properties: {
|
|
4595
|
+
transcriptPath: {
|
|
4596
|
+
type: "string",
|
|
4597
|
+
description: 'Transcript URI (preferred) or relative path from output directory. URI format: "protokoll://transcript/2026/2/12-1606-meeting" (no file extension). Path format: "2026/2/12-1606-meeting" or "2026/2/12-1606-meeting.pkl"'
|
|
4598
|
+
},
|
|
4599
|
+
originalText: {
|
|
4600
|
+
type: "string",
|
|
4601
|
+
description: "Optional explicit source text to enhance (usually the Original tab text). If omitted, tool uses raw transcript text when present, else current content."
|
|
4602
|
+
},
|
|
4603
|
+
model: {
|
|
4604
|
+
type: "string",
|
|
4605
|
+
description: `LLM model for enhancement (default: ${DEFAULT_MODEL})`
|
|
4606
|
+
},
|
|
4607
|
+
contextDirectory: {
|
|
4608
|
+
type: "string",
|
|
4609
|
+
description: "Optional: Path to the .protokoll context directory"
|
|
4610
|
+
}
|
|
4611
|
+
},
|
|
4612
|
+
required: ["transcriptPath"]
|
|
4613
|
+
}
|
|
4614
|
+
};
|
|
4218
4615
|
const updateTranscriptContentTool = {
|
|
4219
4616
|
name: "protokoll_update_transcript_content",
|
|
4220
4617
|
description: "Update the content section of a transcript file while preserving all metadata. Path is relative to the configured output directory. This tool replaces only the content between the --- delimiters, keeping all metadata intact. IMPORTANT: The content parameter should contain ONLY the transcript body text (the text after the --- delimiter), NOT the full transcript file with headers and metadata. If the full transcript is provided, the tool will automatically extract only the content section to prevent duplication.",
|
|
@@ -4419,11 +4816,42 @@ const correctToEntityTool = {
|
|
|
4419
4816
|
required: ["transcriptPath", "selectedText", "entityType"]
|
|
4420
4817
|
}
|
|
4421
4818
|
};
|
|
4819
|
+
const rejectCorrectionTool = {
|
|
4820
|
+
name: "protokoll_reject_correction",
|
|
4821
|
+
description: "Reject a previously applied enhancement correction and undo its text replacement in the transcript. Also logs the rejection in enhancement_log for auditability.",
|
|
4822
|
+
inputSchema: {
|
|
4823
|
+
type: "object",
|
|
4824
|
+
properties: {
|
|
4825
|
+
transcriptPath: {
|
|
4826
|
+
type: "string",
|
|
4827
|
+
description: 'Transcript URI (preferred) or relative path from output directory. URI format: "protokoll://transcript/2026/2/12-1606-meeting" (no file extension). Path format: "2026/2/12-1606-meeting" or "2026/2/12-1606-meeting.pkl"'
|
|
4828
|
+
},
|
|
4829
|
+
correctionEntryId: {
|
|
4830
|
+
type: "number",
|
|
4831
|
+
description: "Enhancement log entry id for the correction_applied event to reject"
|
|
4832
|
+
},
|
|
4833
|
+
contextDirectory: {
|
|
4834
|
+
type: "string",
|
|
4835
|
+
description: "Optional: Path to the .protokoll context directory"
|
|
4836
|
+
}
|
|
4837
|
+
},
|
|
4838
|
+
required: ["transcriptPath", "correctionEntryId"]
|
|
4839
|
+
}
|
|
4840
|
+
};
|
|
4422
4841
|
async function handleReadTranscript(args) {
|
|
4423
4842
|
const absolutePath = await resolveTranscriptPath$1(args.transcriptPath, args.contextDirectory);
|
|
4424
4843
|
const transcriptData = await readTranscript(absolutePath);
|
|
4425
4844
|
const outputDirectory = await getConfiguredDirectory("outputDirectory", args.contextDirectory);
|
|
4426
4845
|
const relativePath = await sanitizePath(absolutePath, outputDirectory);
|
|
4846
|
+
const transcriptHandle = PklTranscript.open(absolutePath, { readOnly: true });
|
|
4847
|
+
let summaries = [];
|
|
4848
|
+
try {
|
|
4849
|
+
const historyArtifact = transcriptHandle.getArtifact("summary_history");
|
|
4850
|
+
const rawHistory = historyArtifact?.data?.toString("utf8") || "[]";
|
|
4851
|
+
summaries = parseStoredSummaries(rawHistory);
|
|
4852
|
+
} finally {
|
|
4853
|
+
transcriptHandle.close();
|
|
4854
|
+
}
|
|
4427
4855
|
return {
|
|
4428
4856
|
filePath: relativePath,
|
|
4429
4857
|
title: transcriptData.metadata.title || "",
|
|
@@ -4443,7 +4871,8 @@ async function handleReadTranscript(args) {
|
|
|
4443
4871
|
},
|
|
4444
4872
|
content: transcriptData.content,
|
|
4445
4873
|
hasRawTranscript: transcriptData.hasRawTranscript,
|
|
4446
|
-
contentLength: transcriptData.content.length
|
|
4874
|
+
contentLength: transcriptData.content.length,
|
|
4875
|
+
summaries
|
|
4447
4876
|
};
|
|
4448
4877
|
}
|
|
4449
4878
|
async function handleListTranscripts(args) {
|
|
@@ -4496,6 +4925,236 @@ async function handleListTranscripts(args) {
|
|
|
4496
4925
|
}
|
|
4497
4926
|
};
|
|
4498
4927
|
}
|
|
4928
|
+
async function handleIdentifyTasksFromTranscript(args) {
|
|
4929
|
+
const absolutePath = await resolveTranscriptPath$1(args.transcriptPath, args.contextDirectory);
|
|
4930
|
+
const transcriptData = await readTranscript(absolutePath);
|
|
4931
|
+
const content = transcriptData.content?.trim() || "";
|
|
4932
|
+
if (!content) {
|
|
4933
|
+
return {
|
|
4934
|
+
transcriptPath: args.transcriptPath,
|
|
4935
|
+
candidates: [],
|
|
4936
|
+
totalCandidates: 0,
|
|
4937
|
+
message: "Transcript content is empty; no task candidates identified."
|
|
4938
|
+
};
|
|
4939
|
+
}
|
|
4940
|
+
const limit = Math.max(1, Math.min(50, args.maxCandidates ?? 25));
|
|
4941
|
+
const includeTagSuggestions = args.includeTagSuggestions !== false;
|
|
4942
|
+
const existingTags = transcriptData.metadata.tags || [];
|
|
4943
|
+
const suggestedEntities = getSuggestedEntities(transcriptData.metadata.entities);
|
|
4944
|
+
const suggestedProject = {
|
|
4945
|
+
id: transcriptData.metadata.projectId || null,
|
|
4946
|
+
name: transcriptData.metadata.project || null
|
|
4947
|
+
};
|
|
4948
|
+
const candidates = splitIntoCandidateSentences(content).map((sentence, index) => {
|
|
4949
|
+
const scored = scoreTaskCandidate(sentence);
|
|
4950
|
+
if (!scored) {
|
|
4951
|
+
return null;
|
|
4952
|
+
}
|
|
4953
|
+
const sentenceTags = includeTagSuggestions ? extractHashtagTags(sentence) : [];
|
|
4954
|
+
const mergedTags = includeTagSuggestions ? Array.from(/* @__PURE__ */ new Set([...existingTags.map((tag) => tag.toLowerCase()), ...sentenceTags])) : [];
|
|
4955
|
+
const candidate = {
|
|
4956
|
+
id: `candidate-${index + 1}`,
|
|
4957
|
+
taskText: normalizeTaskText(sentence),
|
|
4958
|
+
confidence: scored.score,
|
|
4959
|
+
confidenceBucket: toConfidenceBucket(scored.score),
|
|
4960
|
+
rationale: scored.rationale,
|
|
4961
|
+
sourceExcerpt: sentence,
|
|
4962
|
+
suggestedDueDate: inferDueDate(sentence),
|
|
4963
|
+
suggestedProject,
|
|
4964
|
+
suggestedEntities,
|
|
4965
|
+
suggestedTags: mergedTags
|
|
4966
|
+
};
|
|
4967
|
+
return candidate;
|
|
4968
|
+
}).filter((candidate) => candidate !== null).sort((a, b) => b.confidence - a.confidence).slice(0, limit);
|
|
4969
|
+
return {
|
|
4970
|
+
transcriptPath: args.transcriptPath,
|
|
4971
|
+
candidates,
|
|
4972
|
+
totalCandidates: candidates.length,
|
|
4973
|
+
message: candidates.length > 0 ? `Identified ${candidates.length} task candidate(s).` : "No likely task candidates found in transcript content."
|
|
4974
|
+
};
|
|
4975
|
+
}
|
|
4976
|
+
async function handleSummarizeTranscript(args) {
|
|
4977
|
+
const startedAt = Date.now();
|
|
4978
|
+
const logSummary = (message, data) => {
|
|
4979
|
+
process.stdout.write(`Protokoll: [SUMMARY] ${message} ${JSON.stringify(data)}
|
|
4980
|
+
`);
|
|
4981
|
+
};
|
|
4982
|
+
logSummary("Tool call received", {
|
|
4983
|
+
transcriptPath: args.transcriptPath,
|
|
4984
|
+
audience: args.audience,
|
|
4985
|
+
stylePreset: args.stylePreset || "detailed",
|
|
4986
|
+
hasGuidance: !!args.guidance?.trim(),
|
|
4987
|
+
hasSummaryTitle: !!args.summaryTitle?.trim(),
|
|
4988
|
+
model: args.model || DEFAULT_MODEL
|
|
4989
|
+
});
|
|
4990
|
+
const absolutePath = await resolveTranscriptPath$1(args.transcriptPath, args.contextDirectory);
|
|
4991
|
+
logSummary("Resolved transcript path", {
|
|
4992
|
+
transcriptPath: args.transcriptPath,
|
|
4993
|
+
absolutePath
|
|
4994
|
+
});
|
|
4995
|
+
const transcriptData = await readTranscript(absolutePath);
|
|
4996
|
+
const transcriptContent = (transcriptData.content || "").trim();
|
|
4997
|
+
if (!transcriptContent) {
|
|
4998
|
+
logSummary("Transcript content empty, cannot summarize", {
|
|
4999
|
+
transcriptPath: args.transcriptPath,
|
|
5000
|
+
absolutePath
|
|
5001
|
+
});
|
|
5002
|
+
throw new Error("Transcript content is empty; cannot generate summary.");
|
|
5003
|
+
}
|
|
5004
|
+
const audience = (args.audience || "").trim() || "General audience";
|
|
5005
|
+
const stylePreset = (args.stylePreset || "detailed").trim();
|
|
5006
|
+
const selectedStyle = SUMMARY_STYLE_PRESETS[stylePreset] || SUMMARY_STYLE_PRESETS.detailed;
|
|
5007
|
+
const guidance = (args.guidance || "").trim();
|
|
5008
|
+
const model = args.model || DEFAULT_MODEL;
|
|
5009
|
+
const transcriptTitle = transcriptData.metadata.title || "Untitled transcript";
|
|
5010
|
+
const transcriptDate = transcriptData.metadata.date instanceof Date ? transcriptData.metadata.date.toISOString().slice(0, 10) : "unknown date";
|
|
5011
|
+
const preferredTitle = (args.summaryTitle || "").trim();
|
|
5012
|
+
const boundedContent = transcriptContent.length > MAX_CONTENT_LENGTH ? `${transcriptContent.slice(0, MAX_CONTENT_LENGTH)}
|
|
5013
|
+
|
|
5014
|
+
[...transcript truncated for summarization input length...]` : transcriptContent;
|
|
5015
|
+
const truncated = transcriptContent.length > MAX_CONTENT_LENGTH;
|
|
5016
|
+
logSummary("Prepared summary input", {
|
|
5017
|
+
transcriptTitle,
|
|
5018
|
+
transcriptDate,
|
|
5019
|
+
transcriptLength: transcriptContent.length,
|
|
5020
|
+
boundedLength: boundedContent.length,
|
|
5021
|
+
truncated,
|
|
5022
|
+
stylePreset
|
|
5023
|
+
});
|
|
5024
|
+
const reasoning = Reasoning.create({
|
|
5025
|
+
model,
|
|
5026
|
+
reasoningLevel: "medium"
|
|
5027
|
+
});
|
|
5028
|
+
const prompt = [
|
|
5029
|
+
"Create an audience-aware summary for the transcript below.",
|
|
5030
|
+
"",
|
|
5031
|
+
`Transcript title: ${transcriptTitle}`,
|
|
5032
|
+
`Transcript date: ${transcriptDate}`,
|
|
5033
|
+
`Audience: ${audience}`,
|
|
5034
|
+
`Style preset: ${selectedStyle.label}`,
|
|
5035
|
+
preferredTitle ? `Preferred summary title: ${preferredTitle}` : "Preferred summary title: (generate one)",
|
|
5036
|
+
"",
|
|
5037
|
+
"Style instructions:",
|
|
5038
|
+
selectedStyle.instructions,
|
|
5039
|
+
"",
|
|
5040
|
+
"Privacy and sensitivity guardrails:",
|
|
5041
|
+
"- Treat transcript content as potentially sensitive by default.",
|
|
5042
|
+
"- Exclude private internal reflections, personal judgments, or sensitive notes not appropriate for the audience.",
|
|
5043
|
+
"- If unsure whether a detail is audience-appropriate, exclude it or generalize safely.",
|
|
5044
|
+
"- Prefer factual and neutral language over speculative interpretation.",
|
|
5045
|
+
"",
|
|
5046
|
+
"Additional guidance:",
|
|
5047
|
+
guidance || "No extra guidance provided.",
|
|
5048
|
+
"",
|
|
5049
|
+
"Required output shape:",
|
|
5050
|
+
"1) Title",
|
|
5051
|
+
"2) Summary body matching the selected style preset",
|
|
5052
|
+
'3) Optional "Redactions / Exclusions" section listing what was intentionally omitted for audience safety',
|
|
5053
|
+
"",
|
|
5054
|
+
"Transcript:",
|
|
5055
|
+
boundedContent
|
|
5056
|
+
].join("\n");
|
|
5057
|
+
const result = await reasoning.complete({
|
|
5058
|
+
prompt,
|
|
5059
|
+
systemPrompt: "You are an expert meeting summarizer. Return markdown only."
|
|
5060
|
+
});
|
|
5061
|
+
logSummary("Reasoning call completed", {
|
|
5062
|
+
transcriptPath: args.transcriptPath,
|
|
5063
|
+
model: result.model || model,
|
|
5064
|
+
durationMs: result.duration ?? null,
|
|
5065
|
+
finishReason: result.finishReason ?? null
|
|
5066
|
+
});
|
|
5067
|
+
const summary = (result.content || "").trim();
|
|
5068
|
+
if (!summary) {
|
|
5069
|
+
logSummary("Empty summary response", {
|
|
5070
|
+
transcriptPath: args.transcriptPath,
|
|
5071
|
+
model: result.model || model
|
|
5072
|
+
});
|
|
5073
|
+
throw new Error("No summary text generated.");
|
|
5074
|
+
}
|
|
5075
|
+
logSummary("Summary generated successfully", {
|
|
5076
|
+
transcriptPath: args.transcriptPath,
|
|
5077
|
+
summaryLength: summary.length,
|
|
5078
|
+
elapsedMs: Date.now() - startedAt
|
|
5079
|
+
});
|
|
5080
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5081
|
+
const summaryId = `summary-${Date.now()}-${randomUUID$1().slice(0, 8)}`;
|
|
5082
|
+
const stylePresetKey = SUMMARY_STYLE_PRESETS[stylePreset] ? stylePreset : "detailed";
|
|
5083
|
+
const storedSummary = {
|
|
5084
|
+
id: summaryId,
|
|
5085
|
+
title: preferredTitle || `${transcriptTitle} Summary`,
|
|
5086
|
+
audience,
|
|
5087
|
+
guidance,
|
|
5088
|
+
stylePreset: stylePresetKey,
|
|
5089
|
+
styleLabel: selectedStyle.label,
|
|
5090
|
+
content: summary,
|
|
5091
|
+
generatedAt
|
|
5092
|
+
};
|
|
5093
|
+
const transcriptHandle = PklTranscript.open(absolutePath, { readOnly: false });
|
|
5094
|
+
try {
|
|
5095
|
+
const existingHistory = transcriptHandle.getArtifact("summary_history");
|
|
5096
|
+
const existingSummaries = parseStoredSummaries(existingHistory?.data?.toString("utf8") || "[]");
|
|
5097
|
+
const nextSummaries = [storedSummary, ...existingSummaries.filter((entry) => entry.id !== summaryId)];
|
|
5098
|
+
transcriptHandle.addArtifact(
|
|
5099
|
+
"summary_history",
|
|
5100
|
+
Buffer.from(JSON.stringify(nextSummaries), "utf8"),
|
|
5101
|
+
{
|
|
5102
|
+
version: 1,
|
|
5103
|
+
count: nextSummaries.length,
|
|
5104
|
+
updatedAt: generatedAt,
|
|
5105
|
+
model: result.model || model
|
|
5106
|
+
}
|
|
5107
|
+
);
|
|
5108
|
+
} finally {
|
|
5109
|
+
transcriptHandle.close();
|
|
5110
|
+
}
|
|
5111
|
+
logSummary("Summary persisted to transcript artifact storage", {
|
|
5112
|
+
transcriptPath: args.transcriptPath,
|
|
5113
|
+
summaryId,
|
|
5114
|
+
generatedAt
|
|
5115
|
+
});
|
|
5116
|
+
return {
|
|
5117
|
+
summary,
|
|
5118
|
+
audience,
|
|
5119
|
+
stylePreset: stylePresetKey,
|
|
5120
|
+
model: result.model || model,
|
|
5121
|
+
summaryId,
|
|
5122
|
+
generatedAt
|
|
5123
|
+
};
|
|
5124
|
+
}
|
|
5125
|
+
async function handleDeleteTranscriptSummary(args) {
|
|
5126
|
+
const summaryId = (args.summaryId || "").trim();
|
|
5127
|
+
if (!summaryId) {
|
|
5128
|
+
throw new Error("summaryId is required");
|
|
5129
|
+
}
|
|
5130
|
+
const absolutePath = await resolveTranscriptPath$1(args.transcriptPath, args.contextDirectory);
|
|
5131
|
+
const transcriptHandle = PklTranscript.open(absolutePath, { readOnly: false });
|
|
5132
|
+
try {
|
|
5133
|
+
const historyArtifact = transcriptHandle.getArtifact("summary_history");
|
|
5134
|
+
const existingSummaries = parseStoredSummaries(historyArtifact?.data?.toString("utf8") || "[]");
|
|
5135
|
+
const remainingSummaries = existingSummaries.filter((entry) => entry.id !== summaryId);
|
|
5136
|
+
if (remainingSummaries.length === existingSummaries.length) {
|
|
5137
|
+
throw new Error(`Summary not found: ${summaryId}`);
|
|
5138
|
+
}
|
|
5139
|
+
transcriptHandle.addArtifact(
|
|
5140
|
+
"summary_history",
|
|
5141
|
+
Buffer.from(JSON.stringify(remainingSummaries), "utf8"),
|
|
5142
|
+
{
|
|
5143
|
+
version: 1,
|
|
5144
|
+
count: remainingSummaries.length,
|
|
5145
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5146
|
+
deletedSummaryId: summaryId
|
|
5147
|
+
}
|
|
5148
|
+
);
|
|
5149
|
+
return {
|
|
5150
|
+
success: true,
|
|
5151
|
+
summaryId,
|
|
5152
|
+
remaining: remainingSummaries.length
|
|
5153
|
+
};
|
|
5154
|
+
} finally {
|
|
5155
|
+
transcriptHandle.close();
|
|
5156
|
+
}
|
|
5157
|
+
}
|
|
4499
5158
|
async function handleEditTranscript(args) {
|
|
4500
5159
|
await validateNotRemoteMode(args.contextDirectory);
|
|
4501
5160
|
const outputDirectory = await getConfiguredDirectory("outputDirectory", args.contextDirectory);
|
|
@@ -4821,6 +5480,220 @@ async function handleProvideFeedback(args) {
|
|
|
4821
5480
|
transcript.close();
|
|
4822
5481
|
}
|
|
4823
5482
|
}
|
|
5483
|
+
async function handleEnhanceTranscript(args) {
|
|
5484
|
+
const absolutePath = await resolveTranscriptPath$1(args.transcriptPath, args.contextDirectory);
|
|
5485
|
+
const pklPath = ensurePklExtension(absolutePath);
|
|
5486
|
+
const transcript = PklTranscript.open(pklPath, { readOnly: false });
|
|
5487
|
+
let tempDir = null;
|
|
5488
|
+
try {
|
|
5489
|
+
const explicitOriginal = (args.originalText || "").trim();
|
|
5490
|
+
const rawOriginal = (transcript.rawTranscript?.text || "").trim();
|
|
5491
|
+
const currentContent = (transcript.content || "").trim();
|
|
5492
|
+
const sourceText = explicitOriginal || rawOriginal || currentContent;
|
|
5493
|
+
if (!sourceText) {
|
|
5494
|
+
throw new Error("No source text available to enhance. Save or provide Original content first.");
|
|
5495
|
+
}
|
|
5496
|
+
const model = args.model || DEFAULT_MODEL;
|
|
5497
|
+
const startedAt = Date.now();
|
|
5498
|
+
let toolCallCount = 0;
|
|
5499
|
+
const contextDirectories = await getContextDirectories();
|
|
5500
|
+
const context = await create({
|
|
5501
|
+
startingDir: args.contextDirectory || dirname(pklPath),
|
|
5502
|
+
contextDirectories
|
|
5503
|
+
});
|
|
5504
|
+
const outputDirectory = await getConfiguredDirectory("outputDirectory", args.contextDirectory);
|
|
5505
|
+
const defaultStructure = "month";
|
|
5506
|
+
const defaultFilenameOptions = ["date", "time", "subject"];
|
|
5507
|
+
const routingProjects = context.getAllProjects().filter((project) => project.active !== false).map((project) => ({
|
|
5508
|
+
projectId: project.id,
|
|
5509
|
+
destination: {
|
|
5510
|
+
path: project.routing?.destination || outputDirectory,
|
|
5511
|
+
structure: project.routing?.structure || defaultStructure,
|
|
5512
|
+
filename_options: project.routing?.filename_options || [...defaultFilenameOptions],
|
|
5513
|
+
createDirectories: true
|
|
5514
|
+
},
|
|
5515
|
+
classification: project.classification,
|
|
5516
|
+
active: project.active,
|
|
5517
|
+
auto_tags: project.routing?.auto_tags
|
|
5518
|
+
}));
|
|
5519
|
+
const routing = Routing.create({
|
|
5520
|
+
default: {
|
|
5521
|
+
path: outputDirectory,
|
|
5522
|
+
structure: defaultStructure,
|
|
5523
|
+
filename_options: [...defaultFilenameOptions],
|
|
5524
|
+
createDirectories: true
|
|
5525
|
+
},
|
|
5526
|
+
projects: routingProjects,
|
|
5527
|
+
conflict_resolution: "primary"
|
|
5528
|
+
}, context);
|
|
5529
|
+
const fallbackDate = transcript.metadata.date instanceof Date ? transcript.metadata.date : /* @__PURE__ */ new Date();
|
|
5530
|
+
const fallbackHash = transcript.metadata.audioHash || transcript.metadata.id || "";
|
|
5531
|
+
const routingContext = {
|
|
5532
|
+
transcriptText: sourceText,
|
|
5533
|
+
audioDate: fallbackDate,
|
|
5534
|
+
sourceFile: pklPath,
|
|
5535
|
+
hash: fallbackHash
|
|
5536
|
+
};
|
|
5537
|
+
const routeResult = routing.route(routingContext);
|
|
5538
|
+
const projectForReplace = routeResult.projectId || transcript.metadata.projectId || transcript.metadata.project;
|
|
5539
|
+
transcript.enhancementLog.logStep(/* @__PURE__ */ new Date(), "enhance", "enhancement_start", {
|
|
5540
|
+
model,
|
|
5541
|
+
transcriptPath: args.transcriptPath,
|
|
5542
|
+
hasExplicitOriginal: explicitOriginal.length > 0,
|
|
5543
|
+
source: explicitOriginal ? "explicit_original_text" : rawOriginal ? "raw_transcript" : "enhanced_content_fallback",
|
|
5544
|
+
routedProject: routeResult.projectId || null,
|
|
5545
|
+
routedConfidence: routeResult.confidence
|
|
5546
|
+
});
|
|
5547
|
+
tempDir = await mkdtemp(resolve(tmpdir(), "protokoll-enhance-"));
|
|
5548
|
+
const simpleReplace = Phases.createSimpleReplacePhase({ debug: false }, context);
|
|
5549
|
+
const simpleReplaceResult = await simpleReplace.replace(
|
|
5550
|
+
sourceText,
|
|
5551
|
+
{
|
|
5552
|
+
project: projectForReplace || void 0,
|
|
5553
|
+
confidence: routeResult.confidence
|
|
5554
|
+
},
|
|
5555
|
+
tempDir,
|
|
5556
|
+
transcript.metadata.id || "manual-enhancement"
|
|
5557
|
+
);
|
|
5558
|
+
if (simpleReplaceResult.stats.totalReplacements > 0) {
|
|
5559
|
+
transcript.enhancementLog.logStep(/* @__PURE__ */ new Date(), "simple-replace", "phase_complete", {
|
|
5560
|
+
totalReplacements: simpleReplaceResult.stats.totalReplacements,
|
|
5561
|
+
tier1Replacements: simpleReplaceResult.stats.tier1Replacements,
|
|
5562
|
+
tier2Replacements: simpleReplaceResult.stats.tier2Replacements,
|
|
5563
|
+
projectContext: simpleReplaceResult.stats.projectContext,
|
|
5564
|
+
processingTimeMs: simpleReplaceResult.stats.processingTimeMs
|
|
5565
|
+
});
|
|
5566
|
+
for (const mapping of simpleReplaceResult.stats.appliedMappings) {
|
|
5567
|
+
transcript.enhancementLog.logStep(/* @__PURE__ */ new Date(), "simple-replace", "correction_applied", {
|
|
5568
|
+
original: mapping.soundsLike,
|
|
5569
|
+
replacement: mapping.correctText,
|
|
5570
|
+
tier: mapping.tier,
|
|
5571
|
+
occurrences: mapping.occurrences,
|
|
5572
|
+
entityId: mapping.entityId,
|
|
5573
|
+
entityType: mapping.entityType
|
|
5574
|
+
});
|
|
5575
|
+
}
|
|
5576
|
+
}
|
|
5577
|
+
const preIdentifiedEntities = {
|
|
5578
|
+
people: /* @__PURE__ */ new Set(),
|
|
5579
|
+
projects: /* @__PURE__ */ new Set(),
|
|
5580
|
+
terms: /* @__PURE__ */ new Set(),
|
|
5581
|
+
companies: /* @__PURE__ */ new Set()
|
|
5582
|
+
};
|
|
5583
|
+
for (const mapping of simpleReplaceResult.stats.appliedMappings) {
|
|
5584
|
+
if (!mapping.entityId || !mapping.entityType) {
|
|
5585
|
+
continue;
|
|
5586
|
+
}
|
|
5587
|
+
if (mapping.entityType === "person") {
|
|
5588
|
+
preIdentifiedEntities.people.add(mapping.entityId);
|
|
5589
|
+
} else if (mapping.entityType === "project") {
|
|
5590
|
+
preIdentifiedEntities.projects.add(mapping.entityId);
|
|
5591
|
+
} else if (mapping.entityType === "term") {
|
|
5592
|
+
preIdentifiedEntities.terms.add(mapping.entityId);
|
|
5593
|
+
}
|
|
5594
|
+
}
|
|
5595
|
+
const reasoning = Reasoning.create({ model, reasoningLevel: "medium" });
|
|
5596
|
+
const executor = Agentic.create(reasoning, {
|
|
5597
|
+
transcriptText: simpleReplaceResult.text,
|
|
5598
|
+
audioDate: fallbackDate,
|
|
5599
|
+
sourceFile: pklPath,
|
|
5600
|
+
contextInstance: context,
|
|
5601
|
+
routingInstance: routing,
|
|
5602
|
+
interactiveMode: false,
|
|
5603
|
+
preIdentifiedEntities,
|
|
5604
|
+
onToolCallStart: (tool, input) => {
|
|
5605
|
+
toolCallCount++;
|
|
5606
|
+
transcript.enhancementLog.logStep(/* @__PURE__ */ new Date(), "enhance", "tool_start", {
|
|
5607
|
+
callIndex: toolCallCount,
|
|
5608
|
+
tool,
|
|
5609
|
+
input
|
|
5610
|
+
});
|
|
5611
|
+
},
|
|
5612
|
+
onToolCallComplete: (entry) => {
|
|
5613
|
+
transcript.enhancementLog.logStep(entry.timestamp, "enhance", "tool_complete", {
|
|
5614
|
+
tool: entry.tool,
|
|
5615
|
+
input: entry.input,
|
|
5616
|
+
output: entry.output,
|
|
5617
|
+
durationMs: entry.durationMs,
|
|
5618
|
+
success: entry.success
|
|
5619
|
+
});
|
|
5620
|
+
}
|
|
5621
|
+
});
|
|
5622
|
+
const agenticResult = await executor.process(simpleReplaceResult.text);
|
|
5623
|
+
const enhancedText = (agenticResult.enhancedText || "").trim() || sourceText;
|
|
5624
|
+
const enhancementSucceeded = enhancedText.length > 50 && enhancedText !== sourceText;
|
|
5625
|
+
const finalStatus = enhancementSucceeded ? "enhanced" : transcript.metadata.status || "initial";
|
|
5626
|
+
const referenced = agenticResult.state.referencedEntities;
|
|
5627
|
+
const entities = {
|
|
5628
|
+
people: [],
|
|
5629
|
+
projects: [],
|
|
5630
|
+
terms: [],
|
|
5631
|
+
companies: []
|
|
5632
|
+
};
|
|
5633
|
+
for (const personId of referenced.people) {
|
|
5634
|
+
const person = context.getPerson(personId);
|
|
5635
|
+
if (person) {
|
|
5636
|
+
entities.people.push({ id: person.id, name: person.name, type: "person" });
|
|
5637
|
+
}
|
|
5638
|
+
}
|
|
5639
|
+
for (const projectId of referenced.projects) {
|
|
5640
|
+
const project = context.getProject(projectId);
|
|
5641
|
+
if (project) {
|
|
5642
|
+
entities.projects.push({ id: project.id, name: project.name, type: "project" });
|
|
5643
|
+
}
|
|
5644
|
+
}
|
|
5645
|
+
for (const termId of referenced.terms) {
|
|
5646
|
+
const term = context.getTerm(termId);
|
|
5647
|
+
if (term) {
|
|
5648
|
+
entities.terms.push({ id: term.id, name: term.name, type: "term" });
|
|
5649
|
+
}
|
|
5650
|
+
}
|
|
5651
|
+
for (const companyId of referenced.companies) {
|
|
5652
|
+
const company = context.getCompany(companyId);
|
|
5653
|
+
if (company) {
|
|
5654
|
+
entities.companies.push({ id: company.id, name: company.name, type: "company" });
|
|
5655
|
+
}
|
|
5656
|
+
}
|
|
5657
|
+
const hasEntities = entities.people.length > 0 || entities.projects.length > 0 || entities.terms.length > 0 || entities.companies.length > 0;
|
|
5658
|
+
const decidedProjectId = agenticResult.state.routeDecision?.projectId || routeResult.projectId || void 0;
|
|
5659
|
+
const decidedProject = decidedProjectId ? context.getProject(decidedProjectId) : void 0;
|
|
5660
|
+
const decidedConfidence = agenticResult.state.routeDecision?.confidence ?? routeResult.confidence;
|
|
5661
|
+
transcript.updateContent(enhancedText);
|
|
5662
|
+
transcript.updateMetadata({
|
|
5663
|
+
status: finalStatus,
|
|
5664
|
+
projectId: decidedProjectId || transcript.metadata.projectId,
|
|
5665
|
+
project: decidedProject?.name || transcript.metadata.project,
|
|
5666
|
+
confidence: typeof decidedConfidence === "number" ? decidedConfidence : transcript.metadata.confidence,
|
|
5667
|
+
entities: hasEntities ? entities : transcript.metadata.entities
|
|
5668
|
+
});
|
|
5669
|
+
transcript.enhancementLog.logStep(/* @__PURE__ */ new Date(), "enhance", "enhancement_complete", {
|
|
5670
|
+
status: finalStatus,
|
|
5671
|
+
toolsUsed: agenticResult.toolsUsed,
|
|
5672
|
+
totalToolCalls: toolCallCount,
|
|
5673
|
+
iterations: agenticResult.iterations,
|
|
5674
|
+
processingTimeMs: Date.now() - startedAt
|
|
5675
|
+
});
|
|
5676
|
+
return {
|
|
5677
|
+
success: true,
|
|
5678
|
+
transcriptPath: args.transcriptPath,
|
|
5679
|
+
status: finalStatus,
|
|
5680
|
+
projectId: decidedProjectId || null,
|
|
5681
|
+
projectName: decidedProject?.name || null,
|
|
5682
|
+
toolsUsed: agenticResult.toolsUsed,
|
|
5683
|
+
totalToolCalls: toolCallCount,
|
|
5684
|
+
iterations: agenticResult.iterations,
|
|
5685
|
+
processingTimeMs: Date.now() - startedAt,
|
|
5686
|
+
sourceLength: sourceText.length,
|
|
5687
|
+
enhancedLength: enhancedText.length,
|
|
5688
|
+
changed: enhancedText !== sourceText
|
|
5689
|
+
};
|
|
5690
|
+
} finally {
|
|
5691
|
+
if (tempDir) {
|
|
5692
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
5693
|
+
}
|
|
5694
|
+
transcript.close();
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
4824
5697
|
async function handleCreateNote(args) {
|
|
4825
5698
|
const outputDirectory = await getConfiguredDirectory("outputDirectory", args.contextDirectory);
|
|
4826
5699
|
const noteDate = args.date ? new Date(args.date) : /* @__PURE__ */ new Date();
|
|
@@ -4927,8 +5800,89 @@ function applyCorrections(transcriptText, corrections) {
|
|
|
4927
5800
|
}
|
|
4928
5801
|
return correctedText;
|
|
4929
5802
|
}
|
|
5803
|
+
function countOccurrencesCaseInsensitive(text, target) {
|
|
5804
|
+
if (!target.trim()) {
|
|
5805
|
+
return 0;
|
|
5806
|
+
}
|
|
5807
|
+
const regex = new RegExp(target.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
|
|
5808
|
+
const matches = text.match(regex);
|
|
5809
|
+
return matches ? matches.length : 0;
|
|
5810
|
+
}
|
|
5811
|
+
async function handleRejectCorrection(args) {
|
|
5812
|
+
if (!Number.isInteger(args.correctionEntryId) || args.correctionEntryId < 1) {
|
|
5813
|
+
throw new Error("correctionEntryId must be a positive integer");
|
|
5814
|
+
}
|
|
5815
|
+
const absolutePath = await resolveTranscriptPath$1(args.transcriptPath, args.contextDirectory);
|
|
5816
|
+
const transcript = PklTranscript.open(absolutePath, { readOnly: false });
|
|
5817
|
+
try {
|
|
5818
|
+
const allEntries = transcript.getEnhancementLog();
|
|
5819
|
+
const correctionEntry = allEntries.find((entry) => entry.id === args.correctionEntryId);
|
|
5820
|
+
if (!correctionEntry) {
|
|
5821
|
+
throw new Error(`Correction entry not found: ${args.correctionEntryId}`);
|
|
5822
|
+
}
|
|
5823
|
+
if (correctionEntry.action !== "correction_applied") {
|
|
5824
|
+
throw new Error(`Entry ${args.correctionEntryId} is not a correction_applied action`);
|
|
5825
|
+
}
|
|
5826
|
+
const details = correctionEntry.details || {};
|
|
5827
|
+
const original = String(details.original || "").trim();
|
|
5828
|
+
const replacement = String(details.replacement || "").trim();
|
|
5829
|
+
if (!original || !replacement) {
|
|
5830
|
+
throw new Error(`Correction entry ${args.correctionEntryId} is missing original/replacement details`);
|
|
5831
|
+
}
|
|
5832
|
+
const alreadyRejected = allEntries.some((entry) => {
|
|
5833
|
+
if (entry.action !== "correction_rejected") {
|
|
5834
|
+
return false;
|
|
5835
|
+
}
|
|
5836
|
+
const rejectDetails = entry.details || {};
|
|
5837
|
+
return Number(rejectDetails.correctionEntryId) === args.correctionEntryId;
|
|
5838
|
+
});
|
|
5839
|
+
if (alreadyRejected) {
|
|
5840
|
+
return {
|
|
5841
|
+
success: true,
|
|
5842
|
+
alreadyRejected: true,
|
|
5843
|
+
correctionEntryId: args.correctionEntryId,
|
|
5844
|
+
original,
|
|
5845
|
+
replacement,
|
|
5846
|
+
revertedOccurrences: 0,
|
|
5847
|
+
message: `Correction #${args.correctionEntryId} is already rejected`
|
|
5848
|
+
};
|
|
5849
|
+
}
|
|
5850
|
+
const originalContent = transcript.content || "";
|
|
5851
|
+
const beforeCount = countOccurrencesCaseInsensitive(originalContent, replacement);
|
|
5852
|
+
const revertedContent = applyCorrections(
|
|
5853
|
+
originalContent,
|
|
5854
|
+
/* @__PURE__ */ new Map([[replacement, original]])
|
|
5855
|
+
);
|
|
5856
|
+
const afterCount = countOccurrencesCaseInsensitive(revertedContent, replacement);
|
|
5857
|
+
const revertedOccurrences = Math.max(0, beforeCount - afterCount);
|
|
5858
|
+
const rejectionPhase = correctionEntry.phase === "transcribe" || correctionEntry.phase === "enhance" || correctionEntry.phase === "simple-replace" ? correctionEntry.phase : "simple-replace";
|
|
5859
|
+
transcript.updateContent(revertedContent);
|
|
5860
|
+
transcript.enhancementLog.logStep(
|
|
5861
|
+
/* @__PURE__ */ new Date(),
|
|
5862
|
+
rejectionPhase,
|
|
5863
|
+
"correction_rejected",
|
|
5864
|
+
{
|
|
5865
|
+
correctionEntryId: args.correctionEntryId,
|
|
5866
|
+
original,
|
|
5867
|
+
replacement,
|
|
5868
|
+
revertedOccurrences,
|
|
5869
|
+
sourceTimestamp: correctionEntry.timestamp.toISOString()
|
|
5870
|
+
}
|
|
5871
|
+
);
|
|
5872
|
+
return {
|
|
5873
|
+
success: true,
|
|
5874
|
+
correctionEntryId: args.correctionEntryId,
|
|
5875
|
+
original,
|
|
5876
|
+
replacement,
|
|
5877
|
+
revertedOccurrences,
|
|
5878
|
+
message: `Rejected correction #${args.correctionEntryId} and restored "${replacement}" to "${original}"`
|
|
5879
|
+
};
|
|
5880
|
+
} finally {
|
|
5881
|
+
transcript.close();
|
|
5882
|
+
}
|
|
5883
|
+
}
|
|
4930
5884
|
async function handleCorrectToEntity(args) {
|
|
4931
|
-
const { randomUUID } = await import('node:crypto');
|
|
5885
|
+
const { randomUUID: randomUUID2 } = await import('node:crypto');
|
|
4932
5886
|
const { slugify } = await Promise.resolve().then(() => shared);
|
|
4933
5887
|
const ServerConfig = await Promise.resolve().then(() => serverConfig$1);
|
|
4934
5888
|
const context = ServerConfig.getContext();
|
|
@@ -4941,7 +5895,7 @@ async function handleCorrectToEntity(args) {
|
|
|
4941
5895
|
let finalEntityName;
|
|
4942
5896
|
let isNewEntity = false;
|
|
4943
5897
|
if (args.entityName) {
|
|
4944
|
-
const id =
|
|
5898
|
+
const id = randomUUID2();
|
|
4945
5899
|
const slug = slugify(args.entityName);
|
|
4946
5900
|
const entityBase = {
|
|
4947
5901
|
id,
|
|
@@ -6259,15 +7213,20 @@ const tools = [
|
|
|
6259
7213
|
// Transcript Operations
|
|
6260
7214
|
readTranscriptTool,
|
|
6261
7215
|
listTranscriptsTool,
|
|
7216
|
+
identifyTasksFromTranscriptTool,
|
|
7217
|
+
summarizeTranscriptTool,
|
|
7218
|
+
deleteTranscriptSummaryTool,
|
|
6262
7219
|
editTranscriptTool,
|
|
6263
7220
|
changeTranscriptDateTool,
|
|
6264
7221
|
combineTranscriptsTool,
|
|
6265
7222
|
provideFeedbackTool,
|
|
7223
|
+
enhanceTranscriptTool,
|
|
6266
7224
|
updateTranscriptContentTool,
|
|
6267
7225
|
updateTranscriptEntityReferencesTool,
|
|
6268
7226
|
createNoteTool,
|
|
6269
7227
|
getEnhancementLogTool,
|
|
6270
7228
|
correctToEntityTool,
|
|
7229
|
+
rejectCorrectionTool,
|
|
6271
7230
|
// Lifecycle Status & Tasks
|
|
6272
7231
|
setStatusTool,
|
|
6273
7232
|
createTaskTool,
|
|
@@ -6368,6 +7327,12 @@ async function handleToolCall(name, args) {
|
|
|
6368
7327
|
return handleReadTranscript(args);
|
|
6369
7328
|
case "protokoll_list_transcripts":
|
|
6370
7329
|
return handleListTranscripts(args);
|
|
7330
|
+
case "protokoll_identify_tasks_from_transcript":
|
|
7331
|
+
return handleIdentifyTasksFromTranscript(args);
|
|
7332
|
+
case "protokoll_summarize_transcript":
|
|
7333
|
+
return handleSummarizeTranscript(args);
|
|
7334
|
+
case "protokoll_delete_transcript_summary":
|
|
7335
|
+
return handleDeleteTranscriptSummary(args);
|
|
6371
7336
|
case "protokoll_edit_transcript":
|
|
6372
7337
|
return handleEditTranscript(args);
|
|
6373
7338
|
case "protokoll_change_transcript_date":
|
|
@@ -6376,6 +7341,8 @@ async function handleToolCall(name, args) {
|
|
|
6376
7341
|
return handleCombineTranscripts(args);
|
|
6377
7342
|
case "protokoll_provide_feedback":
|
|
6378
7343
|
return handleProvideFeedback(args);
|
|
7344
|
+
case "protokoll_enhance_transcript":
|
|
7345
|
+
return handleEnhanceTranscript(args);
|
|
6379
7346
|
case "protokoll_update_transcript_content":
|
|
6380
7347
|
return handleUpdateTranscriptContent(args);
|
|
6381
7348
|
case "protokoll_update_transcript_entity_references":
|
|
@@ -6386,6 +7353,8 @@ async function handleToolCall(name, args) {
|
|
|
6386
7353
|
return handleGetEnhancementLog(args);
|
|
6387
7354
|
case "protokoll_correct_to_entity":
|
|
6388
7355
|
return handleCorrectToEntity(args);
|
|
7356
|
+
case "protokoll_reject_correction":
|
|
7357
|
+
return handleRejectCorrection(args);
|
|
6389
7358
|
// Lifecycle Status & Tasks
|
|
6390
7359
|
case "protokoll_set_status":
|
|
6391
7360
|
return handleSetStatus(args);
|