@autorag/librarian 2.0.0 → 2.1.0

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 (50) hide show
  1. package/README.md +45 -1
  2. package/dist/agent/search-documents.d.ts +1 -0
  3. package/dist/cli/config.d.ts +6 -0
  4. package/dist/cli/index.js +3423 -306
  5. package/dist/datasource/chunk-store.d.ts +67 -0
  6. package/dist/datasource/connector-skill.d.ts +107 -0
  7. package/dist/datasource/connector.d.ts +72 -0
  8. package/dist/datasource/http.d.ts +49 -0
  9. package/dist/datasource/index.d.ts +14 -0
  10. package/dist/datasource/skills/discord/connector.d.ts +30 -0
  11. package/dist/datasource/skills/discord/index.d.ts +2 -0
  12. package/dist/datasource/skills/discord/skill.d.ts +20 -0
  13. package/dist/datasource/skills/factory.d.ts +35 -0
  14. package/dist/datasource/skills/gdrive/connector.d.ts +30 -0
  15. package/dist/datasource/skills/gdrive/index.d.ts +3 -0
  16. package/dist/datasource/skills/gdrive/rclone-connector.d.ts +51 -0
  17. package/dist/datasource/skills/gdrive/skill.d.ts +24 -0
  18. package/dist/datasource/skills/github/connector.d.ts +28 -0
  19. package/dist/datasource/skills/github/index.d.ts +2 -0
  20. package/dist/datasource/skills/github/skill.d.ts +20 -0
  21. package/dist/datasource/skills/gmail/connector.d.ts +30 -0
  22. package/dist/datasource/skills/gmail/himalaya-connector.d.ts +40 -0
  23. package/dist/datasource/skills/gmail/index.d.ts +3 -0
  24. package/dist/datasource/skills/gmail/skill.d.ts +24 -0
  25. package/dist/datasource/skills/mail-export/connector.d.ts +21 -0
  26. package/dist/datasource/skills/mail-export/index.d.ts +2 -0
  27. package/dist/datasource/skills/mail-export/skill.d.ts +20 -0
  28. package/dist/datasource/skills/notion/connector.d.ts +26 -0
  29. package/dist/datasource/skills/notion/index.d.ts +2 -0
  30. package/dist/datasource/skills/notion/skill.d.ts +20 -0
  31. package/dist/datasource/skills/obsidian/connector.d.ts +22 -0
  32. package/dist/datasource/skills/obsidian/index.d.ts +2 -0
  33. package/dist/datasource/skills/obsidian/skill.d.ts +20 -0
  34. package/dist/datasource/skills/rss/connector.d.ts +28 -0
  35. package/dist/datasource/skills/rss/index.d.ts +2 -0
  36. package/dist/datasource/skills/rss/skill.d.ts +22 -0
  37. package/dist/datasource/skills/slack/connector.d.ts +28 -0
  38. package/dist/datasource/skills/slack/index.d.ts +2 -0
  39. package/dist/datasource/skills/slack/skill.d.ts +20 -0
  40. package/dist/datasource/skills/spotlight/connector.d.ts +59 -0
  41. package/dist/datasource/skills/spotlight/index.d.ts +2 -0
  42. package/dist/datasource/skills/spotlight/skill.d.ts +20 -0
  43. package/dist/index.js +3267 -152
  44. package/dist/parser/defaults.d.ts +2 -0
  45. package/dist/parser/hwp-markdown.d.ts +25 -0
  46. package/dist/parser/hwp.d.ts +7 -0
  47. package/dist/parser/index.d.ts +2 -1
  48. package/dist/parser/rhwp-adapter.d.ts +30 -0
  49. package/package.json +3 -2
  50. package/skills/autorag-setup/SKILL.md +52 -0
package/dist/index.js CHANGED
@@ -2582,6 +2582,477 @@ var init_eml = __esm(() => {
2582
2582
  };
2583
2583
  });
2584
2584
 
2585
+ // src/parser/hwp-markdown.ts
2586
+ function renderHwpMarkdown(document) {
2587
+ const tables = new Map;
2588
+ for (const table of document.tables) {
2589
+ const key = `${table.sectionIndex}:${table.parentParagraphIndex}`;
2590
+ const group = tables.get(key) ?? [];
2591
+ group.push(table);
2592
+ tables.set(key, group);
2593
+ }
2594
+ for (const group of tables.values()) {
2595
+ group.sort((left, right) => left.controlIndex - right.controlIndex);
2596
+ }
2597
+ const blocks = [];
2598
+ for (const paragraph of document.paragraphs) {
2599
+ const text = cleanText(paragraph.text);
2600
+ if (hasRenderableText(text))
2601
+ blocks.push(text);
2602
+ for (const table of tables.get(`${paragraph.sectionIndex}:${paragraph.paragraphIndex}`) ?? []) {
2603
+ const rendered = renderTable(table);
2604
+ if (rendered.length > 0)
2605
+ blocks.push(rendered);
2606
+ }
2607
+ }
2608
+ return blocks.join(`
2609
+
2610
+ `);
2611
+ }
2612
+ function cleanText(text) {
2613
+ return text.replace(/[\u0000\uFFFC]/gu, "");
2614
+ }
2615
+ function hasRenderableText(text) {
2616
+ return text.trim().length > 0;
2617
+ }
2618
+ function renderTable(table) {
2619
+ const cells = table.cells.map((cell) => ({ ...cell, text: renderCell(cell) }));
2620
+ if (!cells.some((cell) => hasRenderableText(cell.text)))
2621
+ return "";
2622
+ if (cells.some((cell) => cell.rowSpan > 1 || cell.columnSpan > 1))
2623
+ return renderMergedTable(cells);
2624
+ const cellsByPosition = new Map(cells.map((cell) => [`${cell.row}:${cell.column}`, cell.text]));
2625
+ const header = Array.from({ length: table.columnCount }, (_, column) => `Column ${column + 1}`);
2626
+ const rows = Array.from({ length: table.rowCount }, (_, row) => Array.from({ length: table.columnCount }, (_2, column) => cellsByPosition.get(`${row}:${column}`) ?? ""));
2627
+ return [
2628
+ `| ${header.join(" | ")} |`,
2629
+ `| ${header.map(() => "---").join(" | ")} |`,
2630
+ ...rows.map((row) => `| ${row.join(" | ")} |`)
2631
+ ].join(`
2632
+ `);
2633
+ }
2634
+ function renderMergedTable(cells) {
2635
+ const rows = new Map;
2636
+ const coveredPositions = new Set;
2637
+ for (const cell of [...cells].sort((left, right) => left.row - right.row || left.column - right.column)) {
2638
+ const position = `${cell.row}:${cell.column}`;
2639
+ if (coveredPositions.has(position))
2640
+ continue;
2641
+ const row = rows.get(cell.row) ?? [];
2642
+ row.push(cell);
2643
+ rows.set(cell.row, row);
2644
+ for (let rowIndex = cell.row;rowIndex < cell.row + Math.max(1, cell.rowSpan); rowIndex += 1) {
2645
+ for (let columnIndex = cell.column;columnIndex < cell.column + Math.max(1, cell.columnSpan); columnIndex += 1) {
2646
+ if (rowIndex !== cell.row || columnIndex !== cell.column)
2647
+ coveredPositions.add(`${rowIndex}:${columnIndex}`);
2648
+ }
2649
+ }
2650
+ }
2651
+ const renderedRows = [...rows.entries()].sort(([left], [right]) => left - right).map(([row, cellsInRow]) => {
2652
+ const text = cellsInRow.sort((left, right) => left.column - right.column).map((cell) => cell.text).filter(hasRenderableText).join(" | ");
2653
+ return hasRenderableText(text) ? `Row ${row + 1}: ${text}` : "";
2654
+ }).filter((row) => row.length > 0);
2655
+ return ["[Table]", ...renderedRows].join(`
2656
+ `);
2657
+ }
2658
+ function renderCell(cell) {
2659
+ return cell.paragraphs.map(cleanText).filter(hasRenderableText).join("<br>").replaceAll("\\", "\\\\").replace(/\r\n|\r|\n/gu, "<br>").replaceAll("|", "\\|");
2660
+ }
2661
+
2662
+ // src/parser/rhwp-adapter.ts
2663
+ import { readFile } from "node:fs/promises";
2664
+ import initRhwp, { HwpDocument } from "@rhwp/core";
2665
+ function createRhwpExtractor(runtime = defaultRhwpRuntime, overrides = {}) {
2666
+ const limits = resolveLimits(overrides);
2667
+ const initialize = runtime === defaultRhwpRuntime ? initializeDefaultRhwpRuntime : createRuntimeInitializer(runtime);
2668
+ return async (bytes) => {
2669
+ await initialize();
2670
+ const document = runtime.open(bytes);
2671
+ try {
2672
+ return extractDocument(document, limits);
2673
+ } finally {
2674
+ document.free();
2675
+ }
2676
+ };
2677
+ }
2678
+ function createRuntimeInitializer(runtime) {
2679
+ let initialization;
2680
+ return () => {
2681
+ if (initialization === undefined) {
2682
+ const attempt = runtime.initialize();
2683
+ initialization = attempt.catch((error) => {
2684
+ initialization = undefined;
2685
+ throw error;
2686
+ });
2687
+ }
2688
+ return initialization;
2689
+ };
2690
+ }
2691
+ function extractDocument(document, limits) {
2692
+ const paragraphs = [];
2693
+ const paragraphCounts = [];
2694
+ const counters = { characters: 0, tables: 0, cells: 0, cellParagraphs: 0 };
2695
+ const sectionCount = readCount(document.getSectionCount(), "section count");
2696
+ checkBudget("maxSections", sectionCount, limits);
2697
+ for (let sectionIndex = 0;sectionIndex < sectionCount; sectionIndex += 1) {
2698
+ const paragraphCount = readCount(document.getParagraphCount(sectionIndex), `paragraph count for section ${sectionIndex}`);
2699
+ paragraphCounts.push(paragraphCount);
2700
+ checkBudget("maxBodyParagraphs", paragraphs.length + paragraphCount, limits);
2701
+ for (let paragraphIndex = 0;paragraphIndex < paragraphCount; paragraphIndex += 1) {
2702
+ const length = readCount(document.getParagraphLength(sectionIndex, paragraphIndex), `paragraph length at ${sectionIndex}:${paragraphIndex}`);
2703
+ const text = readText(document.getTextRange(sectionIndex, paragraphIndex, 0, length), `paragraph text at ${sectionIndex}:${paragraphIndex}`);
2704
+ counters.characters += [...text].length;
2705
+ checkBudget("maxCharacters", counters.characters, limits);
2706
+ paragraphs.push({
2707
+ sectionIndex,
2708
+ paragraphIndex,
2709
+ text
2710
+ });
2711
+ }
2712
+ }
2713
+ return { paragraphs, tables: extractTables(document, limits, counters, paragraphCounts) };
2714
+ }
2715
+ function extractTables(document, limits, counters, paragraphCounts) {
2716
+ const tables = [];
2717
+ const seen = new Set;
2718
+ const pageCount = readCount(document.pageCount(), "page count");
2719
+ for (let pageIndex = 0;pageIndex < pageCount; pageIndex += 1) {
2720
+ const layout = readJsonRecord(document.getPageControlLayout(pageIndex), `page ${pageIndex} control layout`);
2721
+ const controls = readArray(layout.controls, `controls for page ${pageIndex}`);
2722
+ for (let layoutIndex = 0;layoutIndex < controls.length; layoutIndex += 1) {
2723
+ const control = readRecord(controls[layoutIndex], `control ${layoutIndex} on page ${pageIndex}`);
2724
+ if (typeof control.type !== "string") {
2725
+ throw new TypeError(`rhwp returned an invalid control type at page ${pageIndex}:${layoutIndex}`);
2726
+ }
2727
+ if (control.type !== "table")
2728
+ continue;
2729
+ const label = `table at page ${pageIndex}:${layoutIndex}`;
2730
+ const coordinates = readTableCoordinates(control, label, paragraphCounts, limits);
2731
+ if (coordinates === undefined)
2732
+ continue;
2733
+ const key = `${coordinates.sectionIndex}:${coordinates.parentParagraphIndex}:${coordinates.controlIndex}`;
2734
+ if (seen.has(key))
2735
+ continue;
2736
+ seen.add(key);
2737
+ counters.tables += 1;
2738
+ checkBudget("maxTables", counters.tables, limits);
2739
+ tables.push(extractTable(document, coordinates, limits, counters));
2740
+ }
2741
+ }
2742
+ return tables;
2743
+ }
2744
+ function readTableCoordinates(control, label, paragraphCounts, limits) {
2745
+ const hasSectionIndex = Object.hasOwn(control, "secIdx");
2746
+ const hasParagraphIndex = Object.hasOwn(control, "paraIdx");
2747
+ const hasControlIndex = Object.hasOwn(control, "controlIdx");
2748
+ const coordinateCount = Number(hasSectionIndex) + Number(hasParagraphIndex) + Number(hasControlIndex);
2749
+ if (coordinateCount === 0) {
2750
+ validateDeferredNestedTable(control, label, limits);
2751
+ return;
2752
+ }
2753
+ if (coordinateCount !== 3) {
2754
+ throw new TypeError(`rhwp returned a partial coordinate set for ${label}`);
2755
+ }
2756
+ if (Object.hasOwn(control, "cellPath") || Object.hasOwn(control, "parentParaIdx")) {
2757
+ throw new TypeError(`rhwp returned conflicting top-level and nested context for ${label}`);
2758
+ }
2759
+ const coordinates = {
2760
+ sectionIndex: readCount(control.secIdx, `secIdx for ${label}`),
2761
+ parentParagraphIndex: readCount(control.paraIdx, `paraIdx for ${label}`),
2762
+ controlIndex: readCount(control.controlIdx, `controlIdx for ${label}`)
2763
+ };
2764
+ const paragraphCount = paragraphCounts[coordinates.sectionIndex];
2765
+ if (paragraphCount === undefined) {
2766
+ throw new TypeError(`rhwp returned a section coordinate outside the announced document range for ${label}`);
2767
+ }
2768
+ if (coordinates.parentParagraphIndex >= paragraphCount) {
2769
+ throw new TypeError(`rhwp returned a parent paragraph coordinate outside the announced section range for ${label}`);
2770
+ }
2771
+ return coordinates;
2772
+ }
2773
+ function validateDeferredNestedTable(control, label, limits) {
2774
+ if (!Object.hasOwn(control, "cellPath") && !Object.hasOwn(control, "parentParaIdx")) {
2775
+ validateDeferredNestedLayout(control, label, limits);
2776
+ return;
2777
+ }
2778
+ readCount(control.parentParaIdx, `parentParaIdx for deferred nested ${label}`);
2779
+ const cellPath = readArray(control.cellPath, `cellPath for deferred nested ${label}`);
2780
+ if (cellPath.length === 0) {
2781
+ throw new TypeError(`rhwp returned an empty cellPath for deferred nested ${label}`);
2782
+ }
2783
+ checkBudget("maxCells", cellPath.length, limits);
2784
+ for (let pathIndex = 0;pathIndex < cellPath.length; pathIndex += 1) {
2785
+ const entry = readRecord(cellPath[pathIndex], `cellPath entry ${pathIndex} for deferred nested ${label}`);
2786
+ readCount(entry.controlIndex, `controlIndex in cellPath entry ${pathIndex} for deferred nested ${label}`);
2787
+ readCount(entry.cellIndex, `cellIndex in cellPath entry ${pathIndex} for deferred nested ${label}`);
2788
+ readCount(entry.cellParaIndex, `cellParaIndex in cellPath entry ${pathIndex} for deferred nested ${label}`);
2789
+ }
2790
+ }
2791
+ function validateDeferredNestedLayout(control, label, limits) {
2792
+ readFiniteNumber(control.x, `x for deferred nested ${label}`);
2793
+ readFiniteNumber(control.y, `y for deferred nested ${label}`);
2794
+ readPositiveFiniteNumber(control.w, `w for deferred nested ${label}`);
2795
+ readPositiveFiniteNumber(control.h, `h for deferred nested ${label}`);
2796
+ const rowCount = readPositiveCount(control.rowCount, `rowCount for deferred nested ${label}`);
2797
+ const columnCount = readPositiveCount(control.colCount, `colCount for deferred nested ${label}`);
2798
+ const logicalCellCount = readLogicalCellCount(rowCount, columnCount, limits);
2799
+ readCount(control.plane, `plane for deferred nested ${label}`);
2800
+ readSafeInteger(control.zOrder, `zOrder for deferred nested ${label}`);
2801
+ readCount(control.stableIndex, `stableIndex for deferred nested ${label}`);
2802
+ const cells = readArray(control.cells, `cells for deferred nested ${label}`);
2803
+ if (cells.length === 0) {
2804
+ throw new TypeError(`rhwp returned empty cells for deferred nested ${label}`);
2805
+ }
2806
+ checkBudget("maxCells", cells.length, limits);
2807
+ if (cells.length > logicalCellCount) {
2808
+ throw new TypeError(`rhwp returned too many cells for deferred nested ${label}`);
2809
+ }
2810
+ const anchors = new Set;
2811
+ const cellIndexes = new Set;
2812
+ const coveredCoordinates = new Set;
2813
+ for (let cellIndex = 0;cellIndex < cells.length; cellIndex += 1) {
2814
+ const cell = readRecord(cells[cellIndex], `cell ${cellIndex} for deferred nested ${label}`);
2815
+ readFiniteNumber(cell.x, `x for cell ${cellIndex} in deferred nested ${label}`);
2816
+ readFiniteNumber(cell.y, `y for cell ${cellIndex} in deferred nested ${label}`);
2817
+ readPositiveFiniteNumber(cell.w, `w for cell ${cellIndex} in deferred nested ${label}`);
2818
+ readPositiveFiniteNumber(cell.h, `h for cell ${cellIndex} in deferred nested ${label}`);
2819
+ const row = readCount(cell.row, `row for cell ${cellIndex} in deferred nested ${label}`);
2820
+ const column = readCount(cell.col, `col for cell ${cellIndex} in deferred nested ${label}`);
2821
+ const rowSpan = readPositiveCount(cell.rowSpan, `rowSpan for cell ${cellIndex} in deferred nested ${label}`);
2822
+ const columnSpan = readPositiveCount(cell.colSpan, `colSpan for cell ${cellIndex} in deferred nested ${label}`);
2823
+ const runtimeCellIndex = readCount(cell.cellIdx, `cellIdx for cell ${cellIndex} in deferred nested ${label}`);
2824
+ if (row >= rowCount || rowSpan > rowCount - row) {
2825
+ throw new TypeError(`rhwp returned an out-of-range row span for cell ${cellIndex} in deferred nested ${label}`);
2826
+ }
2827
+ if (column >= columnCount || columnSpan > columnCount - column) {
2828
+ throw new TypeError(`rhwp returned an out-of-range col span for cell ${cellIndex} in deferred nested ${label}`);
2829
+ }
2830
+ if (cellIndexes.has(runtimeCellIndex)) {
2831
+ throw new TypeError(`rhwp returned a duplicate cellIdx for deferred nested ${label}`);
2832
+ }
2833
+ cellIndexes.add(runtimeCellIndex);
2834
+ const anchor = `${row}:${column}`;
2835
+ if (anchors.has(anchor)) {
2836
+ throw new TypeError(`rhwp returned a duplicate cell anchor for deferred nested ${label}`);
2837
+ }
2838
+ anchors.add(anchor);
2839
+ for (let coveredRow = row;coveredRow < row + rowSpan; coveredRow += 1) {
2840
+ for (let coveredColumn = column;coveredColumn < column + columnSpan; coveredColumn += 1) {
2841
+ const coordinate = `${coveredRow}:${coveredColumn}`;
2842
+ if (coveredCoordinates.has(coordinate)) {
2843
+ throw new TypeError(`rhwp returned overlapping cell spans for deferred nested ${label}`);
2844
+ }
2845
+ coveredCoordinates.add(coordinate);
2846
+ }
2847
+ }
2848
+ }
2849
+ }
2850
+ function extractTable(document, coordinates, limits, counters) {
2851
+ const { sectionIndex, parentParagraphIndex, controlIndex } = coordinates;
2852
+ const key = `${sectionIndex}:${parentParagraphIndex}:${controlIndex}`;
2853
+ const dimensions = readJsonRecord(document.getTableDimensions(sectionIndex, parentParagraphIndex, controlIndex), `table dimensions at ${key}`);
2854
+ const rowCount = readPositiveCount(dimensions.rowCount, `rowCount for table ${key}`);
2855
+ const columnCount = readPositiveCount(dimensions.colCount, `colCount for table ${key}`);
2856
+ const cellCount = readCount(dimensions.cellCount, `cellCount for table ${key}`);
2857
+ const logicalCellCount = chargeLogicalCells(rowCount, columnCount, counters, limits);
2858
+ if (cellCount === 0 || cellCount > logicalCellCount) {
2859
+ throw new TypeError(`rhwp returned an invalid cellCount for table ${key}; expected 1..${logicalCellCount}, received ${cellCount}`);
2860
+ }
2861
+ const cellGeometry = [];
2862
+ const anchors = new Set;
2863
+ const coveredCoordinates = new Set;
2864
+ for (let cellIndex = 0;cellIndex < cellCount; cellIndex += 1) {
2865
+ const cellInfo = readJsonRecord(document.getCellInfo(sectionIndex, parentParagraphIndex, controlIndex, cellIndex), `cell metadata at ${key}:${cellIndex}`);
2866
+ const row = readCount(cellInfo.row, `row for cell ${key}:${cellIndex}`);
2867
+ const column = readCount(cellInfo.col, `col for cell ${key}:${cellIndex}`);
2868
+ const rowSpan = readPositiveCount(cellInfo.rowSpan, `rowSpan for cell ${key}:${cellIndex}`);
2869
+ const columnSpan = readPositiveCount(cellInfo.colSpan, `colSpan for cell ${key}:${cellIndex}`);
2870
+ if (row >= rowCount) {
2871
+ throw new TypeError(`rhwp returned a row outside the table range for cell ${key}:${cellIndex}`);
2872
+ }
2873
+ if (column >= columnCount) {
2874
+ throw new TypeError(`rhwp returned a col outside the table range for cell ${key}:${cellIndex}`);
2875
+ }
2876
+ if (rowSpan > rowCount - row) {
2877
+ throw new TypeError(`rhwp returned a rowSpan outside the table range for cell ${key}:${cellIndex}`);
2878
+ }
2879
+ if (columnSpan > columnCount - column) {
2880
+ throw new TypeError(`rhwp returned a colSpan outside the table range for cell ${key}:${cellIndex}`);
2881
+ }
2882
+ const anchor = row * columnCount + column;
2883
+ if (anchors.has(anchor)) {
2884
+ throw new TypeError(`rhwp returned a duplicate cell anchor for table ${key} at ${row}:${column}`);
2885
+ }
2886
+ anchors.add(anchor);
2887
+ for (let coveredRow = row;coveredRow < row + rowSpan; coveredRow += 1) {
2888
+ for (let coveredColumn = column;coveredColumn < column + columnSpan; coveredColumn += 1) {
2889
+ const coordinate = coveredRow * columnCount + coveredColumn;
2890
+ if (coveredCoordinates.has(coordinate)) {
2891
+ throw new TypeError(`rhwp returned overlapping cell spans for table ${key} at ${coveredRow}:${coveredColumn}`);
2892
+ }
2893
+ coveredCoordinates.add(coordinate);
2894
+ }
2895
+ }
2896
+ cellGeometry.push({ row, column, rowSpan, columnSpan });
2897
+ }
2898
+ if (coveredCoordinates.size !== logicalCellCount) {
2899
+ throw new TypeError(`rhwp returned cellCount ${cellCount} that covers ${coveredCoordinates.size} of ${logicalCellCount} logical grid coordinates for table ${key}`);
2900
+ }
2901
+ const cells = [];
2902
+ for (let cellIndex = 0;cellIndex < cellGeometry.length; cellIndex += 1) {
2903
+ const geometry = cellGeometry[cellIndex];
2904
+ if (geometry === undefined)
2905
+ throw new TypeError(`rhwp omitted cell geometry at ${key}:${cellIndex}`);
2906
+ const paragraphCount = readCount(document.getCellParagraphCount(sectionIndex, parentParagraphIndex, controlIndex, cellIndex), `cell paragraph count at ${key}:${cellIndex}`);
2907
+ counters.cellParagraphs += paragraphCount;
2908
+ checkBudget("maxCellParagraphs", counters.cellParagraphs, limits);
2909
+ const paragraphs = [];
2910
+ for (let cellParagraphIndex = 0;cellParagraphIndex < paragraphCount; cellParagraphIndex += 1) {
2911
+ const length = readCount(document.getCellParagraphLength(sectionIndex, parentParagraphIndex, controlIndex, cellIndex, cellParagraphIndex), `cell paragraph length at ${key}:${cellIndex}:${cellParagraphIndex}`);
2912
+ const text = readText(document.getTextInCell(sectionIndex, parentParagraphIndex, controlIndex, cellIndex, cellParagraphIndex, 0, length), `cell paragraph text at ${key}:${cellIndex}:${cellParagraphIndex}`);
2913
+ counters.characters += [...text].length;
2914
+ checkBudget("maxCharacters", counters.characters, limits);
2915
+ paragraphs.push(text);
2916
+ }
2917
+ cells.push({
2918
+ ...geometry,
2919
+ paragraphs
2920
+ });
2921
+ }
2922
+ return {
2923
+ sectionIndex,
2924
+ parentParagraphIndex,
2925
+ controlIndex,
2926
+ rowCount,
2927
+ columnCount,
2928
+ cells
2929
+ };
2930
+ }
2931
+ function chargeLogicalCells(rowCount, columnCount, counters, limits) {
2932
+ const logicalCellCount = readLogicalCellCount(rowCount, columnCount, limits);
2933
+ if (logicalCellCount > limits.maxCells - counters.cells) {
2934
+ throw new HwpExtractionBudgetError("maxCells", counters.cells + logicalCellCount, limits.maxCells);
2935
+ }
2936
+ counters.cells += logicalCellCount;
2937
+ return logicalCellCount;
2938
+ }
2939
+ function readLogicalCellCount(rowCount, columnCount, limits) {
2940
+ if (rowCount > Math.floor(limits.maxCells / columnCount)) {
2941
+ throw new HwpExtractionBudgetError("maxCells", rowCount * columnCount, limits.maxCells);
2942
+ }
2943
+ return rowCount * columnCount;
2944
+ }
2945
+ function resolveLimits(overrides) {
2946
+ const limits = { ...DEFAULT_LIMITS };
2947
+ for (const name of Object.keys(DEFAULT_LIMITS)) {
2948
+ const value = overrides[name];
2949
+ if (value === undefined)
2950
+ continue;
2951
+ if (!Number.isSafeInteger(value) || value <= 0) {
2952
+ throw new TypeError(`${name} must be a positive finite safe integer`);
2953
+ }
2954
+ limits[name] = value;
2955
+ }
2956
+ return limits;
2957
+ }
2958
+ function checkBudget(name, actual, limits) {
2959
+ if (actual > limits[name])
2960
+ throw new HwpExtractionBudgetError(name, actual, limits[name]);
2961
+ }
2962
+ function readCount(value, label) {
2963
+ if (!Number.isSafeInteger(value) || value < 0) {
2964
+ throw new TypeError(`rhwp returned an invalid ${label}; expected a non-negative safe integer`);
2965
+ }
2966
+ return value;
2967
+ }
2968
+ function readPositiveCount(value, label) {
2969
+ const count = readCount(value, label);
2970
+ if (count === 0) {
2971
+ throw new TypeError(`rhwp returned an invalid ${label}; expected a positive safe integer`);
2972
+ }
2973
+ return count;
2974
+ }
2975
+ function readSafeInteger(value, label) {
2976
+ if (!Number.isSafeInteger(value)) {
2977
+ throw new TypeError(`rhwp returned an invalid ${label}; expected a safe integer`);
2978
+ }
2979
+ return value;
2980
+ }
2981
+ function readFiniteNumber(value, label) {
2982
+ if (typeof value !== "number" || !Number.isFinite(value)) {
2983
+ throw new TypeError(`rhwp returned an invalid ${label}; expected a finite number`);
2984
+ }
2985
+ return value;
2986
+ }
2987
+ function readPositiveFiniteNumber(value, label) {
2988
+ const number = readFiniteNumber(value, label);
2989
+ if (number <= 0) {
2990
+ throw new TypeError(`rhwp returned an invalid ${label}; expected a positive finite number`);
2991
+ }
2992
+ return number;
2993
+ }
2994
+ function readText(value, label) {
2995
+ if (typeof value !== "string")
2996
+ throw new TypeError(`rhwp returned invalid ${label}; expected a string`);
2997
+ return value;
2998
+ }
2999
+ function readJsonRecord(value, label) {
3000
+ const text = readText(value, label);
3001
+ let parsed;
3002
+ try {
3003
+ parsed = JSON.parse(text);
3004
+ } catch (cause) {
3005
+ throw new TypeError(`rhwp returned malformed JSON for ${label}`, { cause });
3006
+ }
3007
+ return readRecord(parsed, label);
3008
+ }
3009
+ function readRecord(value, label) {
3010
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
3011
+ throw new TypeError(`rhwp returned invalid ${label}; expected an object`);
3012
+ }
3013
+ return value;
3014
+ }
3015
+ function readArray(value, label) {
3016
+ if (!Array.isArray(value))
3017
+ throw new TypeError(`rhwp returned invalid ${label}; expected an array`);
3018
+ return value;
3019
+ }
3020
+ var DEFAULT_LIMITS, HwpExtractionBudgetError, defaultRhwpRuntime, initializeDefaultRhwpRuntime;
3021
+ var init_rhwp_adapter = __esm(() => {
3022
+ DEFAULT_LIMITS = {
3023
+ maxSections: 256,
3024
+ maxBodyParagraphs: 200000,
3025
+ maxTables: 20000,
3026
+ maxCells: 500000,
3027
+ maxCellParagraphs: 1e6,
3028
+ maxCharacters: 20000000
3029
+ };
3030
+ HwpExtractionBudgetError = class HwpExtractionBudgetError extends Error {
3031
+ code = "HWP_EXTRACTION_BUDGET_EXCEEDED";
3032
+ limit;
3033
+ actual;
3034
+ maximum;
3035
+ constructor(limit, actual, maximum) {
3036
+ super(`HWP extraction exceeded ${limit} budget of ${maximum} (received ${actual})`);
3037
+ this.name = "HwpExtractionBudgetError";
3038
+ this.limit = limit;
3039
+ this.actual = actual;
3040
+ this.maximum = maximum;
3041
+ }
3042
+ };
3043
+ defaultRhwpRuntime = {
3044
+ async initialize() {
3045
+ const rhwpModuleUrl = import.meta.resolve("@rhwp/core");
3046
+ const wasmBytes = await readFile(new URL("./rhwp_bg.wasm", rhwpModuleUrl));
3047
+ await initRhwp({ module_or_path: wasmBytes });
3048
+ },
3049
+ open(bytes) {
3050
+ return new HwpDocument(bytes);
3051
+ }
3052
+ };
3053
+ initializeDefaultRhwpRuntime = createRuntimeInitializer(defaultRhwpRuntime);
3054
+ });
3055
+
2585
3056
  // src/parser/xml-text.ts
