@redaksjon/protokoll 1.0.23 → 1.0.24-dev.20260302172144.c9ea98d

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/LICENSE CHANGED
@@ -175,7 +175,7 @@
175
175
 
176
176
  END OF TERMS AND CONDITIONS
177
177
 
178
- Copyright 2025 Tim O'Brien
178
+ Copyright 2026 Tim O'Brien
179
179
 
180
180
  Licensed under the Apache License, Version 2.0 (the "License");
181
181
  you may not use this file except in compliance with the License.
@@ -208,7 +208,7 @@ function isProtokolUri(uri) {
208
208
  return uri.startsWith(`${SCHEME}://`);
209
209
  }
210
210
 
211
- const VERSION = "1.0.23 (HEAD/b233b60 T:v1.0.23 2026-03-01 14:31:50 -0800) linux arm64 v24.14.0";
211
+ const VERSION = "1.0.24-dev.20260302172144.c9ea98d (working/c9ea98d 2026-03-02 09:20:37 -0800) linux arm64 v24.14.0";
212
212
  const PROGRAM_NAME = "protokoll";
213
213
  const DATE_FORMAT_YEAR_MONTH_DAY_HOURS_MINUTES_SECONDS = "YYYY-M-D-HHmmss";
214
214
  const DEFAULT_AUDIO_EXTENSIONS = ["mp3", "mp4", "mpeg", "mpga", "m4a", "wav", "webm", "qta"];
@@ -1667,7 +1667,7 @@ function entryVersionKey(entry) {
1667
1667
  String(Number(entry.sourceSize || 0))
1668
1668
  ].join("|");
1669
1669
  }
