@redaksjon/protokoll 0.2.0 → 0.4.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/README.md +57 -1
- package/dist/main.js +426 -0
- package/dist/main.js.map +1 -1
- package/dist/mcp/prompts/batch_transcription.md +3 -0
- package/dist/mcp/prompts/edit_entity.md +9 -0
- package/dist/mcp/prompts/enrich_entity.md +3 -0
- package/dist/mcp/prompts/find_and_analyze.md +3 -0
- package/dist/mcp/prompts/review_transcript.md +13 -0
- package/dist/mcp/prompts/setup_project.md +12 -0
- package/dist/mcp/prompts/transcribe_with_context.md +8 -0
- package/dist/mcp/server.js +2190 -2159
- package/dist/mcp/server.js.map +1 -1
- package/dist/transcript.js +145 -17
- package/dist/transcript.js.map +1 -1
- package/docs/entity-metadata.md +42 -1
- package/docs/examples.md +172 -1
- package/guide/context-commands.md +417 -16
- package/guide/context-system.md +111 -10
- package/guide/development.md +9 -0
- package/guide/index.md +1 -0
- package/guide/routing.md +215 -0
- package/package.json +1 -1
- package/.cursor/rules/definition-of-done.md +0 -90
- package/.cursor/rules/no-auto-summary-files.md +0 -43
- package/.cursor/rules/no-emoticons.md +0 -57
- package/nodemon.json +0 -14
- package/tsconfig.tsbuildinfo +0 -1
package/dist/transcript.js
CHANGED
|
@@ -18,7 +18,7 @@ import os__default from 'node:os';
|
|
|
18
18
|
import winston from 'winston';
|
|
19
19
|
import { IterationStrategyFactory } from '@riotprompt/riotprompt';
|
|
20
20
|
|
|
21
|
-
const VERSION = "0.
|
|
21
|
+
const VERSION = "0.4.3 (HEAD/94e6969 T:v0.4.3 2026-01-21 16:26:11 -0800) linux x64 v24.12.0";
|
|
22
22
|
const PROGRAM_NAME = "protokoll";
|
|
23
23
|
const DEFAULT_DIFF = true;
|
|
24
24
|
const DEFAULT_LOG = false;
|
|
@@ -30,8 +30,8 @@ const DEFAULT_DEBUG = false;
|
|
|
30
30
|
const DEFAULT_CONTENT_TYPES = ["diff"];
|
|
31
31
|
const DEFAULT_INPUT_DIRECTORY = "./";
|
|
32
32
|
const DEFAULT_OUTPUT_DIRECTORY = "./";
|
|
33
|
-
const DEFAULT_AUDIO_EXTENSIONS = ["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm"];
|
|
34
|
-
const ALLOWED_AUDIO_EXTENSIONS = ["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm"];
|
|
33
|
+
const DEFAULT_AUDIO_EXTENSIONS = ["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm", "qta"];
|
|
34
|
+
const ALLOWED_AUDIO_EXTENSIONS = ["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm", "qta"];
|
|
35
35
|
const DEFAULT_OUTPUT_STRUCTURE = "month";
|
|
36
36
|
const DEFAULT_OUTPUT_FILENAME_OPTIONS = ["date", "time", "subject"];
|
|
37
37
|
const ALLOWED_OUTPUT_STRUCTURES = ["none", "year", "month", "day"];
|
|
@@ -242,6 +242,27 @@ const create$w = (params) => {
|
|
|
242
242
|
};
|
|
243
243
|
};
|
|
244
244
|
|
|
245
|
+
const isParentProject = (projectA, projectB) => {
|
|
246
|
+
return projectB.relationships?.parent === projectA.id;
|
|
247
|
+
};
|
|
248
|
+
const isChildProject = (projectA, projectB) => {
|
|
249
|
+
return projectA.relationships?.parent === projectB.id;
|
|
250
|
+
};
|
|
251
|
+
const areSiblingProjects = (projectA, projectB) => {
|
|
252
|
+
const aSiblings = projectA.relationships?.siblings || [];
|
|
253
|
+
const bSiblings = projectB.relationships?.siblings || [];
|
|
254
|
+
return aSiblings.includes(projectB.id) || bSiblings.includes(projectA.id);
|
|
255
|
+
};
|
|
256
|
+
const getProjectRelationshipDistance = (projectA, projectB) => {
|
|
257
|
+
if (projectA.id === projectB.id) return 0;
|
|
258
|
+
if (isParentProject(projectA, projectB) || isChildProject(projectA, projectB)) return 1;
|
|
259
|
+
if (areSiblingProjects(projectA, projectB)) return 2;
|
|
260
|
+
if (projectA.relationships?.parent && projectB.relationships?.parent && projectA.relationships.parent === projectB.relationships.parent) {
|
|
261
|
+
return 2;
|
|
262
|
+
}
|
|
263
|
+
return -1;
|
|
264
|
+
};
|
|
265
|
+
|
|
245
266
|
const DIRECTORY_TO_TYPE = {
|
|
246
267
|
"people": "person",
|
|
247
268
|
"projects": "project",
|
|
@@ -555,6 +576,55 @@ const create$u = async (options = {}) => {
|
|
|
555
576
|
},
|
|
556
577
|
search: (query) => storage.search(query),
|
|
557
578
|
findBySoundsLike: (phonetic) => storage.findBySoundsLike(phonetic),
|
|
579
|
+
/**
|
|
580
|
+
* Context-aware search that prefers entities related to context project.
|
|
581
|
+
* Still returns standard search results if no context provided.
|
|
582
|
+
*/
|
|
583
|
+
searchWithContext: (query, contextProjectId) => {
|
|
584
|
+
const results = storage.search(query);
|
|
585
|
+
if (!contextProjectId) {
|
|
586
|
+
return results;
|
|
587
|
+
}
|
|
588
|
+
const contextProject = storage.get("project", contextProjectId);
|
|
589
|
+
if (!contextProject) {
|
|
590
|
+
return results;
|
|
591
|
+
}
|
|
592
|
+
const scoredResults = results.map((entity) => {
|
|
593
|
+
let score = 0;
|
|
594
|
+
if (entity.type === "project") {
|
|
595
|
+
const distance = getProjectRelationshipDistance(contextProject, entity);
|
|
596
|
+
if (distance >= 0) {
|
|
597
|
+
score += (3 - distance) * 50;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (entity.type === "term") {
|
|
601
|
+
const term = entity;
|
|
602
|
+
if (term.projects?.includes(contextProjectId)) {
|
|
603
|
+
score += 100;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
return { entity, score };
|
|
607
|
+
});
|
|
608
|
+
return scoredResults.sort((a, b) => b.score - a.score).map((r) => r.entity);
|
|
609
|
+
},
|
|
610
|
+
/**
|
|
611
|
+
* Get all projects related to a given project within maxDistance
|
|
612
|
+
* Distance: 0 = same, 1 = parent/child, 2 = siblings/cousins
|
|
613
|
+
*/
|
|
614
|
+
getRelatedProjects: (projectId, maxDistance = 2) => {
|
|
615
|
+
const project = storage.get("project", projectId);
|
|
616
|
+
if (!project) return [];
|
|
617
|
+
const allProjects = storage.getAll("project");
|
|
618
|
+
const related = [];
|
|
619
|
+
for (const otherProject of allProjects) {
|
|
620
|
+
if (otherProject.id === projectId) continue;
|
|
621
|
+
const distance = getProjectRelationshipDistance(project, otherProject);
|
|
622
|
+
if (distance >= 0 && distance <= maxDistance) {
|
|
623
|
+
related.push({ project: otherProject, distance });
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
return related.sort((a, b) => a.distance - b.distance).map((r) => r.project);
|
|
627
|
+
},
|
|
558
628
|
saveEntity: async (entity) => {
|
|
559
629
|
const closestDir = discoveryResult.discoveredDirs.sort((a, b) => a.level - b.level)[0];
|
|
560
630
|
if (!closestDir) {
|
|
@@ -2336,7 +2406,8 @@ const createRoutingMetadata = (decision) => {
|
|
|
2336
2406
|
};
|
|
2337
2407
|
};
|
|
2338
2408
|
const extractTagsFromSignals = (signals) => {
|
|
2339
|
-
|
|
2409
|
+
const tags = signals.filter((s) => s.type !== "context_type").map((s) => s.value).filter((v) => typeof v === "string");
|
|
2410
|
+
return Array.from(new Set(tags));
|
|
2340
2411
|
};
|
|
2341
2412
|
|
|
2342
2413
|
const create$l = (config) => {
|
|
@@ -3470,7 +3541,13 @@ const create$b = (ctx) => ({
|
|
|
3470
3541
|
}
|
|
3471
3542
|
};
|
|
3472
3543
|
}
|
|
3473
|
-
|
|
3544
|
+
let contextProjectId;
|
|
3545
|
+
if (ctx.routingInstance) {
|
|
3546
|
+
const allProjects2 = context.getAllProjects();
|
|
3547
|
+
const activeProject = allProjects2.find((p) => p.active !== false);
|
|
3548
|
+
contextProjectId = activeProject?.id;
|
|
3549
|
+
}
|
|
3550
|
+
const searchResults = context.searchWithContext(args.name, contextProjectId);
|
|
3474
3551
|
const projectMatches = searchResults.filter((e) => e.type === "project");
|
|
3475
3552
|
const termMatches = searchResults.filter((e) => e.type === "term");
|
|
3476
3553
|
if (projectMatches.length > 0) {
|
|
@@ -3479,7 +3556,8 @@ const create$b = (ctx) => ({
|
|
|
3479
3556
|
success: true,
|
|
3480
3557
|
data: {
|
|
3481
3558
|
found: true,
|
|
3482
|
-
project
|
|
3559
|
+
project,
|
|
3560
|
+
matchedVia: "context_aware_search"
|
|
3483
3561
|
}
|
|
3484
3562
|
};
|
|
3485
3563
|
}
|
|
@@ -3747,6 +3825,26 @@ const create$7 = (ctx) => {
|
|
|
3747
3825
|
};
|
|
3748
3826
|
};
|
|
3749
3827
|
|
|
3828
|
+
const cleanResponseContent = (content) => {
|
|
3829
|
+
let cleaned = content.replace(/^(?:Using tools?|Let me|I'll|I will|Now I'll|First,?\s*I(?:'ll| will)).*?[\r\n]+/gim, "");
|
|
3830
|
+
cleaned = cleaned.replace(/\{"tool":\s*"[^"]+",\s*"input":\s*\{[^}]*\}\}/g, "");
|
|
3831
|
+
cleaned = cleaned.replace(/\b\w+_\w+\(\{[^}]*\}\)/g, "");
|
|
3832
|
+
const lines = cleaned.split("\n");
|
|
3833
|
+
let startIndex = 0;
|
|
3834
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3835
|
+
const line = lines[i].trim();
|
|
3836
|
+
if (line === "") continue;
|
|
3837
|
+
const isCommentary = /^(checking|verifying|looking|searching|analyzing|processing|determining|using|calling|executing|I'm|I am|Let me)/i.test(line) || line.includes("tool") || line.includes('{"') || line.includes("reasoning");
|
|
3838
|
+
if (!isCommentary) {
|
|
3839
|
+
startIndex = i;
|
|
3840
|
+
break;
|
|
3841
|
+
}
|
|
3842
|
+
}
|
|
3843
|
+
if (startIndex > 0) {
|
|
3844
|
+
cleaned = lines.slice(startIndex).join("\n");
|
|
3845
|
+
}
|
|
3846
|
+
return cleaned.trim();
|
|
3847
|
+
};
|
|
3750
3848
|
const create$6 = (reasoning, ctx) => {
|
|
3751
3849
|
const logger = getLogger();
|
|
3752
3850
|
const registry = create$7(ctx);
|
|
@@ -3784,6 +3882,15 @@ CRITICAL RULES:
|
|
|
3784
3882
|
- When you have finished processing, output the COMPLETE corrected transcript as Markdown.
|
|
3785
3883
|
- Do NOT say you don't have the transcript - it's in the conversation history.
|
|
3786
3884
|
|
|
3885
|
+
OUTPUT REQUIREMENTS - EXTREMELY IMPORTANT:
|
|
3886
|
+
- Your final response MUST contain ONLY the corrected transcript text.
|
|
3887
|
+
- DO NOT include any commentary like "Using tools to..." or "Let me verify...".
|
|
3888
|
+
- DO NOT include any explanations about what you're doing or have done.
|
|
3889
|
+
- DO NOT include any tool call information or JSON in your response.
|
|
3890
|
+
- DO NOT include any reasoning or processing notes.
|
|
3891
|
+
- Your output will be inserted directly into the user-facing document.
|
|
3892
|
+
- If you need to use tools, use them silently - don't narrate what you're doing.
|
|
3893
|
+
|
|
3787
3894
|
Available tools:
|
|
3788
3895
|
- lookup_person: Find information about people (use for any name that might be misspelled)
|
|
3789
3896
|
- lookup_project: Find project routing information
|
|
@@ -3803,6 +3910,7 @@ Please:
|
|
|
3803
3910
|
3. Use route_note to determine the destination
|
|
3804
3911
|
4. Then output the COMPLETE corrected transcript as clean Markdown
|
|
3805
3912
|
|
|
3913
|
+
CRITICAL: Your response must contain ONLY the transcript text - no commentary, no explanations, no tool information.
|
|
3806
3914
|
Remember: preserve ALL content, only fix transcription errors.`;
|
|
3807
3915
|
conversationHistory.push({ role: "user", content: initialPrompt });
|
|
3808
3916
|
try {
|
|
@@ -4325,7 +4433,9 @@ Corrections made so far: ${state.resolvedEntities.size > 0 ? Array.from(state.re
|
|
|
4325
4433
|
|
|
4326
4434
|
Continue analyzing. If you need more information, use the tools.
|
|
4327
4435
|
When you're done with tool calls, output the COMPLETE corrected transcript as Markdown.
|
|
4328
|
-
Do NOT summarize - include ALL original content with corrections applied
|
|
4436
|
+
Do NOT summarize - include ALL original content with corrections applied.
|
|
4437
|
+
|
|
4438
|
+
CRITICAL REMINDER: Your response must contain ONLY the transcript text. Do NOT include any commentary, explanations, or processing notes - those will leak into the user-facing document.`;
|
|
4329
4439
|
conversationHistory.push({ role: "user", content: continuationPrompt });
|
|
4330
4440
|
response = await reasoning.complete({
|
|
4331
4441
|
systemPrompt,
|
|
@@ -4346,9 +4456,17 @@ Do NOT summarize - include ALL original content with corrections applied.`;
|
|
|
4346
4456
|
});
|
|
4347
4457
|
}
|
|
4348
4458
|
if (response.content && response.content.length > 50) {
|
|
4349
|
-
|
|
4459
|
+
const cleanedContent = cleanResponseContent(response.content);
|
|
4460
|
+
if (cleanedContent !== response.content) {
|
|
4461
|
+
logger.warn(
|
|
4462
|
+
"Removed leaked internal processing from response (%d -> %d chars)",
|
|
4463
|
+
response.content.length,
|
|
4464
|
+
cleanedContent.length
|
|
4465
|
+
);
|
|
4466
|
+
}
|
|
4467
|
+
state.correctedText = cleanedContent;
|
|
4350
4468
|
state.confidence = 0.9;
|
|
4351
|
-
logger.debug("Final transcript generated: %d characters",
|
|
4469
|
+
logger.debug("Final transcript generated: %d characters", cleanedContent.length);
|
|
4352
4470
|
} else {
|
|
4353
4471
|
logger.debug("Model did not produce transcript, requesting explicitly...");
|
|
4354
4472
|
const finalRequest = `Please output the COMPLETE corrected transcript now.
|
|
@@ -4359,7 +4477,9 @@ ${transcriptText}
|
|
|
4359
4477
|
CORRECTIONS TO APPLY:
|
|
4360
4478
|
${state.resolvedEntities.size > 0 ? Array.from(state.resolvedEntities.entries()).map(([k, v]) => `- "${k}" should be "${v}"`).join("\n") : "None identified"}
|
|
4361
4479
|
|
|
4362
|
-
Output the full transcript as clean Markdown. Do NOT summarize
|
|
4480
|
+
Output the full transcript as clean Markdown. Do NOT summarize.
|
|
4481
|
+
|
|
4482
|
+
CRITICAL: Your response must contain ONLY the corrected transcript text - absolutely no commentary, tool information, or explanations.`;
|
|
4363
4483
|
const finalResponse = await reasoning.complete({
|
|
4364
4484
|
systemPrompt,
|
|
4365
4485
|
prompt: finalRequest
|
|
@@ -4367,7 +4487,15 @@ Output the full transcript as clean Markdown. Do NOT summarize.`;
|
|
|
4367
4487
|
if (finalResponse.usage) {
|
|
4368
4488
|
totalTokens += finalResponse.usage.totalTokens;
|
|
4369
4489
|
}
|
|
4370
|
-
|
|
4490
|
+
const cleanedFinalContent = cleanResponseContent(finalResponse.content || transcriptText);
|
|
4491
|
+
if (cleanedFinalContent !== finalResponse.content) {
|
|
4492
|
+
logger.warn(
|
|
4493
|
+
"Removed leaked internal processing from final response (%d -> %d chars)",
|
|
4494
|
+
finalResponse.content?.length || 0,
|
|
4495
|
+
cleanedFinalContent.length
|
|
4496
|
+
);
|
|
4497
|
+
}
|
|
4498
|
+
state.correctedText = cleanedFinalContent;
|
|
4371
4499
|
state.confidence = 0.8;
|
|
4372
4500
|
}
|
|
4373
4501
|
} catch (error) {
|
|
@@ -4510,14 +4638,14 @@ const create$3 = async (config) => {
|
|
|
4510
4638
|
const routingProjects = contextProjects.filter((project) => project.active !== false).map((project) => ({
|
|
4511
4639
|
projectId: project.id,
|
|
4512
4640
|
destination: {
|
|
4513
|
-
path: project.routing
|
|
4514
|
-
structure: project.routing
|
|
4515
|
-
filename_options: project.routing
|
|
4641
|
+
path: project.routing?.destination || defaultPath,
|
|
4642
|
+
structure: project.routing?.structure || defaultStructure,
|
|
4643
|
+
filename_options: project.routing?.filename_options || defaultFilenameOptions,
|
|
4516
4644
|
createDirectories: true
|
|
4517
4645
|
},
|
|
4518
4646
|
classification: project.classification,
|
|
4519
4647
|
active: project.active,
|
|
4520
|
-
auto_tags: project.routing
|
|
4648
|
+
auto_tags: project.routing?.auto_tags
|
|
4521
4649
|
}));
|
|
4522
4650
|
logger.debug("Loaded %d projects from context for routing", routingProjects.length);
|
|
4523
4651
|
const routingConfig = {
|
|
@@ -6567,7 +6695,7 @@ const listTranscripts = async (options) => {
|
|
|
6567
6695
|
let content;
|
|
6568
6696
|
try {
|
|
6569
6697
|
content = await fs$1.readFile(filePath, "utf-8");
|
|
6570
|
-
} catch
|
|
6698
|
+
} catch {
|
|
6571
6699
|
continue;
|
|
6572
6700
|
}
|
|
6573
6701
|
if (search) {
|
|
@@ -6670,5 +6798,5 @@ const registerTranscriptCommands = (program) => {
|
|
|
6670
6798
|
});
|
|
6671
6799
|
};
|
|
6672
6800
|
|
|
6673
|
-
export { ALLOWED_OUTPUT_FILENAME_OPTIONS as A, listTranscripts as B,
|
|
6801
|
+
export { ALLOWED_OUTPUT_FILENAME_OPTIONS as A, listTranscripts as B, DEFAULT_TEMP_DIRECTORY as C, DEFAULT_REASONING_LEVEL as D, create$p as E, create$d as F, processFeedback as G, applyChanges as H, combineTranscripts as I, editTranscript as J, parseTranscript as K, createCompletion as L, contentFetcher as M, projectAssist as N, PROGRAM_NAME as P, VERSION as V, PROTOKOLL_DEFAULTS as a, DEFAULT_MAX_AUDIO_SIZE as b, create$w as c, create$1 as d, DEFAULT_INTERMEDIATE_DIRECTORY as e, ALLOWED_OUTPUT_STRUCTURES as f, getLogger as g, ALLOWED_AUDIO_EXTENSIONS as h, DEFAULT_OUTPUT_DIRECTORY as i, DEFAULT_INPUT_DIRECTORY as j, DEFAULT_OUTPUT_FILENAME_OPTIONS as k, DEFAULT_OUTPUT_STRUCTURE as l, DEFAULT_AUDIO_EXTENSIONS as m, DEFAULT_TIMEZONE as n, DEFAULT_CONFIG_DIR as o, create$2 as p, create$u as q, create as r, setLogLevel as s, DEFAULT_CONTEXT_DIR_NAME as t, DEFAULT_CONTEXT_CONFIG_FILE_NAME as u, DEFAULT_MODEL as v, DEFAULT_TRANSCRIPTION_MODEL as w, registerActionCommands as x, registerFeedbackCommands as y, registerTranscriptCommands as z };
|
|
6674
6802
|
//# sourceMappingURL=transcript.js.map
|