2586
3057
  import { Buffer as Buffer2 } from "node:buffer";
2587
3058
  import { XMLParser } from "fast-xml-parser";
@@ -2687,15 +3158,25 @@ import { extname as extname2 } from "node:path";
2687
3158
  var HwpParser;
2688
3159
  var init_hwp = __esm(() => {
2689
3160
  init_errors();
3161
+ init_rhwp_adapter();
2690
3162
  init_text();
2691
3163
  init_xml_text();
2692
3164
  HwpParser = class HwpParser extends Parser {
2693
3165
  name = "hwp";
2694
3166
  extensions = [".hwpx", ".hwp"];
3167
+ extractor;
3168
+ constructor(options = {}) {
3169
+ super();
3170
+ this.extractor = options.extractor ?? createRhwpExtractor(undefined, options.limits);
3171
+ }
2695
3172
  async parse(input) {
2696
3173
  try {
2697
3174
  if (extname2(input.virtualPath).toLowerCase() === ".hwp") {
2698
- throw new Error("legacy HWP5 binary parsing is not supported by the pure JavaScript parser");
3175
+ const document = await this.extractor(input.bytes);
3176
+ return {
3177
+ markdown: normalizeMarkdown(renderHwpMarkdown(document)),
3178
+ metadata: { parser: this.name, format: "hwp5" }
3179
+ };
2699
3180
  }
2700
3181
  const chunks = await readZipXmlText(input.bytes, /^(?:Contents|content)\/.*\.xml$/i);
2701
3182
  return {
@@ -2874,7 +3355,7 @@ var init_office = __esm(() => {
2874
3355
  });
2875
3356
 
2876
3357
  // src/parser/opendataloader-pdf.ts
2877
- import { mkdtemp as mkdtemp2, readFile, rm, writeFile } from "node:fs/promises";
3358
+ import { mkdtemp as mkdtemp2, readFile as readFile2, rm, writeFile } from "node:fs/promises";
2878
3359
  import { tmpdir as tmpdir2 } from "node:os";
2879
3360
  import { basename as basename4, join as join12, parse as parse2 } from "node:path";
2880
3361
  import { convert } from "@opendataloader/pdf";
@@ -2917,7 +3398,7 @@ var init_opendataloader_pdf = __esm(() => {
2917
3398
  } : {}
2918
3399
  });
2919
3400
  return {
2920
- markdown: normalizeMarkdown(await readFile(markdownOutputPath(tempRoot, inputPath), "utf8")),
3401
+ markdown: normalizeMarkdown(await readFile2(markdownOutputPath(tempRoot, inputPath), "utf8")),
2921
3402
  metadata: { parser: this.name }
2922
3403
  };
2923
3404
  } catch (cause) {
@@ -2982,7 +3463,7 @@ function createDefaultParserRegistry(options = {}) {
2982
3463
  new DocxParser,
2983
3464
  new PptxParser,
2984
3465
  new XlsxParser,
2985
- new HwpParser,
3466
+ new HwpParser(options.hwp),
2986
3467
  new EmlParser
2987
3468
  ];
2988
3469
  if (options.ocr?.enabled)
@@ -3002,7 +3483,7 @@ var init_defaults = __esm(() => {
3002
3483
  // src/mirror/sync.ts
3003
3484
  import { randomUUID as randomUUID4 } from "node:crypto";
3004
3485
  import { existsSync as existsSync10, mkdirSync as mkdirSync6, renameSync as renameSync4, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "node:fs";
3005
- import { opendir, readFile as readFile2, stat } from "node:fs/promises";
3486
+ import { opendir, readFile as readFile3, stat } from "node:fs/promises";
3006
3487
  import { dirname as dirname5, extname as extname5, resolve as resolve5 } from "node:path";
3007
3488
  async function syncParsedMirrors(options) {
3008
3489
  const registry = options.registry ?? createDefaultParserRegistry(options.parserOptions);
@@ -3055,7 +3536,7 @@ async function syncParsedMirrors(options) {
3055
3536
  if (!unchanged) {
3056
3537
  let parsed;
3057
3538
  try {
3058
- const bytes = await readFile2(entry.sourcePath);
3539
+ const bytes = await readFile3(entry.sourcePath);
3059
3540
  parsed = await parser2.parse({ virtualPath: entry.virtualPath, sourcePath: entry.sourcePath, bytes });
3060
3541
  } catch (error) {
3061
3542
  if (!(error instanceof ParseError))
@@ -6335,7 +6816,8 @@ function recordStructuredResultsSession(sessionId, query, details, sessions, mem
6335
6816
  summary: result.summary,
6336
6817
  evidence: result.evidence.map((evidence) => evidence.lineNumber !== undefined ? { excerpt: evidence.excerpt, lineNumber: evidence.lineNumber } : { excerpt: evidence.excerpt }),
6337
6818
  confidence: confidenceFrom(result.confidence),
6338
- feedbackId: `${sessionId}:${result.number}`
6819
+ feedbackId: `${sessionId}:${result.number}`,
6820
+ source: registry.get(result.number)?.source
6339
6821
  }));
6340
6822
  const answer = details.answer;
6341
6823
  const diagnostics = [];
@@ -8386,155 +8868,2740 @@ var init_agent = __esm(() => {
8386
8868
  TIMESTAMP_TEXT_PATTERN = /^(\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?)?)(?:\s*;\s*.+)?$/;
8387
8869
  });
8388
8870
 
8389
- // src/index.ts
8390
- init_agent();
8391
-
8392
- // src/agent/index.ts
8393
- init_agent();
8394
- init_datasource_skill();
8395
- init_emit_results_tool();
8396
- init_jikji_find_tool();
8397
- init_search_all_tool();
8398
- init_search_datasource_tool();
8399
- init_search_minsync_tool();
8400
- init_system_prompt();
8401
-
8402
- // src/index.ts
8403
- init_system_prompt();
8404
-
8405
- // src/datasource/index.ts
8406
- init_access_context();
8407
-
8408
- // src/datasource/polling.ts
8409
- function isDue(metadata, now, lastIndexedAt) {
8410
- if (metadata.mode !== "poll")
8411
- return false;
8412
- const intervalMs = metadata.intervalMs;
8413
- if (intervalMs === undefined || intervalMs <= 0)
8414
- return false;
8415
- const last = lastIndexedAt ?? metadata.lastIndexedAt;
8416
- if (last === undefined)
8417
- return true;
8418
- if (!Number.isFinite(now) || !Number.isFinite(last))
8419
- return false;
8420
- return now - last >= intervalMs;
8421
- }
8422
- function parseCronExpr(expr) {
8423
- if (typeof expr !== "string") {
8424
- return fail("cron expression must be a string");
8425
- }
8426
- const trimmed = expr.trim();
8427
- if (trimmed.length === 0) {
8428
- return fail("cron expression is empty");
8871
+ // src/datasource/connector.ts
8872
+ import { createHash as createHash7 } from "node:crypto";
8873
+ function connectorFailureToDiagnosticCode(reason) {
8874
+ switch (reason) {
8875
+ case "auth":
8876
+ return "datasource-auth-error";
8877
+ case "permission":
8878
+ return "datasource-permission-denied";
8879
+ case "rate-limited":
8880
+ return "datasource-rate-limited";
8881
+ case "unavailable":
8882
+ case "not-configured":
8883
+ return "datasource-unavailable";
8884
+ case "empty":
8885
+ return "datasource-empty";
8886
+ case "api-error":
8887
+ case "invalid-data":
8888
+ return "datasource-index-failed";
8889
+ }
8890
+ }
8891
+ function boundDiagnosticText(value) {
8892
+ const trimmed = value.trim();
8893
+ return trimmed.length > 500 ? `${trimmed.slice(0, 500)}...` : trimmed;
8894
+ }
8895
+ function sanitizeIdSegment(value) {
8896
+ const normalized = value.replace(/[^\p{L}\p{N}._-]+/gu, "-");
8897
+ let start = 0;
8898
+ while (start < normalized.length && (normalized[start] === "-" || normalized[start] === "."))
8899
+ start += 1;
8900
+ let end = normalized.length;
8901
+ while (end > start && (normalized[end - 1] === "-" || normalized[end - 1] === "."))
8902
+ end -= 1;
8903
+ const cleaned = normalized.slice(start, end);
8904
+ if (cleaned === value && cleaned.length > 0 && cleaned.length <= 80)
8905
+ return cleaned;
8906
+ const hash2 = createHash7("sha256").update(value).digest("hex").slice(0, 8);
8907
+ const stem = cleaned.slice(0, 60);
8908
+ return stem.length > 0 ? `${stem}-${hash2}` : hash2;
8909
+ }
8910
+ var init_connector = () => {};
8911
+
8912
+ // src/datasource/chunk-store.ts
8913
+ import { existsSync as existsSync13, mkdirSync as mkdirSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
8914
+ import { dirname as dirname10, join as join17 } from "node:path";
8915
+ function datasourceChunkStorePath(workspaceRoot, skillName, instanceId) {
8916
+ return join17(workspaceRoot, ".autorag", "datasources", sanitizeIdSegment(skillName), sanitizeIdSegment(instanceId), "chunks.json");
8917
+ }
8918
+
8919
+ class DatasourceChunkStore {
8920
+ skillName;
8921
+ instanceId;
8922
+ storePath;
8923
+ maxChunkChars;
8924
+ chunkList = [];
8925
+ loaded = false;
8926
+ constructor(options) {
8927
+ this.skillName = options.skillName;
8928
+ this.instanceId = options.instanceId;
8929
+ this.maxChunkChars = options.maxChunkChars ?? DEFAULT_MAX_CHUNK_CHARS;
8930
+ this.storePath = options.workspaceRoot === undefined ? undefined : datasourceChunkStorePath(options.workspaceRoot, options.skillName, options.instanceId);
8429
8931
  }
8430
- const fields = trimmed.split(/\s+/u);
8431
- if (fields.length !== 5) {
8432
- return fail(`expected 5 fields, got ${fields.length}`);
8932
+ replaceDocuments(documents) {
8933
+ const chunks = [];
8934
+ const seenIds = new Set;
8935
+ for (const document of documents) {
8936
+ const docSegment = sanitizeIdSegment(document.docId);
8937
+ const hierarchy = (document.hierarchy ?? []).map(sanitizeIdSegment).filter((segment) => segment.length > 0);
8938
+ const parts = splitContent(document.content, this.maxChunkChars);
8939
+ for (const [index, part] of parts.entries()) {
8940
+ let chunkId = parts.length === 1 ? docSegment : `${docSegment}-p${index + 1}`;
8941
+ while (seenIds.has(chunkId))
8942
+ chunkId = `${chunkId}x`;
8943
+ seenIds.add(chunkId);
8944
+ chunks.push({
8945
+ chunkId,
8946
+ docId: document.docId,
8947
+ hierarchy,
8948
+ ...document.title !== undefined ? { title: document.title } : {},
8949
+ content: part,
8950
+ ...document.publishedAt !== undefined ? { publishedAt: document.publishedAt } : {},
8951
+ metadata: { ...document.metadata ?? {} }
8952
+ });
8953
+ }
8954
+ }
8955
+ this.chunkList = chunks;
8956
+ this.loaded = true;
8957
+ this.persist();
8958
+ return chunks.length;
8433
8959
  }
8434
- const [minute, hour, dom, month, dow] = fields;
8435
- const errors = [];
8436
- validateField(minute, 0, 59, "minute", errors);
8437
- validateField(hour, 0, 23, "hour", errors);
8438
- validateField(dom, 1, 31, "day-of-month", errors);
8439
- validateField(month, 1, 12, "month", errors);
8440
- validateField(dow, 0, 7, "day-of-week", errors);
8441
- if (errors.length > 0) {
8442
- return fail(errors.join("; "));
8960
+ load() {
8961
+ if (this.storePath === undefined)
8962
+ return false;
8963
+ try {
8964
+ if (!existsSync13(this.storePath))
8965
+ return false;
8966
+ const parsed = JSON.parse(readFileSync9(this.storePath, "utf8"));
8967
+ if (parsed.version !== STORE_VERSION || !Array.isArray(parsed.chunks))
8968
+ return false;
8969
+ this.chunkList = parsed.chunks.filter((chunk) => typeof chunk?.chunkId === "string" && typeof chunk?.content === "string");
8970
+ this.loaded = true;
8971
+ return true;
8972
+ } catch {
8973
+ return false;
8974
+ }
8443
8975
  }
8444
- return { ok: true, expr: trimmed };
8445
- }
8446
- function fail(error) {
8447
- return { ok: false, error };
8448
- }
8449
- function validateField(field, min, max, label, errors) {
8450
- const parts = field.split(",");
8451
- if (parts.length === 0) {
8452
- errors.push(`${label}: empty`);
8453
- return;
8976
+ ensureLoaded() {
8977
+ if (!this.loaded) {
8978
+ this.load();
8979
+ this.loaded = true;
8980
+ }
8454
8981
  }
8455
- for (const part of parts) {
8456
- const err = validatePart(part, min, max, label);
8457
- if (err !== null)
8458
- errors.push(err);
8982
+ chunks() {
8983
+ this.ensureLoaded();
8984
+ return this.chunkList;
8459
8985
  }
8460
- }
8461
- function validatePart(part, min, max, label) {
8462
- if (part.length === 0)
8463
- return `${label}: empty term`;
8464
- const slashIndex = part.indexOf("/");
8465
- let base = part;
8466
- let stepText;
8467
- if (slashIndex >= 0) {
8468
- base = part.slice(0, slashIndex);
8469
- stepText = part.slice(slashIndex + 1);
8470
- const step = parseNumber(stepText);
8471
- if (step === null || step <= 0) {
8472
- return `${label}: invalid step "${stepText}"`;
8986
+ hierarchies() {
8987
+ this.ensureLoaded();
8988
+ const seen = new Map;
8989
+ for (const chunk of this.chunkList) {
8990
+ seen.set(chunk.hierarchy.join("/"), chunk.hierarchy);
8473
8991
  }
8992
+ return [...seen.values()];
8474
8993
  }
8475
- if (base === "*")
8476
- return null;
8477
- if (base.includes("-")) {
8478
- const [loText, hiText] = base.split("-");
8479
- if (hiText === undefined || loText === undefined) {
8480
- return `${label}: malformed range "${base}"`;
8481
- }
8482
- const lo = parseNumber(loText);
8483
- const hi = parseNumber(hiText);
8484
- if (lo === null || hi === null) {
8485
- return `${label}: non-numeric range "${base}"`;
8486
- }
8487
- if (lo < min || lo > max || hi < min || hi > max) {
8488
- return `${label}: range "${base}" out of bounds (${min}-${max})`;
8489
- }
8490
- if (lo > hi) {
8491
- return `${label}: range "${base}" has low > high`;
8994
+ search(query, topK) {
8995
+ this.ensureLoaded();
8996
+ const queryTerms = [...new Set(tokenize2(query))];
8997
+ if (queryTerms.length === 0 || this.chunkList.length === 0 || topK <= 0)
8998
+ return [];
8999
+ const docCount = this.chunkList.length;
9000
+ const tokenized = this.chunkList.map((chunk) => tokenize2(`${chunk.title ?? ""} ${chunk.content}`));
9001
+ const avgLength = tokenized.reduce((sum, tokens) => sum + tokens.length, 0) / docCount || 1;
9002
+ const termFrequency = (term, tokens) => {
9003
+ let tf = 0;
9004
+ for (const token of tokens) {
9005
+ if (token === term)
9006
+ tf += 1;
9007
+ else if (term.length >= 2 && token.startsWith(term))
9008
+ tf += 0.75;
9009
+ }
9010
+ return tf;
9011
+ };
9012
+ const frequencies = queryTerms.map((term) => tokenized.map((tokens) => termFrequency(term, tokens)));
9013
+ const documentFrequency = frequencies.map((perDoc) => perDoc.reduce((count, tf) => count + (tf > 0 ? 1 : 0), 0));
9014
+ const k1 = 1.2;
9015
+ const b = 0.75;
9016
+ const scored = [];
9017
+ for (const [index, chunk] of this.chunkList.entries()) {
9018
+ const tokens = tokenized[index] ?? [];
9019
+ let score = 0;
9020
+ for (const [termIndex] of queryTerms.entries()) {
9021
+ const df = documentFrequency[termIndex] ?? 0;
9022
+ if (df === 0)
9023
+ continue;
9024
+ const tf = frequencies[termIndex]?.[index] ?? 0;
9025
+ if (tf === 0)
9026
+ continue;
9027
+ const idf = Math.log(1 + (docCount - df + 0.5) / (df + 0.5));
9028
+ score += idf * tf * (k1 + 1) / (tf + k1 * (1 - b + b * tokens.length / avgLength));
9029
+ }
9030
+ if (score > 0)
9031
+ scored.push({ chunk, score });
8492
9032
  }
8493
- return null;
9033
+ scored.sort((a, b2) => b2.score - a.score || a.chunk.chunkId.localeCompare(b2.chunk.chunkId));
9034
+ return scored.slice(0, topK);
8494
9035
  }
8495
- const v = parseNumber(base);
8496
- if (v === null) {
8497
- return `${label}: invalid value "${base}"`;
9036
+ persist() {
9037
+ if (this.storePath === undefined)
9038
+ return;
9039
+ try {
9040
+ mkdirSync10(dirname10(this.storePath), { recursive: true });
9041
+ const payload = {
9042
+ version: STORE_VERSION,
9043
+ skill: this.skillName,
9044
+ instance: this.instanceId,
9045
+ updatedAt: Date.now(),
9046
+ chunks: this.chunkList
9047
+ };
9048
+ writeFileSync7(this.storePath, `${JSON.stringify(payload)}
9049
+ `, "utf8");
9050
+ } catch {}
8498
9051
  }
8499
- if (v < min || v > max) {
8500
- return `${label}: value ${v} out of bounds (${min}-${max})`;
9052
+ }
9053
+ function splitContent(content, maxChars) {
9054
+ const trimmed = content.trim();
9055
+ if (trimmed.length === 0)
9056
+ return [];
9057
+ if (trimmed.length <= maxChars)
9058
+ return [trimmed];
9059
+ const parts = [];
9060
+ const paragraphs = trimmed.split(/\n{2,}/u);
9061
+ let current = "";
9062
+ for (const paragraph of paragraphs) {
9063
+ if (current.length > 0 && current.length + paragraph.length + 2 > maxChars) {
9064
+ parts.push(current);
9065
+ current = "";
9066
+ }
9067
+ if (paragraph.length > maxChars) {
9068
+ if (current.length > 0) {
9069
+ parts.push(current);
9070
+ current = "";
9071
+ }
9072
+ for (let offset = 0;offset < paragraph.length; offset += maxChars) {
9073
+ parts.push(paragraph.slice(offset, offset + maxChars));
9074
+ }
9075
+ continue;
9076
+ }
9077
+ current = current.length === 0 ? paragraph : `${current}
9078
+
9079
+ ${paragraph}`;
8501
9080
  }
8502
- return null;
9081
+ if (current.length > 0)
9082
+ parts.push(current);
9083
+ return parts.filter((part) => part.trim().length > 0);
8503
9084
  }
8504
- function parseNumber(text) {
8505
- if (!/^\d+$/u.test(text))
8506
- return null;
8507
- return Number.parseInt(text, 10);
9085
+ function tokenize2(text) {
9086
+ return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length > 0);
8508
9087
  }
9088
+ var STORE_VERSION = 1, DEFAULT_MAX_CHUNK_CHARS = 2000;
9089
+ var init_chunk_store = __esm(() => {
9090
+ init_connector();
9091
+ });
9092
+
8509
9093
  // src/datasource/scope.ts
8510
- var DATASOURCE_CHUNKS_SEGMENT = "chunks";
8511
9094
  function buildDatasourceInstanceSource(skillName, instanceId) {
8512
9095
  return normalizeVirtualPath(`/${skillName}/${instanceId}`);
8513
9096
  }
8514
9097
  function buildDatasourceChunkSource(skillName, instanceId, chunkId) {
8515
9098
  return normalizeVirtualPath(`/${skillName}/${instanceId}/${DATASOURCE_CHUNKS_SEGMENT}/${chunkId}`);
8516
9099
  }
8517
- function datasourceSourcePath(skillName, instanceId, chunkId) {
8518
- return chunkId === undefined ? buildDatasourceInstanceSource(skillName, instanceId) : buildDatasourceChunkSource(skillName, instanceId, chunkId);
9100
+ function datasourceSourcePath(skillName, instanceId, chunkId) {
9101
+ return chunkId === undefined ? buildDatasourceInstanceSource(skillName, instanceId) : buildDatasourceChunkSource(skillName, instanceId, chunkId);
9102
+ }
9103
+ function datasourceSourceHasFragment(source) {
9104
+ return typeof source === "string" && source.includes("#");
9105
+ }
9106
+ function isDatasourceSource(source) {
9107
+ if (typeof source !== "string" || source.length === 0)
9108
+ return false;
9109
+ if (datasourceSourceHasFragment(source))
9110
+ return false;
9111
+ const normalized = normalizeVirtualPath(source);
9112
+ return normalized !== "/";
9113
+ }
9114
+ function matchesDatasourceScope(source, scope) {
9115
+ if (datasourceSourceHasFragment(source))
9116
+ return false;
9117
+ return matchesVirtualPathScope(source, scope);
9118
+ }
9119
+ var DATASOURCE_CHUNKS_SEGMENT = "chunks";
9120
+ var init_scope = () => {};
9121
+
9122
+ // src/datasource/connector-skill.ts
9123
+ class ConnectorDatasourceSkill {
9124
+ definition;
9125
+ connector;
9126
+ instanceId;
9127
+ instances;
9128
+ pollingIntervalMs;
9129
+ tags;
9130
+ store;
9131
+ dedupeWindowMs;
9132
+ lastIndexedAt;
9133
+ lastPolledAt;
9134
+ lastError;
9135
+ seenDocs = new Map;
9136
+ constructor(definition, options) {
9137
+ this.definition = definition;
9138
+ this.connector = options.connector;
9139
+ this.instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID;
9140
+ this.instances = options.instances !== undefined && options.instances.length > 0 ? options.instances : [this.instanceId];
9141
+ this.pollingIntervalMs = options.pollingIntervalMs ?? DEFAULT_POLLING_INTERVAL_MS;
9142
+ this.tags = options.tags ?? definition.defaultTags;
9143
+ this.lastIndexedAt = options.lastIndexedAt;
9144
+ this.dedupeWindowMs = options.dedupeWindowMs ?? 0;
9145
+ this.store = new DatasourceChunkStore({
9146
+ skillName: definition.skillName,
9147
+ instanceId: this.instanceId,
9148
+ workspaceRoot: options.workspaceRoot,
9149
+ maxChunkChars: options.maxChunkChars
9150
+ });
9151
+ }
9152
+ describe() {
9153
+ return {
9154
+ name: this.definition.skillName,
9155
+ id: this.definition.skillName,
9156
+ type: this.definition.skillType,
9157
+ description: this.definition.description,
9158
+ capabilities: this.definition.capabilities,
9159
+ tags: this.tags,
9160
+ status: "active",
9161
+ datasourceId: this.definition.skillName,
9162
+ instanceId: this.instanceId,
9163
+ instances: this.instances
9164
+ };
9165
+ }
9166
+ polling() {
9167
+ return {
9168
+ mode: this.pollingIntervalMs > 0 ? "poll" : "none",
9169
+ ...this.pollingIntervalMs > 0 ? { intervalMs: this.pollingIntervalMs } : {},
9170
+ lastIndexedAt: this.lastIndexedAt,
9171
+ ...this.lastPolledAt !== undefined ? { lastPolledAt: this.lastPolledAt } : {},
9172
+ ...this.lastError !== undefined ? { lastError: this.lastError } : {}
9173
+ };
9174
+ }
9175
+ async index() {
9176
+ this.lastPolledAt = Date.now();
9177
+ let fetched;
9178
+ try {
9179
+ fetched = await this.connector.fetch();
9180
+ } catch {
9181
+ return this.fail("datasource-unavailable", "connector failed unexpectedly");
9182
+ }
9183
+ if (!fetched.ok) {
9184
+ const code = connectorFailureToDiagnosticCode(fetched.reason);
9185
+ const message = boundDiagnosticText(fetched.message ?? fetched.reason);
9186
+ return this.fail(code, `${fetched.reason}: ${message}`);
9187
+ }
9188
+ const documents = this.applyDedupeWindow(fetched.documents);
9189
+ const chunkCount = this.store.replaceDocuments(documents);
9190
+ this.lastIndexedAt = Date.now();
9191
+ this.lastError = undefined;
9192
+ const diagnostics = (fetched.warnings ?? []).map((warning) => ({
9193
+ code: "datasource-index-failed",
9194
+ severity: "warning",
9195
+ message: boundDiagnosticText(warning),
9196
+ instanceId: this.instanceId,
9197
+ source: this.definition.skillName
9198
+ }));
9199
+ if (chunkCount === 0) {
9200
+ diagnostics.push({
9201
+ code: "datasource-empty",
9202
+ severity: "info",
9203
+ message: "datasource fetch returned no indexable documents",
9204
+ instanceId: this.instanceId,
9205
+ source: this.definition.skillName
9206
+ });
9207
+ }
9208
+ return {
9209
+ ok: true,
9210
+ instanceId: this.instanceId,
9211
+ skill: this.definition.skillName,
9212
+ chunkCount,
9213
+ indexedAt: this.lastIndexedAt,
9214
+ diagnostics
9215
+ };
9216
+ }
9217
+ retrievalMethods() {
9218
+ return [
9219
+ new ConnectorLexicalMethod({
9220
+ skillName: this.definition.skillName,
9221
+ skillType: this.definition.skillType,
9222
+ instanceId: this.instanceId,
9223
+ tags: this.tags,
9224
+ store: this.store
9225
+ })
9226
+ ];
9227
+ }
9228
+ describeSources() {
9229
+ const out = this.instances.map((instanceId) => ({
9230
+ source: datasourceSourcePath(this.definition.skillName, instanceId),
9231
+ datasourceId: this.definition.skillName,
9232
+ skill: this.definition.skillName,
9233
+ instanceId,
9234
+ contentType: this.definition.contentType,
9235
+ metadata: {
9236
+ datasourceId: this.definition.skillName,
9237
+ instanceId,
9238
+ tags: this.tags
9239
+ }
9240
+ }));
9241
+ for (const hierarchy of this.store.hierarchies()) {
9242
+ if (hierarchy.length === 0)
9243
+ continue;
9244
+ out.push({
9245
+ source: `${datasourceSourcePath(this.definition.skillName, this.instanceId)}/${hierarchy.join("/")}`,
9246
+ datasourceId: this.definition.skillName,
9247
+ skill: this.definition.skillName,
9248
+ instanceId: this.instanceId,
9249
+ contentType: this.definition.contentType,
9250
+ metadata: {
9251
+ datasourceId: this.definition.skillName,
9252
+ instanceId: this.instanceId,
9253
+ hierarchy
9254
+ }
9255
+ });
9256
+ }
9257
+ return out;
9258
+ }
9259
+ skillManifest() {
9260
+ const skillName = this.definition.skillName;
9261
+ const instanceScopes = this.instances.map((instanceId) => `- \`${datasourceSourcePath(skillName, instanceId)}\``).join(`
9262
+ `);
9263
+ const cadence = this.pollingIntervalMs > 0 ? `roughly every ${Math.round(this.pollingIntervalMs / 60000)} minute(s) when auto-refresh runs` : "on manual refresh only";
9264
+ return {
9265
+ name: `datasource-${skillName}`,
9266
+ description: this.definition.manifestDescription,
9267
+ content: [
9268
+ `# ${this.definition.description} (${this.definition.skillType})`,
9269
+ "",
9270
+ "## When to use",
9271
+ this.definition.manifestDescription,
9272
+ "",
9273
+ "## Indexing",
9274
+ `Indexing is server-managed and refreshed ${cadence}. You do not trigger indexing; just search.`,
9275
+ "",
9276
+ "## How to search",
9277
+ "Call `search_datasource_documents` with a natural-language `query`. Optionally pass `topK` and a narrowing `scope`. Available authorized scopes:",
9278
+ instanceScopes.length > 0 ? instanceScopes : "- (no authorized instances)",
9279
+ "",
9280
+ "`scope` can only narrow within already-authorized scopes; it can never widen access.",
9281
+ "",
9282
+ "## Output rules",
9283
+ `Datasource source identifiers such as \`/${skillName}/<instance>/chunks/<id>\` are stable and traceable. Result metadata may carry real file paths or account identifiers; you may cite them in the visible answer when they help the user locate the underlying item. Privacy is the operator's responsibility: run AutoRAG with a local LLM if results must not leave this machine.`,
9284
+ ...this.definition.manifestNotes !== undefined && this.definition.manifestNotes.length > 0 ? ["", ...this.definition.manifestNotes] : []
9285
+ ].join(`
9286
+ `)
9287
+ };
9288
+ }
9289
+ applyDedupeWindow(documents) {
9290
+ if (this.dedupeWindowMs <= 0)
9291
+ return documents;
9292
+ const now = Date.now();
9293
+ for (const [docId, seenAt] of this.seenDocs) {
9294
+ if (now - seenAt > this.dedupeWindowMs)
9295
+ this.seenDocs.delete(docId);
9296
+ }
9297
+ const out = [];
9298
+ const inBatch = new Set;
9299
+ for (const document of documents) {
9300
+ if (inBatch.has(document.docId))
9301
+ continue;
9302
+ inBatch.add(document.docId);
9303
+ this.seenDocs.set(document.docId, now);
9304
+ out.push(document);
9305
+ }
9306
+ return out;
9307
+ }
9308
+ fail(code, message) {
9309
+ const sanitized = boundDiagnosticText(message);
9310
+ this.lastError = sanitized;
9311
+ const diagnostic = {
9312
+ code,
9313
+ severity: code === "datasource-unavailable" || code === "datasource-empty" ? "warning" : "error",
9314
+ message: sanitized,
9315
+ instanceId: this.instanceId,
9316
+ source: this.definition.skillName
9317
+ };
9318
+ return {
9319
+ ok: false,
9320
+ instanceId: this.instanceId,
9321
+ skill: this.definition.skillName,
9322
+ indexedAt: Date.now(),
9323
+ diagnostics: [diagnostic],
9324
+ error: sanitized,
9325
+ code,
9326
+ message: sanitized
9327
+ };
9328
+ }
9329
+ }
9330
+
9331
+ class ConnectorLexicalMethod {
9332
+ options;
9333
+ constructor(options) {
9334
+ this.options = options;
9335
+ }
9336
+ describe() {
9337
+ const { skillName, skillType, tags } = this.options;
9338
+ return {
9339
+ name: `${skillName}-lexical`,
9340
+ type: "bm25",
9341
+ description: `Lexical retrieval over indexed ${skillType} chunks`,
9342
+ status: "active",
9343
+ capabilities: ["lexical", "scoped", "path-opaque-sources"],
9344
+ datasourceId: skillName,
9345
+ tags: [...tags]
9346
+ };
9347
+ }
9348
+ async retrieve(query, options) {
9349
+ const trimmed = query.trim();
9350
+ if (trimmed.length === 0)
9351
+ return [];
9352
+ const topK = options.topK ?? DEFAULT_TOP_K2;
9353
+ let hits;
9354
+ try {
9355
+ hits = this.options.store.search(trimmed, Math.max(topK * 3, topK));
9356
+ } catch {
9357
+ return [];
9358
+ }
9359
+ const { skillName, instanceId } = this.options;
9360
+ const mapped = [];
9361
+ for (const { chunk, score } of hits) {
9362
+ const source = datasourceSourcePath(skillName, instanceId, chunk.chunkId);
9363
+ if (!this.matchesScope(source, options.scope, options.allowedScopes))
9364
+ continue;
9365
+ mapped.push({
9366
+ id: `${skillName}:${instanceId}:${chunk.chunkId}`,
9367
+ content: chunk.content,
9368
+ source,
9369
+ score,
9370
+ metadata: {
9371
+ ...chunk.metadata,
9372
+ method: `${skillName}-lexical`,
9373
+ datasourceId: skillName,
9374
+ instanceId,
9375
+ chunkId: chunk.chunkId,
9376
+ ...chunk.title !== undefined ? { title: chunk.title } : {},
9377
+ ...chunk.hierarchy.length > 0 ? { hierarchy: chunk.hierarchy.join("/") } : {},
9378
+ ...chunk.publishedAt !== undefined ? { publishedAt: chunk.publishedAt } : {}
9379
+ }
9380
+ });
9381
+ if (mapped.length >= topK)
9382
+ break;
9383
+ }
9384
+ return mapped;
9385
+ }
9386
+ matchesScope(source, scope, allowedScopes) {
9387
+ if (!matchesDatasourceScope(source, scope))
9388
+ return false;
9389
+ if (allowedScopes === undefined || allowedScopes.length === 0)
9390
+ return true;
9391
+ return allowedScopes.some((entry) => matchesDatasourceScope(source, entry));
9392
+ }
9393
+ }
9394
+ var DEFAULT_POLLING_INTERVAL_MS, DEFAULT_INSTANCE_ID = "default", DEFAULT_TOP_K2 = 20;
9395
+ var init_connector_skill = __esm(() => {
9396
+ init_chunk_store();
9397
+ init_connector();
9398
+ init_scope();
9399
+ DEFAULT_POLLING_INTERVAL_MS = 15 * 60 * 1000;
9400
+ });
9401
+
9402
+ // src/datasource/http.ts
9403
+ function combineSignals(timeoutMs, outer) {
9404
+ const timeout = AbortSignal.timeout(timeoutMs);
9405
+ return outer === undefined ? timeout : AbortSignal.any([timeout, outer]);
9406
+ }
9407
+ function statusToReason(status) {
9408
+ if (status === 401)
9409
+ return "auth";
9410
+ if (status === 403)
9411
+ return "permission";
9412
+ if (status === 429)
9413
+ return "rate-limited";
9414
+ return "api-error";
9415
+ }
9416
+ async function requestText(url, options) {
9417
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
9418
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS3;
9419
+ let response;
9420
+ try {
9421
+ response = await fetchImpl(url, {
9422
+ method: options.method ?? "GET",
9423
+ headers: options.headers,
9424
+ body: options.body,
9425
+ signal: combineSignals(timeoutMs, options.signal)
9426
+ });
9427
+ } catch {
9428
+ return { ok: false, reason: "unavailable", message: "request failed or timed out" };
9429
+ }
9430
+ if (!response.ok) {
9431
+ return { ok: false, reason: statusToReason(response.status), message: `http-${response.status}` };
9432
+ }
9433
+ let text;
9434
+ try {
9435
+ text = await response.text();
9436
+ } catch {
9437
+ return { ok: false, reason: "invalid-data", message: "response body unreadable" };
9438
+ }
9439
+ return { ok: true, status: response.status, text };
9440
+ }
9441
+ function httpText(url, options = {}) {
9442
+ return requestText(url, options);
9443
+ }
9444
+ async function httpJson(url, options = {}) {
9445
+ const result = await requestText(url, options);
9446
+ if (!result.ok)
9447
+ return result;
9448
+ try {
9449
+ return { ok: true, status: result.status, json: JSON.parse(result.text) };
9450
+ } catch {
9451
+ return { ok: false, reason: "invalid-data", message: "response was not valid JSON" };
9452
+ }
9453
+ }
9454
+ function asRecord2(value) {
9455
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
9456
+ }
9457
+ function asArray(value) {
9458
+ return Array.isArray(value) ? value : [];
9459
+ }
9460
+ function asString(value) {
9461
+ return typeof value === "string" ? value : undefined;
9462
+ }
9463
+ function asNumber(value) {
9464
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
9465
+ }
9466
+ function parseEpochMs(value) {
9467
+ if (typeof value !== "string" || value.length === 0)
9468
+ return;
9469
+ const parsed = Date.parse(value);
9470
+ return Number.isNaN(parsed) ? undefined : parsed;
9471
+ }
9472
+ function resolveToken(token, tokenEnv) {
9473
+ if (token !== undefined && token.length > 0)
9474
+ return token;
9475
+ if (tokenEnv !== undefined && tokenEnv.length > 0) {
9476
+ const fromEnv = process.env[tokenEnv];
9477
+ if (fromEnv !== undefined && fromEnv.length > 0)
9478
+ return fromEnv;
9479
+ }
9480
+ return;
9481
+ }
9482
+ var DEFAULT_TIMEOUT_MS3 = 15000;
9483
+
9484
+ // src/datasource/skills/discord/connector.ts
9485
+ class DiscordConnector {
9486
+ options;
9487
+ constructor(options = {}) {
9488
+ this.options = options;
9489
+ }
9490
+ async fetch(signal) {
9491
+ const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV);
9492
+ if (token === undefined)
9493
+ return { ok: false, reason: "not-configured", message: "auth token not configured" };
9494
+ const guildId = this.options.guildId;
9495
+ if (guildId === undefined || guildId.length === 0) {
9496
+ return { ok: false, reason: "not-configured", message: "guild id not configured" };
9497
+ }
9498
+ const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL;
9499
+ const request = {
9500
+ headers: { Authorization: `Bot ${token}` },
9501
+ timeoutMs: this.options.timeoutMs,
9502
+ fetchImpl: this.options.fetchImpl,
9503
+ signal
9504
+ };
9505
+ const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES;
9506
+ const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS;
9507
+ const listResult = await httpJson(`${baseUrl}/guilds/${guildId}/channels`, request);
9508
+ if (!listResult.ok) {
9509
+ return { ok: false, reason: listResult.reason, message: `channel list failed: ${listResult.message}` };
9510
+ }
9511
+ const channels = [];
9512
+ for (const raw of asArray(listResult.json)) {
9513
+ const channel = asRecord2(raw);
9514
+ const id = asString(channel?.id);
9515
+ const name = asString(channel?.name);
9516
+ const type = asNumber(channel?.type);
9517
+ if (id === undefined || name === undefined || type === undefined)
9518
+ continue;
9519
+ if (!TEXT_CHANNEL_TYPES.has(type))
9520
+ continue;
9521
+ channels.push({ id, name });
9522
+ }
9523
+ const filter = this.options.channels;
9524
+ const selected = filter === undefined || filter.length === 0 ? channels : channels.filter((channel) => filter.includes(channel.id) || filter.includes(channel.name));
9525
+ const documents = [];
9526
+ const warnings = [];
9527
+ for (const channel of selected) {
9528
+ if (documents.length >= maxDocuments)
9529
+ break;
9530
+ let before;
9531
+ for (let page = 0;page < maxPages && documents.length < maxDocuments; page += 1) {
9532
+ const url = new URL(`${baseUrl}/channels/${channel.id}/messages`);
9533
+ url.searchParams.set("limit", "100");
9534
+ if (before !== undefined)
9535
+ url.searchParams.set("before", before);
9536
+ const result = await httpJson(url.toString(), request);
9537
+ if (!result.ok) {
9538
+ if (result.reason === "rate-limited") {
9539
+ return { ok: false, reason: "rate-limited", message: "discord: rate limited" };
9540
+ }
9541
+ if (result.reason === "permission") {
9542
+ warnings.push(`channel ${channel.name} skipped: missing permission`);
9543
+ } else {
9544
+ warnings.push(`channel ${channel.name} history failed: ${result.message}`);
9545
+ }
9546
+ break;
9547
+ }
9548
+ const messages = asArray(result.json);
9549
+ if (messages.length === 0)
9550
+ break;
9551
+ let lastId;
9552
+ for (const raw of messages) {
9553
+ const message = asRecord2(raw);
9554
+ const id = asString(message?.id);
9555
+ if (id !== undefined)
9556
+ lastId = id;
9557
+ if (documents.length >= maxDocuments)
9558
+ continue;
9559
+ const content = asString(message?.content);
9560
+ if (message === undefined || id === undefined || content === undefined || content.length === 0)
9561
+ continue;
9562
+ const author = asString(asRecord2(message.author)?.username) ?? "unknown";
9563
+ documents.push({
9564
+ docId: `${channel.id}-${id}`,
9565
+ hierarchy: ["channels", channel.name],
9566
+ title: `#${channel.name}`,
9567
+ content: `${author}: ${content}`,
9568
+ publishedAt: parseEpochMs(message.timestamp),
9569
+ metadata: { channelId: channel.id, messageId: id, author }
9570
+ });
9571
+ }
9572
+ if (lastId === undefined || messages.length < 100)
9573
+ break;
9574
+ before = lastId;
9575
+ }
9576
+ }
9577
+ return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
9578
+ }
9579
+ }
9580
+ var DEFAULT_BASE_URL = "https://discord.com/api/v10", DEFAULT_TOKEN_ENV = "DISCORD_BOT_TOKEN", DEFAULT_MAX_PAGES = 10, DEFAULT_MAX_DOCUMENTS = 500, TEXT_CHANNEL_TYPES;
9581
+ var init_connector2 = __esm(() => {
9582
+ TEXT_CHANNEL_TYPES = new Set([0, 5]);
9583
+ });
9584
+
9585
+ // src/datasource/skills/discord/skill.ts
9586
+ var DISCORD_SKILL_DEFINITION, DiscordSkill;
9587
+ var init_skill = __esm(() => {
9588
+ init_connector_skill();
9589
+ init_connector2();
9590
+ DISCORD_SKILL_DEFINITION = {
9591
+ skillName: "discord",
9592
+ skillType: "discord-guild",
9593
+ description: "Discord guild datasource",
9594
+ capabilities: ["chat", "api", "polling"],
9595
+ defaultTags: ["discord", "chat", "pii"],
9596
+ contentType: "chat",
9597
+ manifestDescription: "Search indexed Discord messages across authorized guild channels and threads. Use for questions about Discord conversations or community discussions.",
9598
+ manifestNotes: ["Channel visibility and history windows are bounded by the server-configured bot permissions."]
9599
+ };
9600
+ DiscordSkill = class DiscordSkill extends ConnectorDatasourceSkill {
9601
+ constructor(options = {}) {
9602
+ const { connector, connectorOptions, ...rest } = options;
9603
+ super(DISCORD_SKILL_DEFINITION, {
9604
+ ...rest,
9605
+ connector: connector ?? new DiscordConnector(connectorOptions ?? {})
9606
+ });
9607
+ }
9608
+ };
9609
+ });
9610
+
9611
+ // src/datasource/skills/discord/index.ts
9612
+ var init_discord = __esm(() => {
9613
+ init_connector2();
9614
+ init_skill();
9615
+ });
9616
+
9617
+ // src/datasource/skills/gdrive/connector.ts
9618
+ class GDriveConnector {
9619
+ options;
9620
+ constructor(options = {}) {
9621
+ this.options = options;
9622
+ }
9623
+ async fetch(signal) {
9624
+ const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV2);
9625
+ if (token === undefined)
9626
+ return { ok: false, reason: "not-configured", message: "auth token not configured" };
9627
+ const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL2;
9628
+ const request = {
9629
+ headers: { Authorization: `Bearer ${token}` },
9630
+ timeoutMs: this.options.timeoutMs,
9631
+ fetchImpl: this.options.fetchImpl,
9632
+ signal
9633
+ };
9634
+ const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES2;
9635
+ const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS2;
9636
+ const files = [];
9637
+ let pageToken;
9638
+ for (let page = 0;page < maxPages; page += 1) {
9639
+ const url = new URL(`${baseUrl}/files`);
9640
+ url.searchParams.set("pageSize", "100");
9641
+ url.searchParams.set("fields", "nextPageToken,files(id,name,mimeType,modifiedTime,parents)");
9642
+ const query = this.options.folderId !== undefined ? `trashed=false and '${this.options.folderId}' in parents` : "trashed=false";
9643
+ url.searchParams.set("q", query);
9644
+ if (this.options.includeSharedDrives === true) {
9645
+ url.searchParams.set("supportsAllDrives", "true");
9646
+ url.searchParams.set("includeItemsFromAllDrives", "true");
9647
+ url.searchParams.set("corpora", "allDrives");
9648
+ }
9649
+ if (pageToken !== undefined)
9650
+ url.searchParams.set("pageToken", pageToken);
9651
+ const result = await httpJson(url.toString(), request);
9652
+ if (!result.ok)
9653
+ return { ok: false, reason: result.reason, message: `file list failed: ${result.message}` };
9654
+ const envelope = asRecord2(result.json);
9655
+ if (envelope === undefined)
9656
+ return { ok: false, reason: "invalid-data", message: "file list malformed" };
9657
+ for (const raw of asArray(envelope.files)) {
9658
+ const file = asRecord2(raw);
9659
+ const id = asString(file?.id);
9660
+ const name = asString(file?.name);
9661
+ const mimeType = asString(file?.mimeType);
9662
+ if (id === undefined || name === undefined || mimeType === undefined)
9663
+ continue;
9664
+ files.push({ id, name, mimeType, modifiedTime: asString(file?.modifiedTime) });
9665
+ }
9666
+ pageToken = asString(envelope.nextPageToken);
9667
+ if (pageToken === undefined || pageToken.length === 0)
9668
+ break;
9669
+ }
9670
+ const documents = [];
9671
+ const warnings = [];
9672
+ for (const file of files) {
9673
+ if (documents.length >= maxDocuments)
9674
+ break;
9675
+ const exportMime = EXPORT_MIME[file.mimeType];
9676
+ let contentUrl;
9677
+ if (exportMime !== undefined) {
9678
+ contentUrl = `${baseUrl}/files/${file.id}/export?mimeType=${encodeURIComponent(exportMime)}`;
9679
+ } else if (DOWNLOAD_MIME.has(file.mimeType)) {
9680
+ contentUrl = `${baseUrl}/files/${file.id}?alt=media`;
9681
+ } else {
9682
+ continue;
9683
+ }
9684
+ const contentResult = await httpText(contentUrl, request);
9685
+ if (!contentResult.ok) {
9686
+ warnings.push(`file export failed: ${contentResult.message}`);
9687
+ continue;
9688
+ }
9689
+ const content = contentResult.text.trim().slice(0, MAX_CONTENT_CHARS);
9690
+ if (content.length === 0)
9691
+ continue;
9692
+ documents.push({
9693
+ docId: file.id,
9694
+ hierarchy: ["files"],
9695
+ title: file.name,
9696
+ content,
9697
+ publishedAt: parseEpochMs(file.modifiedTime),
9698
+ metadata: {
9699
+ mimeType: file.mimeType,
9700
+ ...file.modifiedTime !== undefined ? { modifiedTime: file.modifiedTime } : {}
9701
+ }
9702
+ });
9703
+ }
9704
+ return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
9705
+ }
9706
+ }
9707
+ var DEFAULT_BASE_URL2 = "https://www.googleapis.com/drive/v3", DEFAULT_TOKEN_ENV2 = "GDRIVE_ACCESS_TOKEN", DEFAULT_MAX_PAGES2 = 10, DEFAULT_MAX_DOCUMENTS2 = 500, MAX_CONTENT_CHARS = 1e5, EXPORT_MIME, DOWNLOAD_MIME;
9708
+ var init_connector3 = __esm(() => {
9709
+ EXPORT_MIME = {
9710
+ "application/vnd.google-apps.document": "text/plain",
9711
+ "application/vnd.google-apps.spreadsheet": "text/csv"
9712
+ };
9713
+ DOWNLOAD_MIME = new Set(["text/plain", "text/markdown", "text/csv", "application/json"]);
9714
+ });
9715
+
9716
+ // src/datasource/skills/gdrive/rclone-connector.ts
9717
+ import { spawn as spawn5 } from "node:child_process";
9718
+
9719
+ class RcloneConnector {
9720
+ options;
9721
+ runner;
9722
+ constructor(options = {}) {
9723
+ this.options = options;
9724
+ this.runner = options.runner ?? ((args, timeoutMs) => runBinary(options.binaryPath ?? DEFAULT_BINARY2, args, timeoutMs));
9725
+ }
9726
+ async fetch() {
9727
+ const remote = this.options.remote;
9728
+ if (remote === undefined || remote.length === 0) {
9729
+ return { ok: false, reason: "not-configured", message: "rclone remote not configured" };
9730
+ }
9731
+ const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS4;
9732
+ const exportFormats = this.options.exportFormats ?? DEFAULT_EXPORT_FORMATS;
9733
+ const extensions = this.options.extensions ?? DEFAULT_EXTENSIONS;
9734
+ const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS3;
9735
+ const maxBytes = this.options.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE;
9736
+ let listResult;
9737
+ try {
9738
+ listResult = await this.runner(["lsjson", remote, "--recursive", "--files-only", "--drive-export-formats", exportFormats], timeoutMs);
9739
+ } catch {
9740
+ return { ok: false, reason: "unavailable", message: "rclone binary not found or failed to spawn" };
9741
+ }
9742
+ if (!listResult.ok) {
9743
+ return { ok: false, reason: classifyFailure(listResult.stderr), message: shortFailure(listResult) };
9744
+ }
9745
+ let entries;
9746
+ try {
9747
+ entries = asArray(JSON.parse(listResult.stdout));
9748
+ } catch {
9749
+ return { ok: false, reason: "invalid-data", message: "rclone listing was not valid JSON" };
9750
+ }
9751
+ const documents = [];
9752
+ let readFailures = 0;
9753
+ let skipped = 0;
9754
+ for (const raw of entries) {
9755
+ if (documents.length >= maxDocuments)
9756
+ break;
9757
+ const entry = asRecord2(raw);
9758
+ const path = asString(entry?.Path);
9759
+ const name = asString(entry?.Name);
9760
+ if (entry === undefined || path === undefined || name === undefined)
9761
+ continue;
9762
+ const extension = name.includes(".") ? name.slice(name.lastIndexOf(".")).toLowerCase() : "";
9763
+ if (!extensions.includes(extension)) {
9764
+ skipped += 1;
9765
+ continue;
9766
+ }
9767
+ const size = typeof entry.Size === "number" ? entry.Size : 0;
9768
+ if (size > maxBytes) {
9769
+ skipped += 1;
9770
+ continue;
9771
+ }
9772
+ const target = remote.endsWith(":") || remote.endsWith("/") ? `${remote}${path}` : `${remote}/${path}`;
9773
+ let content = "";
9774
+ try {
9775
+ const catResult = await this.runner(["cat", target, "--drive-export-formats", exportFormats, "--count", String(maxBytes)], timeoutMs);
9776
+ if (!catResult.ok) {
9777
+ readFailures += 1;
9778
+ continue;
9779
+ }
9780
+ content = catResult.stdout.trim().slice(0, MAX_CONTENT_CHARS2);
9781
+ } catch {
9782
+ readFailures += 1;
9783
+ continue;
9784
+ }
9785
+ if (content.length === 0)
9786
+ continue;
9787
+ const segments = path.split("/");
9788
+ documents.push({
9789
+ docId: path,
9790
+ hierarchy: ["files", ...segments.slice(0, -1)],
9791
+ title: name,
9792
+ content,
9793
+ publishedAt: parseEpochMs(asString(entry.ModTime)),
9794
+ metadata: {
9795
+ ...asString(entry.MimeType) !== undefined ? { mimeType: asString(entry.MimeType) } : {},
9796
+ size
9797
+ }
9798
+ });
9799
+ }
9800
+ const warnings = [];
9801
+ if (readFailures > 0)
9802
+ warnings.push(`${readFailures} file(s) failed to read`);
9803
+ if (skipped > 0)
9804
+ warnings.push(`${skipped} file(s) skipped (non-text or oversized)`);
9805
+ return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
9806
+ }
9807
+ }
9808
+ function classifyFailure(stderr) {
9809
+ const lower = stderr.toLowerCase();
9810
+ if (lower.includes("didn't find section") || lower.includes("config file") || lower.includes("no remotes")) {
9811
+ return "not-configured";
9812
+ }
9813
+ if (lower.includes("oauth") || lower.includes("token") || lower.includes("unauthorized") || lower.includes("401")) {
9814
+ return "auth";
9815
+ }
9816
+ if (lower.includes("permission") || lower.includes("403") || lower.includes("forbidden"))
9817
+ return "permission";
9818
+ return "api-error";
9819
+ }
9820
+ function shortFailure(result) {
9821
+ return `rclone exited with code ${result.code ?? "unknown"}`;
9822
+ }
9823
+ function runBinary(binary, args, timeoutMs) {
9824
+ return new Promise((resolvePromise) => {
9825
+ const child = spawn5(binary, args, { stdio: ["ignore", "pipe", "pipe"] });
9826
+ let stdout = "";
9827
+ let stderr = "";
9828
+ const timer = setTimeout(() => child.kill("SIGKILL"), timeoutMs);
9829
+ child.stdout.on("data", (chunk) => {
9830
+ stdout += chunk.toString("utf8");
9831
+ });
9832
+ child.stderr.on("data", (chunk) => {
9833
+ stderr += chunk.toString("utf8");
9834
+ });
9835
+ child.on("error", () => {
9836
+ clearTimeout(timer);
9837
+ resolvePromise({ ok: false, stdout, stderr, code: null });
9838
+ });
9839
+ child.on("close", (code) => {
9840
+ clearTimeout(timer);
9841
+ resolvePromise({ ok: code === 0, stdout, stderr, code });
9842
+ });
9843
+ });
9844
+ }
9845
+ var DEFAULT_BINARY2 = "rclone", DEFAULT_EXTENSIONS, DEFAULT_EXPORT_FORMATS = "txt,csv", DEFAULT_MAX_DOCUMENTS3 = 200, DEFAULT_MAX_BYTES_PER_FILE, DEFAULT_TIMEOUT_MS4 = 60000, MAX_CONTENT_CHARS2 = 1e5;
9846
+ var init_rclone_connector = __esm(() => {
9847
+ DEFAULT_EXTENSIONS = [".txt", ".md", ".csv", ".json", ".html", ".rst", ".org"];
9848
+ DEFAULT_MAX_BYTES_PER_FILE = 2 * 1024 * 1024;
9849
+ });
9850
+
9851
+ // src/datasource/skills/gdrive/skill.ts
9852
+ var GDRIVE_SKILL_DEFINITION, GDriveSkill;
9853
+ var init_skill2 = __esm(() => {
9854
+ init_connector_skill();
9855
+ init_connector3();
9856
+ GDRIVE_SKILL_DEFINITION = {
9857
+ skillName: "gdrive",
9858
+ skillType: "google-drive",
9859
+ description: "Google Drive datasource",
9860
+ capabilities: ["documents", "api", "polling"],
9861
+ defaultTags: ["gdrive", "documents", "pii"],
9862
+ contentType: "document",
9863
+ manifestDescription: "Search indexed Google Drive documents (Docs, Sheets, plain text). Use for questions about files stored in the authorized Drive account or folder.",
9864
+ manifestNotes: [
9865
+ "Shared-drive permissions are enforced by the server-configured OAuth token; unexported binary formats are not indexed."
9866
+ ]
9867
+ };
9868
+ GDriveSkill = class GDriveSkill extends ConnectorDatasourceSkill {
9869
+ constructor(options = {}) {
9870
+ const { connector, connectorOptions, ...rest } = options;
9871
+ super(GDRIVE_SKILL_DEFINITION, {
9872
+ ...rest,
9873
+ connector: connector ?? new GDriveConnector(connectorOptions ?? {})
9874
+ });
9875
+ }
9876
+ };
9877
+ });
9878
+
9879
+ // src/datasource/skills/gdrive/index.ts
9880
+ var init_gdrive = __esm(() => {
9881
+ init_connector3();
9882
+ init_rclone_connector();
9883
+ init_skill2();
9884
+ });
9885
+
9886
+ // src/datasource/skills/github/connector.ts
9887
+ class GitHubConnector {
9888
+ options;
9889
+ constructor(options = {}) {
9890
+ this.options = options;
9891
+ }
9892
+ async fetch(signal) {
9893
+ const repos = this.options.repos ?? [];
9894
+ if (repos.length === 0)
9895
+ return { ok: false, reason: "not-configured", message: "no repositories configured" };
9896
+ const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV3);
9897
+ const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL3;
9898
+ const request = {
9899
+ headers: {
9900
+ Accept: "application/vnd.github+json",
9901
+ "X-GitHub-Api-Version": "2022-11-28",
9902
+ "User-Agent": "autorag-datasource",
9903
+ ...token !== undefined ? { Authorization: `Bearer ${token}` } : {}
9904
+ },
9905
+ timeoutMs: this.options.timeoutMs,
9906
+ fetchImpl: this.options.fetchImpl,
9907
+ signal
9908
+ };
9909
+ const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES3;
9910
+ const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS4;
9911
+ const documents = [];
9912
+ const warnings = [];
9913
+ for (const repo of repos) {
9914
+ if (documents.length >= maxDocuments)
9915
+ break;
9916
+ const [owner, name] = repo.split("/", 2);
9917
+ if (owner === undefined || name === undefined || owner.length === 0 || name.length === 0) {
9918
+ warnings.push(`repository entry ${repos.indexOf(repo) + 1} is not owner/repo`);
9919
+ continue;
9920
+ }
9921
+ for (let page = 1;page <= maxPages && documents.length < maxDocuments; page += 1) {
9922
+ const url = `${baseUrl}/repos/${owner}/${name}/issues?state=all&per_page=100&page=${page}`;
9923
+ const result = await httpJson(url, request);
9924
+ if (!result.ok) {
9925
+ if (result.reason === "auth")
9926
+ return { ok: false, reason: "auth", message: "github: unauthorized" };
9927
+ if (result.reason === "rate-limited" || result.reason === "permission") {
9928
+ return { ok: false, reason: "rate-limited", message: "github: rate limited or forbidden" };
9929
+ }
9930
+ warnings.push(`repo ${owner}-${name} fetch failed: ${result.message}`);
9931
+ break;
9932
+ }
9933
+ const items = asArray(result.json);
9934
+ if (items.length === 0)
9935
+ break;
9936
+ for (const raw of items) {
9937
+ if (documents.length >= maxDocuments)
9938
+ break;
9939
+ const item = asRecord2(raw);
9940
+ const number = asNumber(item?.number);
9941
+ const title = asString(item?.title);
9942
+ if (item === undefined || number === undefined || title === undefined)
9943
+ continue;
9944
+ const isPull = asRecord2(item.pull_request) !== undefined;
9945
+ const body = asString(item.body) ?? "";
9946
+ const labels = asArray(item.labels).map((label) => asString(asRecord2(label)?.name)).filter((label) => label !== undefined);
9947
+ documents.push({
9948
+ docId: `${owner}-${name}-${number}`,
9949
+ hierarchy: [owner, name, isPull ? "pulls" : "issues"],
9950
+ title: `#${number} ${title}`,
9951
+ content: body.length > 0 ? `${title}
9952
+
9953
+ ${body}` : title,
9954
+ publishedAt: parseEpochMs(item.updated_at),
9955
+ metadata: {
9956
+ state: asString(item.state) ?? "unknown",
9957
+ number,
9958
+ kind: isPull ? "pull" : "issue",
9959
+ labels
9960
+ }
9961
+ });
9962
+ }
9963
+ if (items.length < 100)
9964
+ break;
9965
+ }
9966
+ }
9967
+ return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
9968
+ }
9969
+ }
9970
+ var DEFAULT_BASE_URL3 = "https://api.github.com", DEFAULT_TOKEN_ENV3 = "GITHUB_TOKEN", DEFAULT_MAX_PAGES3 = 10, DEFAULT_MAX_DOCUMENTS4 = 500;
9971
+ var init_connector4 = () => {};
9972
+
9973
+ // src/datasource/skills/github/skill.ts
9974
+ var GITHUB_SKILL_DEFINITION, GitHubSkill;
9975
+ var init_skill3 = __esm(() => {
9976
+ init_connector_skill();
9977
+ init_connector4();
9978
+ GITHUB_SKILL_DEFINITION = {
9979
+ skillName: "github",
9980
+ skillType: "github-issues",
9981
+ description: "GitHub Issues/PRs datasource",
9982
+ capabilities: ["issues", "api", "polling"],
9983
+ defaultTags: ["github", "issues"],
9984
+ contentType: "issue",
9985
+ manifestDescription: "Search indexed GitHub issues and pull requests for the configured repositories. Use for questions about bug reports, feature requests, and review discussions.",
9986
+ manifestNotes: ["Repository visibility is bounded by the server-configured token scopes."]
9987
+ };
9988
+ GitHubSkill = class GitHubSkill extends ConnectorDatasourceSkill {
9989
+ constructor(options = {}) {
9990
+ const { connector, connectorOptions, ...rest } = options;
9991
+ super(GITHUB_SKILL_DEFINITION, {
9992
+ ...rest,
9993
+ connector: connector ?? new GitHubConnector(connectorOptions ?? {})
9994
+ });
9995
+ }
9996
+ };
9997
+ });
9998
+
9999
+ // src/datasource/skills/github/index.ts
10000
+ var init_github = __esm(() => {
10001
+ init_connector4();
10002
+ init_skill3();
10003
+ });
10004
+
10005
+ // src/datasource/skills/gmail/himalaya-connector.ts
10006
+ import { spawn as spawn6 } from "node:child_process";
10007
+
10008
+ class HimalayaConnector {
10009
+ options;
10010
+ runner;
10011
+ constructor(options = {}) {
10012
+ this.options = options;
10013
+ this.runner = options.runner ?? ((args, timeoutMs) => runBinary2(options.binaryPath ?? DEFAULT_BINARY3, args, timeoutMs));
10014
+ }
10015
+ async fetch() {
10016
+ const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS5;
10017
+ const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS5;
10018
+ const accountArgs = this.options.account !== undefined ? ["--account", this.options.account] : [];
10019
+ const folderArgs = this.options.folder !== undefined ? ["--folder", this.options.folder] : [];
10020
+ const listArgs = [
10021
+ "envelope",
10022
+ "list",
10023
+ ...accountArgs,
10024
+ ...folderArgs,
10025
+ "--page-size",
10026
+ String(this.options.pageSize ?? DEFAULT_PAGE_SIZE),
10027
+ "--output",
10028
+ "json"
10029
+ ];
10030
+ let listResult;
10031
+ try {
10032
+ listResult = await this.runner(listArgs, timeoutMs);
10033
+ } catch {
10034
+ return { ok: false, reason: "unavailable", message: "himalaya binary not found or failed to spawn" };
10035
+ }
10036
+ if (!listResult.ok) {
10037
+ return { ok: false, reason: classifyFailure2(listResult.stderr), message: shortFailure2(listResult) };
10038
+ }
10039
+ let envelopes;
10040
+ try {
10041
+ envelopes = asArray(JSON.parse(listResult.stdout));
10042
+ } catch {
10043
+ return { ok: false, reason: "invalid-data", message: "envelope listing was not valid JSON" };
10044
+ }
10045
+ const account = this.options.account ?? "default";
10046
+ const folder = this.options.folder ?? "INBOX";
10047
+ const documents = [];
10048
+ let readFailures = 0;
10049
+ for (const raw of envelopes.slice(0, maxDocuments)) {
10050
+ const envelope = asRecord2(raw);
10051
+ const id = asString(envelope?.id);
10052
+ if (envelope === undefined || id === undefined)
10053
+ continue;
10054
+ const subject = asString(envelope.subject) ?? "(no subject)";
10055
+ const from = asRecord2(envelope.from);
10056
+ const fromText = [asString(from?.name), asString(from?.addr)].filter(Boolean).join(" ").trim();
10057
+ let body = "";
10058
+ try {
10059
+ const readResult = await this.runner(["message", "read", ...accountArgs, ...folderArgs, id], timeoutMs);
10060
+ if (readResult.ok)
10061
+ body = readResult.stdout.trim().slice(0, MAX_BODY_CHARS);
10062
+ else
10063
+ readFailures += 1;
10064
+ } catch {
10065
+ readFailures += 1;
10066
+ }
10067
+ const headerBlock = [
10068
+ `Subject: ${subject}`,
10069
+ ...fromText.length > 0 ? [`From: ${fromText}`] : [],
10070
+ ...asString(envelope.date) !== undefined ? [`Date: ${asString(envelope.date)}`] : []
10071
+ ].join(`
10072
+ `);
10073
+ documents.push({
10074
+ docId: `${account}-${folder}-${id}`,
10075
+ hierarchy: ["accounts", account, folder],
10076
+ title: subject,
10077
+ content: body.length > 0 ? `${headerBlock}
10078
+
10079
+ ${body}` : headerBlock,
10080
+ publishedAt: parseHimalayaDate(asString(envelope.date)),
10081
+ metadata: {
10082
+ account,
10083
+ folder,
10084
+ messageId: id,
10085
+ ...asArray(envelope.flags).length > 0 ? {
10086
+ flags: asArray(envelope.flags).map((flag) => asString(flag)).filter(Boolean)
10087
+ } : {}
10088
+ }
10089
+ });
10090
+ }
10091
+ const warnings = readFailures > 0 ? [`${readFailures} message(s) failed to read`] : undefined;
10092
+ return { ok: true, documents, ...warnings !== undefined ? { warnings } : {} };
10093
+ }
10094
+ }
10095
+ function parseHimalayaDate(value) {
10096
+ if (value === undefined || value.length === 0)
10097
+ return;
10098
+ const parsed = Date.parse(value.replace(" ", "T"));
10099
+ return Number.isNaN(parsed) ? undefined : parsed;
10100
+ }
10101
+ function classifyFailure2(stderr) {
10102
+ const lower = stderr.toLowerCase();
10103
+ if (lower.includes("cannot find") && lower.includes("account"))
10104
+ return "not-configured";
10105
+ if (lower.includes("auth") || lower.includes("login") || lower.includes("credential"))
10106
+ return "auth";
10107
+ return "api-error";
10108
+ }
10109
+ function shortFailure2(result) {
10110
+ return `himalaya exited with code ${result.code ?? "unknown"}`;
10111
+ }
10112
+ function runBinary2(binary, args, timeoutMs) {
10113
+ return new Promise((resolvePromise) => {
10114
+ const child = spawn6(binary, args, { stdio: ["ignore", "pipe", "pipe"] });
10115
+ let stdout = "";
10116
+ let stderr = "";
10117
+ const timer = setTimeout(() => child.kill("SIGKILL"), timeoutMs);
10118
+ child.stdout.on("data", (chunk) => {
10119
+ stdout += chunk.toString("utf8");
10120
+ });
10121
+ child.stderr.on("data", (chunk) => {
10122
+ stderr += chunk.toString("utf8");
10123
+ });
10124
+ child.on("error", () => {
10125
+ clearTimeout(timer);
10126
+ resolvePromise({ ok: false, stdout, stderr, code: null });
10127
+ });
10128
+ child.on("close", (code) => {
10129
+ clearTimeout(timer);
10130
+ resolvePromise({ ok: code === 0, stdout, stderr, code });
10131
+ });
10132
+ });
10133
+ }
10134
+ var DEFAULT_BINARY3 = "himalaya", DEFAULT_PAGE_SIZE = 100, DEFAULT_MAX_DOCUMENTS5 = 50, DEFAULT_TIMEOUT_MS5 = 30000, MAX_BODY_CHARS = 50000;
10135
+ var init_himalaya_connector = () => {};
10136
+
10137
+ // src/datasource/skills/gmail/connector.ts
10138
+ class GmailConnector {
10139
+ options;
10140
+ constructor(options = {}) {
10141
+ this.options = options;
10142
+ }
10143
+ async fetch(signal) {
10144
+ const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV4);
10145
+ if (token === undefined)
10146
+ return { ok: false, reason: "not-configured", message: "auth token not configured" };
10147
+ const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL4;
10148
+ const request = {
10149
+ headers: { Authorization: `Bearer ${token}` },
10150
+ timeoutMs: this.options.timeoutMs,
10151
+ fetchImpl: this.options.fetchImpl,
10152
+ signal
10153
+ };
10154
+ const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES4;
10155
+ const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS6;
10156
+ const messageIds = [];
10157
+ let pageToken;
10158
+ for (let page = 0;page < maxPages && messageIds.length < maxDocuments; page += 1) {
10159
+ const url = new URL(`${baseUrl}/users/me/messages`);
10160
+ url.searchParams.set("maxResults", "100");
10161
+ for (const labelId of this.options.labelIds ?? [])
10162
+ url.searchParams.append("labelIds", labelId);
10163
+ if (this.options.query !== undefined)
10164
+ url.searchParams.set("q", this.options.query);
10165
+ if (pageToken !== undefined)
10166
+ url.searchParams.set("pageToken", pageToken);
10167
+ const result = await httpJson(url.toString(), request);
10168
+ if (!result.ok)
10169
+ return { ok: false, reason: result.reason, message: `message list failed: ${result.message}` };
10170
+ const envelope = asRecord2(result.json);
10171
+ if (envelope === undefined)
10172
+ return { ok: false, reason: "invalid-data", message: "message list malformed" };
10173
+ for (const raw of asArray(envelope.messages)) {
10174
+ const id = asString(asRecord2(raw)?.id);
10175
+ if (id !== undefined)
10176
+ messageIds.push(id);
10177
+ }
10178
+ pageToken = asString(envelope.nextPageToken);
10179
+ if (pageToken === undefined || pageToken.length === 0)
10180
+ break;
10181
+ }
10182
+ const documents = [];
10183
+ let failedFetches = 0;
10184
+ for (const id of messageIds.slice(0, maxDocuments)) {
10185
+ const result = await httpJson(`${baseUrl}/users/me/messages/${id}?format=full`, request);
10186
+ if (!result.ok) {
10187
+ if (result.reason === "auth")
10188
+ return { ok: false, reason: "auth", message: "gmail: unauthorized" };
10189
+ if (result.reason === "rate-limited") {
10190
+ return { ok: false, reason: "rate-limited", message: "gmail: rate limited" };
10191
+ }
10192
+ failedFetches += 1;
10193
+ continue;
10194
+ }
10195
+ const message = asRecord2(result.json);
10196
+ if (message === undefined) {
10197
+ failedFetches += 1;
10198
+ continue;
10199
+ }
10200
+ const payload = asRecord2(message.payload);
10201
+ const headers = headerMap(payload);
10202
+ const subject = headers.get("subject") ?? "(no subject)";
10203
+ const body = (extractBody(payload) ?? asString(message.snippet) ?? "").slice(0, MAX_BODY_CHARS2);
10204
+ const labelIds = asArray(message.labelIds).map((label) => asString(label)).filter((label) => label !== undefined);
10205
+ const internalDate = asString(message.internalDate);
10206
+ const publishedAt = internalDate !== undefined && /^\d+$/u.test(internalDate) ? Number(internalDate) : undefined;
10207
+ const headerBlock = [
10208
+ `Subject: ${subject}`,
10209
+ ...headers.has("from") ? [`From: ${headers.get("from")}`] : [],
10210
+ ...headers.has("to") ? [`To: ${headers.get("to")}`] : [],
10211
+ ...headers.has("date") ? [`Date: ${headers.get("date")}`] : []
10212
+ ].join(`
10213
+ `);
10214
+ documents.push({
10215
+ docId: id,
10216
+ hierarchy: ["labels", this.options.labelIds?.[0] ?? "all"],
10217
+ title: subject,
10218
+ content: `${headerBlock}
10219
+
10220
+ ${body}`.trim(),
10221
+ ...publishedAt !== undefined ? { publishedAt } : {},
10222
+ metadata: {
10223
+ ...asString(message.threadId) !== undefined ? { threadId: asString(message.threadId) } : {},
10224
+ labelIds
10225
+ }
10226
+ });
10227
+ }
10228
+ const warnings = failedFetches > 0 ? [`${failedFetches} message(s) failed to fetch`] : undefined;
10229
+ return { ok: true, documents, ...warnings !== undefined ? { warnings } : {} };
10230
+ }
10231
+ }
10232
+ function headerMap(payload) {
10233
+ const map = new Map;
10234
+ for (const raw of asArray(payload?.headers)) {
10235
+ const header = asRecord2(raw);
10236
+ const name = asString(header?.name)?.toLowerCase();
10237
+ const value = asString(header?.value);
10238
+ if (name !== undefined && value !== undefined && !map.has(name))
10239
+ map.set(name, value);
10240
+ }
10241
+ return map;
10242
+ }
10243
+ function extractBody(payload) {
10244
+ if (payload === undefined)
10245
+ return;
10246
+ const mimeType = asString(payload.mimeType);
10247
+ const data = asString(asRecord2(payload.body)?.data);
10248
+ if (mimeType === "text/plain" && data !== undefined)
10249
+ return decodeBase64Url(data);
10250
+ const collected = [];
10251
+ for (const raw of asArray(payload.parts)) {
10252
+ const part = asRecord2(raw);
10253
+ const text = extractBody(part);
10254
+ if (text !== undefined && text.length > 0)
10255
+ collected.push(text);
10256
+ }
10257
+ if (collected.length > 0)
10258
+ return collected.join(`
10259
+ `);
10260
+ return data !== undefined ? decodeBase64Url(data) : undefined;
10261
+ }
10262
+ function decodeBase64Url(data) {
10263
+ try {
10264
+ return Buffer.from(data, "base64url").toString("utf8");
10265
+ } catch {
10266
+ return "";
10267
+ }
10268
+ }
10269
+ var DEFAULT_BASE_URL4 = "https://gmail.googleapis.com/gmail/v1", DEFAULT_TOKEN_ENV4 = "GMAIL_ACCESS_TOKEN", DEFAULT_MAX_PAGES4 = 10, DEFAULT_MAX_DOCUMENTS6 = 500, MAX_BODY_CHARS2 = 50000;
10270
+ var init_connector5 = () => {};
10271
+
10272
+ // src/datasource/skills/gmail/skill.ts
10273
+ var GMAIL_SKILL_DEFINITION, GmailSkill;
10274
+ var init_skill4 = __esm(() => {
10275
+ init_connector_skill();
10276
+ init_connector5();
10277
+ GMAIL_SKILL_DEFINITION = {
10278
+ skillName: "gmail",
10279
+ skillType: "gmail-account",
10280
+ description: "Gmail datasource",
10281
+ capabilities: ["email", "api", "polling"],
10282
+ defaultTags: ["gmail", "email", "pii"],
10283
+ contentType: "email",
10284
+ manifestDescription: "Search indexed Gmail messages from the authorized account and labels. Use for questions about email conversations, decisions, or attachments discussed over email.",
10285
+ manifestNotes: ["Label and folder visibility is bounded by the server-configured OAuth token."]
10286
+ };
10287
+ GmailSkill = class GmailSkill extends ConnectorDatasourceSkill {
10288
+ constructor(options = {}) {
10289
+ const { connector, connectorOptions, ...rest } = options;
10290
+ super(GMAIL_SKILL_DEFINITION, {
10291
+ ...rest,
10292
+ connector: connector ?? new GmailConnector(connectorOptions ?? {})
10293
+ });
10294
+ }
10295
+ };
10296
+ });
10297
+
10298
+ // src/datasource/skills/gmail/index.ts
10299
+ var init_gmail = __esm(() => {
10300
+ init_connector5();
10301
+ init_himalaya_connector();
10302
+ init_skill4();
10303
+ });
10304
+
10305
+ // src/datasource/skills/mail-export/connector.ts
10306
+ import { readdir, readFile as readFile4, stat as stat2 } from "node:fs/promises";
10307
+ import { basename as basename6, dirname as dirname11, extname as extname7, join as join18 } from "node:path";
10308
+ import { simpleParser as simpleParser2 } from "mailparser";
10309
+
10310
+ class MailExportConnector {
10311
+ options;
10312
+ constructor(options = {}) {
10313
+ this.options = options;
10314
+ }
10315
+ async fetch() {
10316
+ const paths = this.options.paths ?? [];
10317
+ if (paths.length === 0)
10318
+ return { ok: false, reason: "not-configured", message: "no mail export paths configured" };
10319
+ const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS7;
10320
+ const maxBytes = this.options.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE2;
10321
+ const files = [];
10322
+ let missingPaths = 0;
10323
+ for (const path of paths) {
10324
+ try {
10325
+ const info = await stat2(path);
10326
+ if (info.isDirectory()) {
10327
+ const entries = await readdir(path, { recursive: true, withFileTypes: true });
10328
+ for (const entry of entries) {
10329
+ if (!entry.isFile())
10330
+ continue;
10331
+ const extension = extname7(entry.name).toLowerCase();
10332
+ if (extension === ".eml" || extension === ".mbox") {
10333
+ files.push(join18(entry.parentPath, entry.name));
10334
+ }
10335
+ }
10336
+ } else if (info.isFile()) {
10337
+ files.push(path);
10338
+ }
10339
+ } catch {
10340
+ missingPaths += 1;
10341
+ }
10342
+ }
10343
+ if (missingPaths === paths.length) {
10344
+ return { ok: false, reason: "unavailable", message: "no configured mail export paths were readable" };
10345
+ }
10346
+ const documents = [];
10347
+ const warnings = [];
10348
+ if (missingPaths > 0)
10349
+ warnings.push(`${missingPaths} configured path(s) were unreadable`);
10350
+ let parseFailures = 0;
10351
+ let skippedLarge = 0;
10352
+ for (const file of files.sort()) {
10353
+ if (documents.length >= maxDocuments)
10354
+ break;
10355
+ let bytes;
10356
+ try {
10357
+ const info = await stat2(file);
10358
+ if (info.size > maxBytes) {
10359
+ skippedLarge += 1;
10360
+ continue;
10361
+ }
10362
+ bytes = await readFile4(file);
10363
+ } catch {
10364
+ parseFailures += 1;
10365
+ continue;
10366
+ }
10367
+ const stem = basename6(file, extname7(file));
10368
+ const mailboxDir = basename6(dirname11(file));
10369
+ const hierarchy = ["mailboxes", mailboxDir, stem];
10370
+ const extension = extname7(file).toLowerCase();
10371
+ const rawMessages = extension === ".mbox" ? splitMbox(bytes.toString("utf8")) : [bytes.toString("utf8")];
10372
+ for (const [index, raw] of rawMessages.entries()) {
10373
+ if (documents.length >= maxDocuments)
10374
+ break;
10375
+ try {
10376
+ const parsed = await simpleParser2(raw);
10377
+ const subject = parsed.subject ?? "(no subject)";
10378
+ const from = Array.isArray(parsed.from) ? parsed.from[0]?.text : parsed.from?.text;
10379
+ const to = Array.isArray(parsed.to) ? parsed.to[0]?.text : parsed.to?.text;
10380
+ const body = (parsed.text ?? "").trim().slice(0, MAX_BODY_CHARS3);
10381
+ const headerBlock = [
10382
+ `Subject: ${subject}`,
10383
+ ...from !== undefined ? [`From: ${from}`] : [],
10384
+ ...to !== undefined ? [`To: ${to}`] : [],
10385
+ ...parsed.date !== undefined ? [`Date: ${parsed.date.toISOString()}`] : []
10386
+ ].join(`
10387
+ `);
10388
+ const content = `${headerBlock}
10389
+
10390
+ ${body}`.trim();
10391
+ if (content.length === 0)
10392
+ continue;
10393
+ documents.push({
10394
+ docId: extension === ".mbox" ? `${stem}-${index + 1}` : stem,
10395
+ hierarchy,
10396
+ title: subject,
10397
+ content,
10398
+ ...parsed.date !== undefined ? { publishedAt: parsed.date.getTime() } : {},
10399
+ metadata: {
10400
+ path: file,
10401
+ subject,
10402
+ ...from !== undefined ? { from } : {},
10403
+ messageIndex: index
10404
+ }
10405
+ });
10406
+ } catch {
10407
+ parseFailures += 1;
10408
+ }
10409
+ }
10410
+ }
10411
+ if (skippedLarge > 0)
10412
+ warnings.push(`${skippedLarge} file(s) exceeded the size limit and were skipped`);
10413
+ if (parseFailures > 0)
10414
+ warnings.push(`${parseFailures} message(s) failed to parse`);
10415
+ return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
10416
+ }
10417
+ }
10418
+ function splitMbox(text) {
10419
+ const lines = text.split(/\r?\n/u);
10420
+ const messages = [];
10421
+ let current = [];
10422
+ for (const line of lines) {
10423
+ if (MBOX_SEPARATOR.test(line) && line.startsWith("From ")) {
10424
+ if (current.length > 0)
10425
+ messages.push(current.join(`
10426
+ `));
10427
+ current = [];
10428
+ continue;
10429
+ }
10430
+ current.push(line);
10431
+ }
10432
+ if (current.length > 0)
10433
+ messages.push(current.join(`
10434
+ `));
10435
+ return messages.filter((message) => message.trim().length > 0);
10436
+ }
10437
+ var DEFAULT_MAX_DOCUMENTS7 = 500, DEFAULT_MAX_BYTES_PER_FILE2, MAX_BODY_CHARS3 = 50000, MBOX_SEPARATOR;
10438
+ var init_connector6 = __esm(() => {
10439
+ DEFAULT_MAX_BYTES_PER_FILE2 = 50 * 1024 * 1024;
10440
+ MBOX_SEPARATOR = /^From .*\d{4}$/mu;
10441
+ });
10442
+
10443
+ // src/datasource/skills/mail-export/skill.ts
10444
+ var MAIL_EXPORT_SKILL_DEFINITION, MailExportSkill;
10445
+ var init_skill5 = __esm(() => {
10446
+ init_connector_skill();
10447
+ init_connector6();
10448
+ MAIL_EXPORT_SKILL_DEFINITION = {
10449
+ skillName: "mail-export",
10450
+ skillType: "mail-export",
10451
+ description: "Local mail export datasource",
10452
+ capabilities: ["email", "filesystem"],
10453
+ defaultTags: ["mail-export", "email", "pii"],
10454
+ contentType: "email",
10455
+ manifestDescription: "Search indexed local mail exports (mbox/eml archives). Use for questions about archived email conversations.",
10456
+ manifestNotes: [
10457
+ "Only the server-configured export paths are indexed; archives are read locally and never uploaded."
10458
+ ]
10459
+ };
10460
+ MailExportSkill = class MailExportSkill extends ConnectorDatasourceSkill {
10461
+ constructor(options = {}) {
10462
+ const { connector, connectorOptions, ...rest } = options;
10463
+ super(MAIL_EXPORT_SKILL_DEFINITION, {
10464
+ ...rest,
10465
+ connector: connector ?? new MailExportConnector(connectorOptions ?? {})
10466
+ });
10467
+ }
10468
+ };
10469
+ });
10470
+
10471
+ // src/datasource/skills/mail-export/index.ts
10472
+ var init_mail_export = __esm(() => {
10473
+ init_connector6();
10474
+ init_skill5();
10475
+ });
10476
+
10477
+ // src/datasource/skills/notion/connector.ts
10478
+ class NotionConnector {
10479
+ options;
10480
+ constructor(options = {}) {
10481
+ this.options = options;
10482
+ }
10483
+ async fetch(signal) {
10484
+ const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV5);
10485
+ if (token === undefined)
10486
+ return { ok: false, reason: "not-configured", message: "auth token not configured" };
10487
+ const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL5;
10488
+ const request = {
10489
+ headers: {
10490
+ Authorization: `Bearer ${token}`,
10491
+ "Notion-Version": NOTION_VERSION,
10492
+ "Content-Type": "application/json"
10493
+ },
10494
+ timeoutMs: this.options.timeoutMs,
10495
+ fetchImpl: this.options.fetchImpl,
10496
+ signal
10497
+ };
10498
+ const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES5;
10499
+ const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS8;
10500
+ const entries = [];
10501
+ let cursor;
10502
+ for (let page = 0;page < maxPages; page += 1) {
10503
+ const body = JSON.stringify({ page_size: 100, ...cursor !== undefined ? { start_cursor: cursor } : {} });
10504
+ const result = await httpJson(`${baseUrl}/search`, { ...request, method: "POST", body });
10505
+ if (!result.ok)
10506
+ return { ok: false, reason: result.reason, message: `search failed: ${result.message}` };
10507
+ const envelope = asRecord2(result.json);
10508
+ if (envelope === undefined)
10509
+ return { ok: false, reason: "invalid-data", message: "search response malformed" };
10510
+ for (const raw of asArray(envelope.results)) {
10511
+ const entry = asRecord2(raw);
10512
+ if (entry !== undefined)
10513
+ entries.push(entry);
10514
+ }
10515
+ cursor = envelope.has_more === true ? asString(envelope.next_cursor) : undefined;
10516
+ if (cursor === undefined)
10517
+ break;
10518
+ }
10519
+ const documents = [];
10520
+ const warnings = [];
10521
+ for (const entry of entries) {
10522
+ if (documents.length >= maxDocuments)
10523
+ break;
10524
+ const id = asString(entry.id);
10525
+ if (id === undefined)
10526
+ continue;
10527
+ const objectType = asString(entry.object) ?? "page";
10528
+ const title = extractTitle(entry);
10529
+ let blockText = "";
10530
+ if (objectType === "page") {
10531
+ const blocksResult = await httpJson(`${baseUrl}/blocks/${id}/children?page_size=100`, request);
10532
+ if (blocksResult.ok) {
10533
+ blockText = extractBlockText(blocksResult.json);
10534
+ } else {
10535
+ warnings.push(`page content fetch failed: ${blocksResult.message}`);
10536
+ }
10537
+ }
10538
+ const content = [title, blockText].filter((part) => part.length > 0).join(`
10539
+
10540
+ `);
10541
+ if (content.length === 0)
10542
+ continue;
10543
+ documents.push({
10544
+ docId: id,
10545
+ hierarchy: hierarchyFor(entry, objectType),
10546
+ ...title.length > 0 ? { title } : {},
10547
+ content,
10548
+ publishedAt: parseEpochMs(entry.last_edited_time),
10549
+ metadata: {
10550
+ objectType,
10551
+ ...asString(entry.last_edited_time) !== undefined ? { lastEditedTime: asString(entry.last_edited_time) } : {}
10552
+ }
10553
+ });
10554
+ }
10555
+ return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
10556
+ }
10557
+ }
10558
+ function hierarchyFor(entry, objectType) {
10559
+ if (objectType === "database")
10560
+ return ["databases", asString(entry.id) ?? "unknown"];
10561
+ const parent = asRecord2(entry.parent);
10562
+ const parentType = asString(parent?.type);
10563
+ if (parentType === "database_id") {
10564
+ return ["databases", asString(parent?.database_id) ?? "unknown"];
10565
+ }
10566
+ if (parentType === "page_id") {
10567
+ return ["pages", asString(parent?.page_id) ?? "unknown"];
10568
+ }
10569
+ return ["pages"];
10570
+ }
10571
+ function extractTitle(entry) {
10572
+ const directTitle = plainText(entry.title);
10573
+ if (directTitle.length > 0)
10574
+ return directTitle;
10575
+ const properties = asRecord2(entry.properties);
10576
+ if (properties === undefined)
10577
+ return "";
10578
+ for (const value of Object.values(properties)) {
10579
+ const property = asRecord2(value);
10580
+ if (property === undefined || property.type !== "title")
10581
+ continue;
10582
+ const text = plainText(property.title);
10583
+ if (text.length > 0)
10584
+ return text;
10585
+ }
10586
+ return "";
10587
+ }
10588
+ function extractBlockText(json) {
10589
+ const envelope = asRecord2(json);
10590
+ const lines = [];
10591
+ for (const raw of asArray(envelope?.results)) {
10592
+ const block = asRecord2(raw);
10593
+ const type = asString(block?.type);
10594
+ if (block === undefined || type === undefined)
10595
+ continue;
10596
+ if (!TEXT_BLOCK_TYPES.includes(type))
10597
+ continue;
10598
+ const payload = asRecord2(block[type]);
10599
+ const text = plainText(payload?.rich_text);
10600
+ if (text.length > 0)
10601
+ lines.push(text);
10602
+ }
10603
+ return lines.join(`
10604
+ `);
10605
+ }
10606
+ function plainText(richText) {
10607
+ return asArray(richText).map((raw) => asString(asRecord2(raw)?.plain_text) ?? "").filter((text) => text.length > 0).join("").trim();
10608
+ }
10609
+ var DEFAULT_BASE_URL5 = "https://api.notion.com/v1", DEFAULT_TOKEN_ENV5 = "NOTION_TOKEN", NOTION_VERSION = "2022-06-28", DEFAULT_MAX_PAGES5 = 10, DEFAULT_MAX_DOCUMENTS8 = 500, TEXT_BLOCK_TYPES;
10610
+ var init_connector7 = __esm(() => {
10611
+ TEXT_BLOCK_TYPES = [
10612
+ "paragraph",
10613
+ "heading_1",
10614
+ "heading_2",
10615
+ "heading_3",
10616
+ "bulleted_list_item",
10617
+ "numbered_list_item",
10618
+ "to_do",
10619
+ "quote",
10620
+ "callout",
10621
+ "code"
10622
+ ];
10623
+ });
10624
+
10625
+ // src/datasource/skills/notion/skill.ts
10626
+ var NOTION_SKILL_DEFINITION, NotionSkill;
10627
+ var init_skill6 = __esm(() => {
10628
+ init_connector_skill();
10629
+ init_connector7();
10630
+ NOTION_SKILL_DEFINITION = {
10631
+ skillName: "notion",
10632
+ skillType: "notion-workspace",
10633
+ description: "Notion workspace datasource",
10634
+ capabilities: ["documents", "api", "polling"],
10635
+ defaultTags: ["notion", "documents"],
10636
+ contentType: "document",
10637
+ manifestDescription: "Search indexed Notion pages and databases shared with the integration. Use for questions about Notion docs, wikis, and database entries.",
10638
+ manifestNotes: ["Only pages shared with the server-configured integration token are indexed."]
10639
+ };
10640
+ NotionSkill = class NotionSkill extends ConnectorDatasourceSkill {
10641
+ constructor(options = {}) {
10642
+ const { connector, connectorOptions, ...rest } = options;
10643
+ super(NOTION_SKILL_DEFINITION, {
10644
+ ...rest,
10645
+ connector: connector ?? new NotionConnector(connectorOptions ?? {})
10646
+ });
10647
+ }
10648
+ };
10649
+ });
10650
+
10651
+ // src/datasource/skills/notion/index.ts
10652
+ var init_notion = __esm(() => {
10653
+ init_connector7();
10654
+ init_skill6();
10655
+ });
10656
+
10657
+ // src/datasource/skills/obsidian/connector.ts
10658
+ import { readdir as readdir2, readFile as readFile5, stat as stat3 } from "node:fs/promises";
10659
+ import { basename as basename7, extname as extname8, join as join19, relative as relative5, sep as sep4 } from "node:path";
10660
+
10661
+ class ObsidianConnector {
10662
+ options;
10663
+ constructor(options = {}) {
10664
+ this.options = options;
10665
+ }
10666
+ async fetch() {
10667
+ const vaultPath = this.options.vaultPath;
10668
+ if (vaultPath === undefined || vaultPath.length === 0) {
10669
+ return { ok: false, reason: "not-configured", message: "vault path not configured" };
10670
+ }
10671
+ try {
10672
+ const info = await stat3(vaultPath);
10673
+ if (!info.isDirectory())
10674
+ return { ok: false, reason: "unavailable", message: "vault path is not a directory" };
10675
+ } catch {
10676
+ return { ok: false, reason: "unavailable", message: "vault path is not readable" };
10677
+ }
10678
+ const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS9;
10679
+ const maxBytes = this.options.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE3;
10680
+ const files = [];
10681
+ const walk = async (dir) => {
10682
+ let entries;
10683
+ try {
10684
+ entries = await readdir2(dir, { withFileTypes: true });
10685
+ } catch {
10686
+ return;
10687
+ }
10688
+ for (const entry of entries) {
10689
+ if (entry.isDirectory()) {
10690
+ if (!SKIPPED_DIRS.has(entry.name))
10691
+ await walk(join19(dir, entry.name));
10692
+ } else if (entry.isFile() && extname8(entry.name).toLowerCase() === ".md") {
10693
+ files.push(join19(dir, entry.name));
10694
+ }
10695
+ }
10696
+ };
10697
+ await walk(vaultPath);
10698
+ const documents = [];
10699
+ let readFailures = 0;
10700
+ let skippedLarge = 0;
10701
+ for (const file of files.sort()) {
10702
+ if (documents.length >= maxDocuments)
10703
+ break;
10704
+ let text;
10705
+ try {
10706
+ const info = await stat3(file);
10707
+ if (info.size > maxBytes) {
10708
+ skippedLarge += 1;
10709
+ continue;
10710
+ }
10711
+ text = await readFile5(file, "utf8");
10712
+ const relativePath = relative5(vaultPath, file);
10713
+ const segments = relativePath.split(sep4);
10714
+ const directorySegments = segments.slice(0, -1);
10715
+ const stem = basename7(file, ".md");
10716
+ const { body, frontmatterTags } = stripFrontmatter(text);
10717
+ const inlineTags = matchAll(body, INLINE_TAG_PATTERN, 1);
10718
+ const links = [];
10719
+ const embeds = [];
10720
+ for (const match of body.matchAll(WIKI_LINK_PATTERN)) {
10721
+ const target = match[2]?.trim();
10722
+ if (target === undefined || target.length === 0)
10723
+ continue;
10724
+ (match[1] === "!" ? embeds : links).push(target);
10725
+ }
10726
+ const heading = /^#\s+(.+)$/mu.exec(body)?.[1]?.trim();
10727
+ const content = body.trim().slice(0, MAX_CONTENT_CHARS3);
10728
+ if (content.length === 0)
10729
+ continue;
10730
+ documents.push({
10731
+ docId: relativePath.split(sep4).join("__").replace(/\.md$/u, ""),
10732
+ hierarchy: ["folders", ...directorySegments],
10733
+ title: heading ?? stem,
10734
+ content,
10735
+ publishedAt: info.mtimeMs,
10736
+ metadata: {
10737
+ path: file,
10738
+ tags: [...new Set([...frontmatterTags, ...inlineTags])],
10739
+ links,
10740
+ embeds,
10741
+ folder: directorySegments.join("/")
10742
+ }
10743
+ });
10744
+ } catch {
10745
+ readFailures += 1;
10746
+ }
10747
+ }
10748
+ const warnings = [];
10749
+ if (skippedLarge > 0)
10750
+ warnings.push(`${skippedLarge} note(s) exceeded the size limit and were skipped`);
10751
+ if (readFailures > 0)
10752
+ warnings.push(`${readFailures} note(s) failed to read`);
10753
+ return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
10754
+ }
10755
+ }
10756
+ function stripFrontmatter(text) {
10757
+ if (!text.startsWith("---"))
10758
+ return { body: text, frontmatterTags: [] };
10759
+ const end = text.indexOf(`
10760
+ ---`, 3);
10761
+ if (end === -1)
10762
+ return { body: text, frontmatterTags: [] };
10763
+ const frontmatter = text.slice(3, end);
10764
+ const body = text.slice(text.indexOf(`
10765
+ `, end + 1) + 1);
10766
+ return { body, frontmatterTags: parseFrontmatterTags(frontmatter) };
10767
+ }
10768
+ function parseFrontmatterTags(frontmatter) {
10769
+ const lines = frontmatter.split(`
10770
+ `);
10771
+ const tags = [];
10772
+ for (const [index, line] of lines.entries()) {
10773
+ const match = /^tags:\s*(.*)$/u.exec(line.trim());
10774
+ if (match === null)
10775
+ continue;
10776
+ const inline = match[1]?.trim() ?? "";
10777
+ if (inline.length > 0) {
10778
+ const cleaned = inline.replace(/^\[/u, "").replace(/\]$/u, "");
10779
+ for (const tag of cleaned.split(/[\s,]+/u)) {
10780
+ const trimmed = tag.trim().replace(/^["'#]+|["']+$/gu, "");
10781
+ if (trimmed.length > 0)
10782
+ tags.push(trimmed);
10783
+ }
10784
+ break;
10785
+ }
10786
+ for (let cursor = index + 1;cursor < lines.length; cursor += 1) {
10787
+ const item = /^\s*-\s*(.+)$/u.exec(lines[cursor] ?? "");
10788
+ if (item === null)
10789
+ break;
10790
+ const trimmed = item[1]?.trim().replace(/^["'#]+|["']+$/gu, "") ?? "";
10791
+ if (trimmed.length > 0)
10792
+ tags.push(trimmed);
10793
+ }
10794
+ break;
10795
+ }
10796
+ return tags;
10797
+ }
10798
+ function matchAll(text, pattern, group) {
10799
+ const out = [];
10800
+ for (const match of text.matchAll(pattern)) {
10801
+ const value = match[group]?.trim();
10802
+ if (value !== undefined && value.length > 0)
10803
+ out.push(value);
10804
+ }
10805
+ return out;
10806
+ }
10807
+ var DEFAULT_MAX_DOCUMENTS9 = 1000, DEFAULT_MAX_BYTES_PER_FILE3, MAX_CONTENT_CHARS3 = 1e5, SKIPPED_DIRS, INLINE_TAG_PATTERN, WIKI_LINK_PATTERN;
10808
+ var init_connector8 = __esm(() => {
10809
+ DEFAULT_MAX_BYTES_PER_FILE3 = 2 * 1024 * 1024;
10810
+ SKIPPED_DIRS = new Set([".obsidian", ".trash", ".git", "node_modules"]);
10811
+ INLINE_TAG_PATTERN = /(?:^|\s)#([A-Za-z0-9_/-]+)/gu;
10812
+ WIKI_LINK_PATTERN = /(!?)\[\[([^\]|]+)(?:\|[^\]]*)?\]\]/gu;
10813
+ });
10814
+
10815
+ // src/datasource/skills/obsidian/skill.ts
10816
+ var OBSIDIAN_SKILL_DEFINITION, ObsidianSkill;
10817
+ var init_skill7 = __esm(() => {
10818
+ init_connector_skill();
10819
+ init_connector8();
10820
+ OBSIDIAN_SKILL_DEFINITION = {
10821
+ skillName: "obsidian",
10822
+ skillType: "obsidian-vault",
10823
+ description: "Obsidian vault datasource",
10824
+ capabilities: ["notes", "filesystem"],
10825
+ defaultTags: ["obsidian", "notes"],
10826
+ contentType: "note",
10827
+ manifestDescription: "Search indexed Obsidian vault notes, including tags, wiki links, and embeds. Use for questions about personal notes and knowledge-base content.",
10828
+ manifestNotes: ["Only the server-configured vault is indexed; note metadata includes tags, links, and embeds."]
10829
+ };
10830
+ ObsidianSkill = class ObsidianSkill extends ConnectorDatasourceSkill {
10831
+ constructor(options = {}) {
10832
+ const { connector, connectorOptions, ...rest } = options;
10833
+ super(OBSIDIAN_SKILL_DEFINITION, {
10834
+ ...rest,
10835
+ connector: connector ?? new ObsidianConnector(connectorOptions ?? {})
10836
+ });
10837
+ }
10838
+ };
10839
+ });
10840
+
10841
+ // src/datasource/skills/obsidian/index.ts
10842
+ var init_obsidian = __esm(() => {
10843
+ init_connector8();
10844
+ init_skill7();
10845
+ });
10846
+
10847
+ // src/datasource/skills/rss/connector.ts
10848
+ import { XMLParser as XMLParser2 } from "fast-xml-parser";
10849
+
10850
+ class RssConnector {
10851
+ options;
10852
+ parser = new XMLParser2({ ignoreAttributes: false });
10853
+ constructor(options = {}) {
10854
+ this.options = options;
10855
+ }
10856
+ async fetch(signal) {
10857
+ const feeds = this.options.feeds ?? [];
10858
+ if (feeds.length === 0)
10859
+ return { ok: false, reason: "not-configured", message: "no feeds configured" };
10860
+ const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS10;
10861
+ const maxItemsPerFeed = this.options.maxItemsPerFeed ?? DEFAULT_MAX_ITEMS_PER_FEED;
10862
+ const documents = [];
10863
+ const warnings = [];
10864
+ let failedFeeds = 0;
10865
+ for (const [index, feed] of feeds.entries()) {
10866
+ if (documents.length >= maxDocuments)
10867
+ break;
10868
+ const result = await httpText(feed.url, {
10869
+ headers: { Accept: ACCEPT_HEADER },
10870
+ timeoutMs: this.options.timeoutMs ?? DEFAULT_FEED_TIMEOUT_MS,
10871
+ fetchImpl: this.options.fetchImpl,
10872
+ signal
10873
+ });
10874
+ if (!result.ok) {
10875
+ failedFeeds += 1;
10876
+ warnings.push(`feed ${index + 1} failed: ${result.message}`);
10877
+ continue;
10878
+ }
10879
+ let parsed;
10880
+ try {
10881
+ parsed = this.parser.parse(result.text);
10882
+ } catch {
10883
+ failedFeeds += 1;
10884
+ warnings.push(`feed ${index + 1} failed: unparseable XML`);
10885
+ continue;
10886
+ }
10887
+ const normalized = normalizeFeed(parsed);
10888
+ if (normalized === undefined) {
10889
+ failedFeeds += 1;
10890
+ warnings.push(`feed ${index + 1} failed: unrecognized feed shape`);
10891
+ continue;
10892
+ }
10893
+ const feedTitle = normalized.title ?? `feed-${index + 1}`;
10894
+ const hierarchy = feed.category !== undefined ? ["feeds", feed.category, feedTitle] : ["feeds", feedTitle];
10895
+ for (const item of normalized.items.slice(0, maxItemsPerFeed)) {
10896
+ if (documents.length >= maxDocuments)
10897
+ break;
10898
+ const title = item.title ?? "(untitled)";
10899
+ const body = stripHtml(item.body ?? "");
10900
+ const content = body.length > 0 ? `${title}
10901
+
10902
+ ${body}` : title;
10903
+ documents.push({
10904
+ docId: item.id,
10905
+ hierarchy,
10906
+ title,
10907
+ content: content.slice(0, MAX_CONTENT_CHARS4),
10908
+ ...item.publishedAt !== undefined ? { publishedAt: item.publishedAt } : {},
10909
+ metadata: { feedIndex: index, feedTitle, categories: item.categories }
10910
+ });
10911
+ }
10912
+ }
10913
+ if (failedFeeds === feeds.length) {
10914
+ return { ok: false, reason: "unavailable", message: "all configured feeds failed" };
10915
+ }
10916
+ return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
10917
+ }
10918
+ }
10919
+ function normalizeFeed(parsed) {
10920
+ const root = asRecord2(parsed);
10921
+ if (root === undefined)
10922
+ return;
10923
+ const channel = asRecord2(asRecord2(root.rss)?.channel);
10924
+ if (channel !== undefined) {
10925
+ const items = asArray(channel.item ?? []).concat(asRecord2(channel.item) !== undefined ? [channel.item] : []);
10926
+ return { title: textOf(channel.title), items: items.map(normalizeRssItem).filter(present) };
10927
+ }
10928
+ const atom = asRecord2(root.feed);
10929
+ if (atom !== undefined) {
10930
+ const entries = asArray(atom.entry ?? []).concat(asRecord2(atom.entry) !== undefined ? [atom.entry] : []);
10931
+ return { title: textOf(atom.title), items: entries.map(normalizeAtomEntry).filter(present) };
10932
+ }
10933
+ return;
10934
+ }
10935
+ function normalizeRssItem(raw) {
10936
+ const item = asRecord2(raw);
10937
+ if (item === undefined)
10938
+ return;
10939
+ const title = textOf(item.title);
10940
+ const guid = textOf(item.guid) ?? textOf(item.link) ?? title;
10941
+ if (guid === undefined)
10942
+ return;
10943
+ return {
10944
+ id: guid,
10945
+ title,
10946
+ body: textOf(item.description),
10947
+ publishedAt: parseEpochMs(textOf(item.pubDate)),
10948
+ categories: categoriesOf(item.category)
10949
+ };
10950
+ }
10951
+ function normalizeAtomEntry(raw) {
10952
+ const entry = asRecord2(raw);
10953
+ if (entry === undefined)
10954
+ return;
10955
+ const title = textOf(entry.title);
10956
+ const id = textOf(entry.id) ?? title;
10957
+ if (id === undefined)
10958
+ return;
10959
+ return {
10960
+ id,
10961
+ title,
10962
+ body: textOf(entry.summary) ?? textOf(entry.content),
10963
+ publishedAt: parseEpochMs(textOf(entry.published) ?? textOf(entry.updated)),
10964
+ categories: categoriesOf(entry.category)
10965
+ };
10966
+ }
10967
+ function textOf(value) {
10968
+ const direct = asString(value);
10969
+ if (direct !== undefined)
10970
+ return direct.trim();
10971
+ if (typeof value === "number")
10972
+ return String(value);
10973
+ const record = asRecord2(value);
10974
+ if (record !== undefined) {
10975
+ const text = asString(record["#text"]) ?? asString(record["@_term"]);
10976
+ if (text !== undefined)
10977
+ return text.trim();
10978
+ if (typeof record["#text"] === "number")
10979
+ return String(record["#text"]);
10980
+ }
10981
+ return;
10982
+ }
10983
+ function categoriesOf(value) {
10984
+ const values = Array.isArray(value) ? value : value !== undefined ? [value] : [];
10985
+ return values.map(textOf).filter((category) => category !== undefined && category.length > 0);
10986
+ }
10987
+ function stripHtml(text) {
10988
+ return text.replace(/<[^>]+>/gu, " ").replace(/\s+/gu, " ").trim();
10989
+ }
10990
+ function present(item) {
10991
+ return item !== undefined;
10992
+ }
10993
+ var DEFAULT_MAX_DOCUMENTS10 = 500, DEFAULT_MAX_ITEMS_PER_FEED = 100, DEFAULT_FEED_TIMEOUT_MS = 30000, MAX_CONTENT_CHARS4 = 20000, ACCEPT_HEADER = "application/rss+xml, application/atom+xml, application/xml, text/xml";
10994
+ var init_connector9 = () => {};
10995
+
10996
+ // src/datasource/skills/rss/skill.ts
10997
+ var RSS_SKILL_DEFINITION, DEFAULT_DEDUPE_WINDOW_MS, RssSkill;
10998
+ var init_skill8 = __esm(() => {
10999
+ init_connector_skill();
11000
+ init_connector9();
11001
+ RSS_SKILL_DEFINITION = {
11002
+ skillName: "rss",
11003
+ skillType: "rss-feeds",
11004
+ description: "RSS/news datasource",
11005
+ capabilities: ["news", "api", "polling"],
11006
+ defaultTags: ["rss", "news", "public"],
11007
+ contentType: "article",
11008
+ manifestDescription: "Search indexed RSS/news feed articles from the configured feeds. Use for questions about recent news items or blog posts the server subscribes to.",
11009
+ manifestNotes: ["Feeds are polled with a dedupe window so re-delivered items are indexed once per window."]
11010
+ };
11011
+ DEFAULT_DEDUPE_WINDOW_MS = 24 * 60 * 60 * 1000;
11012
+ RssSkill = class RssSkill extends ConnectorDatasourceSkill {
11013
+ constructor(options = {}) {
11014
+ const { connector, connectorOptions, ...rest } = options;
11015
+ super(RSS_SKILL_DEFINITION, {
11016
+ ...rest,
11017
+ connector: connector ?? new RssConnector(connectorOptions ?? {}),
11018
+ dedupeWindowMs: options.dedupeWindowMs ?? DEFAULT_DEDUPE_WINDOW_MS
11019
+ });
11020
+ }
11021
+ };
11022
+ });
11023
+
11024
+ // src/datasource/skills/rss/index.ts
11025
+ var init_rss = __esm(() => {
11026
+ init_connector9();
11027
+ init_skill8();
11028
+ });
11029
+
11030
+ // src/datasource/skills/slack/connector.ts
11031
+ class SlackConnector {
11032
+ options;
11033
+ constructor(options = {}) {
11034
+ this.options = options;
11035
+ }
11036
+ async fetch(signal) {
11037
+ const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV6);
11038
+ if (token === undefined)
11039
+ return { ok: false, reason: "not-configured", message: "auth token not configured" };
11040
+ const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL6;
11041
+ const headers = { Authorization: `Bearer ${token}` };
11042
+ const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES6;
11043
+ const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS11;
11044
+ const request = { headers, timeoutMs: this.options.timeoutMs, fetchImpl: this.options.fetchImpl, signal };
11045
+ const channels = [];
11046
+ let cursor;
11047
+ for (let page = 0;page < maxPages; page += 1) {
11048
+ const url = new URL(`${baseUrl}/conversations.list`);
11049
+ url.searchParams.set("limit", "200");
11050
+ url.searchParams.set("types", "public_channel,private_channel");
11051
+ if (cursor !== undefined)
11052
+ url.searchParams.set("cursor", cursor);
11053
+ const result = await httpJson(url.toString(), request);
11054
+ if (!result.ok)
11055
+ return { ok: false, reason: result.reason, message: `channel list failed: ${result.message}` };
11056
+ const envelope = asRecord2(result.json);
11057
+ if (envelope === undefined)
11058
+ return { ok: false, reason: "invalid-data", message: "channel list malformed" };
11059
+ if (envelope.ok !== true) {
11060
+ const error = asString(envelope.error) ?? "unknown-error";
11061
+ if (AUTH_ERRORS.has(error))
11062
+ return { ok: false, reason: "auth", message: `slack: ${error}` };
11063
+ if (error === "ratelimited")
11064
+ return { ok: false, reason: "rate-limited", message: "slack: ratelimited" };
11065
+ return { ok: false, reason: "api-error", message: `slack: ${error}` };
11066
+ }
11067
+ for (const raw of asArray(envelope.channels)) {
11068
+ const channel = asRecord2(raw);
11069
+ const id = asString(channel?.id);
11070
+ const name = asString(channel?.name);
11071
+ if (id !== undefined && name !== undefined)
11072
+ channels.push({ id, name });
11073
+ }
11074
+ cursor = asString(asRecord2(envelope.response_metadata)?.next_cursor);
11075
+ if (cursor === undefined || cursor.length === 0)
11076
+ break;
11077
+ }
11078
+ const filter = this.options.channels;
11079
+ const selected = filter === undefined || filter.length === 0 ? channels : channels.filter((channel) => filter.includes(channel.id) || filter.includes(channel.name));
11080
+ const documents = [];
11081
+ const warnings = [];
11082
+ for (const channel of selected) {
11083
+ if (documents.length >= maxDocuments)
11084
+ break;
11085
+ let historyCursor;
11086
+ for (let page = 0;page < maxPages && documents.length < maxDocuments; page += 1) {
11087
+ const url = new URL(`${baseUrl}/conversations.history`);
11088
+ url.searchParams.set("channel", channel.id);
11089
+ url.searchParams.set("limit", "200");
11090
+ if (historyCursor !== undefined)
11091
+ url.searchParams.set("cursor", historyCursor);
11092
+ const result = await httpJson(url.toString(), request);
11093
+ if (!result.ok) {
11094
+ warnings.push(`channel ${channel.name} history failed: ${result.message}`);
11095
+ break;
11096
+ }
11097
+ const envelope = asRecord2(result.json);
11098
+ if (envelope === undefined || envelope.ok !== true) {
11099
+ const error = asString(envelope?.error) ?? "malformed response";
11100
+ if (AUTH_ERRORS.has(error))
11101
+ return { ok: false, reason: "auth", message: `slack: ${error}` };
11102
+ if (error === "ratelimited")
11103
+ return { ok: false, reason: "rate-limited", message: "slack: ratelimited" };
11104
+ if (CHANNEL_SKIP_ERRORS.has(error))
11105
+ warnings.push(`channel ${channel.name} skipped: ${error}`);
11106
+ else
11107
+ warnings.push(`channel ${channel.name} history failed: ${error}`);
11108
+ break;
11109
+ }
11110
+ for (const raw of asArray(envelope.messages)) {
11111
+ if (documents.length >= maxDocuments)
11112
+ break;
11113
+ const message = asRecord2(raw);
11114
+ if (message === undefined)
11115
+ continue;
11116
+ const text = asString(message.text);
11117
+ const ts = asString(message.ts);
11118
+ const subtype = asString(message.subtype);
11119
+ if (text === undefined || text.length === 0 || ts === undefined)
11120
+ continue;
11121
+ if (subtype === "channel_join")
11122
+ continue;
11123
+ const user = asString(message.user) ?? asString(message.username) ?? "unknown";
11124
+ documents.push({
11125
+ docId: `${channel.id}-${ts.replace(/\./gu, "-")}`,
11126
+ hierarchy: ["channels", channel.name],
11127
+ title: `#${channel.name}`,
11128
+ content: `${user}: ${text}`,
11129
+ publishedAt: Math.round(Number.parseFloat(ts) * 1000),
11130
+ metadata: { channelId: channel.id, ts, user }
11131
+ });
11132
+ }
11133
+ historyCursor = asString(asRecord2(envelope.response_metadata)?.next_cursor);
11134
+ if (historyCursor === undefined || historyCursor.length === 0)
11135
+ break;
11136
+ }
11137
+ }
11138
+ return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
11139
+ }
11140
+ }
11141
+ var DEFAULT_BASE_URL6 = "https://slack.com/api", DEFAULT_TOKEN_ENV6 = "SLACK_BOT_TOKEN", DEFAULT_MAX_PAGES6 = 10, DEFAULT_MAX_DOCUMENTS11 = 500, AUTH_ERRORS, CHANNEL_SKIP_ERRORS;
11142
+ var init_connector10 = __esm(() => {
11143
+ AUTH_ERRORS = new Set(["invalid_auth", "not_authed", "token_revoked", "account_inactive"]);
11144
+ CHANNEL_SKIP_ERRORS = new Set(["missing_scope", "access_denied", "channel_not_found", "not_in_channel"]);
11145
+ });
11146
+
11147
+ // src/datasource/skills/slack/skill.ts
11148
+ var SLACK_SKILL_DEFINITION, SlackSkill;
11149
+ var init_skill9 = __esm(() => {
11150
+ init_connector_skill();
11151
+ init_connector10();
11152
+ SLACK_SKILL_DEFINITION = {
11153
+ skillName: "slack",
11154
+ skillType: "slack-workspace",
11155
+ description: "Slack workspace datasource",
11156
+ capabilities: ["chat", "api", "polling"],
11157
+ defaultTags: ["slack", "chat", "pii"],
11158
+ contentType: "chat",
11159
+ manifestDescription: "Search indexed Slack messages across authorized workspace channels. Use for questions about Slack conversations, decisions made in channels, or who said what.",
11160
+ manifestNotes: [
11161
+ "Message history windows and channel visibility are bounded by the server-configured bot token scopes."
11162
+ ]
11163
+ };
11164
+ SlackSkill = class SlackSkill extends ConnectorDatasourceSkill {
11165
+ constructor(options = {}) {
11166
+ const { connector, connectorOptions, ...rest } = options;
11167
+ super(SLACK_SKILL_DEFINITION, {
11168
+ ...rest,
11169
+ connector: connector ?? new SlackConnector(connectorOptions ?? {})
11170
+ });
11171
+ }
11172
+ };
11173
+ });
11174
+
11175
+ // src/datasource/skills/slack/index.ts
11176
+ var init_slack = __esm(() => {
11177
+ init_connector10();
11178
+ init_skill9();
11179
+ });
11180
+
11181
+ // src/datasource/skills/spotlight/connector.ts
11182
+ import { spawn as spawn7 } from "node:child_process";
11183
+ import { createHash as createHash8 } from "node:crypto";
11184
+ import { readFile as fsReadFile, stat as stat4 } from "node:fs/promises";
11185
+ import { basename as basename8, extname as extname9 } from "node:path";
11186
+
11187
+ class SpotlightConnector {
11188
+ options;
11189
+ run;
11190
+ readFile;
11191
+ platform;
11192
+ constructor(options = {}) {
11193
+ this.options = options;
11194
+ this.run = options.run ?? spawnRunner;
11195
+ this.readFile = options.readFile ?? ((path) => fsReadFile(path, "utf8"));
11196
+ this.platform = options.platform ?? process.platform;
11197
+ }
11198
+ async fetch() {
11199
+ if (this.platform !== "darwin") {
11200
+ return { ok: false, reason: "unavailable", message: "spotlight datasource is supported on macOS only" };
11201
+ }
11202
+ const queries = (this.options.queries ?? []).map((query) => query.trim()).filter((query) => query.length > 0);
11203
+ if (queries.length === 0) {
11204
+ return { ok: false, reason: "not-configured", message: "no spotlight queries configured" };
11205
+ }
11206
+ const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS6;
11207
+ const status = await this.run("mdutil", ["-s", "/"], timeoutMs);
11208
+ if (status.ok && /indexing disabled/iu.test(status.stdout)) {
11209
+ return {
11210
+ ok: false,
11211
+ reason: "unavailable",
11212
+ message: "Spotlight indexing is disabled; enable it with `sudo mdutil -i on /`"
11213
+ };
11214
+ }
11215
+ const maxPerQuery = this.options.maxResultsPerQuery ?? DEFAULT_MAX_RESULTS_PER_QUERY;
11216
+ const maxDocuments = this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS12;
11217
+ const seen = new Set;
11218
+ const paths = [];
11219
+ let mdfindMissing = false;
11220
+ let failedQueries = 0;
11221
+ for (const query of queries) {
11222
+ const args = this.options.onlyIn !== undefined ? ["-onlyin", this.options.onlyIn, query] : [query];
11223
+ const result = await this.run("mdfind", args, timeoutMs);
11224
+ if (!result.ok) {
11225
+ if (result.error === "ENOENT") {
11226
+ mdfindMissing = true;
11227
+ break;
11228
+ }
11229
+ failedQueries += 1;
11230
+ continue;
11231
+ }
11232
+ let kept = 0;
11233
+ for (const line of result.stdout.split(`
11234
+ `)) {
11235
+ const path = line.trim();
11236
+ if (path.length === 0 || seen.has(path))
11237
+ continue;
11238
+ if (kept >= maxPerQuery)
11239
+ break;
11240
+ seen.add(path);
11241
+ paths.push(path);
11242
+ kept += 1;
11243
+ }
11244
+ }
11245
+ if (mdfindMissing) {
11246
+ return { ok: false, reason: "unavailable", message: "mdfind binary not found; Spotlight requires macOS" };
11247
+ }
11248
+ if (failedQueries === queries.length) {
11249
+ return { ok: false, reason: "unavailable", message: "every configured mdfind query failed" };
11250
+ }
11251
+ const maxBytes = this.options.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE4;
11252
+ const documents = [];
11253
+ let permissionDenied = 0;
11254
+ let readFailures = 0;
11255
+ for (const path of paths) {
11256
+ if (documents.length >= maxDocuments)
11257
+ break;
11258
+ try {
11259
+ const info = await stat4(path);
11260
+ if (!info.isFile() || info.size > maxBytes)
11261
+ continue;
11262
+ const text = await this.readFile(path);
11263
+ const content = text.trim().slice(0, MAX_CONTENT_CHARS5);
11264
+ if (content.length === 0)
11265
+ continue;
11266
+ documents.push({
11267
+ docId: createHash8("sha256").update(path).digest("hex").slice(0, 16),
11268
+ hierarchy: ["files"],
11269
+ title: basename8(path),
11270
+ content,
11271
+ publishedAt: info.mtimeMs,
11272
+ metadata: { path, extension: extname9(path).toLowerCase() }
11273
+ });
11274
+ } catch (error) {
11275
+ const code = error.code;
11276
+ if (code === "EPERM" || code === "EACCES")
11277
+ permissionDenied += 1;
11278
+ else
11279
+ readFailures += 1;
11280
+ }
11281
+ }
11282
+ if (documents.length === 0 && permissionDenied > 0) {
11283
+ return {
11284
+ ok: false,
11285
+ reason: "permission",
11286
+ message: `macOS denied access to all Spotlight hits; ${FDA_GUIDANCE}`
11287
+ };
11288
+ }
11289
+ const warnings = [];
11290
+ if (failedQueries > 0)
11291
+ warnings.push(`${failedQueries} mdfind quer(y/ies) failed`);
11292
+ if (permissionDenied > 0) {
11293
+ warnings.push(`${permissionDenied} Spotlight hit(s) were denied by macOS privacy protections; ${FDA_GUIDANCE}`);
11294
+ }
11295
+ if (readFailures > 0)
11296
+ warnings.push(`${readFailures} Spotlight hit(s) failed to read`);
11297
+ return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
11298
+ }
8519
11299
  }
8520
- function datasourceSourceHasFragment(source) {
8521
- return typeof source === "string" && source.includes("#");
11300
+ var DEFAULT_MAX_RESULTS_PER_QUERY = 100, DEFAULT_MAX_DOCUMENTS12 = 300, DEFAULT_MAX_BYTES_PER_FILE4, DEFAULT_TIMEOUT_MS6 = 15000, MAX_CONTENT_CHARS5 = 1e5, MAX_BUFFER_BYTES, FDA_GUIDANCE = "grant Full Disk Access to the terminal or app running AutoRAG (System Settings -> Privacy & Security -> Full Disk Access), then restart it", spawnRunner = (command, args, timeoutMs) => new Promise((resolve9) => {
11301
+ const child = spawn7(command, [...args], { stdio: ["ignore", "pipe", "pipe"] });
11302
+ let stdout = "";
11303
+ let stderr = "";
11304
+ let settled = false;
11305
+ const finish = (result) => {
11306
+ if (settled)
11307
+ return;
11308
+ settled = true;
11309
+ clearTimeout(timer);
11310
+ resolve9(result);
11311
+ };
11312
+ const timer = setTimeout(() => {
11313
+ child.kill("SIGKILL");
11314
+ finish({ ok: false, stdout, stderr: "process timed out", code: null });
11315
+ }, timeoutMs);
11316
+ child.stdout.setEncoding("utf8");
11317
+ child.stderr.setEncoding("utf8");
11318
+ child.stdout.on("data", (chunk) => {
11319
+ if (stdout.length < MAX_BUFFER_BYTES)
11320
+ stdout += chunk;
11321
+ });
11322
+ child.stderr.on("data", (chunk) => {
11323
+ if (stderr.length < MAX_BUFFER_BYTES)
11324
+ stderr += chunk;
11325
+ });
11326
+ child.on("error", (error) => {
11327
+ finish({ ok: false, stdout, stderr: error.message, code: null, error: error.code });
11328
+ });
11329
+ child.on("close", (code) => {
11330
+ finish({ ok: code === 0, stdout, stderr, code });
11331
+ });
11332
+ });
11333
+ var init_connector11 = __esm(() => {
11334
+ DEFAULT_MAX_BYTES_PER_FILE4 = 256 * 1024;
11335
+ MAX_BUFFER_BYTES = 4 * 1024 * 1024;
11336
+ });
11337
+
11338
+ // src/datasource/skills/spotlight/skill.ts
11339
+ var SPOTLIGHT_SKILL_DEFINITION, SpotlightSkill;
11340
+ var init_skill10 = __esm(() => {
11341
+ init_connector_skill();
11342
+ init_connector11();
11343
+ SPOTLIGHT_SKILL_DEFINITION = {
11344
+ skillName: "spotlight",
11345
+ skillType: "spotlight-search",
11346
+ description: "macOS Spotlight datasource",
11347
+ capabilities: ["files", "macos-only", "local-search"],
11348
+ defaultTags: ["spotlight", "files"],
11349
+ contentType: "document",
11350
+ manifestDescription: "Search files indexed by macOS Spotlight via the built-in mdfind CLI. macOS only; no external installs. Use for questions about local documents on the Mac running AutoRAG.",
11351
+ manifestNotes: [
11352
+ "## Platform requirements",
11353
+ "- macOS only: this datasource is unavailable on any other platform.",
11354
+ "- Spotlight indexing must be enabled (`sudo mdutil -i on /`; check with `mdutil -s /`).",
11355
+ "- **Full Disk Access**: Spotlight can find protected files (Mail, Messages, Safari data, parts of ~/Library), but reading their content fails unless the terminal or app running AutoRAG has Full Disk Access. Grant it under System Settings -> Privacy & Security -> Full Disk Access, add the host app (e.g. Terminal, iTerm, or the AutoRAG binary), then restart the app. Permission failures surface as `datasource-permission-denied`.",
11356
+ "",
11357
+ "## Operator cheat sheet",
11358
+ "- `mdfind <query>` — Spotlight search; `mdfind -onlyin <dir> <query>` scopes to a directory; `mdfind -name <text>` matches file names.",
11359
+ "- `mdls <file>` — show Spotlight metadata attributes (kMDItemDisplayName, kMDItemContentType, …) for one file.",
11360
+ "- `mdutil -s /` — index status; `sudo mdutil -i on /` / `-i off /` — enable/disable indexing; `sudo mdutil -E /` — erase and rebuild the index.",
11361
+ "",
11362
+ "Queries are trusted server configuration; indexing re-runs the configured mdfind queries and hydrates text content. Result metadata carries the real absolute file path (`path`) so hits are traceable back to disk; if paths must not leave this machine, run AutoRAG with a local LLM."
11363
+ ]
11364
+ };
11365
+ SpotlightSkill = class SpotlightSkill extends ConnectorDatasourceSkill {
11366
+ constructor(options = {}) {
11367
+ const { connector, connectorOptions, ...rest } = options;
11368
+ super(SPOTLIGHT_SKILL_DEFINITION, {
11369
+ ...rest,
11370
+ connector: connector ?? new SpotlightConnector(connectorOptions ?? {})
11371
+ });
11372
+ }
11373
+ };
11374
+ });
11375
+
11376
+ // src/datasource/skills/spotlight/index.ts
11377
+ var init_spotlight = __esm(() => {
11378
+ init_connector11();
11379
+ init_skill10();
11380
+ });
11381
+
11382
+ // src/datasource/skills/factory.ts
11383
+ function common(config, workspaceRoot) {
11384
+ return {
11385
+ ...config.instanceId !== undefined ? { instanceId: config.instanceId } : {},
11386
+ ...config.pollingIntervalMs !== undefined ? { pollingIntervalMs: config.pollingIntervalMs } : {},
11387
+ ...config.tags !== undefined ? { tags: config.tags } : {},
11388
+ ...workspaceRoot !== undefined ? { workspaceRoot } : {}
11389
+ };
8522
11390
  }
8523
- function isDatasourceSource(source) {
8524
- if (typeof source !== "string" || source.length === 0)
11391
+ function buildDatasourceSkills(config, workspaceRoot) {
11392
+ const skills = [];
11393
+ const unknown = [];
11394
+ for (const [name, raw] of Object.entries(config ?? {})) {
11395
+ if (raw === false)
11396
+ continue;
11397
+ const entry = raw === true ? {} : raw;
11398
+ if (entry.enabled === false)
11399
+ continue;
11400
+ const builder = BUILDERS[name];
11401
+ if (builder === undefined) {
11402
+ unknown.push(name);
11403
+ continue;
11404
+ }
11405
+ skills.push(builder(entry, workspaceRoot));
11406
+ }
11407
+ return { skills, unknown };
11408
+ }
11409
+ var BUILDERS, BUILTIN_DATASOURCE_SKILL_NAMES;
11410
+ var init_factory = __esm(() => {
11411
+ init_discord();
11412
+ init_gdrive();
11413
+ init_rclone_connector();
11414
+ init_github();
11415
+ init_himalaya_connector();
11416
+ init_gmail();
11417
+ init_mail_export();
11418
+ init_notion();
11419
+ init_obsidian();
11420
+ init_rss();
11421
+ init_slack();
11422
+ init_spotlight();
11423
+ BUILDERS = {
11424
+ slack: (config, workspaceRoot) => new SlackSkill({ ...common(config, workspaceRoot), connectorOptions: config.connector }),
11425
+ discord: (config, workspaceRoot) => new DiscordSkill({
11426
+ ...common(config, workspaceRoot),
11427
+ connectorOptions: config.connector
11428
+ }),
11429
+ notion: (config, workspaceRoot) => new NotionSkill({
11430
+ ...common(config, workspaceRoot),
11431
+ connectorOptions: config.connector
11432
+ }),
11433
+ github: (config, workspaceRoot) => new GitHubSkill({
11434
+ ...common(config, workspaceRoot),
11435
+ connectorOptions: config.connector
11436
+ }),
11437
+ gdrive: (config, workspaceRoot) => {
11438
+ const connector = config.connector;
11439
+ if (connector?.backend === "rclone") {
11440
+ const { backend: _backend, ...rcloneOptions } = connector;
11441
+ return new GDriveSkill({
11442
+ ...common(config, workspaceRoot),
11443
+ connector: new RcloneConnector(rcloneOptions)
11444
+ });
11445
+ }
11446
+ return new GDriveSkill({
11447
+ ...common(config, workspaceRoot),
11448
+ connectorOptions: connector
11449
+ });
11450
+ },
11451
+ gmail: (config, workspaceRoot) => {
11452
+ const connector = config.connector;
11453
+ if (connector?.backend === "himalaya") {
11454
+ const { backend: _backend, ...himalayaOptions } = connector;
11455
+ return new GmailSkill({
11456
+ ...common(config, workspaceRoot),
11457
+ connector: new HimalayaConnector(himalayaOptions)
11458
+ });
11459
+ }
11460
+ return new GmailSkill({ ...common(config, workspaceRoot), connectorOptions: connector });
11461
+ },
11462
+ "mail-export": (config, workspaceRoot) => new MailExportSkill({
11463
+ ...common(config, workspaceRoot),
11464
+ connectorOptions: config.connector
11465
+ }),
11466
+ obsidian: (config, workspaceRoot) => new ObsidianSkill({
11467
+ ...common(config, workspaceRoot),
11468
+ connectorOptions: config.connector
11469
+ }),
11470
+ rss: (config, workspaceRoot) => new RssSkill({ ...common(config, workspaceRoot), connectorOptions: config.connector }),
11471
+ spotlight: (config, workspaceRoot) => new SpotlightSkill({
11472
+ ...common(config, workspaceRoot),
11473
+ connectorOptions: config.connector
11474
+ })
11475
+ };
11476
+ BUILTIN_DATASOURCE_SKILL_NAMES = Object.keys(BUILDERS);
11477
+ });
11478
+
11479
+ // src/index.ts
11480
+ init_agent();
11481
+
11482
+ // src/agent/index.ts
11483
+ init_agent();
11484
+ init_datasource_skill();
11485
+ init_emit_results_tool();
11486
+ init_jikji_find_tool();
11487
+ init_search_all_tool();
11488
+ init_search_datasource_tool();
11489
+ init_search_minsync_tool();
11490
+ init_system_prompt();
11491
+
11492
+ // src/index.ts
11493
+ init_system_prompt();
11494
+
11495
+ // src/datasource/index.ts
11496
+ init_access_context();
11497
+ init_chunk_store();
11498
+ init_connector();
11499
+ init_connector_skill();
11500
+
11501
+ // src/datasource/polling.ts
11502
+ function isDue(metadata, now, lastIndexedAt) {
11503
+ if (metadata.mode !== "poll")
8525
11504
  return false;
8526
- if (datasourceSourceHasFragment(source))
11505
+ const intervalMs = metadata.intervalMs;
11506
+ if (intervalMs === undefined || intervalMs <= 0)
8527
11507
  return false;
8528
- const normalized = normalizeVirtualPath(source);
8529
- return normalized !== "/";
8530
- }
8531
- function matchesDatasourceScope(source, scope) {
8532
- if (datasourceSourceHasFragment(source))
11508
+ const last = lastIndexedAt ?? metadata.lastIndexedAt;
11509
+ if (last === undefined)
11510
+ return true;
11511
+ if (!Number.isFinite(now) || !Number.isFinite(last))
8533
11512
  return false;
8534
- return matchesVirtualPathScope(source, scope);
11513
+ return now - last >= intervalMs;
11514
+ }
11515
+ function parseCronExpr(expr) {
11516
+ if (typeof expr !== "string") {
11517
+ return fail("cron expression must be a string");
11518
+ }
11519
+ const trimmed = expr.trim();
11520
+ if (trimmed.length === 0) {
11521
+ return fail("cron expression is empty");
11522
+ }
11523
+ const fields = trimmed.split(/\s+/u);
11524
+ if (fields.length !== 5) {
11525
+ return fail(`expected 5 fields, got ${fields.length}`);
11526
+ }
11527
+ const [minute, hour, dom, month, dow] = fields;
11528
+ const errors = [];
11529
+ validateField(minute, 0, 59, "minute", errors);
11530
+ validateField(hour, 0, 23, "hour", errors);
11531
+ validateField(dom, 1, 31, "day-of-month", errors);
11532
+ validateField(month, 1, 12, "month", errors);
11533
+ validateField(dow, 0, 7, "day-of-week", errors);
11534
+ if (errors.length > 0) {
11535
+ return fail(errors.join("; "));
11536
+ }
11537
+ return { ok: true, expr: trimmed };
11538
+ }
11539
+ function fail(error) {
11540
+ return { ok: false, error };
11541
+ }
11542
+ function validateField(field, min, max, label, errors) {
11543
+ const parts = field.split(",");
11544
+ if (parts.length === 0) {
11545
+ errors.push(`${label}: empty`);
11546
+ return;
11547
+ }
11548
+ for (const part of parts) {
11549
+ const err = validatePart(part, min, max, label);
11550
+ if (err !== null)
11551
+ errors.push(err);
11552
+ }
11553
+ }
11554
+ function validatePart(part, min, max, label) {
11555
+ if (part.length === 0)
11556
+ return `${label}: empty term`;
11557
+ const slashIndex = part.indexOf("/");
11558
+ let base = part;
11559
+ let stepText;
11560
+ if (slashIndex >= 0) {
11561
+ base = part.slice(0, slashIndex);
11562
+ stepText = part.slice(slashIndex + 1);
11563
+ const step = parseNumber(stepText);
11564
+ if (step === null || step <= 0) {
11565
+ return `${label}: invalid step "${stepText}"`;
11566
+ }
11567
+ }
11568
+ if (base === "*")
11569
+ return null;
11570
+ if (base.includes("-")) {
11571
+ const [loText, hiText] = base.split("-");
11572
+ if (hiText === undefined || loText === undefined) {
11573
+ return `${label}: malformed range "${base}"`;
11574
+ }
11575
+ const lo = parseNumber(loText);
11576
+ const hi = parseNumber(hiText);
11577
+ if (lo === null || hi === null) {
11578
+ return `${label}: non-numeric range "${base}"`;
11579
+ }
11580
+ if (lo < min || lo > max || hi < min || hi > max) {
11581
+ return `${label}: range "${base}" out of bounds (${min}-${max})`;
11582
+ }
11583
+ if (lo > hi) {
11584
+ return `${label}: range "${base}" has low > high`;
11585
+ }
11586
+ return null;
11587
+ }
11588
+ const v = parseNumber(base);
11589
+ if (v === null) {
11590
+ return `${label}: invalid value "${base}"`;
11591
+ }
11592
+ if (v < min || v > max) {
11593
+ return `${label}: value ${v} out of bounds (${min}-${max})`;
11594
+ }
11595
+ return null;
11596
+ }
11597
+ function parseNumber(text) {
11598
+ if (!/^\d+$/u.test(text))
11599
+ return null;
11600
+ return Number.parseInt(text, 10);
8535
11601
  }
8536
-
8537
11602
  // src/datasource/registry.ts
11603
+ init_scope();
11604
+
8538
11605
  class DatasourceSkillRegistry {
8539
11606
  skills = new Map;
8540
11607
  register(skill) {
@@ -8597,16 +11664,25 @@ function safePolling(skill) {
8597
11664
  return;
8598
11665
  }
8599
11666
  }
11667
+
11668
+ // src/datasource/index.ts
11669
+ init_scope();
11670
+ init_discord();
11671
+ init_factory();
11672
+ init_gdrive();
11673
+ init_github();
11674
+ init_gmail();
11675
+
8600
11676
  // src/datasource/skills/katok/client.ts
8601
- import { spawn as spawn5 } from "node:child_process";
11677
+ import { spawn as spawn8 } from "node:child_process";
8602
11678
 
8603
11679
  // src/datasource/skills/katok/paths.ts
8604
- import { join as join17 } from "node:path";
11680
+ import { join as join20 } from "node:path";
8605
11681
  var AUTORAG_DIRNAME = ".autorag";
8606
11682
  var DATASOURCES_DIRNAME = "datasources";
8607
11683
  var KATOK_DATASOURCE_DIRNAME = "katok";
8608
11684
  function katokDatasourceRoot(workspaceRoot) {
8609
- return join17(workspaceRoot, AUTORAG_DIRNAME, DATASOURCES_DIRNAME, KATOK_DATASOURCE_DIRNAME);
11685
+ return join20(workspaceRoot, AUTORAG_DIRNAME, DATASOURCES_DIRNAME, KATOK_DATASOURCE_DIRNAME);
8610
11686
  }
8611
11687
 
8612
11688
  // src/datasource/skills/katok/types.ts
@@ -8720,7 +11796,7 @@ class KatokClient {
8720
11796
  function spawnKatok(request) {
8721
11797
  return new Promise((resolve9) => {
8722
11798
  const { options, args, env, signal } = request;
8723
- const child = spawn5(commandFor2(options.binaryPath), args, {
11799
+ const child = spawn8(commandFor2(options.binaryPath), args, {
8724
11800
  env,
8725
11801
  stdio: ["ignore", "pipe", "pipe"]
8726
11802
  });
@@ -8871,22 +11947,22 @@ function parseJsonObject(stdout) {
8871
11947
  return;
8872
11948
  return parsed;
8873
11949
  }
8874
- function asRecord2(value) {
11950
+ function asRecord3(value) {
8875
11951
  if (value === null || typeof value !== "object" || Array.isArray(value))
8876
11952
  return;
8877
11953
  return value;
8878
11954
  }
8879
- function asString(value) {
11955
+ function asString2(value) {
8880
11956
  return typeof value === "string" ? value : undefined;
8881
11957
  }
8882
- function asNumber(value) {
11958
+ function asNumber2(value) {
8883
11959
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
8884
11960
  }
8885
11961
  function asBoolean(value) {
8886
11962
  return typeof value === "boolean" ? value : undefined;
8887
11963
  }
8888
11964
  function normalizeDoctor(raw) {
8889
- const version = asString(raw.version);
11965
+ const version = asString2(raw.version);
8890
11966
  const ready = asBoolean(raw.ready);
8891
11967
  if (ready === undefined)
8892
11968
  return;
@@ -8897,12 +11973,12 @@ function normalizeSync(raw) {
8897
11973
  const synced = asBoolean(raw.synced);
8898
11974
  if (synced === undefined)
8899
11975
  return;
8900
- const messageCount = asNumber(raw.messageCount);
11976
+ const messageCount = asNumber2(raw.messageCount);
8901
11977
  const metadata = stripKnown(raw, new Set(["synced", "messageCount"]));
8902
11978
  return { synced, ...messageCount !== undefined ? { messageCount } : {}, metadata };
8903
11979
  }
8904
11980
  function normalizeIndex(raw) {
8905
- const chunkCount = asNumber(raw.chunkCount);
11981
+ const chunkCount = asNumber2(raw.chunkCount);
8906
11982
  if (chunkCount === undefined)
8907
11983
  return;
8908
11984
  const metadata = stripKnown(raw, new Set(["chunkCount"]));
@@ -8914,12 +11990,12 @@ function normalizeHits(raw) {
8914
11990
  return;
8915
11991
  const normalized = [];
8916
11992
  for (const entry of hits) {
8917
- const record = asRecord2(entry);
11993
+ const record = asRecord3(entry);
8918
11994
  if (record === undefined)
8919
11995
  return;
8920
- const chunkId = asString(record.chunkId);
8921
- const score = asNumber(record.score);
8922
- const content = asString(record.content);
11996
+ const chunkId = asString2(record.chunkId);
11997
+ const score = asNumber2(record.score);
11998
+ const content = asString2(record.content);
8923
11999
  if (chunkId === undefined || chunkId.length === 0 || score === undefined || content === undefined)
8924
12000
  return;
8925
12001
  const metadata = stripKnown(record, new Set(["chunkId", "score", "content"]));
@@ -8928,8 +12004,8 @@ function normalizeHits(raw) {
8928
12004
  return normalized;
8929
12005
  }
8930
12006
  function normalizeChunk(raw) {
8931
- const chunkId = asString(raw.chunkId);
8932
- const content = asString(raw.content);
12007
+ const chunkId = asString2(raw.chunkId);
12008
+ const content = asString2(raw.content);
8933
12009
  if (chunkId === undefined || chunkId.length === 0 || content === undefined)
8934
12010
  return;
8935
12011
  const metadata = stripKnown(raw, new Set(["chunkId", "content"]));
@@ -8941,7 +12017,7 @@ function normalizeContext(raw) {
8941
12017
  return;
8942
12018
  const chunks = [];
8943
12019
  for (const entry of chunksValue) {
8944
- const record = asRecord2(entry);
12020
+ const record = asRecord3(entry);
8945
12021
  if (record === undefined)
8946
12022
  return;
8947
12023
  const chunk = normalizeChunk(record);
@@ -8979,9 +12055,10 @@ function ok(data, result) {
8979
12055
  return { ok: true, data, stdout: result.stdout, stderr: result.stderr, code: result.code ?? 0 };
8980
12056
  }
8981
12057
  // src/datasource/skills/katok/methods.ts
12058
+ init_scope();
8982
12059
  var KAKAO_DATASOURCE_ID = "kakao";
8983
12060
  var DEFAULT_KAKAO_TAGS = ["kakaotalk", "personal", "pii"];
8984
- var DEFAULT_TOP_K2 = 20;
12061
+ var DEFAULT_TOP_K3 = 20;
8985
12062
 
8986
12063
  class KatokBm25Method {
8987
12064
  client;
@@ -9036,7 +12113,7 @@ async function retrieveKatok(client, mode, methodName, instanceId, query, option
9036
12113
  const trimmed = query.trim();
9037
12114
  if (trimmed.length === 0)
9038
12115
  return [];
9039
- const topK = options.topK ?? DEFAULT_TOP_K2;
12116
+ const topK = options.topK ?? DEFAULT_TOP_K3;
9040
12117
  const searchOptions = { topK, signal: options.signal };
9041
12118
  let result;
9042
12119
  try {
@@ -9086,10 +12163,11 @@ function toRetrievalResult(hit, source, methodName, instanceId, mode) {
9086
12163
  };
9087
12164
  }
9088
12165
  // src/datasource/skills/katok/skill.ts
12166
+ init_scope();
9089
12167
  var KAKAO_DATASOURCE_ID2 = "kakao";
9090
12168
  var KAKAO_SKILL_TYPE = "kakaotalk";
9091
- var DEFAULT_INSTANCE_ID = "default";
9092
- var DEFAULT_POLLING_INTERVAL_MS = 15 * 60 * 1000;
12169
+ var DEFAULT_INSTANCE_ID2 = "default";
12170
+ var DEFAULT_POLLING_INTERVAL_MS2 = 15 * 60 * 1000;
9093
12171
  var DEFAULT_KAKAO_TAGS2 = ["kakaotalk", "personal", "pii"];
9094
12172
 
9095
12173
  class KatokSkill {
@@ -9101,9 +12179,9 @@ class KatokSkill {
9101
12179
  lastIndexedAt;
9102
12180
  constructor(options) {
9103
12181
  this.client = options.client;
9104
- this.instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID;
12182
+ this.instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID2;
9105
12183
  this.instances = options.instances !== undefined && options.instances.length > 0 ? options.instances : [this.instanceId];
9106
- this.pollingIntervalMs = options.pollingIntervalMs ?? DEFAULT_POLLING_INTERVAL_MS;
12184
+ this.pollingIntervalMs = options.pollingIntervalMs ?? DEFAULT_POLLING_INTERVAL_MS2;
9107
12185
  this.tags = options.tags ?? DEFAULT_KAKAO_TAGS2;
9108
12186
  this.lastIndexedAt = options.lastIndexedAt;
9109
12187
  }
@@ -9240,6 +12318,14 @@ function sanitizeDiagnosticText2(value) {
9240
12318
  }
9241
12319
  return value;
9242
12320
  }
12321
+ // src/datasource/index.ts
12322
+ init_mail_export();
12323
+ init_notion();
12324
+ init_obsidian();
12325
+ init_rss();
12326
+ init_slack();
12327
+ init_spotlight();
12328
+
9243
12329
  // src/index.ts
9244
12330
  init_jikji();
9245
12331
 
@@ -9274,6 +12360,7 @@ export {
9274
12360
  syncMinSyncWorkspace,
9275
12361
  selectReleaseAsset,
9276
12362
  saveMirrorIndex,
12363
+ sanitizeIdSegment,
9277
12364
  rewriteEmbedderConfig,
9278
12365
  resolveReturnedPath,
9279
12366
  renderMemoryContext,
@@ -9311,15 +12398,25 @@ export {
9311
12398
  detectMirrorStaleness,
9312
12399
  datasourceSourcePath,
9313
12400
  datasourceSourceHasFragment,
12401
+ datasourceChunkStorePath,
9314
12402
  createEmitResultsTool,
9315
12403
  createDefaultParserRegistry,
9316
12404
  createCheckMemoryTool,
12405
+ connectorFailureToDiagnosticCode,
9317
12406
  cachedJikjiBinaryPath,
9318
12407
  buildSystemPrompt,
9319
12408
  buildMinSyncPathMap,
12409
+ buildDatasourceSkills,
9320
12410
  buildDatasourceInstanceSource,
9321
12411
  buildDatasourceChunkSource,
12412
+ boundDiagnosticText,
9322
12413
  XlsxParser,
12414
+ SpotlightSkill,
12415
+ SpotlightConnector,
12416
+ SlackSkill,
12417
+ SlackConnector,
12418
+ RssSkill,
12419
+ RssConnector,
9323
12420
  RetrievalMethodRegistry,
9324
12421
  RetrievalMemory,
9325
12422
  ResultMerger,
@@ -9330,9 +12427,15 @@ export {
9330
12427
  ParseError,
9331
12428
  ParallelRetriever,
9332
12429
  OpendataloaderPdfParser,
12430
+ ObsidianSkill,
12431
+ ObsidianConnector,
12432
+ NotionSkill,
12433
+ NotionConnector,
9333
12434
  MinSyncVectorMethod,
9334
12435
  MinSyncReleaseError,
9335
12436
  MinSyncClient,
12437
+ MailExportSkill,
12438
+ MailExportConnector,
9336
12439
  MINSYNC_SUBDIR,
9337
12440
  MINSYNC_FILES_SUBDIR,
9338
12441
  MINSYNC_CONFIG_FILE,
@@ -9346,15 +12449,27 @@ export {
9346
12449
  JIKJI_CRATE_NAME,
9347
12450
  ImageOcrParser,
9348
12451
  HwpParser,
12452
+ GmailSkill,
12453
+ GmailConnector,
12454
+ GitHubSkill,
12455
+ GitHubConnector,
12456
+ GDriveSkill,
12457
+ GDriveConnector,
9349
12458
  EmlParser,
9350
12459
  EMIT_AUTORAG_RESULTS_TOOL_NAME,
9351
12460
  DocxParser,
12461
+ DiscordSkill,
12462
+ DiscordConnector,
9352
12463
  DatasourceSkillRegistry,
9353
12464
  DatasourceResultFilter,
12465
+ DatasourceChunkStore,
9354
12466
  DatasourceAccessContext,
9355
12467
  DEFAULT_MAX_SOURCE_BYTES,
9356
12468
  DEFAULT_JIKJI_OPTIONS,
9357
12469
  DATASOURCE_CHUNKS_SEGMENT,
12470
+ ConnectorLexicalMethod,
12471
+ ConnectorDatasourceSkill,
12472
+ BUILTIN_DATASOURCE_SKILL_NAMES,
9358
12473
  BM25UnavailableError,
9359
12474
  BM25Method,
9360
12475
  AutoRAGAgent