1670
- async function listFilesWithMetadataCompat$1(outputStorage, prefix, pattern) {
1670
+ async function listFilesWithMetadataCompat$2(outputStorage, prefix, pattern) {
1671
1671
  const withMetadata = outputStorage.listFilesWithMetadata;
1672
1672
  if (typeof withMetadata === "function") {
1673
1673
  return withMetadata.call(outputStorage, prefix, pattern);
@@ -1809,7 +1809,7 @@ class TranscriptIndexService {
1809
1809
  }
1810
1810
  async refreshIndex() {
1811
1811
  const refreshStartedAt = Date.now();
1812
- const listed = await listFilesWithMetadataCompat$1(this.outputStorage, "");
1812
+ const listed = await listFilesWithMetadataCompat$2(this.outputStorage, "");
1813
1813
  const candidates = listed.map((metadata) => ({ ...metadata, path: normalizePath$1(metadata.path) })).filter((metadata) => isTranscriptCandidate(metadata.path));
1814
1814
  const byPath = new Map(candidates.map((metadata) => [metadata.path, metadata]));
1815
1815
  const dirtyAtStart = new Set(this.dirtyPaths);
@@ -2101,7 +2101,7 @@ async function resolveTranscriptPathByFilename(outputStorage, outputDirectory, t
2101
2101
  if (indexedMatches.length > 0) {
2102
2102
  return indexedMatches;
2103
2103
  }
2104
- const listed = await listFilesWithMetadataCompat$1(outputStorage, "", ".pkl");
2104
+ const listed = await listFilesWithMetadataCompat$2(outputStorage, "", ".pkl");
2105
2105
  return listed.map((entry) => normalizePath$1(entry.path)).filter((entryPath) => targetFilenames.has(entryPath.split("/").pop() || ""));
2106
2106
  }
2107
2107
 
@@ -2465,7 +2465,7 @@ function isYamlPath(pathValue) {
2465
2465
  const normalized = normalizePath(pathValue).toLowerCase();
2466
2466
  return normalized.endsWith(".yaml") || normalized.endsWith(".yml");
2467
2467
  }
2468
- async function listFilesWithMetadataCompat(provider, prefix, pattern) {
2468
+ async function listFilesWithMetadataCompat$1(provider, prefix, pattern) {
2469
2469
  if (typeof provider.listFilesWithMetadata === "function") {
2470
2470
  return provider.listFilesWithMetadata(prefix, pattern);
2471
2471
  }
@@ -2565,7 +2565,7 @@ class EntityIndexService {
2565
2565
  this.contextGcs.projectId
2566
2566
  );
2567
2567
  const directory = ENTITY_DIRECTORY$1[entityType];
2568
- const listed = await listFilesWithMetadataCompat(provider, `${directory}/`);
2568
+ const listed = await listFilesWithMetadataCompat$1(provider, `${directory}/`);
2569
2569
  const yamlEntries = listed.map((metadata) => ({ ...metadata, path: normalizePath(metadata.path) })).filter((metadata) => isYamlPath(metadata.path));
2570
2570
  const byPath = new Map(yamlEntries.map((metadata) => [metadata.path, metadata]));
2571
2571
  const existing = this.byType.get(entityType) || /* @__PURE__ */ new Map();
@@ -6069,6 +6069,18 @@ async function resolveStorageTranscriptPath(transcriptPath, outputStorage) {
6069
6069
  }
6070
6070
  return null;
6071
6071
  }
6072
+ async function getProjectLookupContext(contextDirectory) {
6073
+ const ServerConfig = await Promise.resolve().then(() => serverConfig$1);
6074
+ const serverContext = ServerConfig.getContext();
6075
+ if (serverContext?.hasContext()) {
6076
+ return serverContext;
6077
+ }
6078
+ const contextDirectories = await getContextDirectories();
6079
+ return create({
6080
+ startingDir: contextDirectory || process.cwd(),
6081
+ contextDirectories
6082
+ });
6083
+ }
6072
6084
  async function openToolTranscript(transcriptPath, contextDirectory) {
6073
6085
  const ServerConfig = await Promise.resolve().then(() => serverConfig$1);
6074
6086
  const outputStorage = ServerConfig.getOutputStorage();
@@ -7115,6 +7127,7 @@ async function handleEditTranscript(args) {
7115
7127
  const outputDirectory = await getConfiguredDirectory("outputDirectory", args.contextDirectory);
7116
7128
  const ServerConfig = await Promise.resolve().then(() => serverConfig$1);
7117
7129
  const outputStorage = ServerConfig.getOutputStorage();
7130
+ const serverContext = ServerConfig.getContext();
7118
7131
  if (outputStorage.name === "gcs") {
7119
7132
  const access = await openToolTranscript(args.transcriptPath, args.contextDirectory);
7120
7133
  const resolvedStoragePath = access.storagePath || access.pklPath;
@@ -7131,17 +7144,24 @@ async function handleEditTranscript(args) {
7131
7144
  changes2.push("title updated");
7132
7145
  }
7133
7146
  if (args.projectId) {
7134
- const contextDirectories = await getContextDirectories();
7135
- const context = await create({
7136
- startingDir: args.contextDirectory || process.cwd(),
7137
- contextDirectories
7138
- });
7147
+ const context = await getProjectLookupContext(args.contextDirectory);
7139
7148
  const project = await context.getProject(args.projectId);
7140
- if (!project) {
7141
- throw new Error(`Project not found: ${args.projectId}`);
7149
+ let resolvedId;
7150
+ let resolvedName;
7151
+ if (project) {
7152
+ resolvedId = project.id;
7153
+ resolvedName = project.name;
7154
+ } else {
7155
+ const gcsEntity = await findContextEntityInGcs("project", args.projectId);
7156
+ if (gcsEntity && typeof gcsEntity.id === "string" && typeof gcsEntity.name === "string") {
7157
+ resolvedId = gcsEntity.id;
7158
+ resolvedName = gcsEntity.name;
7159
+ } else {
7160
+ throw new Error(`Project not found: ${args.projectId}`);
7161
+ }
7142
7162
  }
7143
- metadataUpdates.projectId = project.id;
7144
- metadataUpdates.project = project.name;
7163
+ metadataUpdates.projectId = resolvedId;
7164
+ metadataUpdates.project = resolvedName;
7145
7165
  const existingEntities = transcript.metadata.entities || {
7146
7166
  people: [],
7147
7167
  projects: [],
@@ -7151,8 +7171,8 @@ async function handleEditTranscript(args) {
7151
7171
  metadataUpdates.entities = {
7152
7172
  people: existingEntities.people || [],
7153
7173
  projects: [{
7154
- id: project.id,
7155
- name: project.name,
7174
+ id: resolvedId,
7175
+ name: resolvedName,
7156
7176
  type: "project"
7157
7177
  }],
7158
7178
  terms: existingEntities.terms || [],
@@ -7218,7 +7238,10 @@ async function handleEditTranscript(args) {
7218
7238
  let finalOutputPath = absolutePath;
7219
7239
  let wasRenamed = false;
7220
7240
  if (args.title || args.projectId || args.tagsToAdd || args.tagsToRemove) {
7221
- const contextDirectories = await getContextDirectories();
7241
+ let contextDirectories = await getContextDirectories();
7242
+ if ((!contextDirectories || contextDirectories.length === 0) && serverContext?.hasContext()) {
7243
+ contextDirectories = serverContext.getContextDirs();
7244
+ }
7222
7245
  const result = await Transcript.editTranscript(absolutePath, {
7223
7246
  title: args.title,
7224
7247
  projectId: args.projectId,
@@ -7770,6 +7793,11 @@ async function handleEnhanceTranscript(args) {
7770
7793
  const project = context.getProject(projectId);
7771
7794
  if (project) {
7772
7795
  entities.projects.push({ id: project.id, name: project.name, type: "project" });
7796
+ } else {
7797
+ const gcsEntity = await findContextEntityInGcs("project", projectId);
7798
+ if (gcsEntity && typeof gcsEntity.id === "string" && typeof gcsEntity.name === "string") {
7799
+ entities.projects.push({ id: gcsEntity.id, name: gcsEntity.name, type: "project" });
7800
+ }
7773
7801
  }
7774
7802
  }
7775
7803
  for (const termId of referenced.terms) {
@@ -7786,13 +7814,24 @@ async function handleEnhanceTranscript(args) {
7786
7814
  }
7787
7815
  const hasEntities = entities.people.length > 0 || entities.projects.length > 0 || entities.terms.length > 0 || entities.companies.length > 0;
7788
7816
  const decidedProjectId = agenticResult.state.routeDecision?.projectId || routeResult.projectId || void 0;
7789
- const decidedProject = decidedProjectId ? context.getProject(decidedProjectId) : void 0;
7817
+ let decidedProjectName;
7818
+ if (decidedProjectId) {
7819
+ const decidedProject = context.getProject(decidedProjectId);
7820
+ if (decidedProject) {
7821
+ decidedProjectName = decidedProject.name;
7822
+ } else {
7823
+ const gcsEntity = await findContextEntityInGcs("project", decidedProjectId);
7824
+ if (gcsEntity && typeof gcsEntity.name === "string") {
7825
+ decidedProjectName = gcsEntity.name;
7826
+ }
7827
+ }
7828
+ }
7790
7829
  const decidedConfidence = agenticResult.state.routeDecision?.confidence ?? routeResult.confidence;
7791
7830
  transcript.updateContent(enhancedText);
7792
7831
  transcript.updateMetadata({
7793
7832
  status: finalStatus,
7794
7833
  projectId: decidedProjectId || transcript.metadata.projectId,
7795
- project: decidedProject?.name || transcript.metadata.project,
7834
+ project: decidedProjectName || transcript.metadata.project,
7796
7835
  confidence: typeof decidedConfidence === "number" ? decidedConfidence : transcript.metadata.confidence,
7797
7836
  entities: hasEntities ? entities : transcript.metadata.entities
7798
7837
  });
@@ -7817,7 +7856,7 @@ async function handleEnhanceTranscript(args) {
7817
7856
  transcriptPath: args.transcriptPath,
7818
7857
  status: finalStatus,
7819
7858
  projectId: decidedProjectId || null,
7820
- projectName: decidedProject?.name || null,
7859
+ projectName: decidedProjectName || null,
7821
7860
  toolsUsed: agenticResult.toolsUsed,
7822
7861
  totalToolCalls: toolCallCount,
7823
7862
  iterations: agenticResult.iterations,
@@ -7876,14 +7915,15 @@ async function handleCreateNote(args) {
7876
7915
  let projectName;
7877
7916
  if (args.projectId) {
7878
7917
  try {
7879
- const contextDirectories = await getContextDirectories();
7880
- const context = await create({
7881
- startingDir: args.contextDirectory || process.cwd(),
7882
- contextDirectories
7883
- });
7918
+ const context = await getProjectLookupContext(args.contextDirectory);
7884
7919
  const project = await context.getProject(args.projectId);
7885
7920
  if (project) {
7886
7921
  projectName = project.name;
7922
+ } else {
7923
+ const gcsEntity = await findContextEntityInGcs("project", args.projectId);
7924
+ if (gcsEntity && typeof gcsEntity.name === "string") {
7925
+ projectName = gcsEntity.name;
7926
+ }
7887
7927
  }
7888
7928
  } catch {
7889
7929
  }
@@ -9164,6 +9204,82 @@ function getCompletionTime(metadata) {
9164
9204
  const lastTransition = metadata.history.sort((a, b) => b.at.getTime() - a.at.getTime())[0];
9165
9205
  return lastTransition?.at.toISOString() || "";
9166
9206
  }
9207
+ function isQueueCandidatePath(pathValue) {
9208
+ const normalized = pathValue.replace(/^\/+/, "").replace(/\\/g, "/");
9209
+ if (!normalized.toLowerCase().endsWith(".pkl")) {
9210
+ return false;
9211
+ }
9212
+ if (normalized.startsWith("uploads/") || normalized.includes("/uploads/")) {
9213
+ return false;
9214
+ }
9215
+ if (normalized.startsWith(".intermediate/") || normalized.includes("/.intermediate/")) {
9216
+ return false;
9217
+ }
9218
+ return true;
9219
+ }
9220
+ function isUploadPlaceholderPath(pathValue) {
9221
+ const normalized = pathValue.replace(/^\/+/, "").replace(/\\/g, "/").toLowerCase();
9222
+ if (!normalized.endsWith("-upload.pkl")) {
9223
+ return false;
9224
+ }
9225
+ return !normalized.includes("/");
9226
+ }
9227
+ async function listFilesWithMetadataCompat(provider, prefix, pattern) {
9228
+ const withMetadata = provider.listFilesWithMetadata;
9229
+ if (typeof withMetadata === "function") {
9230
+ return withMetadata.call(provider, prefix, pattern);
9231
+ }
9232
+ const listed = await provider.listFiles(prefix, pattern);
9233
+ return listed.map((pathValue) => ({
9234
+ path: pathValue,
9235
+ size: 1,
9236
+ updatedAt: null
9237
+ }));
9238
+ }
9239
+ async function materializeTranscriptFromStorage(outputStorage, transcriptPath) {
9240
+ const fileName = transcriptPath.split("/").pop() || "transcript.pkl";
9241
+ const tempPath = join(
9242
+ tmpdir(),
9243
+ `protokoll-queue-pkl-${Date.now()}-${Math.random().toString(36).slice(2)}-${fileName}`
9244
+ );
9245
+ const contents = await outputStorage.readFile(transcriptPath);
9246
+ await fs.writeFile(tempPath, contents);
9247
+ return tempPath;
9248
+ }
9249
+ async function findQueueTranscriptsFromStorage(outputStorage) {
9250
+ const files = await listFilesWithMetadataCompat(outputStorage, "", "-upload.pkl");
9251
+ const candidates = files.map((metadata) => ({ ...metadata, path: metadata.path.replace(/^\/+/, "").replace(/\\/g, "/") })).filter((metadata) => isQueueCandidatePath(metadata.path)).filter((metadata) => isUploadPlaceholderPath(metadata.path));
9252
+ const uploaded = [];
9253
+ const transcribing = [];
9254
+ for (const metadata of candidates) {
9255
+ let tempPath = null;
9256
+ try {
9257
+ tempPath = await materializeTranscriptFromStorage(outputStorage, metadata.path);
9258
+ const transcript = PklTranscript.open(tempPath, { readOnly: true });
9259
+ const transcriptMetadata = transcript.metadata;
9260
+ await transcript.close();
9261
+ if (transcriptMetadata.status === "uploaded") {
9262
+ uploaded.push({ uuid: transcriptMetadata.id, metadata: transcriptMetadata });
9263
+ } else if (transcriptMetadata.status === "transcribing") {
9264
+ transcribing.push({ uuid: transcriptMetadata.id, metadata: transcriptMetadata });
9265
+ }
9266
+ } catch {
9267
+ } finally {
9268
+ if (tempPath) {
9269
+ await fs.rm(tempPath, { force: true });
9270
+ }
9271
+ }
9272
+ }
9273
+ const sortByDateAsc = (a, b) => {
9274
+ const aTime = a.metadata.date?.getTime() || 0;
9275
+ const bTime = b.metadata.date?.getTime() || 0;
9276
+ return aTime - bTime;
9277
+ };
9278
+ return {
9279
+ uploaded: uploaded.sort(sortByDateAsc),
9280
+ transcribing: transcribing.sort(sortByDateAsc)
9281
+ };
9282
+ }
9167
9283
  async function findRecentTranscripts(searchDirectories, limit) {
9168
9284
  const results = [];
9169
9285
  const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1e3);
@@ -9202,8 +9318,17 @@ async function findRecentTranscripts(searchDirectories, limit) {
9202
9318
  }
9203
9319
  async function handleQueueStatus() {
9204
9320
  const outputDir = getOutputDirectory();
9205
- const uploaded = await findUploadedTranscripts([outputDir]);
9206
- const transcribing = await findTranscribingTranscripts([outputDir]);
9321
+ const outputStorage = getOutputStorage();
9322
+ let uploaded = [];
9323
+ let transcribing = [];
9324
+ if (outputStorage.name === "gcs") {
9325
+ const storageQueue = await findQueueTranscriptsFromStorage(outputStorage);
9326
+ uploaded = storageQueue.uploaded;
9327
+ transcribing = storageQueue.transcribing;
9328
+ } else {
9329
+ uploaded = await findUploadedTranscripts([outputDir]);
9330
+ transcribing = await findTranscribingTranscripts([outputDir]);
9331
+ }
9207
9332
  const recent = await findRecentTranscripts([outputDir], 10);
9208
9333
  return {
9209
9334
  pending: uploaded.map((t) => ({