@hiai-gg/docsmint 0.8.1 → 0.8.2

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.
Files changed (83) hide show
  1. package/README.md +34 -148
  2. package/dist/backend/index.js +396 -39
  3. package/dist/backend-account-runtime-cleanup.js +2 -0
  4. package/dist/backend-api-key-facade.js +2 -0
  5. package/dist/backend-pipeline-cancellation.js +2 -0
  6. package/dist/client.d.ts +2 -1
  7. package/dist/client.js +3 -0
  8. package/dist/frontend/{CategoryDialog-D_xFSWGh.js → CategoryDialog-B6zqtwE6.js} +2 -2
  9. package/dist/frontend/{ConfirmDialog-Cfq3D2Di.js → ConfirmDialog-Dk7wwcj9.js} +1 -1
  10. package/dist/frontend/{CreateSnapshotDialog-BHODxZWK.js → CreateSnapshotDialog-BPYjO1qN.js} +2 -2
  11. package/dist/frontend/{DatePicker-CZyCnQ1q.js → DatePicker-SiGLx9yh.js} +46 -46
  12. package/dist/frontend/{DeleteDialog-DaJOO6SB.js → DeleteDialog-D1jox8h1.js} +2 -2
  13. package/dist/frontend/{FolderNode-C_91NlEO.js → FolderNode-BG4l6I5C.js} +100 -99
  14. package/dist/frontend/{HiAiEditor-DtUGbQg6.js → HiAiEditor-BpmzvTCP.js} +977 -1008
  15. package/dist/frontend/{SettingsDialog-BlIZqk2-.js → SettingsDialog-2d6ZA4Ze.js} +3 -3
  16. package/dist/frontend/Sidebar-Db_ffhAb.js +2149 -0
  17. package/dist/frontend/{TagCreateDialog-BlGz4O9Z.js → TagCreateDialog-C5j3H49u.js} +2 -2
  18. package/dist/frontend/app-shell.js +3 -3
  19. package/dist/frontend/components/category-dialog.js +1 -1
  20. package/dist/frontend/components/create-snapshot-dialog.js +1 -1
  21. package/dist/frontend/components/delete-dialog.js +1 -1
  22. package/dist/frontend/components/editor/compact-editor.js +1 -1
  23. package/dist/frontend/components/editor/document-editor.js +1 -1
  24. package/dist/frontend/components/editor/extensions.js +1 -1
  25. package/dist/frontend/components/editor/markdown-toggle.js +1 -1
  26. package/dist/frontend/components/editor/markdown.js +1 -1
  27. package/dist/frontend/components/folder-node.js +1 -1
  28. package/dist/frontend/components/save-as-dialog.js +2 -2
  29. package/dist/frontend/components/settings.js +1 -1
  30. package/dist/frontend/components/sidebar.js +1 -1
  31. package/dist/frontend/components/tag-create-dialog.js +1 -1
  32. package/dist/frontend/components/version-history.js +3 -3
  33. package/dist/frontend/dashboard.js +1034 -1034
  34. package/dist/frontend/{dialog-tHAH3-9M.js → dialog-DC97Hnxo.js} +1 -1
  35. package/dist/frontend/dist-BLpDfrDC.js +316 -0
  36. package/dist/frontend/dist-D7zL6zdU.js +40 -0
  37. package/dist/frontend/{dist-Cg1BpNS7.js → dist-qd_OnyUC.js} +492 -516
  38. package/dist/frontend/dropdown-menu-COZwS7c0.js +493 -0
  39. package/dist/frontend/editorExtensions-Cu__YKW7.js +158 -0
  40. package/dist/frontend/ellipsis-vertical-CObXWXZW.js +41 -0
  41. package/dist/frontend/frontend.css +1 -1
  42. package/dist/frontend/{editorExtensions-B-mKiWqq.js → lib-DYgslUw1.js} +6782 -6935
  43. package/dist/frontend/loader-circle-DnyUzAmN.js +25 -0
  44. package/dist/frontend/{markdown-CMij27Gg.js → markdown-Co8ZAKf4.js} +2 -1
  45. package/dist/frontend/search.js +2 -2
  46. package/dist/frontend/{confirm-dialog-BFTxjLv-.js → share-context-DooXPYKd.js} +2 -3
  47. package/dist/frontend/theme.js +1 -1
  48. package/dist/frontend/{theme.svelte-Bzitjfh9.js → theme.svelte-gLPpG6eU.js} +1 -0
  49. package/dist/frontend-ssr/app-shell.js +3 -3
  50. package/dist/frontend-ssr/assets/{FolderNode-CClgk8BL.js → FolderNode-CscMQ9sf.js} +2 -1
  51. package/dist/frontend-ssr/assets/{HiAiEditor-_-m7MJDd.js → HiAiEditor-DCpulE6N.js} +4 -50
  52. package/dist/frontend-ssr/assets/{SettingsDialog-BdvHRAxF.js → SettingsDialog-CpPxZahd.js} +1 -1
  53. package/dist/frontend-ssr/assets/{Sidebar-B3Saqn0O.js → Sidebar-BJ1XZ1DH.js} +7 -5
  54. package/dist/frontend-ssr/assets/dist-CshZmNmp.js +50 -0
  55. package/dist/frontend-ssr/assets/dist-DqeAICTy.js +300 -0
  56. package/dist/frontend-ssr/assets/{dropdown-menu-CdEpn_LT.js → dropdown-menu--acO7nXs.js} +1 -94
  57. package/dist/frontend-ssr/assets/ellipsis-vertical-BErUuhWg.js +96 -0
  58. package/dist/frontend-ssr/assets/{markdown-MhPwNWrC.js → markdown-Co31-Tl2.js} +1 -1
  59. package/dist/frontend-ssr/assets/{confirm-dialog-DeOR0SnQ.js → share-context-BvXQrIIH.js} +0 -1
  60. package/dist/frontend-ssr/assets/{theme.svelte-shZrSxz8.js → theme.svelte-CGgnJc3k.js} +1 -0
  61. package/dist/frontend-ssr/components/editor/compact-editor.js +1 -1
  62. package/dist/frontend-ssr/components/editor/document-editor.js +1 -1
  63. package/dist/frontend-ssr/components/editor/extensions.js +1 -1
  64. package/dist/frontend-ssr/components/editor/markdown-toggle.js +1 -1
  65. package/dist/frontend-ssr/components/editor/markdown.js +1 -1
  66. package/dist/frontend-ssr/components/folder-node.js +1 -1
  67. package/dist/frontend-ssr/components/settings.js +1 -1
  68. package/dist/frontend-ssr/components/sidebar.js +1 -1
  69. package/dist/frontend-ssr/dashboard.js +6 -4
  70. package/dist/frontend-ssr/search.js +1 -1
  71. package/dist/frontend-ssr/theme.js +1 -1
  72. package/dist/lifecycle-runtime.js +2 -0
  73. package/dist/mcp-cli.js +4 -1
  74. package/dist/mcp-server.js +4 -1
  75. package/dist/types.d.ts +12 -0
  76. package/package.json +1 -1
  77. package/packages/cli/src/index.ts +1 -1
  78. package/packages/db/src/schema.ts +2 -0
  79. package/server.json +2 -2
  80. package/dist/frontend/Sidebar-BVGm5BRR.js +0 -2147
  81. package/dist/frontend/dropdown-menu-AJYeFPHu.js +0 -530
  82. package/dist/frontend-ssr/assets/{DatePicker-TZVeMZ2S.js → DatePicker-CCylVH0q.js} +12 -12
  83. package/dist/frontend-ssr/assets/{editorExtensions-qNlZVP98.js → editorExtensions-rhletNji.js} +1 -1
@@ -45791,6 +45791,8 @@ var init_schema2 = __esm(() => {
45791
45791
  completedBatches: integer("completed_batches").notNull().default(0),
45792
45792
  failedBatches: integer("failed_batches").notNull().default(0),
45793
45793
  errorCode: text("error_code"),
45794
+ graphErrorCode: text("graph_error_code"),
45795
+ summarizeErrorCode: text("summarize_error_code"),
45794
45796
  attempts: integer("attempts").notNull().default(0),
45795
45797
  requestedAt: timestamp("requested_at").defaultNow().notNull(),
45796
45798
  startedAt: timestamp("started_at"),
@@ -196414,7 +196416,8 @@ var basePipelineJobFields = {
196414
196416
  requestedAt: exports_external2.iso.datetime(),
196415
196417
  source: pipelineSourceSchema,
196416
196418
  refreshMode: pipelineRefreshModeSchema,
196417
- embeddingContextHash: exports_external2.string().min(1).optional()
196419
+ embeddingContextHash: exports_external2.string().min(1).optional(),
196420
+ warningRetry: exports_external2.literal(true).optional()
196418
196421
  };
196419
196422
  function normalizeLegacyPipelineJob(value) {
196420
196423
  if (!value || typeof value !== "object" || Array.isArray(value))
@@ -228647,6 +228650,33 @@ function encodeS3CopySource(bucket, key) {
228647
228650
  // ../../backend/src/api/routes/documents.ts
228648
228651
  init_logger3();
228649
228652
 
228653
+ // ../../backend/src/lib/pipeline-error.ts
228654
+ class PipelineProviderError extends Error {
228655
+ code;
228656
+ retryable;
228657
+ constructor(code2, retryable, message2 = code2) {
228658
+ super(message2);
228659
+ this.code = code2;
228660
+ this.retryable = retryable;
228661
+ this.name = "PipelineProviderError";
228662
+ }
228663
+ }
228664
+ function pipelineErrorCode(error53, fallback) {
228665
+ if (error53 instanceof PipelineProviderError)
228666
+ return error53.code;
228667
+ return fallback;
228668
+ }
228669
+ function isRetryablePipelineError(code2) {
228670
+ return new Set([
228671
+ "provider_failure",
228672
+ "provider_timeout",
228673
+ "invalid_provider_response",
228674
+ "queue_enqueue_failed",
228675
+ "age_unavailable",
228676
+ "age_persist_failed"
228677
+ ]).has(code2);
228678
+ }
228679
+
228650
228680
  // ../../backend/src/lib/version-prune.ts
228651
228681
  init_schema2();
228652
228682
  init_drizzle_orm();
@@ -228682,6 +228712,30 @@ async function maybePruneVersions(documentId) {
228682
228712
  }
228683
228713
 
228684
228714
  // ../../backend/src/api/routes/documents.ts
228715
+ init_names();
228716
+
228717
+ // ../../backend/src/queue/pipeline-warning-retry.ts
228718
+ function resolveActiveWarningRetry(stageStarted, retryJobExists) {
228719
+ if (!stageStarted)
228720
+ return "enqueue";
228721
+ return retryJobExists ? "deduplicate" : "conflict";
228722
+ }
228723
+ function warningRetryJobId(stage, generationId) {
228724
+ return `${stage}-warning-retry-${generationId}`;
228725
+ }
228726
+ function summaryJobIdForPipeline(job2) {
228727
+ return job2.warningRetry ? warningRetryJobId("summarize", job2.generationId) : JOB_IDS.summarize(job2.generationId, job2.workspaceId);
228728
+ }
228729
+ function planWarningStageRetry(state) {
228730
+ const graph = state.graphStatus === "failed" && isRetryablePipelineError(state.graphErrorCode ?? "graph_failed");
228731
+ const summarize = state.summarizeStatus === "failed" && isRetryablePipelineError(state.summarizeErrorCode ?? "summary_failed");
228732
+ if (!graph && !summarize)
228733
+ return null;
228734
+ return { graph, summarize, entryStage: graph ? "graph" : "summarize" };
228735
+ }
228736
+
228737
+ // ../../backend/src/api/routes/documents.ts
228738
+ init_queues();
228685
228739
  class DocumentPurgeNotFoundError extends Error {
228686
228740
  constructor() {
228687
228741
  super("document_purge_not_found");
@@ -229237,6 +229291,8 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
229237
229291
  embedStatus: documentPipelineRuns.embedStatus,
229238
229292
  graphStatus: documentPipelineRuns.graphStatus,
229239
229293
  summarizeStatus: documentPipelineRuns.summarizeStatus,
229294
+ graphErrorCode: documentPipelineRuns.graphErrorCode,
229295
+ summarizeErrorCode: documentPipelineRuns.summarizeErrorCode,
229240
229296
  finalizeStatus: documentPipelineRuns.finalizeStatus,
229241
229297
  totalBatches: documentPipelineRuns.totalBatches,
229242
229298
  completedBatches: documentPipelineRuns.completedBatches,
@@ -229266,6 +229322,22 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
229266
229322
  completed: run3.completedBatches,
229267
229323
  failed: run3.failedBatches
229268
229324
  },
229325
+ warnings: [
229326
+ ...run3.graphStatus === "failed" ? [
229327
+ {
229328
+ stage: "graph",
229329
+ code: run3.graphErrorCode ?? "graph_failed",
229330
+ retryable: isRetryablePipelineError(run3.graphErrorCode ?? "graph_failed")
229331
+ }
229332
+ ] : [],
229333
+ ...run3.summarizeStatus === "failed" ? [
229334
+ {
229335
+ stage: "summarize",
229336
+ code: run3.summarizeErrorCode ?? "summary_failed",
229337
+ retryable: isRetryablePipelineError(run3.summarizeErrorCode ?? "summary_failed")
229338
+ }
229339
+ ] : []
229340
+ ],
229269
229341
  updatedAt: run3.updatedAt
229270
229342
  };
229271
229343
  } catch (err) {
@@ -229273,6 +229345,194 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
229273
229345
  set2.status = 500;
229274
229346
  return { error: "Failed to load pipeline progress" };
229275
229347
  }
229348
+ }).post("/documents/:id/pipeline/retry-warnings", async ({ params: params2, set: set2, request }) => {
229349
+ const parsedParams = documentIdParamsSchema.safeParse(params2);
229350
+ if (!parsedParams.success) {
229351
+ set2.status = 400;
229352
+ return { error: "Invalid document id" };
229353
+ }
229354
+ const documentId = parsedParams.data.id;
229355
+ const ip = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? request.headers.get("x-real-ip") ?? "unknown";
229356
+ const rl = await writeRateLimiter(ip, request);
229357
+ if (!rl.allowed) {
229358
+ set2.status = 429;
229359
+ set2.headers = rateLimitHeaders(0, rl.retryAfter);
229360
+ return { error: "Too many requests" };
229361
+ }
229362
+ set2.headers = rateLimitHeaders(rl.remaining);
229363
+ const access = await resolveContentAccess(request);
229364
+ const ctx = access.ctx;
229365
+ if (ctx.role === "none") {
229366
+ set2.status = 401;
229367
+ return { error: "Unauthorized" };
229368
+ }
229369
+ if (!canAccessContent(access, "write")) {
229370
+ set2.status = 403;
229371
+ return { error: "Forbidden" };
229372
+ }
229373
+ let claimed;
229374
+ try {
229375
+ const retry2 = await withTenant(ctx, async (tx) => {
229376
+ await acquireDocumentPipelineLock(tx, documentId);
229377
+ const [run3] = await tx.select({
229378
+ status: documentPipelineRuns.status,
229379
+ generationId: documentPipelineRuns.generationId,
229380
+ revision: documentPipelineRuns.revision,
229381
+ ownerId: documentPipelineRuns.ownerId,
229382
+ workspaceId: documentPipelineRuns.workspaceId,
229383
+ refreshMode: documentPipelineRuns.refreshMode,
229384
+ embeddingContextHash: documentPipelineRuns.embeddingContextHash,
229385
+ embedStatus: documentPipelineRuns.embedStatus,
229386
+ graphStatus: documentPipelineRuns.graphStatus,
229387
+ summarizeStatus: documentPipelineRuns.summarizeStatus,
229388
+ graphErrorCode: documentPipelineRuns.graphErrorCode,
229389
+ summarizeErrorCode: documentPipelineRuns.summarizeErrorCode
229390
+ }).from(documentPipelineRuns).innerJoin(documents, and(eq(documents.id, documentPipelineRuns.documentId), eq(documents.activeEmbeddingGeneration, documentPipelineRuns.generationId), eq(documents.contentHash, documentPipelineRuns.revision))).where(and(eq(documentPipelineRuns.documentId, documentId), tenantOwnerCondition(documents.ownerId, documents.workspaceId, ctx), eq(documentPipelineRuns.embedStatus, "ready"), isNull(documents.deletedAt), ...access.restricted ? [
229391
+ access.categoryId ? effectiveDocumentCategoryCondition(documents.categoryId, documents.folderId, ctx, access.categoryId) : sql`false`
229392
+ ] : [])).orderBy(desc(documentPipelineRuns.updatedAt)).limit(1).for("update");
229393
+ if (!run3)
229394
+ return null;
229395
+ if (run3.status === "processing") {
229396
+ const graph2 = ["retrying", "processing"].includes(run3.graphStatus);
229397
+ const summarize2 = ["retrying", "processing"].includes(run3.summarizeStatus);
229398
+ return graph2 || summarize2 ? {
229399
+ ...run3,
229400
+ graph: graph2,
229401
+ summarize: summarize2,
229402
+ deduplicated: true,
229403
+ stageStarted: run3.graphStatus === "processing" || run3.summarizeStatus === "processing"
229404
+ } : null;
229405
+ }
229406
+ if (run3.status !== "ready_with_warnings")
229407
+ return null;
229408
+ const plan = planWarningStageRetry(run3);
229409
+ if (!plan)
229410
+ return null;
229411
+ const { graph, summarize } = plan;
229412
+ await tx.update(documentPipelineRuns).set({
229413
+ status: "processing",
229414
+ finalizeStatus: "pending",
229415
+ ...graph ? { graphStatus: "retrying", graphErrorCode: null } : {},
229416
+ ...summarize ? {
229417
+ summarizeStatus: "retrying",
229418
+ summarizeErrorCode: null
229419
+ } : {},
229420
+ updatedAt: new Date
229421
+ }).where(eq(documentPipelineRuns.generationId, run3.generationId));
229422
+ return {
229423
+ ...run3,
229424
+ graph,
229425
+ summarize,
229426
+ deduplicated: false,
229427
+ stageStarted: false
229428
+ };
229429
+ });
229430
+ if (!retry2) {
229431
+ set2.status = 409;
229432
+ return { error: "No retryable warning stages" };
229433
+ }
229434
+ if (!retry2.deduplicated)
229435
+ claimed = retry2;
229436
+ const stage = retry2.graph ? "graph" : "summarize";
229437
+ const retryJobId = warningRetryJobId(stage, retry2.generationId);
229438
+ const staleJobs = [
229439
+ ...retry2.graph ? [
229440
+ [
229441
+ "graph",
229442
+ JOB_IDS.graph(retry2.generationId, retry2.workspaceId ?? undefined)
229443
+ ]
229444
+ ] : [],
229445
+ ...retry2.summarize ? [
229446
+ [
229447
+ "summarize",
229448
+ JOB_IDS.summarize(retry2.generationId, retry2.workspaceId ?? undefined)
229449
+ ]
229450
+ ] : [],
229451
+ [
229452
+ "finalize",
229453
+ JOB_IDS.finalize(retry2.generationId, retry2.workspaceId ?? undefined)
229454
+ ]
229455
+ ];
229456
+ if (!retry2.deduplicated) {
229457
+ for (const [queuedStage, jobId] of staleJobs) {
229458
+ const oldJob = await getPipelineQueue(queuedStage, config3.REDIS_URL).getJob(jobId);
229459
+ await oldJob?.remove();
229460
+ }
229461
+ }
229462
+ const queue2 = getPipelineQueue(stage, config3.REDIS_URL);
229463
+ const existingRetryJob = await queue2.getJob(retryJobId);
229464
+ if (retry2.deduplicated) {
229465
+ const activeRetry = resolveActiveWarningRetry(retry2.stageStarted, Boolean(existingRetryJob));
229466
+ if (activeRetry === "conflict") {
229467
+ set2.status = 409;
229468
+ return { error: "No retryable warning stages" };
229469
+ }
229470
+ if (activeRetry === "deduplicate")
229471
+ return {
229472
+ documentId,
229473
+ generationId: retry2.generationId,
229474
+ deduplicated: true,
229475
+ retriedStages: [
229476
+ ...retry2.graph ? ["graph"] : [],
229477
+ ...retry2.summarize ? ["summarize"] : []
229478
+ ]
229479
+ };
229480
+ }
229481
+ const existingRetryState = await existingRetryJob?.getState();
229482
+ if (existingRetryState === "completed" || existingRetryState === "failed") {
229483
+ await existingRetryJob?.remove();
229484
+ }
229485
+ const data = {
229486
+ schemaVersion: PIPELINE_SCHEMA_VERSION,
229487
+ stage,
229488
+ documentId,
229489
+ ownerId: retry2.ownerId,
229490
+ ...retry2.workspaceId ? { workspaceId: retry2.workspaceId } : {},
229491
+ generationId: retry2.generationId,
229492
+ revision: retry2.revision,
229493
+ requestedAt: new Date().toISOString(),
229494
+ source: "api",
229495
+ refreshMode: retry2.refreshMode === "incremental" ? "incremental" : "full",
229496
+ ...retry2.embeddingContextHash ? { embeddingContextHash: retry2.embeddingContextHash } : {},
229497
+ warningRetry: true
229498
+ };
229499
+ await queue2.add(stage, data, {
229500
+ ...DEFAULT_JOB_OPTIONS,
229501
+ jobId: retryJobId,
229502
+ priority: SOURCE_PRIORITY.api
229503
+ });
229504
+ return {
229505
+ documentId,
229506
+ generationId: retry2.generationId,
229507
+ deduplicated: retry2.deduplicated,
229508
+ retriedStages: [
229509
+ ...retry2.graph ? ["graph"] : [],
229510
+ ...retry2.summarize ? ["summarize"] : []
229511
+ ]
229512
+ };
229513
+ } catch (err) {
229514
+ const failedClaim = claimed;
229515
+ if (failedClaim) {
229516
+ await withTenant(ctx, (tx) => tx.update(documentPipelineRuns).set({
229517
+ status: "ready_with_warnings",
229518
+ finalizeStatus: "ready",
229519
+ ...failedClaim.graph ? {
229520
+ graphStatus: "failed",
229521
+ graphErrorCode: "queue_enqueue_failed"
229522
+ } : {},
229523
+ ...failedClaim.summarize ? {
229524
+ summarizeStatus: "failed",
229525
+ summarizeErrorCode: "queue_enqueue_failed"
229526
+ } : {},
229527
+ updatedAt: new Date
229528
+ }).where(and(eq(documentPipelineRuns.generationId, failedClaim.generationId), eq(documentPipelineRuns.status, "processing")))).catch(() => {
229529
+ return;
229530
+ });
229531
+ }
229532
+ logger3.error({ err, documentId }, "Failed to retry warning stages");
229533
+ set2.status = 500;
229534
+ return { error: "Failed to retry warning stages" };
229535
+ }
229276
229536
  }).get("/documents/:id/knowledge-summary", async ({ params: params2, set: set2, request }) => {
229277
229537
  const parsedParams = documentIdParamsSchema.safeParse(params2);
229278
229538
  if (!parsedParams.success) {
@@ -229362,6 +229622,8 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
229362
229622
  embedStatus: documentPipelineRuns.embedStatus,
229363
229623
  graphStatus: documentPipelineRuns.graphStatus,
229364
229624
  summarizeStatus: documentPipelineRuns.summarizeStatus,
229625
+ graphErrorCode: documentPipelineRuns.graphErrorCode,
229626
+ summarizeErrorCode: documentPipelineRuns.summarizeErrorCode,
229365
229627
  finalizeStatus: documentPipelineRuns.finalizeStatus,
229366
229628
  totalBatches: documentPipelineRuns.totalBatches,
229367
229629
  completedBatches: documentPipelineRuns.completedBatches,
@@ -229405,6 +229667,22 @@ var documentRoutes = new Elysia({ prefix: "/api" }).get("/documents", async ({ q
229405
229667
  completed: run3.completedBatches,
229406
229668
  failed: run3.failedBatches
229407
229669
  },
229670
+ warnings: [
229671
+ ...run3.graphStatus === "failed" ? [
229672
+ {
229673
+ stage: "graph",
229674
+ code: run3.graphErrorCode ?? "graph_failed",
229675
+ retryable: isRetryablePipelineError(run3.graphErrorCode ?? "graph_failed")
229676
+ }
229677
+ ] : [],
229678
+ ...run3.summarizeStatus === "failed" ? [
229679
+ {
229680
+ stage: "summarize",
229681
+ code: run3.summarizeErrorCode ?? "summary_failed",
229682
+ retryable: isRetryablePipelineError(run3.summarizeErrorCode ?? "summary_failed")
229683
+ }
229684
+ ] : []
229685
+ ],
229408
229686
  updatedAt: run3.updatedAt
229409
229687
  } : null
229410
229688
  };
@@ -232252,22 +232530,40 @@ function uniqueChatProviders(providers) {
232252
232530
  return unique;
232253
232531
  }
232254
232532
  async function requestStructuredChat(options) {
232533
+ const attempt = await requestStructuredChatDetailed(options);
232534
+ return attempt.ok ? attempt.result : null;
232535
+ }
232536
+ async function requestStructuredChatDetailed(options) {
232255
232537
  const providers = uniqueChatProviders([
232256
232538
  options.primary,
232257
232539
  options.fallback,
232258
232540
  ...options.fallbacks ?? []
232259
232541
  ]);
232542
+ let lastError = new PipelineProviderError("provider_unavailable", false, "No chat provider was configured");
232260
232543
  for (const provider of providers) {
232261
232544
  try {
232262
232545
  const raw = await requestChatContent(provider, options);
232263
- const parsed = parseJson(raw);
232546
+ let parsed;
232547
+ try {
232548
+ parsed = parseJson(raw);
232549
+ } catch {
232550
+ lastError = new PipelineProviderError("invalid_provider_response", true);
232551
+ continue;
232552
+ }
232264
232553
  const result = options.outputSchema.safeParse(parsed);
232265
- if (!result.success)
232554
+ if (!result.success) {
232555
+ lastError = new PipelineProviderError("permanent_validation_failure", false);
232266
232556
  continue;
232267
- return { data: result.data, model: provider.model };
232268
- } catch {}
232557
+ }
232558
+ return {
232559
+ ok: true,
232560
+ result: { data: result.data, model: provider.model }
232561
+ };
232562
+ } catch (error53) {
232563
+ lastError = error53 instanceof PipelineProviderError ? error53 : error53 instanceof Error && error53.name === "AbortError" ? new PipelineProviderError("provider_timeout", true) : new PipelineProviderError("provider_failure", true);
232564
+ }
232269
232565
  }
232270
- return null;
232566
+ return { ok: false, error: lastError };
232271
232567
  }
232272
232568
  async function requestChatContent(provider, options) {
232273
232569
  const controller = new AbortController;
@@ -232292,11 +232588,16 @@ async function requestChatContent(provider, options) {
232292
232588
  })
232293
232589
  });
232294
232590
  if (!response.ok)
232295
- throw new Error(`chat provider returned ${response.status}`);
232296
- const body = await response.json();
232591
+ throw new PipelineProviderError("provider_failure", true, `chat provider returned ${response.status}`);
232592
+ let body;
232593
+ try {
232594
+ body = await response.json();
232595
+ } catch {
232596
+ throw new PipelineProviderError("invalid_provider_response", true);
232597
+ }
232297
232598
  const content50 = body.choices?.[0]?.message?.content;
232298
232599
  if (typeof content50 !== "string" || !content50.trim()) {
232299
- throw new Error("chat provider returned empty content");
232600
+ throw new PipelineProviderError("invalid_provider_response", true);
232300
232601
  }
232301
232602
  return content50;
232302
232603
  } finally {
@@ -232573,18 +232874,21 @@ function rerankUrl(baseUrl) {
232573
232874
  return normalized.endsWith("/rerank") ? normalized : `${normalized}/rerank`;
232574
232875
  }
232575
232876
  async function requestRerank(input) {
232576
- const documents2 = input.candidates.map((candidate) => candidate.text.trim()).filter((text4) => text4.length > 0);
232577
- if (documents2.length === 0)
232877
+ const candidates = input.candidates.map((candidate, originalIndex) => ({
232878
+ id: candidate.id,
232879
+ document: candidate.text.trim(),
232880
+ originalIndex
232881
+ })).filter((candidate) => candidate.document.length > 0);
232882
+ if (candidates.length === 0)
232578
232883
  return null;
232579
- const ids = input.candidates.map((candidate) => candidate.id);
232580
- const topN = Math.min(input.topN ?? config3.SEARCH_RERANK_TOP_N, documents2.length);
232884
+ const topN = Math.min(input.topN ?? config3.SEARCH_RERANK_TOP_N, candidates.length);
232581
232885
  const timeoutMs = config3.SEARCH_RERANK_TIMEOUT_MS;
232582
232886
  const started = performance.now();
232583
- incrementCounterBy(METRIC_NAMES.SEARCH_RERANK_CANDIDATES_TOTAL, documents2.length);
232887
+ incrementCounterBy(METRIC_NAMES.SEARCH_RERANK_CANDIDATES_TOTAL, candidates.length);
232584
232888
  try {
232585
232889
  for (const slot of configuredSlots()) {
232586
232890
  try {
232587
- const hits = await callRerankSlot(slot, input.query, documents2, ids, {
232891
+ const hits = await callRerankSlot(slot, input.query, candidates, {
232588
232892
  topN,
232589
232893
  timeoutMs
232590
232894
  });
@@ -232602,7 +232906,7 @@ async function requestRerank(input) {
232602
232906
  recordDuration(METRIC_NAMES.SEARCH_RERANK_DURATION_MS, performance.now() - started);
232603
232907
  }
232604
232908
  }
232605
- async function callRerankSlot(slot, query, documents2, ids, options) {
232909
+ async function callRerankSlot(slot, query, candidates, options) {
232606
232910
  const controller = new AbortController;
232607
232911
  const timer2 = setTimeout(() => controller.abort(), options.timeoutMs);
232608
232912
  const headers = {
@@ -232618,7 +232922,7 @@ async function callRerankSlot(slot, query, documents2, ids, options) {
232618
232922
  body: JSON.stringify({
232619
232923
  model: slot.model,
232620
232924
  query,
232621
- documents: documents2,
232925
+ documents: candidates.map((candidate) => candidate.document),
232622
232926
  top_n: options.topN
232623
232927
  })
232624
232928
  });
@@ -232627,14 +232931,16 @@ async function callRerankSlot(slot, query, documents2, ids, options) {
232627
232931
  }
232628
232932
  const body = await response.json();
232629
232933
  const hits = [];
232934
+ const seenIds = new Set;
232630
232935
  for (const [rank, row] of (body.results ?? []).entries()) {
232631
- if (typeof row.index !== "number" || row.index < 0)
232936
+ if (typeof row.index !== "number" || !Number.isInteger(row.index) || row.index < 0)
232632
232937
  continue;
232633
- const id3 = ids[row.index];
232634
- if (!id3)
232938
+ const candidate = candidates[row.index];
232939
+ if (!candidate || seenIds.has(candidate.id))
232635
232940
  continue;
232941
+ seenIds.add(candidate.id);
232636
232942
  const score = typeof row.relevance_score === "number" && Number.isFinite(row.relevance_score) ? row.relevance_score : 0;
232637
- hits.push({ id: id3, score, rank: rank + 1 });
232943
+ hits.push({ id: candidate.id, score, rank: rank + 1 });
232638
232944
  }
232639
232945
  return hits;
232640
232946
  } finally {
@@ -235895,7 +236201,11 @@ async function extractEntities(chunkText2, documentId, options = {}) {
235895
236201
  } catch (err) {
235896
236202
  await releaseDedupClaim();
235897
236203
  logger3.warn({ err, documentId }, "Entity extraction LLM call failed \u2014 skipping");
235898
- return { status: "failed", warning: "provider_failed", entities: [] };
236204
+ return {
236205
+ status: "failed",
236206
+ warning: err instanceof Error && err.message ? err.message : "provider_failure",
236207
+ entities: []
236208
+ };
235899
236209
  }
235900
236210
  }
235901
236211
  if (entities === null) {
@@ -235994,7 +236304,7 @@ async function callEntityExtractionLLM(text4, options) {
235994
236304
  const [primary, ...fallbacks] = providers;
235995
236305
  if (!primary)
235996
236306
  return null;
235997
- const result = await requestStructuredChat({
236307
+ const attempt = await requestStructuredChatDetailed({
235998
236308
  primary,
235999
236309
  fallbacks,
236000
236310
  messages: [
@@ -236005,7 +236315,9 @@ async function callEntityExtractionLLM(text4, options) {
236005
236315
  maxTokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,
236006
236316
  temperature: options.temperature ?? DEFAULT_TEMPERATURE
236007
236317
  });
236008
- return result ? parseExtractionResponse(JSON.stringify(result.data)) : null;
236318
+ if (!attempt.ok)
236319
+ throw attempt.error;
236320
+ return parseExtractionResponse(JSON.stringify(attempt.result.data));
236009
236321
  }
236010
236322
  function resolveGraphProviderKey(baseUrl, explicitKey) {
236011
236323
  return resolveChatProviderKey(baseUrl, explicitKey, config3.OPENROUTER_API_KEY);
@@ -236199,11 +236511,11 @@ async function buildKnowledgeSummary(document2, provider) {
236199
236511
  }
236200
236512
  const output = await provider(document2);
236201
236513
  if (!output)
236202
- throw new Error("summary_provider_failed");
236514
+ throw new Error("provider_failure");
236203
236515
  const language = output.language.trim();
236204
236516
  const description = output.description.trim();
236205
236517
  if (!language || !description)
236206
- throw new Error("summary_provider_invalid");
236518
+ throw new Error("permanent_validation_failure");
236207
236519
  const seen = new Set;
236208
236520
  const keywords = [];
236209
236521
  for (const raw of output.keywords) {
@@ -236258,7 +236570,7 @@ async function requestKnowledgeSummary(document2) {
236258
236570
  provider(config3.GRAPH_EXTRACT_FALLBACK_BASE_URL, config3.GRAPH_EXTRACT_FALLBACK_API_KEY, config3.GRAPH_EXTRACT_FALLBACK_MODEL),
236259
236571
  provider(config3.GRAPH_EXTRACT_FALLBACK_2_BASE_URL, config3.GRAPH_EXTRACT_FALLBACK_2_API_KEY, config3.GRAPH_EXTRACT_FALLBACK_2_MODEL)
236260
236572
  ].filter((item) => Boolean(item));
236261
- const result = await requestStructuredChat({
236573
+ const attempt = await requestStructuredChatDetailed({
236262
236574
  primary,
236263
236575
  fallbacks,
236264
236576
  messages: [
@@ -236274,7 +236586,9 @@ ${document2.content.slice(0, 50000)}`
236274
236586
  maxTokens: 768,
236275
236587
  temperature: 0
236276
236588
  });
236277
- return result ? { ...result.data, model: result.model } : null;
236589
+ if (!attempt.ok)
236590
+ throw attempt.error;
236591
+ return { ...attempt.result.data, model: attempt.result.model };
236278
236592
  }
236279
236593
 
236280
236594
  // ../../backend/src/queue/document-revision.ts
@@ -236775,6 +237089,14 @@ function stagePatch(stage, status2) {
236775
237089
  return { summarizeStatus: status2 };
236776
237090
  return { finalizeStatus: status2 };
236777
237091
  }
237092
+ function stageErrorPatch(stage, status2, errorCode) {
237093
+ const value = status2 === "failed" ? errorCode ?? "unknown_failure" : null;
237094
+ if (stage === "graph")
237095
+ return { graphErrorCode: value };
237096
+ if (stage === "summarize")
237097
+ return { summarizeErrorCode: value };
237098
+ return {};
237099
+ }
236778
237100
  function pipelineStageStatus(value) {
236779
237101
  return value === "ready_with_warnings" ? "failed" : value;
236780
237102
  }
@@ -236804,6 +237126,7 @@ async function cancelStalePipelineRun(job2, errorCode = "stale_revision") {
236804
237126
  async function setStageStatus(generationId, stage, status2, errorCode) {
236805
237127
  await withTenant(admin, (tx) => tx.update(documentPipelineRuns).set({
236806
237128
  ...stagePatch(stage, status2),
237129
+ ...stageErrorPatch(stage, status2, errorCode),
236807
237130
  ...errorCode ? { errorCode } : {},
236808
237131
  updatedAt: new Date
236809
237132
  }).where(and(eq(documentPipelineRuns.generationId, generationId), ne(documentPipelineRuns.status, "cancelled"))));
@@ -237114,7 +237437,8 @@ function createPipelineStageDependencies(redisUrl) {
237114
237437
  generationId: run3.generationId,
237115
237438
  revision: run3.revision,
237116
237439
  embeddingContextHash: run3.embeddingContextHash ?? undefined,
237117
- embedStatus: pipelineStageStatus(run3.embedStatus)
237440
+ embedStatus: pipelineStageStatus(run3.embedStatus),
237441
+ summarizeStatus: pipelineStageStatus(run3.summarizeStatus)
237118
237442
  } : null;
237119
237443
  },
237120
237444
  async extract(job2) {
@@ -237142,7 +237466,15 @@ function createPipelineStageDependencies(redisUrl) {
237142
237466
  const data = { ...job2, stage: "summarize" };
237143
237467
  await enqueueIfActive("summarize", "summarize", data, {
237144
237468
  ...DEFAULT_JOB_OPTIONS,
237145
- jobId: JOB_IDS.summarize(job2.generationId, job2.workspaceId),
237469
+ jobId: summaryJobIdForPipeline(job2),
237470
+ priority: SOURCE_PRIORITY[job2.source]
237471
+ });
237472
+ },
237473
+ async enqueueFinalize(job2) {
237474
+ const data = { ...job2, stage: "finalize" };
237475
+ await enqueueIfActive("finalize", "finalize", data, {
237476
+ ...DEFAULT_JOB_OPTIONS,
237477
+ jobId: `${JOB_IDS.finalize(job2.generationId, job2.workspaceId)}-graph-retry-${job2.requestedAt.replace(/\D/g, "")}`,
237146
237478
  priority: SOURCE_PRIORITY[job2.source]
237147
237479
  });
237148
237480
  }
@@ -237637,10 +237969,12 @@ async function processSummaryStage(job2, dependencies) {
237637
237969
  } catch (error53) {
237638
237970
  if (!await continueIfCurrent())
237639
237971
  return;
237640
- await dependencies.setSummaryStatus(job2.generationId, "failed", error53 instanceof Error ? error53.name : "summary_failed");
237972
+ await dependencies.setSummaryStatus(job2.generationId, "failed", pipelineErrorCode(error53, "summary_failed"));
237641
237973
  if (!await continueIfCurrent())
237642
237974
  return;
237643
237975
  await dependencies.enqueueFinalize(job2);
237976
+ if (error53 instanceof PipelineProviderError && error53.retryable)
237977
+ throw error53;
237644
237978
  return;
237645
237979
  }
237646
237980
  if (!await continueIfCurrent())
@@ -237655,7 +237989,7 @@ async function processGraphStageFailure(job2, error53, dependencies) {
237655
237989
  }
237656
237990
  if (await dependencies.isCancelled?.(job2))
237657
237991
  throw error53;
237658
- await dependencies.setGraphStatus(job2.generationId, "failed", error53 instanceof Error ? error53.name : "graph_failed");
237992
+ await dependencies.setGraphStatus(job2.generationId, "failed", pipelineErrorCode(error53, "graph_failed"));
237659
237993
  if (!await dependencies.isCancelled?.(job2)) {
237660
237994
  await dependencies.enqueueSummarize(job2);
237661
237995
  }
@@ -237689,6 +238023,18 @@ function createGraphWorker(deps) {
237689
238023
  const run3 = await deps.getRun(job2);
237690
238024
  if (!run3)
237691
238025
  throw new Error("Pipeline run not found");
238026
+ const continuePipeline = async () => {
238027
+ if (await deps.isCancelled?.(job2))
238028
+ return;
238029
+ if (run3.summarizeStatus === "ready" || run3.summarizeStatus === "skipped") {
238030
+ if (deps.enqueueFinalize)
238031
+ await deps.enqueueFinalize(job2);
238032
+ else
238033
+ await deps.enqueueSummarize(job2);
238034
+ return;
238035
+ }
238036
+ await deps.enqueueSummarize(job2);
238037
+ };
237692
238038
  if (run3.ownerId !== job2.ownerId || run3.documentId !== job2.documentId) {
237693
238039
  throw new Error("Pipeline owner mismatch");
237694
238040
  }
@@ -237706,8 +238052,7 @@ function createGraphWorker(deps) {
237706
238052
  if (await deps.isCancelled?.(job2))
237707
238053
  return;
237708
238054
  await deps.setGraphStatus(job2.generationId, "skipped", "embedding_not_ready");
237709
- if (!await deps.isCancelled?.(job2))
237710
- await deps.enqueueSummarize(job2);
238055
+ await continuePipeline();
237711
238056
  return;
237712
238057
  }
237713
238058
  if (await deps.isCancelled?.(job2))
@@ -237727,15 +238072,27 @@ function createGraphWorker(deps) {
237727
238072
  return;
237728
238073
  }
237729
238074
  if (outcome.status === "unavailable" || outcome.status === "failed") {
238075
+ if (outcome.status === "failed" && [
238076
+ "provider_failure",
238077
+ "provider_timeout",
238078
+ "invalid_provider_response"
238079
+ ].includes(outcome.warning)) {
238080
+ throw new PipelineProviderError(outcome.warning, true);
238081
+ }
237730
238082
  await deps.setGraphStatus(job2.generationId, "failed", outcome.warning);
237731
- if (!await deps.isCancelled?.(job2))
237732
- await deps.enqueueSummarize(job2);
238083
+ await continuePipeline();
237733
238084
  return;
237734
238085
  }
237735
238086
  await deps.setGraphStatus(job2.generationId, "ready");
237736
- if (!await deps.isCancelled?.(job2))
237737
- await deps.enqueueSummarize(job2);
238087
+ await continuePipeline();
237738
238088
  } catch (error53) {
238089
+ if (error53 instanceof PipelineProviderError) {
238090
+ await deps.setGraphStatus(job2.generationId, "failed", error53.code);
238091
+ await continuePipeline();
238092
+ if (error53.retryable)
238093
+ throw error53;
238094
+ return;
238095
+ }
237739
238096
  await processGraphStageFailure(job2, error53, deps);
237740
238097
  }
237741
238098
  };
@@ -237942,7 +238299,7 @@ var swaggerConfig = {
237942
238299
  },
237943
238300
  info: {
237944
238301
  title: "DocsMint API",
237945
- version: "0.8.1",
238302
+ version: "0.8.2",
237946
238303
  description: "Self-hosted AI-native knowledge workspace and installable PWA with hybrid search, GraphRAG, REST, SDK, CLI, and MCP access for people and AI agents.",
237947
238304
  contact: { name: "HiAi-gg", url: "https://github.com/HiAi-gg/docsmint" },
237948
238305
  license: {
@@ -284,6 +284,8 @@ var documentPipelineRuns = pgTable("document_pipeline_runs", {
284
284
  completedBatches: integer("completed_batches").notNull().default(0),
285
285
  failedBatches: integer("failed_batches").notNull().default(0),
286
286
  errorCode: text("error_code"),
287
+ graphErrorCode: text("graph_error_code"),
288
+ summarizeErrorCode: text("summarize_error_code"),
287
289
  attempts: integer("attempts").notNull().default(0),
288
290
  requestedAt: timestamp("requested_at").defaultNow().notNull(),
289
291
  startedAt: timestamp("started_at"),