@autorag/librarian 2.5.2 → 2.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +7 -6
  2. package/dist/agent/agent.d.ts +9 -14
  3. package/dist/agent/index.d.ts +1 -2
  4. package/dist/agent/search-single-datasource-tool.d.ts +14 -8
  5. package/dist/cli/index.js +2130 -1057
  6. package/dist/core.js +1711 -665
  7. package/dist/datasource/errors.d.ts +1 -1
  8. package/dist/datasource/index.d.ts +3 -2
  9. package/dist/datasource/skills/discrawl/skill.d.ts +1 -1
  10. package/dist/datasource/skills/discrawl/types.d.ts +2 -2
  11. package/dist/datasource/skills/github-gist/connector.d.ts +38 -0
  12. package/dist/datasource/skills/github-gist/index.d.ts +3 -0
  13. package/dist/datasource/skills/github-gist/semantic.d.ts +102 -0
  14. package/dist/datasource/skills/github-gist/skill.d.ts +39 -0
  15. package/dist/datasource/skills/lark/client.d.ts +23 -0
  16. package/dist/datasource/skills/lark/index.d.ts +2 -0
  17. package/dist/datasource/skills/lark/skill.d.ts +36 -0
  18. package/dist/datasource/skills/lazykatok/client.d.ts +28 -0
  19. package/dist/datasource/skills/lazykatok/index.d.ts +12 -0
  20. package/dist/datasource/skills/{katok → lazykatok}/methods.d.ts +12 -12
  21. package/dist/datasource/skills/lazykatok/paths.d.ts +40 -0
  22. package/dist/datasource/skills/{katok → lazykatok}/skill.d.ts +10 -10
  23. package/dist/datasource/skills/lazykatok/types.d.ts +134 -0
  24. package/dist/datasource/skills/mailcrawl/client.d.ts +2 -0
  25. package/dist/datasource/skills/mailcrawl/types.d.ts +12 -0
  26. package/dist/datasource/types.d.ts +4 -4
  27. package/dist/embedding-runtime/manifest.d.ts +8 -0
  28. package/dist/embedding-runtime/types.d.ts +4 -0
  29. package/dist/index.js +1719 -671
  30. package/dist/process/terminate-tree.d.ts +2 -0
  31. package/dist/retrieval/types.d.ts +1 -1
  32. package/licenses/embedding-assets.json +20 -4
  33. package/package.json +1 -1
  34. package/skills/autorag-doctor/SKILL.md +4 -3
  35. package/skills/autorag-lite-setup/SKILL.md +2 -2
  36. package/skills/autorag-setup/SKILL.md +9 -6
  37. package/dist/agent/search-datasource-tool.d.ts +0 -30
  38. package/dist/datasource/skills/katok/client.d.ts +0 -21
  39. package/dist/datasource/skills/katok/index.d.ts +0 -12
  40. package/dist/datasource/skills/katok/paths.d.ts +0 -40
  41. package/dist/datasource/skills/katok/types.d.ts +0 -123
package/dist/index.js CHANGED
@@ -3826,14 +3826,22 @@ var init_manifest = __esm(() => {
3826
3826
  revision: "370f27d7550e0def9b39c1f16d3fbaa13aa67728",
3827
3827
  sha256: "06507c7b42688469c4e7298b0a1e16deff06caf291cf0a5b278c308249c3e439",
3828
3828
  license: "Apache-2.0",
3829
- noticeReference: "Qwen3 model card and Apache-2.0 license"
3829
+ noticeReference: "Qwen3 model card and Apache-2.0 license",
3830
+ intendedUse: "Local loopback embedding for AutoRAG MinSync and native-datasource semantic retrieval. Not a generative model.",
3831
+ unsuitableUse: "Chat or generation, remote embedding of corpus text, shipping weights in the npm package, any GGUF other than the pinned SHA-256.",
3832
+ upstreamCardUrl: "https://huggingface.co/Qwen/Qwen3-Embedding-0.6B",
3833
+ modelCard: "docs/model-cards/qwen3-embedding-0.6b.md"
3830
3834
  },
3831
3835
  embeddinggemma: {
3832
3836
  url: "https://huggingface.co/ggml-org/embeddinggemma-300M-GGUF/resolve/0f741b5a6585bd53aeb15cd1372c56f2a0f65e12/embeddinggemma-300M-Q8_0.gguf",
3833
3837
  revision: "0f741b5a6585bd53aeb15cd1372c56f2a0f65e12",
3834
3838
  sha256: "b5ce9d77a3fc4b3b39ccb5643c36777911cc4eb46a66962eadfa3f5f60490d63",
3835
3839
  license: "Gemma Terms of Use",
3836
- noticeReference: "Google EmbeddingGemma model card and Gemma Terms of Use"
3840
+ noticeReference: "Google EmbeddingGemma model card and Gemma Terms of Use",
3841
+ intendedUse: "Local loopback embedding for AutoRAG MinSync and native-datasource semantic retrieval. Not a generative model.",
3842
+ unsuitableUse: "Chat or generation, remote embedding of corpus text, shipping weights in the npm package, any GGUF other than the pinned SHA-256, uses restricted by the Gemma Prohibited Use Policy.",
3843
+ upstreamCardUrl: "https://ai.google.dev/gemma/docs/embeddinggemma/model_card",
3844
+ modelCard: "docs/model-cards/embeddinggemma-300m.md"
3837
3845
  }
3838
3846
  };
3839
3847
  });
@@ -9243,68 +9251,8 @@ var init_search_all_tool = __esm(() => {
9243
9251
  });
9244
9252
  });
9245
9253
 
9246
- // src/agent/search-datasource-tool.ts
9247
- import { Type as Type12 } from "typebox";
9248
- function createSearchDatasourceDocumentsTool(provider) {
9249
- return {
9250
- name: SEARCH_DATASOURCE_DOCUMENTS_TOOL_NAME,
9251
- label: "Search Datasource Documents",
9252
- description: "Search configured external datasource skills such as cloud drives and KakaoTalk chats, fanning out to every datasource CLI. When the question targets one connection, prefer its dedicated search_datasource_<name> tool, which spawns only that connection's CLIs. Load the matching datasource skill first; authority is server-configured and tool arguments can only provide query, topK, and an optional narrowing scope.",
9253
- parameters: searchDatasourceSchema,
9254
- async execute(_toolCallId, params) {
9255
- const query = params.query.trim();
9256
- if (query.length === 0) {
9257
- return {
9258
- content: [{ type: "text", text: "Datasource query was empty; no datasource documents searched." }],
9259
- details: { method: "datasource", resultCount: 0, sources: [], diagnostics: [] }
9260
- };
9261
- }
9262
- const { results, diagnostics } = await provider.searchDatasourceDocuments(query, {
9263
- topK: params.topK,
9264
- scope: params.scope
9265
- });
9266
- return {
9267
- content: [{ type: "text", text: formatDatasourceResults(results, diagnostics) }],
9268
- details: {
9269
- method: "datasource",
9270
- resultCount: results.length,
9271
- sources: [...new Set(results.map((result) => result.source))],
9272
- diagnostics,
9273
- results: toRetrievalTraceResults(results)
9274
- }
9275
- };
9276
- }
9277
- };
9278
- }
9279
- function formatDatasourceResults(results, diagnostics) {
9280
- const diagnosticSummary = diagnostics.length > 0 ? `
9281
-
9282
- Diagnostics: ${diagnostics.map((d) => `${d.source ?? "datasource"}:${d.code}`).join(", ")}` : "";
9283
- if (results.length === 0)
9284
- return `No datasource results.${diagnosticSummary}`;
9285
- const rows = results.map((result, index) => {
9286
- const line = result.content.replace(/\s+/gu, " ").slice(0, 500);
9287
- return `[${index + 1}] ${result.source} score=${result.score.toFixed(4)}
9288
- ${line}`;
9289
- });
9290
- return `Datasource results:
9291
-
9292
- ${rows.join(`
9293
-
9294
- `)}${diagnosticSummary}`;
9295
- }
9296
- var SEARCH_DATASOURCE_DOCUMENTS_TOOL_NAME = "search_datasource_documents", searchDatasourceSchema;
9297
- var init_search_datasource_tool = __esm(() => {
9298
- init_search_documents();
9299
- searchDatasourceSchema = Type12.Object({
9300
- query: Type12.String({ description: "Query to search across configured external datasource skills." }),
9301
- topK: Type12.Optional(Type12.Integer({ description: "Maximum number of datasource chunks to return. Defaults to 50." })),
9302
- scope: Type12.Optional(Type12.String({ description: "Optional opaque datasource scope, e.g. /kakao/account or /kakao/account/**." }))
9303
- });
9304
- });
9305
-
9306
9254
  // src/agent/search-minsync-tool.ts
9307
- import { Type as Type13 } from "typebox";
9255
+ import { Type as Type12 } from "typebox";
9308
9256
  function createSearchMinSyncDocumentsTool(getMethod, resolveScope = (scope) => scope) {
9309
9257
  return {
9310
9258
  name: SEARCH_MINSYNC_DOCUMENTS_TOOL_NAME,
@@ -9375,12 +9323,12 @@ ${rows.join(`
9375
9323
  var SEARCH_MINSYNC_DOCUMENTS_TOOL_NAME = "semantic_search_local_docs", searchMinSyncSchema;
9376
9324
  var init_search_minsync_tool = __esm(() => {
9377
9325
  init_search_documents();
9378
- searchMinSyncSchema = Type13.Object({
9379
- query: Type13.String({
9326
+ searchMinSyncSchema = Type12.Object({
9327
+ query: Type12.String({
9380
9328
  description: "Semantic query to search parsed document mirrors with MinSync vector retrieval."
9381
9329
  }),
9382
- topK: Type13.Optional(Type13.Integer({ description: "Maximum number of MinSync semantic chunks to return. Defaults to 50." })),
9383
- scope: Type13.Optional(Type13.String({ description: "Optional opaque virtual-path scope, e.g. /docs or /docs/**." }))
9330
+ topK: Type12.Optional(Type12.Integer({ description: "Maximum number of MinSync semantic chunks to return. Defaults to 50." })),
9331
+ scope: Type12.Optional(Type12.String({ description: "Optional opaque virtual-path scope, e.g. /docs or /docs/**." }))
9384
9332
  });
9385
9333
  });
9386
9334
 
@@ -9398,7 +9346,7 @@ function datasourceSearchToolName(datasourceId) {
9398
9346
  }
9399
9347
 
9400
9348
  // src/agent/search-single-datasource-tool.ts
9401
- import { Type as Type14 } from "typebox";
9349
+ import { Type as Type13 } from "typebox";
9402
9350
  function createSingleDatasourceSearchTools(provider, specs) {
9403
9351
  return specs.map((spec) => createTool(provider, spec));
9404
9352
  }
@@ -9407,7 +9355,7 @@ function createTool(provider, spec) {
9407
9355
  return {
9408
9356
  name: singleDatasourceToolName(spec.datasourceId),
9409
9357
  label: `Search ${spec.datasourceId}`,
9410
- description: `Search only the "${spec.datasourceId}" datasource connection: ${spec.description} ` + `Unlike search_datasource_documents this spawns no other datasource CLIs; prefer it whenever the question targets this connection.${scopeLine} ` + `Authority is server-configured; tool arguments can only provide query, topK, and an optional narrowing scope.`,
9358
+ description: `Search only the "${spec.datasourceId}" datasource connection: ${spec.description} ` + `This tool spawns only this connection's CLIs and returns only this connection's hits.${scopeLine} ` + `Authority is server-configured; tool arguments can only provide query, topK, and an optional narrowing scope.`,
9411
9359
  parameters: searchSingleDatasourceSchema,
9412
9360
  async execute(_toolCallId, params) {
9413
9361
  const query = params.query.trim();
@@ -9441,15 +9389,31 @@ function createTool(provider, spec) {
9441
9389
  }
9442
9390
  };
9443
9391
  }
9392
+ function formatDatasourceResults(results, diagnostics) {
9393
+ const diagnosticSummary = diagnostics.length > 0 ? `
9394
+
9395
+ Diagnostics: ${diagnostics.map((d) => `${d.source ?? "datasource"}:${d.code}`).join(", ")}` : "";
9396
+ if (results.length === 0)
9397
+ return `No datasource results.${diagnosticSummary}`;
9398
+ const rows = results.map((result, index) => {
9399
+ const line = result.content.replace(/\s+/gu, " ").slice(0, 500);
9400
+ return `[${index + 1}] ${result.source} score=${result.score.toFixed(4)}
9401
+ ${line}`;
9402
+ });
9403
+ return `Datasource results:
9404
+
9405
+ ${rows.join(`
9406
+
9407
+ `)}${diagnosticSummary}`;
9408
+ }
9444
9409
  var singleDatasourceToolName, searchSingleDatasourceSchema;
9445
9410
  var init_search_single_datasource_tool = __esm(() => {
9446
- init_search_datasource_tool();
9447
9411
  init_search_documents();
9448
9412
  singleDatasourceToolName = datasourceSearchToolName;
9449
- searchSingleDatasourceSchema = Type14.Object({
9450
- query: Type14.String({ description: "Query to search within this datasource connection." }),
9451
- topK: Type14.Optional(Type14.Integer({ description: "Maximum number of chunks to return. Defaults to 50." })),
9452
- scope: Type14.Optional(Type14.String({ description: "Optional scope narrowing inside this datasource, e.g. one instance root." }))
9413
+ searchSingleDatasourceSchema = Type13.Object({
9414
+ query: Type13.String({ description: "Query to search within this datasource connection." }),
9415
+ topK: Type13.Optional(Type13.Integer({ description: "Maximum number of chunks to return. Defaults to 50." })),
9416
+ scope: Type13.Optional(Type13.String({ description: "Optional scope narrowing inside this datasource, e.g. one instance root." }))
9453
9417
  });
9454
9418
  });
9455
9419
 
@@ -9467,7 +9431,6 @@ function buildSystemPrompt(config) {
9467
9431
  toolLine(config, "jikji_find", "local discovery through Jikji answer packs"),
9468
9432
  toolLine(config, "search_all_documents", "fan out across every configured retrieval method and merge results"),
9469
9433
  toolLine(config, "semantic_search_local_docs", "semantic MinSync search over parsed document mirrors"),
9470
- toolLine(config, "search_datasource_documents", "search authorized external datasources; scope may only narrow access"),
9471
9434
  toolLine(config, "load_datasource_skill", "load instructions for an authorized datasource"),
9472
9435
  toolLine(config, "scan_duplicate_documents", "read-only dupey scan of configured local document roots"),
9473
9436
  toolLine(config, "web_search", "search the public internet for current information beyond the local corpus and knowledge cutoff"),
@@ -9475,13 +9438,12 @@ function buildSystemPrompt(config) {
9475
9438
  toolLine(config, "check_memory", "inspect advisory retrieval hints from prior feedback"),
9476
9439
  toolLine(config, "recommend_peer_targets", "recommend local peer personas to ask about a topic without contacting them"),
9477
9440
  toolLine(config, "emit_autorag_results", "return the final structured answer and number-to-source mapping"),
9478
- ...config.toolNames.filter((name) => name.startsWith("search_datasource_") && name !== "search_datasource_documents").map((name) => `- **${name}**: search only the ${name.slice("search_datasource_".length).replace(/_/g, "-")} datasource connection, spawning no other datasource CLIs`),
9441
+ ...config.toolNames.filter((name) => name.startsWith("search_datasource_")).map((name) => `- **${name}**: search only the ${name.slice("search_datasource_".length).replace(/_/g, "-")} datasource connection, spawning no other datasource CLIs`),
9479
9442
  ...config.toolNames.filter((name) => ![
9480
9443
  "bash",
9481
9444
  "jikji_find",
9482
9445
  "search_all_documents",
9483
9446
  "semantic_search_local_docs",
9484
- "search_datasource_documents",
9485
9447
  "load_datasource_skill",
9486
9448
  "scan_duplicate_documents",
9487
9449
  "web_search",
@@ -9514,7 +9476,7 @@ ${FENCING_GUARD_LINE}
9514
9476
  ` : "";
9515
9477
  const webResearch = toolAvailable(config, "web_search") || toolAvailable(config, "web_fetch") ? `## Web Research
9516
9478
 
9517
- \`web_search\` searches the public internet for current information beyond the local corpus and the model's knowledge cutoff; prefer primary sources (official docs, papers) and corroborate key claims with multiple sources. \`web_fetch\` reads a specific http(s) URL as markdown/text — pages found via web_search, official docs, papers. web_fetch only accepts http(s) URLs: never local file paths (use bash) or datasource virtual ids such as /kakao/... (use search_datasource_documents). Keep result URLs for traceability. Web queries leave the machine: never include private corpus content or secrets in web_search queries or fetched URLs.
9479
+ \`web_search\` searches the public internet for current information beyond the local corpus and the model's knowledge cutoff; prefer primary sources (official docs, papers) and corroborate key claims with multiple sources. \`web_fetch\` reads a specific http(s) URL as markdown/text — pages found via web_search, official docs, papers. web_fetch only accepts http(s) URLs: never local file paths (use bash) or datasource virtual ids such as /kakao/... (use that connection's dedicated search_datasource_<id> tool). Keep result URLs for traceability. Web queries leave the machine: never include private corpus content or secrets in web_search queries or fetched URLs.
9518
9480
  ` : "";
9519
9481
  return `You are AutoRAG, a ${modelId} librarian agent for document collections, cloud drives, images, and messenger history.
9520
9482
 
@@ -9543,7 +9505,7 @@ ${noSearchTools}
9543
9505
  - Use MinSync lexical mode for exact terminology, MinSync vector search for semantic similarity, and \`search_all_documents\` when hybrid ranking over the same MinSync chunks can help.
9544
9506
  - Use \`bash\` to read already-retrieved local files with cat/head/sed. find/grep/rg must be small and bounded: one already-known directory from retrieval, a tight pattern, and a cap (head, maxdepth, or file types). Never recursively scan a whole search root (Downloads, Documents, Desktop, or /); those calls miss the bash timeout and stall the search loop.
9545
9507
  - If retrieval is empty, retry a simpler query or synonyms through retrieval tools first. Do not widen filesystem discovery to compensate.
9546
- - Local retrieval sources are absolute filesystem paths and may be read with \`bash\` after verifying the returned path. Datasource retrieval sources use slash-prefixed virtual identifiers such as /kakao/..., /mailcrawl/..., /slack/..., /discord/..., and /github/...; they are not OS paths and must never be passed to \`cd\`, \`cat\`, or other filesystem tools. Search or fetch them through \`search_datasource_documents\` and the loaded datasource skill/native CLI.
9508
+ - Local retrieval sources are absolute filesystem paths and may be read with \`bash\` after verifying the returned path. Datasource retrieval sources use slash-prefixed virtual identifiers such as /kakao/..., /mailcrawl/..., /slack/..., /discord/..., and /github/...; they are not OS paths and must never be passed to \`cd\`, \`cat\`, or other filesystem tools. Search them through the connection's dedicated \`search_datasource_<id>\` tool and the loaded datasource skill/native CLI; every authorized connection has its own tool, and \`search_all_documents\` still spans all of them at once.
9547
9509
  - When exploring local files and folders, actively use \`jikji_find\` as your primary discovery tool. Do not manually traverse folders with exploratory bash commands; reserve \`bash\` for targeted reading of identified files (cat, head, sed).
9548
9510
  - When search results or evidence contain conflicting information, treat the freshest and most recent information as authoritative and correct.
9549
9511
  - Cross-check important claims against the original source and preserve real source paths.
@@ -11586,7 +11548,7 @@ var init_render = __esm(() => {
11586
11548
  });
11587
11549
 
11588
11550
  // src/agent/web-fetch-tool.ts
11589
- import { Type as Type15 } from "typebox";
11551
+ import { Type as Type14 } from "typebox";
11590
11552
  function truncateHead(output, maxBytes, maxLines) {
11591
11553
  const lines = output.split(`
11592
11554
  `);
@@ -11618,7 +11580,7 @@ function createWebFetchTool(options = {}) {
11618
11580
  content: [
11619
11581
  {
11620
11582
  type: "text",
11621
- text: `web_fetch only reads http(s) URLs; received "${url}". Local files belong to bash and datasource virtual ids to search_datasource_documents.`
11583
+ text: `web_fetch only reads http(s) URLs; received "${url}". Local files belong to bash and datasource virtual ids to their dedicated datasource search tools.`
11622
11584
  }
11623
11585
  ],
11624
11586
  details: {
@@ -11683,9 +11645,9 @@ var WEB_FETCH_TOOL_NAME = "web_fetch", FETCH_DEFAULT_MAX_LINES = 300, FETCH_MAX_
11683
11645
  var init_web_fetch_tool = __esm(() => {
11684
11646
  init_render();
11685
11647
  FETCH_MAX_OUTPUT_BYTES = 50 * 1024;
11686
- webFetchSchema = Type15.Object({
11687
- url: Type15.String({ description: "The http(s) URL to fetch and read." }),
11688
- raw: Type15.Optional(Type15.Boolean({ description: "Return the raw response body without markdown rendering or formatting." }))
11648
+ webFetchSchema = Type14.Object({
11649
+ url: Type14.String({ description: "The http(s) URL to fetch and read." }),
11650
+ raw: Type14.Optional(Type14.Boolean({ description: "Return the raw response body without markdown rendering or formatting." }))
11689
11651
  });
11690
11652
  });
11691
11653
 
@@ -15205,7 +15167,7 @@ var init_search = __esm(() => {
15205
15167
  });
15206
15168
 
15207
15169
  // src/agent/web-search-tool.ts
15208
- import { Type as Type16 } from "typebox";
15170
+ import { Type as Type15 } from "typebox";
15209
15171
  function createWebSearchTool(options = {}) {
15210
15172
  return {
15211
15173
  name: WEB_SEARCH_TOOL_NAME,
@@ -15275,15 +15237,15 @@ function createWebSearchTool(options = {}) {
15275
15237
  var WEB_SEARCH_TOOL_NAME = "web_search", webSearchSchema;
15276
15238
  var init_web_search_tool = __esm(() => {
15277
15239
  init_search();
15278
- webSearchSchema = Type16.Object({
15279
- query: Type16.String({
15240
+ webSearchSchema = Type15.Object({
15241
+ query: Type15.String({
15280
15242
  description: 'Search query. Supports Google-style directives: site:/-site:, after:/before: (YYYY-MM-DD), inurl:, intitle:, filetype:, "exact phrase", -term, OR.'
15281
15243
  }),
15282
- recency: Type16.Optional(Type16.Union([Type16.Literal("day"), Type16.Literal("week"), Type16.Literal("month"), Type16.Literal("year")], {
15244
+ recency: Type15.Optional(Type15.Union([Type15.Literal("day"), Type15.Literal("week"), Type15.Literal("month"), Type15.Literal("year")], {
15283
15245
  description: "Optional recency window for results (pure time filter)."
15284
15246
  })),
15285
- limit: Type16.Optional(Type16.Integer({ description: "Maximum number of results to return." })),
15286
- num_search_results: Type16.Optional(Type16.Integer({ description: "Alias for limit (result count hint)." }))
15247
+ limit: Type15.Optional(Type15.Integer({ description: "Maximum number of results to return." })),
15248
+ num_search_results: Type15.Optional(Type15.Integer({ description: "Alias for limit (result count hint)." }))
15287
15249
  });
15288
15250
  });
15289
15251
 
@@ -15411,7 +15373,6 @@ class AutoRAGAgent {
15411
15373
  this.memory.load();
15412
15374
  this.runLogger = new AutoRAGRunLogger(join22(dirname12(memPath), "logs", "runs.jsonl"));
15413
15375
  const checkMemoryTool = createCheckMemoryTool(this.memory);
15414
- const searchDatasourceTool = createSearchDatasourceDocumentsTool(this);
15415
15376
  const singleDatasourceTools = createSingleDatasourceSearchTools(this, this.singleDatasourceToolSpecs());
15416
15377
  this.searchToolNames = new Set([...SEARCH_TOOLS, ...singleDatasourceTools.map((tool) => tool.name)]);
15417
15378
  const searchMinSyncTool = createSearchMinSyncDocumentsTool(() => this.remoteFilteredRetrievalMethod(this.minSyncMethod), (scope) => this.resolveRetrievalScope(scope));
@@ -15431,7 +15392,6 @@ class AutoRAGAgent {
15431
15392
  const reservedNames = new Set([
15432
15393
  BASH_TOOL_NAME,
15433
15394
  "check_memory",
15434
- SEARCH_DATASOURCE_DOCUMENTS_TOOL_NAME,
15435
15395
  ...singleDatasourceTools.map((tool) => tool.name),
15436
15396
  LOAD_DATASOURCE_SKILL_TOOL_NAME,
15437
15397
  EMIT_AUTORAG_RESULTS_TOOL_NAME,
@@ -15459,7 +15419,6 @@ class AutoRAGAgent {
15459
15419
  checkMemoryTool,
15460
15420
  searchMinSyncTool,
15461
15421
  searchAllTool,
15462
- searchDatasourceTool,
15463
15422
  ...singleDatasourceTools,
15464
15423
  loadDatasourceSkillTool,
15465
15424
  ...webSearchTool !== undefined ? [webSearchTool] : [],
@@ -16628,30 +16587,6 @@ ${initialRetrievalContext ?? "Pending; continue only with a brief progress state
16628
16587
  async searchAllDocuments(query, options = {}) {
16629
16588
  return this.retrieveWithDiagnostics(query, { topK: options.topK, scope: options.scope });
16630
16589
  }
16631
- async searchDatasourceDocuments(query, options = {}) {
16632
- const retrievalOptions = {
16633
- ...this.activeRetrievalOptions,
16634
- topK: options.topK,
16635
- scope: options.scope
16636
- };
16637
- const ctx = this.datasourceAccessContext(retrievalOptions);
16638
- const methods = this.methodRegistry.list().filter((method) => {
16639
- const descriptor = method.describe();
16640
- return descriptor.datasourceId !== undefined && ctx.isAccessible(descriptor);
16641
- });
16642
- if (methods.length === 0)
16643
- return { results: [], diagnostics: [] };
16644
- const { results: byMethod, diagnostics } = await this.retriever.retrieveWithDiagnostics(methods, query, retrievalOptions);
16645
- const filteredByMethod = this.datasourceFilter.filter(byMethod, methods, ctx, options.scope);
16646
- for (const results of filteredByMethod.values()) {
16647
- for (const result of results)
16648
- retrievalOptions.observedSources?.add(result.source);
16649
- }
16650
- return {
16651
- results: this.rerankWithMemory(query, this.merger.merge(filteredByMethod, { topK: options.topK ?? MERGED_EVIDENCE_CEILING, dedup: true })),
16652
- diagnostics
16653
- };
16654
- }
16655
16590
  async searchSingleDatasourceDocuments(datasourceId, query, options = {}) {
16656
16591
  const retrievalOptions = {
16657
16592
  ...this.activeRetrievalOptions,
@@ -16888,7 +16823,6 @@ var init_agent = __esm(() => {
16888
16823
  init_local_model();
16889
16824
  init_peer_target_tool();
16890
16825
  init_search_all_tool();
16891
- init_search_datasource_tool();
16892
16826
  init_search_documents();
16893
16827
  init_search_minsync_tool();
16894
16828
  init_search_single_datasource_tool();
@@ -16899,7 +16833,6 @@ var init_agent = __esm(() => {
16899
16833
  SEARCH_TOOLS = [
16900
16834
  SEARCH_MINSYNC_DOCUMENTS_TOOL_NAME,
16901
16835
  SEARCH_ALL_DOCUMENTS_TOOL_NAME,
16902
- SEARCH_DATASOURCE_DOCUMENTS_TOOL_NAME,
16903
16836
  JIKJI_FIND_TOOL_NAME
16904
16837
  ];
16905
16838
  RemoteSessionRejectedError = class RemoteSessionRejectedError extends Error {
@@ -17577,7 +17510,7 @@ class ClawGallerySkill {
17577
17510
  "- Never use `lexical` unless a sparse V-SPLADE index exists; it must not fall back to dense vectors.",
17578
17511
  "- `hybrid` is the default and asks ClawGallery to RRF-fuse keyword results with every available dense and sparse vector channel.",
17579
17512
  "",
17580
- `Use the dedicated \`${datasourceSearchToolName(CLAWGALLERY_DATASOURCE_ID)}\` tool with a natural-language query. Do not use \`search_datasource_documents\` for this datasource — it fans out to every datasource CLI. Prefer hybrid/default search unless you know which index capability is available. If an explicit vector mode is needed, inspect \`clawgallery vdr status --json\` first when capability details are available.`,
17513
+ `Use the dedicated \`${datasourceSearchToolName(CLAWGALLERY_DATASOURCE_ID)}\` tool with a natural-language query. Prefer hybrid/default search unless you know which index capability is available. If an explicit vector mode is needed, inspect \`clawgallery vdr status --json\` first when capability details are available.`,
17581
17514
  "",
17582
17515
  "With only V-SPLADE synced, use lexical or hybrid. With only dense VDR synced, use embedding or hybrid. When both are synced, use hybrid so dense VDR and sparse V-SPLADE results can coexist and be RRF-combined.",
17583
17516
  "",
@@ -18426,7 +18359,7 @@ class ConnectorDatasourceSkill {
18426
18359
  `Indexing is server-managed and refreshed ${cadence}. You do not trigger indexing; just search.`,
18427
18360
  "",
18428
18361
  "## How to search",
18429
- `Call the dedicated \`${datasourceSearchToolName(skillName)}\` tool with a natural-language \`query\`. Optionally pass \`topK\` and a narrowing \`scope\`. Do not use \`search_datasource_documents\` for this datasource — it fans out to every datasource CLI. Available authorized scopes:`,
18362
+ `Call the dedicated \`${datasourceSearchToolName(skillName)}\` tool with a natural-language \`query\`. Optionally pass \`topK\` and a narrowing \`scope\`. Available authorized scopes:`,
18430
18363
  instanceScopes.length > 0 ? instanceScopes : "- (no authorized instances)",
18431
18364
  "",
18432
18365
  "`scope` can only narrow within already-authorized scopes; it can never widen access.",
@@ -18565,7 +18498,7 @@ function createCloudDriveSkillDefinition(skillName, provider) {
18565
18498
  "Configure and authenticate the remote with `rclone config`; AutoRAG never receives or stores provider credentials.",
18566
18499
  "Google Drive is Tier-1 supported. OneDrive and other rclone remotes use the same manifest contract. iCloud Drive is experimental and requires periodic Apple ID reauthentication.",
18567
18500
  "Indexing is incremental: `rclone lsjson` inventories metadata, then only added or changed indexable files are mirrored. Search uses the last completed snapshot while a failed sync is retried.",
18568
- `This connection is independently addressable. Load \`datasource-${skillName}\`, then call \`${datasourceSearchToolName(skillName)}\` with an optional narrowing scope under \`/${skillName}/<instance>/**\`. Do not use \`search_datasource_documents\` for this datasource — it fans out to every datasource CLI.`
18501
+ `This connection is independently addressable. Load \`datasource-${skillName}\`, then call \`${datasourceSearchToolName(skillName)}\` with an optional narrowing scope under \`/${skillName}/<instance>/**\`.`
18569
18502
  ],
18570
18503
  nativeCliNote: "The underlying remote can be browsed directly with the `rclone` CLI through `bash` (e.g. `rclone lsl <remote>:<path>` or `rclone cat <remote>:<path>/<file>`); the dedicated tool searches the mirrored, parsed snapshot."
18571
18504
  };
@@ -18609,6 +18542,40 @@ var init_cloud_drive = __esm(() => {
18609
18542
  init_skill2();
18610
18543
  });
18611
18544
 
18545
+ // src/process/terminate-tree.ts
18546
+ import { spawn as spawn9 } from "node:child_process";
18547
+ function terminateProcessTree(child, signal = "SIGKILL") {
18548
+ const pid = child.pid;
18549
+ if (pid === undefined)
18550
+ return false;
18551
+ if (process.platform === "win32") {
18552
+ const killer = spawn9("taskkill", ["/pid", String(pid), "/t", "/f"], { stdio: "ignore", windowsHide: true });
18553
+ killer.on("error", () => killDirectChild(child, signal));
18554
+ killer.on("close", (code) => {
18555
+ if (code !== 0)
18556
+ killDirectChild(child, signal);
18557
+ });
18558
+ return true;
18559
+ }
18560
+ try {
18561
+ process.kill(-pid, signal);
18562
+ return true;
18563
+ } catch {
18564
+ return killDirectChild(child, signal);
18565
+ }
18566
+ }
18567
+ function killDirectChild(child, signal) {
18568
+ if (child.exitCode !== null || child.signalCode !== null)
18569
+ return false;
18570
+ try {
18571
+ return child.kill(signal);
18572
+ } catch {
18573
+ return false;
18574
+ }
18575
+ }
18576
+ var TREE_KILL_GRACE_MS = 500;
18577
+ var init_terminate_tree = () => {};
18578
+
18612
18579
  // src/datasource/skills/discrawl/types.ts
18613
18580
  var ENGLISH_ONLY_EMBEDDING_MODELS, DEFAULT_DISCRAWL_EMBEDDING_MODEL = "embeddinggemma", DEFAULT_DISCRAWL_BINARY = "discrawl", DEFAULT_DISCRAWL_TIMEOUT_MS = 30000, DEFAULT_DISCRAWL_MAX_BUFFER_BYTES = 1048576, DEFAULT_DISCRAWL_MODE = "hybrid";
18614
18581
  var init_types4 = __esm(() => {
@@ -18625,7 +18592,7 @@ var init_types4 = __esm(() => {
18625
18592
  });
18626
18593
 
18627
18594
  // src/datasource/skills/discrawl/client.ts
18628
- import { spawn as spawn9 } from "node:child_process";
18595
+ import { spawn as spawn10 } from "node:child_process";
18629
18596
  import { mkdirSync as mkdirSync14, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "node:fs";
18630
18597
  import { dirname as dirname15 } from "node:path";
18631
18598
 
@@ -18762,27 +18729,52 @@ function managedConfigPath(workspace) {
18762
18729
  return `${workspace}/.autorag/datasources/discrawl/config.toml`;
18763
18730
  }
18764
18731
  function spawnDiscrawl(request) {
18765
- return new Promise((resolve) => {
18732
+ return new Promise((resolveResult) => {
18766
18733
  const { options, args, env, signal } = request;
18767
18734
  const portable = portableSpawnCommand(options.binaryPath ?? DEFAULT_DISCRAWL_BINARY, args);
18768
- const child = spawn9(portable.command, [...portable.args], {
18735
+ const child = spawn10(portable.command, [...portable.args], {
18769
18736
  env,
18770
18737
  ...request.cwd === undefined ? {} : { cwd: request.cwd },
18771
18738
  detached: process.platform !== "win32",
18739
+ windowsHide: true,
18772
18740
  stdio: ["ignore", "pipe", "pipe"]
18773
18741
  });
18774
18742
  let stdout = { text: "", bytes: 0, capped: false };
18775
18743
  let stderr = { text: "", bytes: 0, capped: false };
18776
18744
  let settled = false;
18777
18745
  let finalReason;
18746
+ let pipeGuard;
18778
18747
  const maxBuffer = options.maxBufferBytes ?? DEFAULT_DISCRAWL_MAX_BUFFER_BYTES;
18748
+ const settleWith = (result) => {
18749
+ if (settled)
18750
+ return;
18751
+ settled = true;
18752
+ clearTimeout(timeout);
18753
+ if (pipeGuard !== undefined)
18754
+ clearTimeout(pipeGuard);
18755
+ signal?.removeEventListener("abort", abortHandler);
18756
+ resolveResult(result);
18757
+ };
18758
+ const finish = (code) => {
18759
+ if (finalReason !== undefined) {
18760
+ settleWith({ ok: false, reason: finalReason, stdout: stdout.text, stderr: stderr.text, code });
18761
+ return;
18762
+ }
18763
+ settleWith({
18764
+ ok: code === 0,
18765
+ reason: code === 0 ? undefined : "nonzero-exit",
18766
+ stdout: stdout.text,
18767
+ stderr: stderr.text,
18768
+ code
18769
+ });
18770
+ };
18779
18771
  const timeout = setTimeout(() => {
18780
18772
  finalReason = "timeout";
18781
- terminate3(child);
18773
+ terminateProcessTree(child, "SIGKILL");
18782
18774
  }, options.timeoutMs ?? DEFAULT_DISCRAWL_TIMEOUT_MS);
18783
18775
  const abortHandler = () => {
18784
18776
  finalReason = "aborted";
18785
- terminate3(child);
18777
+ terminateProcessTree(child, "SIGKILL");
18786
18778
  };
18787
18779
  if (signal?.aborted)
18788
18780
  abortHandler();
@@ -18793,43 +18785,27 @@ function spawnDiscrawl(request) {
18793
18785
  stdout = appendBounded2(stdout, chunk, maxBuffer);
18794
18786
  if (stdout.capped) {
18795
18787
  finalReason = "stdout-too-large";
18796
- terminate3(child);
18788
+ terminateProcessTree(child, "SIGKILL");
18797
18789
  }
18798
18790
  });
18799
18791
  child.stderr.on("data", (chunk) => {
18800
18792
  stderr = appendBounded2(stderr, chunk, maxBuffer);
18801
18793
  if (stderr.capped) {
18802
18794
  finalReason = "stderr-too-large";
18803
- terminate3(child);
18795
+ terminateProcessTree(child, "SIGKILL");
18804
18796
  }
18805
18797
  });
18806
- child.on("error", (error) => {
18798
+ child.on("exit", () => {
18807
18799
  if (settled)
18808
18800
  return;
18809
- settled = true;
18810
- clearTimeout(timeout);
18811
- signal?.removeEventListener("abort", abortHandler);
18812
- const reason = error.code === "ENOENT" ? "binary-missing" : "spawn-error";
18813
- resolve({ ok: false, reason, stdout: stdout.text, stderr: describeSpawnFailure(reason), code: null });
18801
+ terminateProcessTree(child, "SIGKILL");
18802
+ pipeGuard = setTimeout(() => finish(child.exitCode), TREE_KILL_GRACE_MS);
18814
18803
  });
18815
- child.on("close", (code) => {
18816
- if (settled)
18817
- return;
18818
- settled = true;
18819
- clearTimeout(timeout);
18820
- signal?.removeEventListener("abort", abortHandler);
18821
- if (finalReason !== undefined) {
18822
- resolve({ ok: false, reason: finalReason, stdout: stdout.text, stderr: stderr.text, code });
18823
- return;
18824
- }
18825
- resolve({
18826
- ok: code === 0,
18827
- reason: code === 0 ? undefined : "nonzero-exit",
18828
- stdout: stdout.text,
18829
- stderr: stderr.text,
18830
- code
18831
- });
18804
+ child.on("error", (error) => {
18805
+ const reason = error.code === "ENOENT" ? "binary-missing" : "spawn-error";
18806
+ settleWith({ ok: false, reason, stdout: stdout.text, stderr: describeSpawnFailure(reason), code: null });
18832
18807
  });
18808
+ child.on("close", (code) => finish(code));
18833
18809
  });
18834
18810
  }
18835
18811
  function nativeGatewayBaseUrl(baseUrl) {
@@ -18893,17 +18869,6 @@ function findUserTokenKey(env) {
18893
18869
  }
18894
18870
  return;
18895
18871
  }
18896
- function terminate3(child) {
18897
- if (child.killed)
18898
- return;
18899
- if (process.platform !== "win32" && child.pid !== undefined) {
18900
- try {
18901
- process.kill(-child.pid, "SIGKILL");
18902
- return;
18903
- } catch {}
18904
- }
18905
- child.kill("SIGKILL");
18906
- }
18907
18872
  function describeSpawnFailure(reason) {
18908
18873
  return reason === "binary-missing" ? "the discrawl binary could not be found" : "the discrawl binary could not be started";
18909
18874
  }
@@ -19123,6 +19088,7 @@ function ok2(data, result) {
19123
19088
  var USER_TOKEN_KEYS, SAFE_INHERITED_ENV_KEYS, SAFE_DISCRAWL_ENV_PREFIX = "DISCRAWL_";
19124
19089
  var init_client4 = __esm(() => {
19125
19090
  init_portable_spawn();
19091
+ init_terminate_tree();
19126
19092
  init_types4();
19127
19093
  USER_TOKEN_KEYS = new Set([
19128
19094
  "discord_user_token",
@@ -19435,7 +19401,7 @@ class DiscrawlSkill {
19435
19401
  `Indexing is server-managed and refreshed ${cadence}. Sync is incremental (cursor-based) and embeddings are queued per changed message. You do not trigger indexing; just search.`,
19436
19402
  "",
19437
19403
  "## How to search",
19438
- `Call the dedicated \`${datasourceSearchToolName(DISCORD_DATASOURCE_ID2)}\` tool with a natural-language \`query\`. Optionally pass \`topK\` and a narrowing \`scope\`. Do not use \`search_datasource_documents\` for this datasource — it fans out to every datasource CLI. Available authorized scopes:`,
19404
+ `Call the dedicated \`${datasourceSearchToolName(DISCORD_DATASOURCE_ID2)}\` tool with a natural-language \`query\`. Optionally pass \`topK\` and a narrowing \`scope\`. Available authorized scopes:`,
19439
19405
  instanceScopes.length > 0 ? instanceScopes : "- (no authorized instances)",
19440
19406
  "",
19441
19407
  "`scope` can only narrow within already-authorized scopes; it can never widen access.",
@@ -19674,47 +19640,1232 @@ var init_github = __esm(() => {
19674
19640
  init_skill4();
19675
19641
  });
19676
19642
 
19677
- // src/datasource/skills/katok/types.ts
19678
- var DEFAULT_KATOK_BINARY = "katok", DEFAULT_KATOK_TIMEOUT_MS = 60000, DEFAULT_KATOK_MAX_BUFFER_BYTES = 1048576;
19679
- var init_types5 = () => {};
19680
-
19681
- // src/datasource/skills/katok/client.ts
19682
- import { spawn as spawn10 } from "node:child_process";
19643
+ // src/datasource/skills/github-gist/connector.ts
19644
+ import { execFile as execFile3 } from "node:child_process";
19645
+ import { existsSync as existsSync20, mkdirSync as mkdirSync15, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "node:fs";
19646
+ import { dirname as dirname16 } from "node:path";
19647
+ async function defaultGhTokenRunner() {
19648
+ return new Promise((resolve) => {
19649
+ execFile3("gh", ["auth", "token"], { timeout: GH_TOKEN_TIMEOUT_MS }, (error, stdout) => {
19650
+ if (error) {
19651
+ resolve(undefined);
19652
+ return;
19653
+ }
19654
+ const token = stdout.trim();
19655
+ resolve(token.length > 0 ? token : undefined);
19656
+ });
19657
+ });
19658
+ }
19683
19659
 
19684
- class KatokClient {
19660
+ class GitHubGistConnector {
19685
19661
  options;
19686
19662
  constructor(options = {}) {
19687
19663
  this.options = options;
19688
19664
  }
19689
- async doctor(signal) {
19690
- const result = await this.run(["doctor", "--json"], signal);
19691
- if (!result.ok)
19692
- return toFailure2(result);
19693
- const parsed = parseJsonObject2(result.stdout);
19694
- if (parsed === undefined)
19695
- return toFailure2(result, "invalid-json");
19696
- const data = normalizeDoctor2(parsed);
19697
- return data === undefined ? toFailure2(result, "invalid-shape") : ok3(data, result);
19665
+ async fetch(signal) {
19666
+ const token = await this.resolveAuth();
19667
+ if (token === undefined) {
19668
+ return {
19669
+ ok: false,
19670
+ reason: "not-configured",
19671
+ message: "no GitHub token: set GITHUB_TOKEN (or connector.tokenEnv) or authenticate the gh CLI"
19672
+ };
19673
+ }
19674
+ const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL4;
19675
+ const request = {
19676
+ headers: {
19677
+ Accept: "application/vnd.github+json",
19678
+ "X-GitHub-Api-Version": "2022-11-28",
19679
+ "User-Agent": "autorag-datasource",
19680
+ Authorization: `Bearer ${token}`
19681
+ },
19682
+ timeoutMs: this.options.timeoutMs,
19683
+ fetchImpl: this.options.fetchImpl,
19684
+ signal
19685
+ };
19686
+ const maxPages = this.options.maxPages ?? DEFAULT_MAX_PAGES2;
19687
+ const maxGists = this.options.maxGists ?? DEFAULT_MAX_GISTS;
19688
+ const listed = new Map;
19689
+ for (let page = 1;page <= maxPages && listed.size < maxGists; page += 1) {
19690
+ const url = `${baseUrl}/gists?per_page=100&page=${page}`;
19691
+ const result = await httpJson(url, request);
19692
+ if (!result.ok) {
19693
+ if (result.reason === "auth")
19694
+ return { ok: false, reason: "auth", message: "github: unauthorized" };
19695
+ if (result.reason === "rate-limited" || result.reason === "permission") {
19696
+ return { ok: false, reason: "rate-limited", message: "github: rate limited or forbidden" };
19697
+ }
19698
+ return { ok: false, reason: "api-error", message: `github: gist list failed: ${result.message}` };
19699
+ }
19700
+ const items = asArray(result.json);
19701
+ if (items.length === 0)
19702
+ break;
19703
+ for (const raw of items) {
19704
+ if (listed.size >= maxGists)
19705
+ break;
19706
+ const item = asRecord(raw);
19707
+ const id = asString(item?.id);
19708
+ const updatedAt = asString(item?.updated_at);
19709
+ if (id === undefined || updatedAt === undefined)
19710
+ continue;
19711
+ listed.set(id, { updatedAt });
19712
+ }
19713
+ if (items.length < 100)
19714
+ break;
19715
+ }
19716
+ const previous = this.loadState();
19717
+ const changedIds = [];
19718
+ for (const [id, entry] of listed) {
19719
+ if (previous.gists[id] !== entry.updatedAt)
19720
+ changedIds.push(id);
19721
+ }
19722
+ const deletedDocIds = Object.keys(previous.gists).filter((id) => !listed.has(id));
19723
+ const documents = [];
19724
+ const warnings = [];
19725
+ const failedIds = new Set;
19726
+ for (const id of changedIds) {
19727
+ const result = await httpJson(`${baseUrl}/gists/${encodeURIComponent(id)}`, request);
19728
+ if (!result.ok) {
19729
+ warnings.push(`gist ${id} fetch failed: ${result.message}`);
19730
+ failedIds.add(id);
19731
+ continue;
19732
+ }
19733
+ const document = toDocument(asRecord(result.json));
19734
+ if (document !== undefined)
19735
+ documents.push(document);
19736
+ }
19737
+ this.saveState({
19738
+ version: STATE_VERSION,
19739
+ gists: Object.fromEntries([...listed].filter(([id]) => !failedIds.has(id)).map(([id, e]) => [id, e.updatedAt]))
19740
+ });
19741
+ const changed = changedIds.length > 0 || deletedDocIds.length > 0;
19742
+ return {
19743
+ ok: true,
19744
+ documents,
19745
+ changed,
19746
+ deletedDocIds,
19747
+ ...warnings.length > 0 ? { warnings } : {}
19748
+ };
19698
19749
  }
19699
- async sync(signal) {
19700
- const result = await this.run(["sync", "--json"], signal);
19701
- if (!result.ok)
19702
- return toFailure2(result);
19703
- const parsed = parseJsonObject2(result.stdout);
19704
- if (parsed === undefined)
19705
- return toFailure2(result, "invalid-json");
19706
- const data = normalizeSync2(parsed);
19707
- return data === undefined ? toFailure2(result, "invalid-shape") : ok3(data, result);
19750
+ async resolveAuth() {
19751
+ const direct = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV2);
19752
+ if (direct !== undefined)
19753
+ return direct;
19754
+ if (this.options.ghCliFallback === false)
19755
+ return;
19756
+ const runner = this.options.ghTokenRunner ?? defaultGhTokenRunner;
19757
+ try {
19758
+ return await runner();
19759
+ } catch {
19760
+ return;
19761
+ }
19708
19762
  }
19709
- async index(signal) {
19710
- const result = await this.run(["index", "--json"], signal);
19763
+ loadState() {
19764
+ const statePath = this.options.statePath;
19765
+ if (statePath === undefined || !existsSync20(statePath))
19766
+ return { version: STATE_VERSION, gists: {} };
19767
+ try {
19768
+ const parsed = JSON.parse(readFileSync16(statePath, "utf8"));
19769
+ if (parsed.version !== STATE_VERSION || typeof parsed.gists !== "object" || parsed.gists === null) {
19770
+ return { version: STATE_VERSION, gists: {} };
19771
+ }
19772
+ return parsed;
19773
+ } catch {
19774
+ return { version: STATE_VERSION, gists: {} };
19775
+ }
19776
+ }
19777
+ saveState(state) {
19778
+ const statePath = this.options.statePath;
19779
+ if (statePath === undefined)
19780
+ return;
19781
+ try {
19782
+ mkdirSync15(dirname16(statePath), { recursive: true });
19783
+ writeFileSync14(statePath, `${JSON.stringify(state)}
19784
+ `, "utf8");
19785
+ } catch {}
19786
+ }
19787
+ }
19788
+ function toDocument(gist) {
19789
+ if (gist === undefined)
19790
+ return;
19791
+ const id = asString(gist.id);
19792
+ if (id === undefined)
19793
+ return;
19794
+ const description = asString(gist.description) ?? "";
19795
+ const filesRecord = asRecord(gist.files) ?? {};
19796
+ const fileNames = Object.keys(filesRecord);
19797
+ const sections = [];
19798
+ for (const name of fileNames) {
19799
+ const file = asRecord(filesRecord[name]);
19800
+ const filename = asString(file?.filename) ?? name;
19801
+ const content = asString(file?.content) ?? "";
19802
+ sections.push(`## ${filename}
19803
+ ${content}`);
19804
+ }
19805
+ const title = description.length > 0 ? description : fileNames[0] ?? id;
19806
+ const content = [description, ...sections].filter((part) => part.length > 0).join(`
19807
+
19808
+ `);
19809
+ return {
19810
+ docId: id,
19811
+ hierarchy: ["gists"],
19812
+ title,
19813
+ content: content.length > 0 ? content : title,
19814
+ publishedAt: parseEpochMs(gist.updated_at),
19815
+ metadata: {
19816
+ gistId: id,
19817
+ public: gist.public === true,
19818
+ files: fileNames,
19819
+ ...asString(gist.html_url) !== undefined ? { htmlUrl: asString(gist.html_url) } : {}
19820
+ }
19821
+ };
19822
+ }
19823
+ var DEFAULT_BASE_URL4 = "https://api.github.com", DEFAULT_TOKEN_ENV2 = "GITHUB_TOKEN", DEFAULT_MAX_PAGES2 = 10, DEFAULT_MAX_GISTS = 500, GH_TOKEN_TIMEOUT_MS = 5000, STATE_VERSION = 1;
19824
+ var init_connector3 = () => {};
19825
+
19826
+ // src/datasource/skills/github-gist/semantic.ts
19827
+ import { createHash as createHash7 } from "node:crypto";
19828
+ import { existsSync as existsSync21, mkdirSync as mkdirSync16, readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "node:fs";
19829
+ import { dirname as dirname17 } from "node:path";
19830
+ function contentHash(chunk) {
19831
+ return createHash7("sha256").update(`${chunk.title ?? ""}
19832
+ ${chunk.content}`).digest("hex");
19833
+ }
19834
+ function sameIdentity(a, b) {
19835
+ return a.provider === b.provider && a.model === b.model && a.dimension === b.dimension;
19836
+ }
19837
+ function cosine(a, b) {
19838
+ let dot = 0;
19839
+ let normA = 0;
19840
+ let normB = 0;
19841
+ const length = Math.min(a.length, b.length);
19842
+ for (let i = 0;i < length; i += 1) {
19843
+ const x = a[i] ?? 0;
19844
+ const y = b[i] ?? 0;
19845
+ dot += x * y;
19846
+ normA += x * x;
19847
+ normB += y * y;
19848
+ }
19849
+ if (normA === 0 || normB === 0)
19850
+ return 0;
19851
+ return dot / (Math.sqrt(normA) * Math.sqrt(normB));
19852
+ }
19853
+
19854
+ class GistSemanticIndex {
19855
+ statePath;
19856
+ identity;
19857
+ entries = {};
19858
+ loaded = false;
19859
+ constructor(options = {}) {
19860
+ this.statePath = options.statePath;
19861
+ }
19862
+ async sync(chunks, embedder) {
19863
+ this.ensureLoaded();
19864
+ let identity;
19865
+ try {
19866
+ identity = await embedder.identity();
19867
+ } catch (error) {
19868
+ return { ok: false, message: error instanceof Error ? error.message : String(error) };
19869
+ }
19870
+ if (this.identity === undefined || !sameIdentity(this.identity, identity)) {
19871
+ this.entries = {};
19872
+ this.identity = identity;
19873
+ }
19874
+ const currentIds = new Set(chunks.map((chunk) => chunk.chunkId));
19875
+ for (const chunkId of Object.keys(this.entries)) {
19876
+ if (!currentIds.has(chunkId))
19877
+ delete this.entries[chunkId];
19878
+ }
19879
+ const pending = chunks.filter((chunk) => {
19880
+ const entry = this.entries[chunk.chunkId];
19881
+ return entry === undefined || entry.hash !== contentHash(chunk);
19882
+ });
19883
+ if (pending.length === 0) {
19884
+ this.persist();
19885
+ return { ok: true, embedded: 0 };
19886
+ }
19887
+ let vectors;
19888
+ try {
19889
+ vectors = await embedder.embed(pending.map((chunk) => `${chunk.title ?? ""}
19890
+ ${chunk.content}`));
19891
+ } catch (error) {
19892
+ return { ok: false, message: error instanceof Error ? error.message : String(error) };
19893
+ }
19894
+ if (vectors.length !== pending.length) {
19895
+ return { ok: false, message: `embedder returned ${vectors.length} vectors for ${pending.length} chunks` };
19896
+ }
19897
+ for (const [index, chunk] of pending.entries()) {
19898
+ const vector = vectors[index] ?? [];
19899
+ this.entries[chunk.chunkId] = { hash: contentHash(chunk), vector };
19900
+ }
19901
+ this.persist();
19902
+ return { ok: true, embedded: pending.length };
19903
+ }
19904
+ searchByVector(queryVector, topK) {
19905
+ this.ensureLoaded();
19906
+ const scored = [];
19907
+ for (const [chunkId, entry] of Object.entries(this.entries)) {
19908
+ const score = cosine(queryVector, entry.vector);
19909
+ if (score > 0)
19910
+ scored.push({ chunkId, score });
19911
+ }
19912
+ scored.sort((a, b) => b.score - a.score || a.chunkId.localeCompare(b.chunkId));
19913
+ return scored.slice(0, topK);
19914
+ }
19915
+ ensureLoaded() {
19916
+ if (this.loaded)
19917
+ return;
19918
+ this.loaded = true;
19919
+ if (this.statePath === undefined || !existsSync21(this.statePath))
19920
+ return;
19921
+ try {
19922
+ const parsed = JSON.parse(readFileSync17(this.statePath, "utf8"));
19923
+ if (parsed.version !== VECTORS_VERSION || typeof parsed.entries !== "object" || parsed.entries === null)
19924
+ return;
19925
+ if (typeof parsed.identity?.dimension !== "number")
19926
+ return;
19927
+ this.identity = parsed.identity;
19928
+ this.entries = { ...parsed.entries };
19929
+ } catch {}
19930
+ }
19931
+ persist() {
19932
+ if (this.statePath === undefined)
19933
+ return;
19934
+ if (this.identity === undefined)
19935
+ return;
19936
+ try {
19937
+ mkdirSync16(dirname17(this.statePath), { recursive: true });
19938
+ const payload = {
19939
+ version: VECTORS_VERSION,
19940
+ identity: this.identity,
19941
+ entries: this.entries
19942
+ };
19943
+ writeFileSync15(this.statePath, `${JSON.stringify(payload)}
19944
+ `, "utf8");
19945
+ } catch {}
19946
+ }
19947
+ }
19948
+
19949
+ class GitHubGistSemanticMethod {
19950
+ options;
19951
+ constructor(options) {
19952
+ this.options = options;
19953
+ }
19954
+ describe() {
19955
+ const { skillName, skillType, tags } = this.options;
19956
+ return {
19957
+ name: `${skillName}-semantic`,
19958
+ type: "vector",
19959
+ description: `Semantic retrieval over embedded ${skillType} chunks`,
19960
+ status: "active",
19961
+ capabilities: ["semantic", "scoped", "path-opaque-sources"],
19962
+ datasourceId: skillName,
19963
+ tags: [...tags]
19964
+ };
19965
+ }
19966
+ async retrieve(query, options) {
19967
+ const trimmed = query.trim();
19968
+ if (trimmed.length === 0)
19969
+ return [];
19970
+ const topK = options.topK ?? DEFAULT_TOP_K3;
19971
+ let queryVector;
19972
+ try {
19973
+ queryVector = (await this.options.embedder.embed([trimmed]))[0];
19974
+ } catch {
19975
+ return [];
19976
+ }
19977
+ if (queryVector === undefined)
19978
+ return [];
19979
+ const hits = this.options.index.searchByVector(queryVector, Math.max(topK * 3, topK));
19980
+ if (hits.length === 0)
19981
+ return [];
19982
+ const byChunkId = new Map(this.options.store.chunks().map((chunk) => [chunk.chunkId, chunk]));
19983
+ const { skillName, instanceId } = this.options;
19984
+ const mapped = [];
19985
+ for (const { chunkId, score } of hits) {
19986
+ const chunk = byChunkId.get(chunkId);
19987
+ if (chunk === undefined)
19988
+ continue;
19989
+ const source = datasourceSourcePath(skillName, instanceId, chunk.chunkId);
19990
+ if (!this.matchesScope(source, options.scope, options.allowedScopes))
19991
+ continue;
19992
+ mapped.push({
19993
+ id: `${skillName}:${instanceId}:${chunk.chunkId}`,
19994
+ content: chunk.content,
19995
+ source,
19996
+ score,
19997
+ metadata: {
19998
+ ...chunk.metadata,
19999
+ method: `${skillName}-semantic`,
20000
+ datasourceId: skillName,
20001
+ instanceId,
20002
+ chunkId: chunk.chunkId,
20003
+ ...chunk.title !== undefined ? { title: chunk.title } : {},
20004
+ ...chunk.hierarchy.length > 0 ? { hierarchy: chunk.hierarchy.join("/") } : {},
20005
+ ...chunk.publishedAt !== undefined ? { publishedAt: chunk.publishedAt } : {}
20006
+ }
20007
+ });
20008
+ if (mapped.length >= topK)
20009
+ break;
20010
+ }
20011
+ return mapped;
20012
+ }
20013
+ matchesScope(source, scope, allowedScopes) {
20014
+ if (!matchesDatasourceScope(source, scope))
20015
+ return false;
20016
+ if (allowedScopes === undefined || allowedScopes.length === 0)
20017
+ return true;
20018
+ return allowedScopes.some((entry) => matchesDatasourceScope(source, entry));
20019
+ }
20020
+ }
20021
+ function createGatewayGistEmbedder(options = {}) {
20022
+ let ensured;
20023
+ async function ensure() {
20024
+ if (ensured !== undefined)
20025
+ return ensured;
20026
+ let runtime = options.runtime;
20027
+ if (runtime === undefined) {
20028
+ const module = await Promise.resolve().then(() => (init_embedding_runtime(), {}));
20029
+ runtime = { ensureRuntime };
20030
+ }
20031
+ const result = await runtime.ensureRuntime({ profileId: options.profileId, cachedOnly: true });
20032
+ ensured = {
20033
+ baseUrl: result.baseUrl,
20034
+ identity: {
20035
+ provider: result.identity.provider,
20036
+ model: result.identity.model,
20037
+ dimension: result.identity.dimension
20038
+ }
20039
+ };
20040
+ return ensured;
20041
+ }
20042
+ return {
20043
+ identity: async () => (await ensure()).identity,
20044
+ async embed(texts) {
20045
+ if (texts.length === 0)
20046
+ return [];
20047
+ const { baseUrl, identity } = await ensure();
20048
+ const fetchImpl = options.fetchImpl ?? fetch;
20049
+ const embeddings = [];
20050
+ for (let offset = 0;offset < texts.length; offset += GIST_EMBED_BATCH_SIZE) {
20051
+ const batch = texts.slice(offset, offset + GIST_EMBED_BATCH_SIZE);
20052
+ const response = await fetchImpl(`${baseUrl}/v1/embeddings`, {
20053
+ method: "POST",
20054
+ headers: { "Content-Type": "application/json" },
20055
+ body: JSON.stringify({ model: identity.model, input: [...batch] })
20056
+ });
20057
+ if (!response.ok)
20058
+ throw new Error(`gateway /v1/embeddings returned HTTP ${response.status}`);
20059
+ const json = await response.json();
20060
+ const rows = [...json.data ?? []].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
20061
+ const batchEmbeddings = rows.map((row) => row.embedding ?? []);
20062
+ if (batchEmbeddings.length !== batch.length || batchEmbeddings.some((row) => row.length !== identity.dimension)) {
20063
+ throw new Error("gateway /v1/embeddings returned an invalid embedding batch");
20064
+ }
20065
+ embeddings.push(...batchEmbeddings);
20066
+ }
20067
+ return embeddings;
20068
+ }
20069
+ };
20070
+ }
20071
+ var VECTORS_VERSION = 1, DEFAULT_TOP_K3 = 20, GIST_EMBED_BATCH_SIZE = 32;
20072
+ var init_semantic = __esm(() => {
20073
+ init_scope2();
20074
+ });
20075
+
20076
+ // src/datasource/skills/github-gist/skill.ts
20077
+ import { join as join25 } from "node:path";
20078
+ function gistDatasourceDir(workspaceRoot, skillName, instanceId) {
20079
+ return join25(workspaceRoot, ".autorag", "datasources", sanitizeIdSegment(skillName), sanitizeIdSegment(instanceId));
20080
+ }
20081
+ var GITHUB_GIST_SKILL_DEFINITION, DEFAULT_INSTANCE_ID3 = "default", GitHubGistSkill;
20082
+ var init_skill5 = __esm(() => {
20083
+ init_connector();
20084
+ init_connector_skill();
20085
+ init_connector3();
20086
+ init_semantic();
20087
+ GITHUB_GIST_SKILL_DEFINITION = {
20088
+ skillName: "github-gist",
20089
+ skillType: "github-gist",
20090
+ description: "GitHub Gist datasource (authenticated account)",
20091
+ capabilities: ["gists", "api", "polling", "incremental", "lexical", "semantic"],
20092
+ defaultTags: ["github", "gists"],
20093
+ contentType: "gist",
20094
+ manifestDescription: "Search the authenticated GitHub account's indexed gists — code snippets, notes, and design memos. Use for questions about content the user saved as a gist.",
20095
+ manifestNotes: [
20096
+ "Gist visibility is bounded by the configured token scopes; secret gists are included when the token allows.",
20097
+ "Semantic retrieval embeds chunks through the local loopback embedding gateway; gist content never leaves the machine for embeddings."
20098
+ ]
20099
+ };
20100
+ GitHubGistSkill = class GitHubGistSkill extends ConnectorDatasourceSkill {
20101
+ semanticIndex;
20102
+ semanticEmbedder;
20103
+ semanticMethod;
20104
+ constructor(options = {}) {
20105
+ const { connector, connectorOptions, semantic, ...rest } = options;
20106
+ const instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID3;
20107
+ const statePath = connectorOptions?.statePath ?? (options.workspaceRoot === undefined ? undefined : join25(gistDatasourceDir(options.workspaceRoot, GITHUB_GIST_SKILL_DEFINITION.skillName, instanceId), "state.json"));
20108
+ super(GITHUB_GIST_SKILL_DEFINITION, {
20109
+ ...rest,
20110
+ connector: connector ?? new GitHubGistConnector({
20111
+ ...connectorOptions ?? {},
20112
+ ...statePath !== undefined && connectorOptions?.statePath === undefined ? { statePath } : {}
20113
+ })
20114
+ });
20115
+ if (semantic?.enabled !== false) {
20116
+ this.semanticIndex = new GistSemanticIndex({
20117
+ statePath: semantic?.statePath ?? (options.workspaceRoot === undefined ? undefined : join25(gistDatasourceDir(options.workspaceRoot, GITHUB_GIST_SKILL_DEFINITION.skillName, instanceId), "vectors.json"))
20118
+ });
20119
+ this.semanticEmbedder = semantic?.embedder ?? createGatewayGistEmbedder(semantic?.embedderOptions ?? {});
20120
+ this.semanticMethod = new GitHubGistSemanticMethod({
20121
+ skillName: this.describe().name,
20122
+ skillType: GITHUB_GIST_SKILL_DEFINITION.skillType,
20123
+ instanceId: this.instanceId,
20124
+ tags: this.describe().tags,
20125
+ store: this.store,
20126
+ index: this.semanticIndex,
20127
+ embedder: this.semanticEmbedder
20128
+ });
20129
+ }
20130
+ }
20131
+ async index() {
20132
+ const result = await super.index();
20133
+ if (!result.ok || this.semanticIndex === undefined || this.semanticEmbedder === undefined)
20134
+ return result;
20135
+ const sync = await this.semanticIndex.sync(this.store.chunks(), this.semanticEmbedder);
20136
+ if (sync.ok)
20137
+ return result;
20138
+ return {
20139
+ ...result,
20140
+ diagnostics: [
20141
+ ...result.diagnostics,
20142
+ {
20143
+ code: "datasource-index-failed",
20144
+ severity: "warning",
20145
+ message: boundDiagnosticText2(`semantic-unavailable: ${sync.message}`),
20146
+ instanceId: this.instanceId,
20147
+ source: this.describe().name
20148
+ }
20149
+ ]
20150
+ };
20151
+ }
20152
+ retrievalMethods() {
20153
+ const methods = super.retrievalMethods();
20154
+ return this.semanticMethod === undefined ? methods : [...methods, this.semanticMethod];
20155
+ }
20156
+ };
20157
+ });
20158
+
20159
+ // src/datasource/skills/github-gist/index.ts
20160
+ var init_github_gist = __esm(() => {
20161
+ init_connector3();
20162
+ init_semantic();
20163
+ init_skill5();
20164
+ });
20165
+
20166
+ // src/datasource/crawler-client.ts
20167
+ import { spawn as spawn11 } from "node:child_process";
20168
+
20169
+ class CrawlerCliClient {
20170
+ profile;
20171
+ options;
20172
+ constructor(profile, options = {}) {
20173
+ this.profile = profile;
20174
+ this.options = options;
20175
+ }
20176
+ async sync(signal) {
20177
+ const result = await this.run(this.profile.syncArgs(this.options), signal);
20178
+ if (!result.ok)
20179
+ return toFailure2(result);
20180
+ const count = this.profile.parseSyncCount(result.stdout);
20181
+ if (count === undefined)
20182
+ return toFailure2(result, "invalid-output");
20183
+ return { ok: true, count, stdout: result.stdout, stderr: result.stderr, code: result.code ?? 0 };
20184
+ }
20185
+ async search(query, options = {}) {
20186
+ const topK = options.topK ?? 20;
20187
+ const result = await this.run(this.profile.searchArgs(this.options, query, topK), options.signal);
20188
+ if (!result.ok)
20189
+ return toFailure2(result);
20190
+ const hits = this.profile.parseHits(result.stdout);
20191
+ if (hits === undefined)
20192
+ return toFailure2(result, "invalid-output");
20193
+ return { ok: true, hits, stdout: result.stdout, stderr: result.stderr, code: result.code ?? 0 };
20194
+ }
20195
+ async run(args, signal) {
20196
+ const env = controlledEnv3(this.profile.allowedEnvPrefixes, this.options.env);
20197
+ env.CRAWLKIT_NO_UPDATE_CHECK = "1";
20198
+ return spawnCrawler(this.options.binaryPath ?? this.profile.binaryName, args, env, signal, this.options, this.options.workspacePath);
20199
+ }
20200
+ }
20201
+ function spawnCrawler(command, args, env, signal, options, cwd) {
20202
+ return new Promise((resolve) => {
20203
+ const portable = portableSpawnCommand(command, args);
20204
+ const child = spawn11(portable.command, [...portable.args], {
20205
+ env,
20206
+ ...cwd === undefined ? {} : { cwd },
20207
+ stdio: ["ignore", "pipe", "pipe"]
20208
+ });
20209
+ let stdout = { text: "", bytes: 0, capped: false };
20210
+ let stderr = { text: "", bytes: 0, capped: false };
20211
+ let settled = false;
20212
+ let finalReason;
20213
+ const maxBuffer = options.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES3;
20214
+ const timeout = setTimeout(() => {
20215
+ finalReason = "timeout";
20216
+ terminate3(child);
20217
+ }, options.timeoutMs ?? DEFAULT_TIMEOUT_MS8);
20218
+ const abortHandler = () => {
20219
+ finalReason = "aborted";
20220
+ terminate3(child);
20221
+ };
20222
+ if (signal?.aborted)
20223
+ abortHandler();
20224
+ signal?.addEventListener("abort", abortHandler, { once: true });
20225
+ child.stdout.setEncoding("utf8");
20226
+ child.stderr.setEncoding("utf8");
20227
+ child.stdout.on("data", (chunk) => {
20228
+ stdout = appendBounded3(stdout, chunk, maxBuffer);
20229
+ if (stdout.capped) {
20230
+ finalReason = "stdout-too-large";
20231
+ terminate3(child);
20232
+ }
20233
+ });
20234
+ child.stderr.on("data", (chunk) => {
20235
+ stderr = appendBounded3(stderr, chunk, maxBuffer);
20236
+ if (stderr.capped) {
20237
+ finalReason = "stderr-too-large";
20238
+ terminate3(child);
20239
+ }
20240
+ });
20241
+ child.on("error", (error) => {
20242
+ if (settled)
20243
+ return;
20244
+ settled = true;
20245
+ clearTimeout(timeout);
20246
+ signal?.removeEventListener("abort", abortHandler);
20247
+ resolve({
20248
+ ok: false,
20249
+ reason: error.code === "ENOENT" ? "binary-missing" : "spawn-error",
20250
+ stdout: stdout.text,
20251
+ stderr: "",
20252
+ code: null
20253
+ });
20254
+ });
20255
+ child.on("close", (code) => {
20256
+ if (settled)
20257
+ return;
20258
+ settled = true;
20259
+ clearTimeout(timeout);
20260
+ signal?.removeEventListener("abort", abortHandler);
20261
+ resolve({
20262
+ ok: code === 0 && finalReason === undefined,
20263
+ ...finalReason !== undefined ? { reason: finalReason } : code === 0 ? {} : { reason: "nonzero-exit" },
20264
+ stdout: stdout.text,
20265
+ stderr: stderr.text,
20266
+ code
20267
+ });
20268
+ });
20269
+ });
20270
+ }
20271
+ function controlledEnv3(allowedPrefixes, configured) {
20272
+ const env = {};
20273
+ for (const [key, value] of Object.entries(process.env)) {
20274
+ if (value !== undefined && isAllowedEnvKey(key, allowedPrefixes))
20275
+ env[key] = value;
20276
+ }
20277
+ for (const [key, value] of Object.entries(configured ?? {})) {
20278
+ if (value === undefined)
20279
+ delete env[key];
20280
+ else if (isAllowedEnvKey(key, allowedPrefixes))
20281
+ env[key] = value;
20282
+ }
20283
+ return env;
20284
+ }
20285
+ function isAllowedEnvKey(key, allowedPrefixes) {
20286
+ return SAFE_ENV_KEYS.has(key) || process.platform === "win32" && [...SAFE_ENV_KEYS].some((safeKey) => safeKey.toUpperCase() === key.toUpperCase()) || key.startsWith("CRAWLKIT_") || allowedPrefixes.some((prefix) => key.startsWith(prefix));
20287
+ }
20288
+ function appendBounded3(state, chunk, maxBytes) {
20289
+ const nextBytes = state.bytes + Buffer.byteLength(chunk);
20290
+ if (nextBytes <= maxBytes)
20291
+ return { text: state.text + chunk, bytes: nextBytes, capped: false };
20292
+ const remaining = Math.max(maxBytes - state.bytes, 0);
20293
+ return { text: state.text + chunk.slice(0, remaining), bytes: maxBytes, capped: true };
20294
+ }
20295
+ function terminate3(child) {
20296
+ if (!child.killed)
20297
+ child.kill("SIGTERM");
20298
+ }
20299
+ function toFailure2(result, reason) {
20300
+ return {
20301
+ ok: false,
20302
+ reason: reason ?? result.reason ?? "nonzero-exit",
20303
+ stdout: result.stdout,
20304
+ stderr: boundDiagnosticText4(result),
20305
+ code: result.code
20306
+ };
20307
+ }
20308
+ function boundDiagnosticText4(result) {
20309
+ if (result.stderr.length > 0)
20310
+ return result.stderr.trim().slice(0, 4000);
20311
+ if (result.stdout.length > 0)
20312
+ return result.stdout.trim().slice(0, 4000);
20313
+ return "";
20314
+ }
20315
+ var DEFAULT_TIMEOUT_MS8 = 60000, DEFAULT_MAX_BUFFER_BYTES3 = 1048576, SAFE_ENV_KEYS;
20316
+ var init_crawler_client = __esm(() => {
20317
+ init_portable_spawn();
20318
+ SAFE_ENV_KEYS = new Set([
20319
+ "HOME",
20320
+ "LANG",
20321
+ "LC_ALL",
20322
+ "PATH",
20323
+ "TMPDIR",
20324
+ "TMP",
20325
+ "TEMP",
20326
+ "NO_COLOR",
20327
+ "APPDATA",
20328
+ "ComSpec",
20329
+ "LOCALAPPDATA",
20330
+ "PATHEXT",
20331
+ "SystemDrive",
20332
+ "SystemRoot",
20333
+ "USERPROFILE",
20334
+ "WINDIR"
20335
+ ]);
20336
+ });
20337
+
20338
+ // src/datasource/skills/lark/client.ts
20339
+ class LarkClient {
20340
+ version;
20341
+ auth;
20342
+ messages;
20343
+ docs;
20344
+ sleep;
20345
+ constructor(options = {}) {
20346
+ const cli = {
20347
+ binaryPath: options.binaryPath ?? "lark-cli",
20348
+ ...options.env !== undefined ? { env: options.env } : {},
20349
+ ...options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}
20350
+ };
20351
+ const chatIds = (options.chatIds ?? []).filter(isSafeId);
20352
+ this.version = new CrawlerCliClient(VERSION_PROFILE, cli);
20353
+ this.auth = new CrawlerCliClient(AUTH_PROFILE, cli);
20354
+ this.messages = new CrawlerCliClient(messageProfile(chatIds), cli);
20355
+ this.docs = new CrawlerCliClient(DOC_PROFILE, cli);
20356
+ this.sleep = options.sleep ?? delay2;
20357
+ }
20358
+ probeVersion() {
20359
+ return this.version.sync();
20360
+ }
20361
+ probeAuth() {
20362
+ return this.auth.sync();
20363
+ }
20364
+ searchMessages(query, topK) {
20365
+ return this.retry(() => this.messages.search(query, topK === undefined ? {} : { topK }));
20366
+ }
20367
+ searchDocs(query, topK) {
20368
+ return this.retry(() => this.docs.search(query, topK === undefined ? {} : { topK }));
20369
+ }
20370
+ async retry(run) {
20371
+ let failure;
20372
+ for (let attempt = 0;attempt < MAX_ATTEMPTS; attempt += 1) {
20373
+ const result = await run();
20374
+ if (result.ok)
20375
+ return result;
20376
+ failure = result;
20377
+ if (!isRateLimited(result) || attempt === MAX_ATTEMPTS - 1)
20378
+ return result;
20379
+ await this.sleep(200 * 2 ** attempt);
20380
+ }
20381
+ return failure ?? { ok: false, reason: "spawn-error", stdout: "", stderr: "", code: null };
20382
+ }
20383
+ }
20384
+ function messageProfile(chatIds) {
20385
+ return {
20386
+ binaryName: "lark-cli",
20387
+ allowedEnvPrefixes: [],
20388
+ syncArgs: () => [],
20389
+ searchArgs: (_options, query, topK) => {
20390
+ const args = [
20391
+ "im",
20392
+ "+messages-search",
20393
+ "--query",
20394
+ query,
20395
+ "--format",
20396
+ "json",
20397
+ "--page-size",
20398
+ String(clampPageSize(topK, MESSAGE_PAGE_MAX))
20399
+ ];
20400
+ if (chatIds.length > 0)
20401
+ args.push("--chat-id", chatIds.join(","));
20402
+ return args;
20403
+ },
20404
+ parseSyncCount: () => 0,
20405
+ parseHits: parseMessages
20406
+ };
20407
+ }
20408
+ function parseMessages(stdout) {
20409
+ const data = envelopeData(stdout);
20410
+ if (data === undefined)
20411
+ return;
20412
+ const rows = data.messages;
20413
+ if (!Array.isArray(rows))
20414
+ return;
20415
+ const hits = [];
20416
+ for (const row of rows) {
20417
+ if (!isRecord12(row))
20418
+ return;
20419
+ const id = scalarString(row.message_id);
20420
+ const content = scalarString(row.content);
20421
+ if (id === undefined || content === undefined || !isSafeId(id))
20422
+ continue;
20423
+ const chatId = scalarString(row.chat_id);
20424
+ const observedAt = scalarString(row.create_time);
20425
+ hits.push({
20426
+ id,
20427
+ content,
20428
+ score: 1 / (hits.length + 1),
20429
+ ...scalarString(row.chat_name) !== undefined ? { title: scalarString(row.chat_name) } : {},
20430
+ metadata: {
20431
+ ...chatId !== undefined ? { chatId } : {},
20432
+ ...scalarString(row.chat_type) !== undefined ? { chatType: scalarString(row.chat_type) } : {},
20433
+ ...scalarString(row.chat_name) !== undefined ? { chatName: scalarString(row.chat_name) } : {},
20434
+ ...observedAt !== undefined ? { observedAt } : {},
20435
+ coverage: "server-side-unverified"
20436
+ }
20437
+ });
20438
+ }
20439
+ return hits;
20440
+ }
20441
+ function parseDocs(stdout) {
20442
+ const data = envelopeData(stdout);
20443
+ if (data === undefined)
20444
+ return;
20445
+ const rows = data.results;
20446
+ if (!Array.isArray(rows))
20447
+ return;
20448
+ const hits = [];
20449
+ for (const row of rows) {
20450
+ if (!isRecord12(row))
20451
+ return;
20452
+ const meta = isRecord12(row.result_meta) ? row.result_meta : {};
20453
+ const token = firstString(meta, ["token", "docs_token"]) ?? firstString(row, ["token", "docs_token"]) ?? tokenFromUrl(firstString(meta, ["url"]));
20454
+ const content = stripHighlight(firstString(row, ["summary_highlighted", "title_highlighted", "summary", "title"]) ?? "");
20455
+ if (token === undefined || !isSafeId(token) || content.length === 0)
20456
+ continue;
20457
+ const permalink = firstString(meta, ["url"]);
20458
+ const observedAt = firstString(meta, ["update_time_iso", "update_time"]) ?? scalarString(meta.update_time);
20459
+ hits.push({
20460
+ id: token,
20461
+ content,
20462
+ score: 1 / (hits.length + 1),
20463
+ ...firstString(row, ["title", "title_highlighted"]) !== undefined ? { title: stripHighlight(firstString(row, ["title", "title_highlighted"]) ?? "") } : {},
20464
+ metadata: {
20465
+ ...permalink !== undefined ? { permalink } : {},
20466
+ ...observedAt !== undefined ? { observedAt } : {},
20467
+ coverage: "server-side-unverified"
20468
+ }
20469
+ });
20470
+ }
20471
+ return hits;
20472
+ }
20473
+ function envelopeData(stdout) {
20474
+ const trimmed = stdout.trim();
20475
+ if (trimmed.length === 0)
20476
+ return;
20477
+ let parsed;
20478
+ try {
20479
+ parsed = JSON.parse(trimmed);
20480
+ } catch {
20481
+ return;
20482
+ }
20483
+ if (!isRecord12(parsed) || parsed.ok !== true || !isRecord12(parsed.data))
20484
+ return;
20485
+ return parsed.data;
20486
+ }
20487
+ function firstString(record, keys) {
20488
+ for (const key of keys) {
20489
+ const value = scalarString(record[key]);
20490
+ if (value !== undefined)
20491
+ return value;
20492
+ }
20493
+ return;
20494
+ }
20495
+ function scalarString(value) {
20496
+ if (typeof value === "string" && value.length > 0)
20497
+ return value;
20498
+ if (typeof value === "number" && Number.isFinite(value))
20499
+ return String(value);
20500
+ return;
20501
+ }
20502
+ function tokenFromUrl(url) {
20503
+ if (url === undefined)
20504
+ return;
20505
+ const path = url.split("?")[0]?.split("#")[0] ?? "";
20506
+ const parts = path.split("/").filter((part) => part.length > 0);
20507
+ const segment = parts[parts.length - 1];
20508
+ return segment !== undefined && isSafeId(segment) ? segment : undefined;
20509
+ }
20510
+ function stripHighlight(value) {
20511
+ return value.replaceAll("<h>", "").replaceAll("</h>", "").replaceAll("<hb>", "").replaceAll("</hb>", "").trim();
20512
+ }
20513
+ function isSafeId(value) {
20514
+ return value.length > 0 && value !== "." && value !== ".." && !value.includes("#") && !value.includes("/");
20515
+ }
20516
+ function isRecord12(value) {
20517
+ return typeof value === "object" && value !== null && !Array.isArray(value);
20518
+ }
20519
+ function isRateLimited(failure) {
20520
+ return failure.stderr.includes(RATE_LIMIT_CODE) || failure.stdout.includes(RATE_LIMIT_CODE);
20521
+ }
20522
+ function clampPageSize(topK, max) {
20523
+ if (!Number.isFinite(topK))
20524
+ return Math.min(20, max);
20525
+ const truncated = Math.trunc(topK);
20526
+ if (truncated < 1)
20527
+ return 1;
20528
+ if (truncated > max)
20529
+ return max;
20530
+ return truncated;
20531
+ }
20532
+ function delay2(ms) {
20533
+ return new Promise((resolve) => {
20534
+ setTimeout(resolve, ms);
20535
+ });
20536
+ }
20537
+ var MESSAGE_PAGE_MAX = 50, DOC_PAGE_MAX = 20, RATE_LIMIT_CODE = "99991400", MAX_ATTEMPTS = 7, VERSION_PROFILE, AUTH_PROFILE, DOC_PROFILE;
20538
+ var init_client5 = __esm(() => {
20539
+ init_crawler_client();
20540
+ VERSION_PROFILE = {
20541
+ binaryName: "lark-cli",
20542
+ allowedEnvPrefixes: [],
20543
+ syncArgs: () => ["--version"],
20544
+ searchArgs: () => [],
20545
+ parseSyncCount: () => 0,
20546
+ parseHits: () => []
20547
+ };
20548
+ AUTH_PROFILE = {
20549
+ binaryName: "lark-cli",
20550
+ allowedEnvPrefixes: [],
20551
+ syncArgs: () => ["auth", "status", "--json"],
20552
+ searchArgs: () => [],
20553
+ parseSyncCount: () => 0,
20554
+ parseHits: () => []
20555
+ };
20556
+ DOC_PROFILE = {
20557
+ binaryName: "lark-cli",
20558
+ allowedEnvPrefixes: [],
20559
+ syncArgs: () => [],
20560
+ searchArgs: (_options, query, topK) => [
20561
+ "drive",
20562
+ "+search",
20563
+ "--query",
20564
+ query,
20565
+ "--format",
20566
+ "json",
20567
+ "--page-size",
20568
+ String(clampPageSize(topK, DOC_PAGE_MAX))
20569
+ ],
20570
+ parseSyncCount: () => 0,
20571
+ parseHits: parseDocs
20572
+ };
20573
+ });
20574
+
20575
+ // src/datasource/skills/lark/skill.ts
20576
+ class LarkSkill {
20577
+ client;
20578
+ datasourceId;
20579
+ instanceId;
20580
+ instances;
20581
+ tags;
20582
+ chatIds;
20583
+ embedderBaseUrl;
20584
+ lastIndexedAt;
20585
+ lastError;
20586
+ constructor(options = {}) {
20587
+ this.datasourceId = options.datasourceId ?? "lark";
20588
+ this.instanceId = isSafeId(options.instanceId ?? "default") ? options.instanceId ?? "default" : "default";
20589
+ this.instances = options.instances !== undefined && options.instances.length > 0 ? options.instances : [this.instanceId];
20590
+ this.tags = options.tags ?? DEFAULT_TAGS2;
20591
+ this.chatIds = options.chatIds ?? [];
20592
+ this.embedderBaseUrl = options.embedderBaseUrl;
20593
+ this.client = options.client ?? new LarkClient({
20594
+ ...options.binaryPath !== undefined ? { binaryPath: options.binaryPath } : {},
20595
+ ...options.env !== undefined ? { env: options.env } : {},
20596
+ ...options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {},
20597
+ ...options.chatIds !== undefined ? { chatIds: options.chatIds } : {},
20598
+ ...options.sleep !== undefined ? { sleep: options.sleep } : {}
20599
+ });
20600
+ }
20601
+ describe() {
20602
+ return {
20603
+ name: this.datasourceId,
20604
+ id: this.datasourceId,
20605
+ type: "lark-remote",
20606
+ description: "Lark/Feishu remote search via lark-cli. Credentials stay in the CLI keychain.",
20607
+ capabilities: ["external-cli", "remote-search", "scoped"],
20608
+ tags: this.tags,
20609
+ status: "active",
20610
+ requiresExternalCli: true,
20611
+ datasourceId: this.datasourceId,
20612
+ instanceId: this.instanceId,
20613
+ instances: this.instances
20614
+ };
20615
+ }
20616
+ polling() {
20617
+ return {
20618
+ mode: "none",
20619
+ ...this.lastIndexedAt !== undefined ? { lastIndexedAt: this.lastIndexedAt } : {},
20620
+ ...this.lastError !== undefined ? { lastError: this.lastError } : {}
20621
+ };
20622
+ }
20623
+ async index() {
20624
+ try {
20625
+ if (isRemoteEmbedder(this.embedderBaseUrl)) {
20626
+ return this.fail("datasource-embedding-egress-rejected", "remote embedding egress is not allowed for lark");
20627
+ }
20628
+ const diagnostics = [this.note(REMOTE_NOTE, "datasource-empty", "info")];
20629
+ const version = await this.client.probeVersion();
20630
+ if (!version.ok) {
20631
+ const message = probeMessage("--version", version);
20632
+ this.lastError = message;
20633
+ diagnostics.push(this.note(message, "datasource-unavailable", "warning"));
20634
+ return this.ok(diagnostics);
20635
+ }
20636
+ const auth = await this.client.probeAuth();
20637
+ if (!auth.ok) {
20638
+ const message = probeMessage("auth status", auth);
20639
+ this.lastError = message;
20640
+ diagnostics.push(this.note(message, "datasource-auth-error", "warning"));
20641
+ return this.ok(diagnostics);
20642
+ }
20643
+ this.lastError = undefined;
20644
+ this.lastIndexedAt = Date.now();
20645
+ return this.ok(diagnostics);
20646
+ } catch (error) {
20647
+ const message = error instanceof Error ? error.message : "lark index failed";
20648
+ return this.fail("datasource-unavailable", message);
20649
+ }
20650
+ }
20651
+ retrievalMethods() {
20652
+ return [new LarkRemoteMethod(this, "messages"), new LarkRemoteMethod(this, "docs")];
20653
+ }
20654
+ describeSources() {
20655
+ return this.instances.map((instanceId) => ({
20656
+ source: normalizeVirtualPath2(`/${this.datasourceId}/${instanceId}`),
20657
+ datasourceId: this.datasourceId,
20658
+ skill: this.datasourceId,
20659
+ instanceId,
20660
+ contentType: "lark",
20661
+ metadata: { datasourceId: this.datasourceId, instanceId, tags: this.tags }
20662
+ }));
20663
+ }
20664
+ skillManifest() {
20665
+ const scopes = this.instances.map((instanceId) => `- \`${normalizeVirtualPath2(`/${this.datasourceId}/${instanceId}`)}\``).join(`
20666
+ `);
20667
+ const chats = this.chatIds.length === 0 ? "Message search covers every chat the logged-in identity can already see." : `Message search is limited to configured chat ids: ${this.chatIds.join(", ")}.`;
20668
+ return {
20669
+ name: `datasource-${this.datasourceId}`,
20670
+ description: "Search the signed-in Lark or Feishu tenant for messages and cloud documents. The tenant keeps the index; AutoRAG does not copy it.",
20671
+ content: [
20672
+ "# Lark / Feishu (lark-cli remote search)",
20673
+ "",
20674
+ "Search live Lark/Feishu messages and cloud documents through the official `lark-cli`. AutoRAG does not open the desktop client's private databases, does not keep a local archive, and never sees app secrets or user tokens. Those stay in the CLI keychain.",
20675
+ "",
20676
+ "## When to use",
20677
+ "Questions about Lark/Feishu chats or docs (docx, wiki, drive, sheets, bitable) the signed-in user can already open.",
20678
+ "",
20679
+ "## How to search",
20680
+ `Call the dedicated \`${datasourceSearchToolName(this.datasourceId)}\` tool with a query and optional narrowing scope. Available authorized scopes:`,
20681
+ scopes,
20682
+ "",
20683
+ "`scope` can only narrow within already-authorized scopes; it can never widen access. A scope under `messages` searches chats. A scope under `docs` searches documents.",
20684
+ "",
20685
+ chats,
20686
+ "",
20687
+ "Server ranking is not BM25, vector, or hybrid. Coverage of old messages, retention, and chat types is whatever the tenant indexed for this token, and that coverage is not guaranteed. A hit is a live pointer: later edits or recalls change the text. Document search rejects a query longer than 30 Unicode characters.",
20688
+ "",
20689
+ "## Read a hit",
20690
+ "Search snippets are not the whole record. Read them with the CLI, not with `bash` on the `/lark/...` identity:",
20691
+ "- `lark-cli im +messages-mget --message-ids <message_id> --format json`",
20692
+ "- `lark-cli docs +fetch --doc <token> --doc-format markdown`",
20693
+ "",
20694
+ "## Native CLI",
20695
+ "The external `lark-cli` owns login and credentials. When the dedicated tool cannot express a filter, call it through `bash`:",
20696
+ '- `lark-cli im +messages-search --query "<query>" --format json`',
20697
+ '- `lark-cli drive +search --query "<query>" --format json`',
20698
+ "Login is `lark-cli auth login` with explicit scopes (`search:message` for chats, `search:docs:read` for documents), then `lark-cli auth status`. `auth login --recommend` does not grant those scopes by itself.",
20699
+ `Never pass datasource virtual paths (\`/${this.datasourceId}/...\`) to bash; they are not OS paths.`
20700
+ ].join(`
20701
+ `)
20702
+ };
20703
+ }
20704
+ async search(surface, query, options) {
20705
+ const trimmed = query.trim();
20706
+ if (trimmed.length === 0)
20707
+ return [];
20708
+ if (!surfaceSelected(options.scope, this.datasourceId, this.instanceId, surface))
20709
+ return [];
20710
+ const result = surface === "messages" ? await this.client.searchMessages(trimmed, options.topK) : await this.client.searchDocs(trimmed, options.topK);
20711
+ if (!result.ok)
20712
+ throw datasourceCliError(this.datasourceId, "search", result);
20713
+ const hits = [];
20714
+ for (const hit of result.hits) {
20715
+ if (!isSafeId(hit.id))
20716
+ continue;
20717
+ const source = normalizeVirtualPath2(`/${this.datasourceId}/${this.instanceId}/${surface}/${hit.id}`);
20718
+ if (source.includes("#"))
20719
+ continue;
20720
+ hits.push({
20721
+ id: hit.id,
20722
+ content: hit.content,
20723
+ source,
20724
+ score: hit.score,
20725
+ metadata: {
20726
+ ...hit.metadata ?? {},
20727
+ datasourceId: this.datasourceId,
20728
+ instanceId: this.instanceId,
20729
+ surface
20730
+ }
20731
+ });
20732
+ }
20733
+ return hits;
20734
+ }
20735
+ methodTags(surface) {
20736
+ if (this.tags === DEFAULT_TAGS2)
20737
+ return [surface === "messages" ? CHAT_TAG : DOCS_TAG];
20738
+ return this.tags;
20739
+ }
20740
+ ok(diagnostics) {
20741
+ return {
20742
+ ok: true,
20743
+ instanceId: this.instanceId,
20744
+ skill: this.datasourceId,
20745
+ chunkCount: 0,
20746
+ indexedAt: this.lastIndexedAt ?? Date.now(),
20747
+ diagnostics
20748
+ };
20749
+ }
20750
+ fail(code, message) {
20751
+ return {
20752
+ ok: false,
20753
+ instanceId: this.instanceId,
20754
+ skill: this.datasourceId,
20755
+ indexedAt: Date.now(),
20756
+ diagnostics: [this.note(message, code, "error")],
20757
+ error: message,
20758
+ code,
20759
+ message
20760
+ };
20761
+ }
20762
+ note(message, code, severity) {
20763
+ return { code, severity, message, instanceId: this.instanceId, source: this.datasourceId };
20764
+ }
20765
+ }
20766
+
20767
+ class LarkRemoteMethod {
20768
+ skill;
20769
+ surface;
20770
+ constructor(skill, surface) {
20771
+ this.skill = skill;
20772
+ this.surface = surface;
20773
+ }
20774
+ describe() {
20775
+ const descriptor = this.skill.describe();
20776
+ return {
20777
+ name: `${descriptor.datasourceId ?? "lark"}-${this.surface}`,
20778
+ type: "remote",
20779
+ description: this.surface === "messages" ? "Server-side Lark/Feishu message search via lark-cli" : "Server-side Lark/Feishu document search via lark-cli",
20780
+ status: "active",
20781
+ capabilities: ["remote-search", "scoped", "external-cli", "path-opaque-sources"],
20782
+ datasourceId: descriptor.datasourceId,
20783
+ tags: this.skill.methodTags(this.surface)
20784
+ };
20785
+ }
20786
+ retrieve(query, options) {
20787
+ return this.skill.search(this.surface, query, options);
20788
+ }
20789
+ }
20790
+ function surfaceSelected(scope, datasourceId, instanceId, surface) {
20791
+ if (scope === undefined || scope.trim().length === 0)
20792
+ return true;
20793
+ const root = normalizeVirtualPath2(`/${datasourceId}/${instanceId}/${surface}`);
20794
+ const normalized = normalizeVirtualPath2(scope);
20795
+ if (normalized === root || normalized.startsWith(`${root}/`))
20796
+ return true;
20797
+ return matchesDatasourceScope(root, normalized);
20798
+ }
20799
+ function probeMessage(command, failure) {
20800
+ const stderr = failure.stderr.trim();
20801
+ const exit = failure.code === null || failure.code === undefined ? "no exit code" : `exit code ${failure.code}`;
20802
+ return `lark-cli ${command} failed (${failure.reason}, ${exit})${stderr.length > 0 ? `: ${stderr}` : ""}`;
20803
+ }
20804
+ function isRemoteEmbedder(value) {
20805
+ if (value === undefined)
20806
+ return false;
20807
+ const trimmed = value.trim().toLowerCase();
20808
+ return trimmed.startsWith("https://") || trimmed.startsWith("http://");
20809
+ }
20810
+ var CHAT_TAG = "lark:chat", DOCS_TAG = "lark:docs", DEFAULT_TAGS2, REMOTE_NOTE = "remote-search datasource: no local mirror";
20811
+ var init_skill6 = __esm(() => {
20812
+ init_errors2();
20813
+ init_scope2();
20814
+ init_client5();
20815
+ DEFAULT_TAGS2 = [CHAT_TAG, DOCS_TAG];
20816
+ });
20817
+
20818
+ // src/datasource/skills/lark/index.ts
20819
+ var init_lark = __esm(() => {
20820
+ init_client5();
20821
+ init_skill6();
20822
+ });
20823
+
20824
+ // src/datasource/skills/lazykatok/types.ts
20825
+ var DEFAULT_LAZYKATOK_BINARY = "lazykatok", DEFAULT_LAZYKATOK_TIMEOUT_MS = 60000, DEFAULT_LAZYKATOK_SOURCE = "macos", DEFAULT_LAZYKATOK_MAX_BUFFER_BYTES;
20826
+ var init_types5 = __esm(() => {
20827
+ DEFAULT_LAZYKATOK_MAX_BUFFER_BYTES = 16 * 1048576;
20828
+ });
20829
+
20830
+ // src/datasource/skills/lazykatok/client.ts
20831
+ import { spawn as spawn12 } from "node:child_process";
20832
+ import { existsSync as existsSync22 } from "node:fs";
20833
+ import { delimiter as delimiter4, join as join26 } from "node:path";
20834
+
20835
+ class LazykatokClient {
20836
+ options;
20837
+ constructor(options = {}) {
20838
+ this.options = options;
20839
+ }
20840
+ async doctor(signal) {
20841
+ const result = await this.run(["doctor", "--json"], signal);
19711
20842
  if (!result.ok)
19712
- return toFailure2(result);
20843
+ return toFailure3(result);
20844
+ const parsed = parseJsonObject2(result.stdout);
20845
+ if (parsed === undefined)
20846
+ return toFailure3(result, "invalid-json");
20847
+ const data = normalizeDoctor2(parsed);
20848
+ return data === undefined ? toFailure3(result, "invalid-shape") : ok3(data, result);
20849
+ }
20850
+ async sync(signal) {
20851
+ const result = await this.run(syncArgs(this.options, process.platform), signal);
20852
+ if (!result.ok)
20853
+ return toFailure3(result);
20854
+ const parsed = parseJsonObject2(result.stdout);
20855
+ if (parsed === undefined)
20856
+ return toFailure3(result, "invalid-json");
20857
+ const data = normalizeSync2(parsed);
20858
+ return data === undefined ? toFailure3(result, "invalid-shape") : ok3(data, result);
20859
+ }
20860
+ async index(signal) {
20861
+ const result = await this.run(["index", "--json"], signal);
20862
+ if (!result.ok)
20863
+ return toFailure3(result);
19713
20864
  const parsed = parseJsonObject2(result.stdout);
19714
20865
  if (parsed === undefined)
19715
- return toFailure2(result, "invalid-json");
20866
+ return toFailure3(result, "invalid-json");
19716
20867
  const data = normalizeIndex(parsed);
19717
- return data === undefined ? toFailure2(result, "invalid-shape") : ok3(data, result);
20868
+ return data === undefined ? toFailure3(result, "invalid-shape") : ok3(data, result);
19718
20869
  }
19719
20870
  async search(mode, query, options) {
19720
20871
  const args = ["search", mode, query, "--json"];
@@ -19722,74 +20873,100 @@ class KatokClient {
19722
20873
  args.push("--limit", String(options.topK));
19723
20874
  const result = await this.run(args, options?.signal);
19724
20875
  if (!result.ok)
19725
- return toFailure2(result);
20876
+ return toFailure3(result);
19726
20877
  const parsed = parseJsonValue(result.stdout);
19727
20878
  if (parsed === undefined)
19728
- return toFailure2(result, "invalid-json");
20879
+ return toFailure3(result, "invalid-json");
19729
20880
  const hits = normalizeHits2(parsed);
19730
- return hits === undefined ? toFailure2(result, "invalid-shape") : searchOk2(hits, result);
20881
+ return hits === undefined ? toFailure3(result, "invalid-shape") : searchOk2(hits, result);
19731
20882
  }
19732
20883
  async chunkGet(chunkId, signal) {
19733
20884
  const result = await this.run(["chunk", "get", chunkId, "--json"], signal);
19734
20885
  if (!result.ok)
19735
- return toFailure2(result);
20886
+ return toFailure3(result);
19736
20887
  const parsed = parseJsonObject2(result.stdout);
19737
20888
  if (parsed === undefined)
19738
- return toFailure2(result, "invalid-json");
20889
+ return toFailure3(result, "invalid-json");
19739
20890
  const data = normalizeChunk(parsed);
19740
- return data === undefined ? toFailure2(result, "invalid-shape") : ok3(data, result);
20891
+ return data === undefined ? toFailure3(result, "invalid-shape") : ok3(data, result);
19741
20892
  }
19742
20893
  async context(chunkId, signal) {
19743
- const result = await this.run(["context", chunkId, "--json"], signal);
20894
+ const result = await this.run(["chunk", "context", chunkId, "--json"], signal);
19744
20895
  if (!result.ok)
19745
- return toFailure2(result);
20896
+ return toFailure3(result);
19746
20897
  const parsed = parseJsonObject2(result.stdout);
19747
20898
  if (parsed === undefined)
19748
- return toFailure2(result, "invalid-json");
20899
+ return toFailure3(result, "invalid-json");
19749
20900
  const data = normalizeContext(parsed);
19750
- return data === undefined ? toFailure2(result, "invalid-shape") : ok3(data, result);
20901
+ return data === undefined ? toFailure3(result, "invalid-shape") : ok3(data, result);
19751
20902
  }
19752
20903
  async parent(chunkId, signal) {
19753
- const result = await this.run(["parent", chunkId, "--json"], signal);
20904
+ const result = await this.run(["chunk", "parent", chunkId, "--json"], signal);
19754
20905
  if (!result.ok)
19755
- return toFailure2(result);
19756
- const parsed = parseJsonObject2(result.stdout);
20906
+ return toFailure3(result);
20907
+ const parsed = parseJsonValue(result.stdout);
19757
20908
  if (parsed === undefined)
19758
- return toFailure2(result, "invalid-json");
19759
- const data = normalizeChunk(parsed);
19760
- return data === undefined ? toFailure2(result, "invalid-shape") : ok3(data, result);
20909
+ return toFailure3(result, "invalid-json");
20910
+ const data = normalizeParentWindows(parsed);
20911
+ return data === undefined ? toFailure3(result, "invalid-shape") : ok3(data, result);
19761
20912
  }
19762
20913
  async run(args, signal) {
19763
- const env = controlledEnv3(this.options.env);
19764
- return spawnKatok({
20914
+ const env = controlledEnv4(this.options.env);
20915
+ return spawnLazykatok({
19765
20916
  options: this.options,
19766
- args: [...args, ...commonArgs2(this.options)],
20917
+ args: [...commonArgs2(this.options), ...args],
19767
20918
  env,
19768
20919
  signal
19769
20920
  });
19770
20921
  }
19771
20922
  }
19772
- function spawnKatok(request) {
20923
+ function spawnLazykatok(request) {
19773
20924
  return new Promise((resolve) => {
19774
20925
  const { options, args, env, signal } = request;
19775
- const portable = portableSpawnCommand(commandFor2(options.binaryPath), args);
19776
- const child = spawn10(portable.command, [...portable.args], {
20926
+ const portable = portableSpawnCommand(commandFor2(options, env), args);
20927
+ const child = spawn12(portable.command, [...portable.args], {
19777
20928
  env,
19778
20929
  ...request.cwd === undefined ? {} : { cwd: request.cwd },
20930
+ detached: process.platform !== "win32",
20931
+ windowsHide: true,
19779
20932
  stdio: ["ignore", "pipe", "pipe"]
19780
20933
  });
19781
20934
  let stdout = { text: "", bytes: 0, capped: false };
19782
20935
  let stderr = { text: "", bytes: 0, capped: false };
19783
20936
  let settled = false;
19784
20937
  let finalReason;
19785
- const maxBuffer = options.maxBufferBytes ?? DEFAULT_KATOK_MAX_BUFFER_BYTES;
20938
+ let pipeGuard;
20939
+ const maxBuffer = options.maxBufferBytes ?? DEFAULT_LAZYKATOK_MAX_BUFFER_BYTES;
20940
+ const settleWith = (result) => {
20941
+ if (settled)
20942
+ return;
20943
+ settled = true;
20944
+ clearTimeout(timeout);
20945
+ if (pipeGuard !== undefined)
20946
+ clearTimeout(pipeGuard);
20947
+ signal?.removeEventListener("abort", abortHandler);
20948
+ resolve(result);
20949
+ };
20950
+ const finish = (code) => {
20951
+ if (finalReason !== undefined) {
20952
+ settleWith({ ok: false, reason: finalReason, stdout: stdout.text, stderr: stderr.text, code });
20953
+ return;
20954
+ }
20955
+ settleWith({
20956
+ ok: code === 0,
20957
+ reason: code === 0 ? undefined : "nonzero-exit",
20958
+ stdout: stdout.text,
20959
+ stderr: stderr.text,
20960
+ code
20961
+ });
20962
+ };
19786
20963
  const timeout = setTimeout(() => {
19787
20964
  finalReason = "timeout";
19788
- terminate4(child);
19789
- }, options.timeoutMs ?? DEFAULT_KATOK_TIMEOUT_MS);
20965
+ terminateProcessTree(child, "SIGKILL");
20966
+ }, options.timeoutMs ?? DEFAULT_LAZYKATOK_TIMEOUT_MS);
19790
20967
  const abortHandler = () => {
19791
20968
  finalReason = "aborted";
19792
- terminate4(child);
20969
+ terminateProcessTree(child, "SIGKILL");
19793
20970
  };
19794
20971
  if (signal?.aborted)
19795
20972
  abortHandler();
@@ -19797,50 +20974,55 @@ function spawnKatok(request) {
19797
20974
  child.stdout.setEncoding("utf8");
19798
20975
  child.stderr.setEncoding("utf8");
19799
20976
  child.stdout.on("data", (chunk) => {
19800
- stdout = appendBounded3(stdout, chunk, maxBuffer);
20977
+ stdout = appendBounded4(stdout, chunk, maxBuffer);
19801
20978
  if (stdout.capped) {
19802
20979
  finalReason = "stdout-too-large";
19803
- terminate4(child);
20980
+ terminateProcessTree(child, "SIGKILL");
19804
20981
  }
19805
20982
  });
19806
20983
  child.stderr.on("data", (chunk) => {
19807
- stderr = appendBounded3(stderr, chunk, maxBuffer);
20984
+ stderr = appendBounded4(stderr, chunk, maxBuffer);
19808
20985
  if (stderr.capped) {
19809
20986
  finalReason = "stderr-too-large";
19810
- terminate4(child);
20987
+ terminateProcessTree(child, "SIGKILL");
19811
20988
  }
19812
20989
  });
19813
20990
  child.on("error", (error) => {
19814
- if (settled)
19815
- return;
19816
- settled = true;
19817
- clearTimeout(timeout);
19818
- signal?.removeEventListener("abort", abortHandler);
19819
20991
  const reason = error.code === "ENOENT" ? "binary-missing" : "spawn-error";
19820
- resolve({ ok: false, reason, stdout: stdout.text, stderr: describeSpawnFailure2(reason), code: null });
20992
+ settleWith({ ok: false, reason, stdout: stdout.text, stderr: describeSpawnFailure2(reason), code: null });
19821
20993
  });
19822
- child.on("close", (code) => {
20994
+ child.on("exit", () => {
19823
20995
  if (settled)
19824
20996
  return;
19825
- settled = true;
19826
- clearTimeout(timeout);
19827
- signal?.removeEventListener("abort", abortHandler);
19828
- if (finalReason !== undefined) {
19829
- resolve({ ok: false, reason: finalReason, stdout: stdout.text, stderr: stderr.text, code });
19830
- return;
19831
- }
19832
- resolve({
19833
- ok: code === 0,
19834
- reason: code === 0 ? undefined : "nonzero-exit",
19835
- stdout: stdout.text,
19836
- stderr: stderr.text,
19837
- code
19838
- });
20997
+ terminateProcessTree(child, "SIGKILL");
20998
+ pipeGuard = setTimeout(() => finish(child.exitCode), TREE_KILL_GRACE_MS);
20999
+ });
21000
+ child.on("close", (code) => {
21001
+ finish(code);
19839
21002
  });
19840
21003
  });
19841
21004
  }
19842
- function commandFor2(binaryPath) {
19843
- return binaryPath === undefined ? DEFAULT_KATOK_BINARY : binaryPath;
21005
+ function commandFor2(options, env) {
21006
+ if (options.binaryPath !== undefined)
21007
+ return options.binaryPath;
21008
+ return lookupInPath2(DEFAULT_LAZYKATOK_BINARY, env) ?? DEFAULT_LAZYKATOK_BINARY;
21009
+ }
21010
+ function lookupInPath2(executable, env) {
21011
+ const pathEnv = env.PATH;
21012
+ if (typeof pathEnv !== "string" || pathEnv.length === 0)
21013
+ return;
21014
+ for (const dir of pathEnv.split(delimiter4)) {
21015
+ if (dir.length === 0)
21016
+ continue;
21017
+ const candidate = join26(dir, executable);
21018
+ if (existsSync22(candidate))
21019
+ return candidate;
21020
+ }
21021
+ return;
21022
+ }
21023
+ function syncArgs(options, platform = process.platform) {
21024
+ const source = options.source ?? (platform === "darwin" ? DEFAULT_LAZYKATOK_SOURCE : undefined);
21025
+ return ["sync", ...source === undefined ? [] : ["--source", source], "--json"];
19844
21026
  }
19845
21027
  function commonArgs2(options) {
19846
21028
  const args = [];
@@ -19850,42 +21032,31 @@ function commonArgs2(options) {
19850
21032
  args.push("--config", options.configPath);
19851
21033
  return args;
19852
21034
  }
19853
- function controlledEnv3(configuredEnv) {
21035
+ function controlledEnv4(configuredEnv) {
19854
21036
  const env = {};
19855
21037
  for (const [key, value] of Object.entries(process.env)) {
19856
- if (value !== undefined && isAllowedKatokEnvKey(key))
21038
+ if (value !== undefined && isAllowedLazykatokEnvKey(key))
19857
21039
  env[key] = value;
19858
21040
  }
19859
21041
  for (const [key, value] of Object.entries(configuredEnv ?? {})) {
19860
21042
  if (value === undefined) {
19861
21043
  delete env[key];
19862
- } else if (isAllowedKatokEnvKey(key)) {
21044
+ } else if (isAllowedLazykatokEnvKey(key)) {
19863
21045
  env[key] = value;
19864
21046
  }
19865
21047
  }
19866
21048
  return env;
19867
21049
  }
19868
- function isAllowedKatokEnvKey(key) {
19869
- return SAFE_INHERITED_ENV_KEYS2.has(key) || key.startsWith(SAFE_KATOK_ENV_PREFIX);
21050
+ function isAllowedLazykatokEnvKey(key) {
21051
+ return SAFE_INHERITED_ENV_KEYS2.has(key) || SAFE_CLI_ENV_PREFIXES.some((prefix) => key.startsWith(prefix));
19870
21052
  }
19871
21053
  function searchOk2(hits, result) {
19872
21054
  return { ok: true, hits, data: { hits }, stdout: result.stdout, stderr: result.stderr, code: result.code ?? 0 };
19873
21055
  }
19874
- function terminate4(child) {
19875
- if (child.killed)
19876
- return;
19877
- if (process.platform !== "win32" && child.pid !== undefined) {
19878
- try {
19879
- process.kill(-child.pid, "SIGKILL");
19880
- return;
19881
- } catch {}
19882
- }
19883
- child.kill("SIGKILL");
19884
- }
19885
21056
  function describeSpawnFailure2(reason) {
19886
- return reason === "binary-missing" ? "the katok binary could not be found" : "the katok binary could not be started";
21057
+ return reason === "binary-missing" ? "the lazykatok binary could not be found" : "the lazykatok binary could not be started";
19887
21058
  }
19888
- function appendBounded3(state, chunk, maxBytes) {
21059
+ function appendBounded4(state, chunk, maxBytes) {
19889
21060
  const chunkBytes = Buffer.byteLength(chunk);
19890
21061
  const nextBytes = state.bytes + chunkBytes;
19891
21062
  if (nextBytes <= maxBytes)
@@ -19925,25 +21096,25 @@ function asBoolean(value) {
19925
21096
  }
19926
21097
  function normalizeDoctor2(raw) {
19927
21098
  const version = asString3(raw.version);
19928
- const ready = asBoolean(raw.ready);
21099
+ const ready = asBoolean(raw.ready) ?? (asRecord3(raw.freshness) === undefined ? undefined : true);
19929
21100
  if (ready === undefined)
19930
21101
  return;
19931
21102
  const metadata = stripKnown(raw, new Set(["version", "ready"]));
19932
21103
  return { ...version !== undefined ? { version } : {}, ready, metadata };
19933
21104
  }
19934
21105
  function normalizeSync2(raw) {
19935
- const synced = asBoolean(raw.synced);
19936
- if (synced === undefined)
21106
+ const synced = asBoolean(raw.synced) ?? (asNumber3(raw.total_messages) === undefined ? undefined : true);
21107
+ if (synced === undefined || raw.ok === false)
19937
21108
  return;
19938
- const messageCount = asNumber3(raw.messageCount);
21109
+ const messageCount = asNumber3(raw.messageCount) ?? asNumber3(raw.total_messages);
19939
21110
  const metadata = stripKnown(raw, new Set(["synced", "messageCount"]));
19940
21111
  return { synced, ...messageCount !== undefined ? { messageCount } : {}, metadata };
19941
21112
  }
19942
21113
  function normalizeIndex(raw) {
19943
- const chunkCount = asNumber3(raw.chunkCount);
21114
+ const chunkCount = asNumber3(raw.chunkCount) ?? asNumber3(raw.candidate_chunks);
19944
21115
  if (chunkCount === undefined)
19945
21116
  return;
19946
- const metadata = stripKnown(raw, new Set(["chunkCount"]));
21117
+ const metadata = stripKnown(raw, new Set(["chunkCount", "candidate_chunks", "documents"]));
19947
21118
  return { chunkCount, metadata };
19948
21119
  }
19949
21120
  function normalizeHits2(raw) {
@@ -19962,6 +21133,18 @@ function normalizeHits2(raw) {
19962
21133
  }
19963
21134
  return normalized;
19964
21135
  }
21136
+ function chatIdentityMetadata(record) {
21137
+ const chatName = asString3(record.chat_name);
21138
+ const senderNickname = asString3(record.sender_nickname);
21139
+ const startedAt = asString3(record.started_at);
21140
+ const endedAt = asString3(record.ended_at);
21141
+ return {
21142
+ ...chatName !== undefined ? { chatName } : {},
21143
+ ...senderNickname !== undefined ? { senderNickname } : {},
21144
+ ...startedAt !== undefined ? { startedAt } : {},
21145
+ ...endedAt !== undefined ? { endedAt } : {}
21146
+ };
21147
+ }
19965
21148
  function normalizeHit(record) {
19966
21149
  const chunkId = asString3(record.chunkId) ?? asString3(record.chunk_id);
19967
21150
  const score = asNumber3(record.score);
@@ -19969,47 +21152,55 @@ function normalizeHit(record) {
19969
21152
  if (chunkId === undefined || chunkId.length === 0 || score === undefined || content === undefined)
19970
21153
  return;
19971
21154
  const metadata = stripKnown(record, new Set(["chunkId", "chunk_id", "score", "content", "snippet"]));
19972
- const chatName = asString3(record.chat_name);
19973
- const senderNickname = asString3(record.sender_nickname);
19974
- const startedAt = asString3(record.started_at);
19975
- const endedAt = asString3(record.ended_at);
19976
- return {
19977
- chunkId,
19978
- score,
19979
- content,
19980
- metadata: {
19981
- ...metadata,
19982
- ...chatName !== undefined ? { chatName } : {},
19983
- ...senderNickname !== undefined ? { senderNickname } : {},
19984
- ...startedAt !== undefined ? { startedAt } : {},
19985
- ...endedAt !== undefined ? { endedAt } : {}
19986
- }
19987
- };
21155
+ return { chunkId, score, content, metadata: { ...metadata, ...chatIdentityMetadata(record) } };
19988
21156
  }
19989
21157
  function normalizeChunk(raw) {
19990
- const chunkId = asString3(raw.chunkId);
19991
- const content = asString3(raw.content);
21158
+ const chunkId = asString3(raw.chunkId) ?? asString3(raw.chunk_id) ?? asString3(raw.parent_id);
21159
+ const content = asString3(raw.content) ?? asString3(raw.text);
19992
21160
  if (chunkId === undefined || chunkId.length === 0 || content === undefined)
19993
21161
  return;
19994
- const metadata = stripKnown(raw, new Set(["chunkId", "content"]));
19995
- return { chunkId, content, metadata };
21162
+ const metadata = stripKnown(raw, new Set(["chunkId", "chunk_id", "parent_id", "content", "text"]));
21163
+ return { chunkId, content, metadata: { ...metadata, ...chatIdentityMetadata(raw) } };
19996
21164
  }
19997
21165
  function normalizeContext(raw) {
19998
- const chunksValue = raw.chunks;
19999
- if (!Array.isArray(chunksValue))
21166
+ const legacyChunks = raw.chunks;
21167
+ if (Array.isArray(legacyChunks)) {
21168
+ const chunks = normalizeChunkList(legacyChunks);
21169
+ return chunks === undefined ? undefined : { chunks, metadata: stripKnown(raw, new Set(["chunks"])) };
21170
+ }
21171
+ const target = asRecord3(raw.chunk);
21172
+ if (target === undefined)
21173
+ return;
21174
+ const chunk = normalizeChunk(target);
21175
+ if (chunk === undefined)
21176
+ return;
21177
+ const before = normalizeChunkList(raw.previous) ?? [];
21178
+ const after = normalizeChunkList(raw.next) ?? [];
21179
+ return {
21180
+ chunks: [...before, chunk, ...after],
21181
+ metadata: stripKnown(raw, new Set(["chunk"]))
21182
+ };
21183
+ }
21184
+ function normalizeChunkList(value) {
21185
+ if (!Array.isArray(value))
20000
21186
  return;
20001
21187
  const chunks = [];
20002
- for (const entry of chunksValue) {
21188
+ for (const entry of value) {
20003
21189
  const record = asRecord3(entry);
20004
21190
  if (record === undefined)
20005
- return;
21191
+ continue;
20006
21192
  const chunk = normalizeChunk(record);
20007
- if (chunk === undefined)
20008
- return;
20009
- chunks.push(chunk);
21193
+ if (chunk !== undefined)
21194
+ chunks.push(chunk);
20010
21195
  }
20011
- const metadata = stripKnown(raw, new Set(["chunks"]));
20012
- return { chunks, metadata };
21196
+ return chunks;
21197
+ }
21198
+ function normalizeParentWindows(raw) {
21199
+ const windows = normalizeChunkList(raw);
21200
+ if (windows !== undefined)
21201
+ return windows;
21202
+ const nested = asRecord3(raw)?.parent_windows;
21203
+ return normalizeChunkList(nested);
20013
21204
  }
20014
21205
  function stripKnown(raw, known) {
20015
21206
  const metadata = {};
@@ -20019,16 +21210,16 @@ function stripKnown(raw, known) {
20019
21210
  }
20020
21211
  return metadata;
20021
21212
  }
20022
- function toFailure2(result, reason) {
21213
+ function toFailure3(result, reason) {
20023
21214
  return {
20024
21215
  ok: false,
20025
21216
  reason: reason ?? result.reason ?? "nonzero-exit",
20026
21217
  stdout: result.stdout,
20027
- stderr: boundDiagnosticText4(result.stderr),
21218
+ stderr: boundDiagnosticText5(result.stderr),
20028
21219
  code: result.code
20029
21220
  };
20030
21221
  }
20031
- function boundDiagnosticText4(value) {
21222
+ function boundDiagnosticText5(value) {
20032
21223
  if (value.length === 0)
20033
21224
  return "";
20034
21225
  return value.trim().slice(0, 4000);
@@ -20036,22 +21227,24 @@ function boundDiagnosticText4(value) {
20036
21227
  function ok3(data, result) {
20037
21228
  return { ok: true, data, stdout: result.stdout, stderr: result.stderr, code: result.code ?? 0 };
20038
21229
  }
20039
- var SAFE_INHERITED_ENV_KEYS2, SAFE_KATOK_ENV_PREFIX = "KATOK_";
20040
- var init_client5 = __esm(() => {
21230
+ var SAFE_INHERITED_ENV_KEYS2, SAFE_CLI_ENV_PREFIXES;
21231
+ var init_client6 = __esm(() => {
20041
21232
  init_portable_spawn();
21233
+ init_terminate_tree();
20042
21234
  init_types5();
20043
21235
  SAFE_INHERITED_ENV_KEYS2 = new Set(["HOME", "LANG", "LC_ALL", "PATH", "TMPDIR", "TMP", "TEMP"]);
21236
+ SAFE_CLI_ENV_PREFIXES = ["KATOK_", "LAZYKATOK_"];
20044
21237
  });
20045
21238
 
20046
- // src/datasource/skills/katok/paths.ts
20047
- function katokSourcePath(instanceId, chunkId) {
20048
- return `/${KATOK_SOURCE_KIND}/${instanceId}/chunks/${chunkId}`;
21239
+ // src/datasource/skills/lazykatok/paths.ts
21240
+ function lazykatokSourcePath(instanceId, chunkId) {
21241
+ return `/${LAZYKATOK_SOURCE_KIND}/${instanceId}/chunks/${chunkId}`;
20049
21242
  }
20050
- var KATOK_SOURCE_KIND = "kakao";
21243
+ var LAZYKATOK_SOURCE_KIND = "kakao";
20051
21244
  var init_paths4 = () => {};
20052
21245
 
20053
- // src/datasource/skills/katok/methods.ts
20054
- class KatokBm25Method {
21246
+ // src/datasource/skills/lazykatok/methods.ts
21247
+ class LazykatokBm25Method {
20055
21248
  client;
20056
21249
  instanceId;
20057
21250
  tags;
@@ -20064,7 +21257,7 @@ class KatokBm25Method {
20064
21257
  return {
20065
21258
  name: "kakao-bm25",
20066
21259
  type: "bm25",
20067
- description: "BM25 lexical retrieval over KakaoTalk chat chunks via the external katok CLI",
21260
+ description: "BM25 lexical retrieval over KakaoTalk chat chunks via the external lazykatok CLI",
20068
21261
  status: "active",
20069
21262
  capabilities: ["lexical", "keyword-mode", "external-cli", "path-opaque-sources"],
20070
21263
  datasourceId: KAKAO_DATASOURCE_ID,
@@ -20072,11 +21265,11 @@ class KatokBm25Method {
20072
21265
  };
20073
21266
  }
20074
21267
  retrieve(query, options) {
20075
- return retrieveKatok(this.client, "keyword", "kakao-bm25", this.instanceId, query, options);
21268
+ return retrieveLazykatok(this.client, "keyword", "kakao-bm25", this.instanceId, query, options);
20076
21269
  }
20077
21270
  }
20078
21271
 
20079
- class KatokSemanticMethod {
21272
+ class LazykatokSemanticMethod {
20080
21273
  client;
20081
21274
  instanceId;
20082
21275
  tags;
@@ -20089,7 +21282,7 @@ class KatokSemanticMethod {
20089
21282
  return {
20090
21283
  name: "kakao-semantic",
20091
21284
  type: "vector",
20092
- description: "Semantic vector retrieval over KakaoTalk chat chunks via the external katok CLI",
21285
+ description: "Semantic vector retrieval over KakaoTalk chat chunks via the external lazykatok CLI",
20093
21286
  status: "active",
20094
21287
  capabilities: ["semantic", "vector-mode", "external-cli", "path-opaque-sources"],
20095
21288
  datasourceId: KAKAO_DATASOURCE_ID,
@@ -20097,29 +21290,29 @@ class KatokSemanticMethod {
20097
21290
  };
20098
21291
  }
20099
21292
  retrieve(query, options) {
20100
- return retrieveKatok(this.client, "semantic", "kakao-semantic", this.instanceId, query, options);
21293
+ return retrieveLazykatok(this.client, "semantic", "kakao-semantic", this.instanceId, query, options);
20101
21294
  }
20102
21295
  }
20103
- async function retrieveKatok(client, mode, methodName, instanceId, query, options) {
21296
+ async function retrieveLazykatok(client, mode, methodName, instanceId, query, options) {
20104
21297
  const trimmed = query.trim();
20105
21298
  if (trimmed.length === 0)
20106
21299
  return [];
20107
- const topK = options.topK ?? DEFAULT_TOP_K3;
21300
+ const topK = options.topK ?? DEFAULT_TOP_K4;
20108
21301
  const searchOptions = { topK, signal: options.signal };
20109
21302
  const result = await client.search(mode, trimmed, searchOptions);
20110
21303
  if (!result.ok)
20111
21304
  throw datasourceCliError(KAKAO_DATASOURCE_ID, `search --mode ${mode}`, result);
20112
21305
  const mapped = [];
20113
21306
  for (const hit of result.hits) {
20114
- const source = katokSource(instanceId, hit);
21307
+ const source = lazykatokSource(instanceId, hit);
20115
21308
  mapped.push(toRetrievalResult2(hit, source, methodName, instanceId, mode));
20116
21309
  if (mapped.length >= topK)
20117
21310
  break;
20118
21311
  }
20119
21312
  return mapped;
20120
21313
  }
20121
- function katokSource(instanceId, hit) {
20122
- return katokSourcePath(instanceId, hit.chunkId);
21314
+ function lazykatokSource(instanceId, hit) {
21315
+ return lazykatokSourcePath(instanceId, hit.chunkId);
20123
21316
  }
20124
21317
  function toRetrievalResult2(hit, source, methodName, instanceId, mode) {
20125
21318
  return {
@@ -20137,15 +21330,15 @@ function toRetrievalResult2(hit, source, methodName, instanceId, mode) {
20137
21330
  }
20138
21331
  };
20139
21332
  }
20140
- var KAKAO_DATASOURCE_ID = "kakao", DEFAULT_KAKAO_TAGS, DEFAULT_TOP_K3 = 20;
21333
+ var KAKAO_DATASOURCE_ID = "kakao", DEFAULT_KAKAO_TAGS, DEFAULT_TOP_K4 = 20;
20141
21334
  var init_methods3 = __esm(() => {
20142
21335
  init_errors2();
20143
21336
  init_paths4();
20144
21337
  DEFAULT_KAKAO_TAGS = ["kakaotalk", "personal", "pii"];
20145
21338
  });
20146
21339
 
20147
- // src/datasource/skills/katok/skill.ts
20148
- class KatokSkill {
21340
+ // src/datasource/skills/lazykatok/skill.ts
21341
+ class LazykatokSkill {
20149
21342
  client;
20150
21343
  instanceId;
20151
21344
  instances;
@@ -20154,7 +21347,7 @@ class KatokSkill {
20154
21347
  lastIndexedAt;
20155
21348
  constructor(options) {
20156
21349
  this.client = options.client;
20157
- this.instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID3;
21350
+ this.instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID4;
20158
21351
  this.instances = options.instances !== undefined && options.instances.length > 0 ? options.instances : [this.instanceId];
20159
21352
  this.pollingIntervalMs = options.pollingIntervalMs ?? DEFAULT_POLLING_INTERVAL_MS3;
20160
21353
  this.tags = options.tags ?? DEFAULT_KAKAO_TAGS2;
@@ -20165,7 +21358,7 @@ class KatokSkill {
20165
21358
  name: KAKAO_DATASOURCE_ID2,
20166
21359
  id: KAKAO_DATASOURCE_ID2,
20167
21360
  type: KAKAO_SKILL_TYPE,
20168
- description: "KakaoTalk datasource via the external katok CLI",
21361
+ description: "KakaoTalk datasource via the external lazykatok CLI",
20169
21362
  capabilities: ["chat", "external-cli", "polling", "bm25", "semantic"],
20170
21363
  tags: this.tags,
20171
21364
  status: "active",
@@ -20215,8 +21408,8 @@ class KatokSkill {
20215
21408
  }
20216
21409
  retrievalMethods() {
20217
21410
  return [
20218
- new KatokBm25Method({ client: this.client, instanceId: this.instanceId, tags: this.tags }),
20219
- new KatokSemanticMethod({ client: this.client, instanceId: this.instanceId, tags: this.tags })
21411
+ new LazykatokBm25Method({ client: this.client, instanceId: this.instanceId, tags: this.tags }),
21412
+ new LazykatokSemanticMethod({ client: this.client, instanceId: this.instanceId, tags: this.tags })
20220
21413
  ];
20221
21414
  }
20222
21415
  describeSources() {
@@ -20243,7 +21436,7 @@ class KatokSkill {
20243
21436
  content: [
20244
21437
  `# KakaoTalk datasource (${KAKAO_SKILL_TYPE})`,
20245
21438
  "",
20246
- "This skill searches KakaoTalk chats that are indexed through the external `katok` CLI. AutoRAG never reads KakaoTalk databases directly.",
21439
+ "This skill searches KakaoTalk chats that are indexed through the external `lazykatok` CLI. AutoRAG never reads KakaoTalk databases directly.",
20247
21440
  "",
20248
21441
  "## When to use",
20249
21442
  "Use this skill when the question is about KakaoTalk conversations, chat participants, or content shared inside chats.",
@@ -20252,16 +21445,16 @@ class KatokSkill {
20252
21445
  `Indexing is server-managed and refreshed ${cadence}. You do not trigger indexing; just search.`,
20253
21446
  "",
20254
21447
  "## How to search",
20255
- `Call the dedicated \`${datasourceSearchToolName(KAKAO_DATASOURCE_ID2)}\` tool with a natural-language \`query\` and \`topK\`. Do not use \`search_datasource_documents\` for this datasource — it fans out to every datasource CLI. This datasource does not support per-source scope narrowing. Authorized datasource:`,
21448
+ `Call the dedicated \`${datasourceSearchToolName(KAKAO_DATASOURCE_ID2)}\` tool with a natural-language \`query\` and \`topK\`. This datasource does not support per-source scope narrowing. Authorized datasource:`,
20256
21449
  instanceSources.length > 0 ? instanceSources : "- (no configured instances)",
20257
21450
  "",
20258
- "Access is controlled by the trusted datasource tag; chat/channel filtering is owned by katok.",
21451
+ "Access is controlled by the trusted datasource tag; chat/channel filtering is owned by lazykatok.",
20259
21452
  "",
20260
21453
  "## Native CLI",
20261
- "The external `katok` CLI owns the archive, index, and credentials. When the dedicated tool cannot express what you need, call katok directly through `bash`:",
20262
- '- `katok search bm25 "<query>" --json --limit 20` — lexical search (`semantic` mode also available)',
20263
- "- `katok chunk get <chunkId> --json` — fetch one chunk by id (chunk ids appear in result metadata)",
20264
- "- `katok chunk context <chunkId> --json` — surrounding messages of a chunk",
21454
+ "The external `lazykatok` CLI owns the archive, index, and credentials. When the dedicated tool cannot express what you need, call lazykatok directly through `bash`:",
21455
+ '- `lazykatok search bm25 "<query>" --json --limit 20` — lexical search (`semantic` mode also available)',
21456
+ "- `lazykatok chunk get <chunkId> --json` — fetch one chunk by id (chunk ids appear in result metadata)",
21457
+ "- `lazykatok chunk context <chunkId> --json` — surrounding messages of a chunk",
20265
21458
  "Never pass datasource virtual paths (`/kakao/...`) to bash; they are not OS paths.",
20266
21459
  "",
20267
21460
  "## Output rules",
@@ -20291,8 +21484,8 @@ class KatokSkill {
20291
21484
  };
20292
21485
  }
20293
21486
  }
20294
- var KAKAO_DATASOURCE_ID2 = "kakao", KAKAO_SKILL_TYPE = "kakaotalk", DEFAULT_INSTANCE_ID3 = "default", DEFAULT_POLLING_INTERVAL_MS3, DEFAULT_KAKAO_TAGS2;
20295
- var init_skill5 = __esm(() => {
21487
+ var KAKAO_DATASOURCE_ID2 = "kakao", KAKAO_SKILL_TYPE = "kakaotalk", DEFAULT_INSTANCE_ID4 = "default", DEFAULT_POLLING_INTERVAL_MS3, DEFAULT_KAKAO_TAGS2;
21488
+ var init_skill7 = __esm(() => {
20296
21489
  init_skip();
20297
21490
  init_scope2();
20298
21491
  init_methods3();
@@ -20300,17 +21493,17 @@ var init_skill5 = __esm(() => {
20300
21493
  DEFAULT_KAKAO_TAGS2 = ["kakaotalk", "personal", "pii"];
20301
21494
  });
20302
21495
 
20303
- // src/datasource/skills/katok/index.ts
20304
- var init_katok = __esm(() => {
20305
- init_client5();
21496
+ // src/datasource/skills/lazykatok/index.ts
21497
+ var init_lazykatok = __esm(() => {
21498
+ init_client6();
20306
21499
  init_methods3();
20307
- init_skill5();
21500
+ init_skill7();
20308
21501
  init_types5();
20309
21502
  });
20310
21503
 
20311
21504
  // src/datasource/skills/mail-export/connector.ts
20312
21505
  import { readdir as readdir2, readFile as readFile7, stat as stat5 } from "node:fs/promises";
20313
- import { basename as basename10, dirname as dirname16, extname as extname9, join as join25 } from "node:path";
21506
+ import { basename as basename10, dirname as dirname18, extname as extname9, join as join27 } from "node:path";
20314
21507
  import { simpleParser as simpleParser2 } from "mailparser";
20315
21508
 
20316
21509
  class MailExportConnector {
@@ -20336,7 +21529,7 @@ class MailExportConnector {
20336
21529
  continue;
20337
21530
  const extension = extname9(entry.name).toLowerCase();
20338
21531
  if (extension === ".eml" || extension === ".mbox") {
20339
- files.push(join25(entry.parentPath, entry.name));
21532
+ files.push(join27(entry.parentPath, entry.name));
20340
21533
  }
20341
21534
  }
20342
21535
  } else if (info.isFile()) {
@@ -20371,7 +21564,7 @@ class MailExportConnector {
20371
21564
  continue;
20372
21565
  }
20373
21566
  const stem = basename10(file, extname9(file));
20374
- const mailboxDir = basename10(dirname16(file));
21567
+ const mailboxDir = basename10(dirname18(file));
20375
21568
  const hierarchy = ["mailboxes", mailboxDir, stem];
20376
21569
  const extension = extname9(file).toLowerCase();
20377
21570
  const rawMessages = extension === ".mbox" ? splitMbox(bytes.toString("utf8")) : [bytes.toString("utf8")];
@@ -20441,16 +21634,16 @@ function splitMbox(text) {
20441
21634
  return messages.filter((message) => message.trim().length > 0);
20442
21635
  }
20443
21636
  var DEFAULT_MAX_DOCUMENTS3 = 500, DEFAULT_MAX_BYTES_PER_FILE2, MAX_BODY_CHARS = 50000, MBOX_SEPARATOR;
20444
- var init_connector3 = __esm(() => {
21637
+ var init_connector4 = __esm(() => {
20445
21638
  DEFAULT_MAX_BYTES_PER_FILE2 = 50 * 1024 * 1024;
20446
21639
  MBOX_SEPARATOR = /^From .*\d{4}$/mu;
20447
21640
  });
20448
21641
 
20449
21642
  // src/datasource/skills/mail-export/skill.ts
20450
21643
  var MAIL_EXPORT_SKILL_DEFINITION, MailExportSkill;
20451
- var init_skill6 = __esm(() => {
21644
+ var init_skill8 = __esm(() => {
20452
21645
  init_connector_skill();
20453
- init_connector3();
21646
+ init_connector4();
20454
21647
  MAIL_EXPORT_SKILL_DEFINITION = {
20455
21648
  skillName: "mail-export",
20456
21649
  skillType: "mail-export",
@@ -20476,26 +21669,26 @@ var init_skill6 = __esm(() => {
20476
21669
 
20477
21670
  // src/datasource/skills/mail-export/index.ts
20478
21671
  var init_mail_export = __esm(() => {
20479
- init_connector3();
20480
- init_skill6();
21672
+ init_connector4();
21673
+ init_skill8();
20481
21674
  });
20482
21675
 
20483
21676
  // src/datasource/skills/mailcrawl/config.ts
20484
- import { chmodSync, mkdirSync as mkdirSync15 } from "node:fs";
21677
+ import { chmodSync, mkdirSync as mkdirSync17 } from "node:fs";
20485
21678
  function validateMailcrawlInstanceId(instanceId) {
20486
21679
  if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(instanceId)) {
20487
21680
  throw new Error("mailcrawl instanceId must be a safe single path segment");
20488
21681
  }
20489
21682
  }
20490
21683
  function ensurePrivateMailcrawlDataDir(path) {
20491
- mkdirSync15(path, { recursive: true, mode: 448 });
21684
+ mkdirSync17(path, { recursive: true, mode: 448 });
20492
21685
  if (process.platform !== "win32")
20493
21686
  chmodSync(path, 448);
20494
21687
  }
20495
21688
  var init_config = () => {};
20496
21689
 
20497
21690
  // src/datasource/skills/mailcrawl/client.ts
20498
- import { spawn as spawn11 } from "node:child_process";
21691
+ import { spawn as spawn13 } from "node:child_process";
20499
21692
 
20500
21693
  class MailcrawlClient {
20501
21694
  options;
@@ -20515,7 +21708,7 @@ class MailcrawlClient {
20515
21708
  ...this.options.fixture === undefined ? [] : ["--fixture", this.options.fixture],
20516
21709
  ...this.options.himalayaConfig === undefined ? [] : ["--himalaya-config", this.options.himalayaConfig]
20517
21710
  ];
20518
- const result = await this.run(args, signal);
21711
+ const result = await this.run(args, signal, this.indexTimeoutMs());
20519
21712
  if (!result.ok)
20520
21713
  return failure2(result);
20521
21714
  const parsed = parseObject2(result.stdout);
@@ -20547,7 +21740,7 @@ class MailcrawlClient {
20547
21740
  "--json",
20548
21741
  query.trim()
20549
21742
  ];
20550
- const result = await this.run(args, options?.signal);
21743
+ const result = await this.run(args, options?.signal, this.options.timeoutMs ?? DEFAULT_SEARCH_TIMEOUT_MS2);
20551
21744
  if (!result.ok)
20552
21745
  return failure2(result);
20553
21746
  const parsed = parseJson3(result.stdout);
@@ -20568,7 +21761,10 @@ class MailcrawlClient {
20568
21761
  code: result.code ?? 0
20569
21762
  };
20570
21763
  }
20571
- async run(args, signal) {
21764
+ indexTimeoutMs() {
21765
+ return this.options.indexTimeoutMs ?? this.options.timeoutMs ?? DEFAULT_INDEX_TIMEOUT_MS;
21766
+ }
21767
+ async run(args, signal, timeoutMs) {
20572
21768
  const violation = findRemoteEmbeddingViolation({ ...process.env, ...this.options.env ?? {} });
20573
21769
  if (violation !== undefined) {
20574
21770
  return { ok: false, reason: "remote-embedding-rejected", stdout: "", stderr: "", code: null };
@@ -20578,21 +21774,21 @@ class MailcrawlClient {
20578
21774
  ensurePrivateMailcrawlDataDir(dataDir);
20579
21775
  const env = {};
20580
21776
  for (const [key, value] of Object.entries(process.env)) {
20581
- if (value !== undefined && (SAFE_ENV_KEYS.has(key) || SAFE_MAILCRAWL_ENV_KEYS.has(key)))
21777
+ if (value !== undefined && (SAFE_ENV_KEYS2.has(key) || SAFE_MAILCRAWL_ENV_KEYS.has(key)))
20582
21778
  env[key] = value;
20583
21779
  }
20584
21780
  for (const [key, value] of Object.entries(this.options.env ?? {})) {
20585
21781
  if (value === undefined)
20586
21782
  delete env[key];
20587
- else if (SAFE_ENV_KEYS.has(key) || SAFE_MAILCRAWL_ENV_KEYS.has(key))
21783
+ else if (SAFE_ENV_KEYS2.has(key) || SAFE_MAILCRAWL_ENV_KEYS.has(key))
20588
21784
  env[key] = value;
20589
21785
  }
20590
21786
  if (dataDir !== undefined)
20591
21787
  env.MAILCRAWL_DATA_DIR = dataDir;
20592
- return spawnMailcrawl(this.options.binaryPath ?? DEFAULT_BINARY4, args, env, this.options, signal);
21788
+ return spawnMailcrawl(this.options.binaryPath ?? DEFAULT_BINARY4, args, env, this.options, signal, timeoutMs);
20593
21789
  }
20594
21790
  async index(signal) {
20595
- const result = await this.run(["index", "--json"], signal);
21791
+ const result = await this.run(["index", "--json"], signal, this.indexTimeoutMs());
20596
21792
  if (!result.ok)
20597
21793
  return failure2(result);
20598
21794
  const parsed = parseObject2(result.stdout);
@@ -20602,27 +21798,30 @@ class MailcrawlClient {
20602
21798
  return ok4({
20603
21799
  embedded: number(parsed, "embedded"),
20604
21800
  reused: number(parsed, "reused"),
20605
- generation: string(parsed, "generation")
21801
+ generation: string(parsed, "generation"),
21802
+ archiveRevision: string(parsed, "archiveRevision"),
21803
+ rebuilt: typeof parsed.rebuilt === "boolean" ? parsed.rebuilt : undefined,
21804
+ embedder: string(parsed, "embedder")
20606
21805
  }, result);
20607
21806
  }
20608
21807
  }
20609
- function spawnMailcrawl(binary, args, env, options, signal) {
21808
+ function spawnMailcrawl(binary, args, env, options, signal, timeoutMs) {
20610
21809
  return new Promise((resolveResult) => {
20611
21810
  const portable = portableSpawnCommand(binary, args);
20612
- const child = spawn11(portable.command, portable.args, {
21811
+ const child = spawn13(portable.command, portable.args, {
20613
21812
  env,
20614
21813
  detached: process.platform !== "win32",
20615
21814
  stdio: ["ignore", "pipe", "pipe"]
20616
21815
  });
20617
21816
  let stdout = { text: "", bytes: 0, exceeded: false }, stderr = { text: "", bytes: 0, exceeded: false }, settled = false, reason;
20618
- const max = options.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES3;
21817
+ const max = options.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES4;
20619
21818
  const timer = setTimeout(() => {
20620
21819
  reason = "timeout";
20621
- terminate5(child);
20622
- }, options.timeoutMs ?? DEFAULT_TIMEOUT_MS8);
21820
+ terminate4(child);
21821
+ }, timeoutMs);
20623
21822
  const abort = () => {
20624
21823
  reason = "aborted";
20625
- terminate5(child);
21824
+ terminate4(child);
20626
21825
  };
20627
21826
  if (signal?.aborted)
20628
21827
  abort();
@@ -20633,14 +21832,14 @@ function spawnMailcrawl(binary, args, env, options, signal) {
20633
21832
  stdout = appendOutput(stdout, chunk, max);
20634
21833
  if (stdout.exceeded && reason === undefined) {
20635
21834
  reason = "stdout-too-large";
20636
- terminate5(child);
21835
+ terminate4(child);
20637
21836
  }
20638
21837
  });
20639
21838
  child.stderr.on("data", (chunk) => {
20640
21839
  stderr = appendOutput(stderr, chunk, max);
20641
21840
  if (stderr.exceeded && reason === undefined) {
20642
21841
  reason = "stderr-too-large";
20643
- terminate5(child);
21842
+ terminate4(child);
20644
21843
  }
20645
21844
  });
20646
21845
  child.on("error", (error) => {
@@ -20673,7 +21872,7 @@ function spawnMailcrawl(binary, args, env, options, signal) {
20673
21872
  });
20674
21873
  });
20675
21874
  }
20676
- function terminate5(child) {
21875
+ function terminate4(child) {
20677
21876
  if (child.killed)
20678
21877
  return;
20679
21878
  if (process.platform !== "win32" && child.pid !== undefined) {
@@ -20748,11 +21947,11 @@ function failure2(result) {
20748
21947
  ok: false,
20749
21948
  reason: result.reason ?? "nonzero-exit",
20750
21949
  stdout: result.stdout,
20751
- stderr: boundDiagnosticText5(result.stderr),
21950
+ stderr: boundDiagnosticText6(result.stderr),
20752
21951
  code: result.code
20753
21952
  };
20754
21953
  }
20755
- function boundDiagnosticText5(value) {
21954
+ function boundDiagnosticText6(value) {
20756
21955
  if (value.length === 0)
20757
21956
  return "";
20758
21957
  return value.trim().slice(0, 4000);
@@ -20780,10 +21979,22 @@ function findRemoteEmbeddingViolation(env) {
20780
21979
  const lower = key.toLowerCase();
20781
21980
  if (REMOTE_EMBEDDING_ENV_KEYS.has(lower))
20782
21981
  return key;
20783
- if (lower === "mailcrawl_embedder" && /^https?:\/\//iu.test(value.trim()))
20784
- return key;
21982
+ if (lower === "mailcrawl_embedder" || lower === "mailcrawl_embed_url") {
21983
+ if (isRemoteHttpEndpoint(value))
21984
+ return key;
21985
+ }
21986
+ }
21987
+ return;
21988
+ }
21989
+ function isRemoteHttpEndpoint(value) {
21990
+ const trimmed = value.trim();
21991
+ if (!/^https?:\/\//iu.test(trimmed))
21992
+ return false;
21993
+ try {
21994
+ return !LOOPBACK_HOSTS2.has(new URL(trimmed).hostname);
21995
+ } catch {
21996
+ return true;
20785
21997
  }
20786
- return;
20787
21998
  }
20788
21999
  function truncateUtf8(value, maxBytes) {
20789
22000
  if (Buffer.byteLength(value) <= maxBytes)
@@ -20797,14 +22008,26 @@ function truncateUtf8(value, maxBytes) {
20797
22008
  }
20798
22009
  return "";
20799
22010
  }
20800
- var DEFAULT_BINARY4 = "mailcrawl", DEFAULT_TIMEOUT_MS8 = 60000, DEFAULT_MAX_BUFFER_BYTES3 = 1048576, SAFE_ENV_KEYS, SAFE_MAILCRAWL_ENV_KEYS, REMOTE_EMBEDDING_ENV_KEYS;
20801
- var init_client6 = __esm(() => {
22011
+ var DEFAULT_BINARY4 = "mailcrawl", DEFAULT_SEARCH_TIMEOUT_MS2 = 60000, DEFAULT_INDEX_TIMEOUT_MS = 1800000, DEFAULT_MAX_BUFFER_BYTES4 = 1048576, LOOPBACK_HOSTS2, SAFE_ENV_KEYS2, SAFE_MAILCRAWL_ENV_KEYS, REMOTE_EMBEDDING_ENV_KEYS;
22012
+ var init_client7 = __esm(() => {
20802
22013
  init_portable_spawn();
20803
22014
  init_config();
20804
- SAFE_ENV_KEYS = new Set(["HOME", "LANG", "LC_ALL", "PATH", "TMPDIR", "TMP", "TEMP", "NO_COLOR"]);
22015
+ LOOPBACK_HOSTS2 = new Set(["127.0.0.1", "localhost", "[::1]", "::1"]);
22016
+ SAFE_ENV_KEYS2 = new Set(["HOME", "LANG", "LC_ALL", "PATH", "TMPDIR", "TMP", "TEMP", "NO_COLOR"]);
20805
22017
  SAFE_MAILCRAWL_ENV_KEYS = new Set([
20806
22018
  "MAILCRAWL_DATA_DIR",
20807
22019
  "MAILCRAWL_EMBEDDER",
22020
+ "MAILCRAWL_EMBEDDER_PROVIDER",
22021
+ "MAILCRAWL_EMBED_URL",
22022
+ "MAILCRAWL_EMBED_MODEL",
22023
+ "MAILCRAWL_EMBED_DIM",
22024
+ "MAILCRAWL_EMBED_TIMEOUT",
22025
+ "MAILCRAWL_EMBED_BATCH_SIZE",
22026
+ "MAILCRAWL_QUERY_PREFIX",
22027
+ "MAILCRAWL_PASSAGE_PREFIX",
22028
+ "MAILCRAWL_NATIVE_MODEL",
22029
+ "MAILCRAWL_NATIVE_DEVICE",
22030
+ "MAILCRAWL_NATIVE_DTYPE",
20808
22031
  "MAILCRAWL_KIWI_MODEL",
20809
22032
  "MAILCRAWL_JA_HELPER",
20810
22033
  "MAILCRAWL_ZH_HELPER"
@@ -20999,7 +22222,7 @@ Use this skill for authorized local email retrieval. AutoRAG invokes the externa
20999
22222
  Indexing is server-managed and incremental; do not trigger sync yourself.
21000
22223
 
21001
22224
  ## Search
21002
- Call the dedicated \`${datasourceSearchToolName("mailcrawl")}\` tool with a natural-language query. Do not use \`search_datasource_documents\` for this datasource — it fans out to every datasource CLI. Available scopes:
22225
+ Call the dedicated \`${datasourceSearchToolName("mailcrawl")}\` tool with a natural-language query. Available scopes:
21003
22226
  ${scopes}
21004
22227
 
21005
22228
  The selectable retrieval modes are BM25, semantic, and hybrid. Scope arguments can only narrow trusted access. Source identifiers remain opaque and hierarchical.
@@ -21032,9 +22255,9 @@ Never pass datasource virtual paths (\`/mailcrawl/...\`) to bash; they are not O
21032
22255
  }
21033
22256
  }
21034
22257
  var TAGS;
21035
- var init_skill7 = __esm(() => {
22258
+ var init_skill9 = __esm(() => {
21036
22259
  init_scope2();
21037
- init_client6();
22260
+ init_client7();
21038
22261
  init_config();
21039
22262
  init_methods4();
21040
22263
  TAGS = ["mailcrawl", "email", "pii"];
@@ -21042,181 +22265,9 @@ var init_skill7 = __esm(() => {
21042
22265
 
21043
22266
  // src/datasource/skills/mailcrawl/index.ts
21044
22267
  var init_mailcrawl = __esm(() => {
21045
- init_client6();
22268
+ init_client7();
21046
22269
  init_methods4();
21047
- init_skill7();
21048
- });
21049
-
21050
- // src/datasource/crawler-client.ts
21051
- import { spawn as spawn12 } from "node:child_process";
21052
-
21053
- class CrawlerCliClient {
21054
- profile;
21055
- options;
21056
- constructor(profile, options = {}) {
21057
- this.profile = profile;
21058
- this.options = options;
21059
- }
21060
- async sync(signal) {
21061
- const result = await this.run(this.profile.syncArgs(this.options), signal);
21062
- if (!result.ok)
21063
- return toFailure3(result);
21064
- const count = this.profile.parseSyncCount(result.stdout);
21065
- if (count === undefined)
21066
- return toFailure3(result, "invalid-output");
21067
- return { ok: true, count, stdout: result.stdout, stderr: result.stderr, code: result.code ?? 0 };
21068
- }
21069
- async search(query, options = {}) {
21070
- const topK = options.topK ?? 20;
21071
- const result = await this.run(this.profile.searchArgs(this.options, query, topK), options.signal);
21072
- if (!result.ok)
21073
- return toFailure3(result);
21074
- const hits = this.profile.parseHits(result.stdout);
21075
- if (hits === undefined)
21076
- return toFailure3(result, "invalid-output");
21077
- return { ok: true, hits, stdout: result.stdout, stderr: result.stderr, code: result.code ?? 0 };
21078
- }
21079
- async run(args, signal) {
21080
- const env = controlledEnv4(this.profile.allowedEnvPrefixes, this.options.env);
21081
- env.CRAWLKIT_NO_UPDATE_CHECK = "1";
21082
- return spawnCrawler(this.options.binaryPath ?? this.profile.binaryName, args, env, signal, this.options, this.options.workspacePath);
21083
- }
21084
- }
21085
- function spawnCrawler(command, args, env, signal, options, cwd) {
21086
- return new Promise((resolve) => {
21087
- const portable = portableSpawnCommand(command, args);
21088
- const child = spawn12(portable.command, [...portable.args], {
21089
- env,
21090
- ...cwd === undefined ? {} : { cwd },
21091
- stdio: ["ignore", "pipe", "pipe"]
21092
- });
21093
- let stdout = { text: "", bytes: 0, capped: false };
21094
- let stderr = { text: "", bytes: 0, capped: false };
21095
- let settled = false;
21096
- let finalReason;
21097
- const maxBuffer = options.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES4;
21098
- const timeout = setTimeout(() => {
21099
- finalReason = "timeout";
21100
- terminate6(child);
21101
- }, options.timeoutMs ?? DEFAULT_TIMEOUT_MS9);
21102
- const abortHandler = () => {
21103
- finalReason = "aborted";
21104
- terminate6(child);
21105
- };
21106
- if (signal?.aborted)
21107
- abortHandler();
21108
- signal?.addEventListener("abort", abortHandler, { once: true });
21109
- child.stdout.setEncoding("utf8");
21110
- child.stderr.setEncoding("utf8");
21111
- child.stdout.on("data", (chunk) => {
21112
- stdout = appendBounded4(stdout, chunk, maxBuffer);
21113
- if (stdout.capped) {
21114
- finalReason = "stdout-too-large";
21115
- terminate6(child);
21116
- }
21117
- });
21118
- child.stderr.on("data", (chunk) => {
21119
- stderr = appendBounded4(stderr, chunk, maxBuffer);
21120
- if (stderr.capped) {
21121
- finalReason = "stderr-too-large";
21122
- terminate6(child);
21123
- }
21124
- });
21125
- child.on("error", (error) => {
21126
- if (settled)
21127
- return;
21128
- settled = true;
21129
- clearTimeout(timeout);
21130
- signal?.removeEventListener("abort", abortHandler);
21131
- resolve({
21132
- ok: false,
21133
- reason: error.code === "ENOENT" ? "binary-missing" : "spawn-error",
21134
- stdout: stdout.text,
21135
- stderr: "",
21136
- code: null
21137
- });
21138
- });
21139
- child.on("close", (code) => {
21140
- if (settled)
21141
- return;
21142
- settled = true;
21143
- clearTimeout(timeout);
21144
- signal?.removeEventListener("abort", abortHandler);
21145
- resolve({
21146
- ok: code === 0 && finalReason === undefined,
21147
- ...finalReason !== undefined ? { reason: finalReason } : code === 0 ? {} : { reason: "nonzero-exit" },
21148
- stdout: stdout.text,
21149
- stderr: stderr.text,
21150
- code
21151
- });
21152
- });
21153
- });
21154
- }
21155
- function controlledEnv4(allowedPrefixes, configured) {
21156
- const env = {};
21157
- for (const [key, value] of Object.entries(process.env)) {
21158
- if (value !== undefined && isAllowedEnvKey(key, allowedPrefixes))
21159
- env[key] = value;
21160
- }
21161
- for (const [key, value] of Object.entries(configured ?? {})) {
21162
- if (value === undefined)
21163
- delete env[key];
21164
- else if (isAllowedEnvKey(key, allowedPrefixes))
21165
- env[key] = value;
21166
- }
21167
- return env;
21168
- }
21169
- function isAllowedEnvKey(key, allowedPrefixes) {
21170
- return SAFE_ENV_KEYS2.has(key) || process.platform === "win32" && [...SAFE_ENV_KEYS2].some((safeKey) => safeKey.toUpperCase() === key.toUpperCase()) || key.startsWith("CRAWLKIT_") || allowedPrefixes.some((prefix) => key.startsWith(prefix));
21171
- }
21172
- function appendBounded4(state, chunk, maxBytes) {
21173
- const nextBytes = state.bytes + Buffer.byteLength(chunk);
21174
- if (nextBytes <= maxBytes)
21175
- return { text: state.text + chunk, bytes: nextBytes, capped: false };
21176
- const remaining = Math.max(maxBytes - state.bytes, 0);
21177
- return { text: state.text + chunk.slice(0, remaining), bytes: maxBytes, capped: true };
21178
- }
21179
- function terminate6(child) {
21180
- if (!child.killed)
21181
- child.kill("SIGTERM");
21182
- }
21183
- function toFailure3(result, reason) {
21184
- return {
21185
- ok: false,
21186
- reason: reason ?? result.reason ?? "nonzero-exit",
21187
- stdout: result.stdout,
21188
- stderr: boundDiagnosticText6(result),
21189
- code: result.code
21190
- };
21191
- }
21192
- function boundDiagnosticText6(result) {
21193
- if (result.stderr.length > 0)
21194
- return result.stderr.trim().slice(0, 4000);
21195
- if (result.stdout.length > 0)
21196
- return result.stdout.trim().slice(0, 4000);
21197
- return "";
21198
- }
21199
- var DEFAULT_TIMEOUT_MS9 = 60000, DEFAULT_MAX_BUFFER_BYTES4 = 1048576, SAFE_ENV_KEYS2;
21200
- var init_crawler_client = __esm(() => {
21201
- init_portable_spawn();
21202
- SAFE_ENV_KEYS2 = new Set([
21203
- "HOME",
21204
- "LANG",
21205
- "LC_ALL",
21206
- "PATH",
21207
- "TMPDIR",
21208
- "TMP",
21209
- "TEMP",
21210
- "NO_COLOR",
21211
- "APPDATA",
21212
- "ComSpec",
21213
- "LOCALAPPDATA",
21214
- "PATHEXT",
21215
- "SystemDrive",
21216
- "SystemRoot",
21217
- "USERPROFILE",
21218
- "WINDIR"
21219
- ]);
22270
+ init_skill9();
21220
22271
  });
21221
22272
 
21222
22273
  // src/datasource/crawler-skill.ts
@@ -21237,7 +22288,7 @@ class CrawlerDatasourceSkill {
21237
22288
  description: `${definition.description} (${options.datasourceId})`
21238
22289
  };
21239
22290
  this.client = options.client;
21240
- this.instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID4;
22291
+ this.instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID5;
21241
22292
  this.instances = options.instances !== undefined && options.instances.length > 0 ? options.instances : [this.instanceId];
21242
22293
  this.pollingIntervalMs = options.pollingIntervalMs ?? DEFAULT_POLLING_INTERVAL_MS4;
21243
22294
  this.tags = options.tags ?? definition.defaultTags;
@@ -21324,7 +22375,7 @@ class CrawlerDatasourceSkill {
21324
22375
  this.definition.manifestDescription,
21325
22376
  "",
21326
22377
  "## How to search",
21327
- `Call the dedicated \`${datasourceSearchToolName(this.definition.datasourceId)}\` tool with a query and optional narrowing scope. Do not use \`search_datasource_documents\` for this datasource — it fans out to every datasource CLI. Available authorized scopes:`,
22378
+ `Call the dedicated \`${datasourceSearchToolName(this.definition.datasourceId)}\` tool with a query and optional narrowing scope. Available authorized scopes:`,
21328
22379
  scopes,
21329
22380
  "",
21330
22381
  "`scope` can only narrow within already-authorized scopes; it can never widen access.",
@@ -21431,7 +22482,7 @@ function matchesScope2(source, scope, allowedScopes) {
21431
22482
  function failureCode2(reason) {
21432
22483
  return reason === "binary-missing" || reason === "spawn-error" || reason === "timeout" || reason === "aborted" ? "datasource-unavailable" : "datasource-index-failed";
21433
22484
  }
21434
- var DEFAULT_INSTANCE_ID4 = "default", DEFAULT_POLLING_INTERVAL_MS4;
22485
+ var DEFAULT_INSTANCE_ID5 = "default", DEFAULT_POLLING_INTERVAL_MS4;
21435
22486
  var init_crawler_skill = __esm(() => {
21436
22487
  init_errors2();
21437
22488
  init_scope2();
@@ -21453,7 +22504,7 @@ function parseCount(stdout) {
21453
22504
  return;
21454
22505
  if (Array.isArray(parsed))
21455
22506
  return parsed.length;
21456
- if (!isRecord12(parsed))
22507
+ if (!isRecord13(parsed))
21457
22508
  return;
21458
22509
  for (const key of ["pages", "page_count", "pageCount", "synced", "count"]) {
21459
22510
  const value = parsed[key];
@@ -21468,12 +22519,12 @@ function parseHits(stdout) {
21468
22519
  return parseTabSeparatedHits(trimmed);
21469
22520
  }
21470
22521
  const parsed = parseJson4(stdout);
21471
- const rows = Array.isArray(parsed) ? parsed : isRecord12(parsed) && Array.isArray(parsed.results) ? parsed.results : undefined;
22522
+ const rows = Array.isArray(parsed) ? parsed : isRecord13(parsed) && Array.isArray(parsed.results) ? parsed.results : undefined;
21472
22523
  if (rows === undefined)
21473
22524
  return;
21474
22525
  const hits = [];
21475
22526
  for (const [index, row] of rows.entries()) {
21476
- if (!isRecord12(row))
22527
+ if (!isRecord13(row))
21477
22528
  return;
21478
22529
  const id = stringValue2(row, ["page_id", "id"]);
21479
22530
  const content = stringValue2(row, ["snippet", "text", "content"]);
@@ -21537,7 +22588,7 @@ function parseJson4(stdout) {
21537
22588
  return;
21538
22589
  }
21539
22590
  }
21540
- function isRecord12(value) {
22591
+ function isRecord13(value) {
21541
22592
  return typeof value === "object" && value !== null && !Array.isArray(value);
21542
22593
  }
21543
22594
  function stringValue2(record, keys) {
@@ -21586,46 +22637,16 @@ var init_notion = __esm(() => {
21586
22637
  };
21587
22638
  });
21588
22639
 
21589
- // src/process/terminate-tree.ts
21590
- import { spawn as spawn13 } from "node:child_process";
21591
- function terminateProcessTree(child, signal = "SIGTERM") {
21592
- const pid = child.pid;
21593
- if (pid === undefined)
21594
- return false;
21595
- if (process.platform === "win32") {
21596
- const killer = spawn13("taskkill", ["/pid", String(pid), "/t", "/f"], { stdio: "ignore", windowsHide: true });
21597
- killer.on("error", () => killDirectChild(child, signal));
21598
- return true;
21599
- }
21600
- try {
21601
- process.kill(-pid, signal);
21602
- return true;
21603
- } catch {
21604
- return killDirectChild(child, signal);
21605
- }
21606
- }
21607
- function killDirectChild(child, signal) {
21608
- if (child.exitCode !== null || child.signalCode !== null)
21609
- return false;
21610
- try {
21611
- return child.kill(signal);
21612
- } catch {
21613
- return false;
21614
- }
21615
- }
21616
- var TREE_KILL_GRACE_MS = 500;
21617
- var init_terminate_tree = () => {};
21618
-
21619
22640
  // src/datasource/skills/obsidian/paths.ts
21620
- import { join as join26 } from "node:path";
22641
+ import { join as join28 } from "node:path";
21621
22642
  function obsidianDatasourceRoot(workspaceRoot, instanceId) {
21622
- return join26(workspaceRoot, AUTORAG_DIRNAME, DATASOURCES_DIRNAME, OBSIDIAN_DATASOURCE_DIRNAME, instanceId);
22643
+ return join28(workspaceRoot, AUTORAG_DIRNAME, DATASOURCES_DIRNAME, OBSIDIAN_DATASOURCE_DIRNAME, instanceId);
21623
22644
  }
21624
22645
  function obsidianQmdConfigDir(workspaceRoot, instanceId) {
21625
- return join26(obsidianDatasourceRoot(workspaceRoot, instanceId), "config");
22646
+ return join28(obsidianDatasourceRoot(workspaceRoot, instanceId), "config");
21626
22647
  }
21627
22648
  function obsidianQmdCacheDir(workspaceRoot, instanceId) {
21628
- return join26(obsidianDatasourceRoot(workspaceRoot, instanceId), "cache");
22649
+ return join28(obsidianDatasourceRoot(workspaceRoot, instanceId), "cache");
21629
22650
  }
21630
22651
  function stripEdgeDashes(value) {
21631
22652
  let start = 0;
@@ -21648,7 +22669,7 @@ var DEFAULT_QMD_BINARY = "qmd", DEFAULT_QMD_TIMEOUT_MS = 60000, DEFAULT_QMD_MAX_
21648
22669
 
21649
22670
  // src/datasource/skills/obsidian/client.ts
21650
22671
  import { spawn as spawn14 } from "node:child_process";
21651
- import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "node:fs";
22672
+ import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "node:fs";
21652
22673
  import { isAbsolute as isAbsolute6, resolve as resolve12 } from "node:path";
21653
22674
 
21654
22675
  class QmdClient {
@@ -21668,10 +22689,10 @@ class QmdClient {
21668
22689
  const cacheDir = obsidianQmdCacheDir(workspaceRoot, instanceId);
21669
22690
  try {
21670
22691
  if (this.options.configPath === undefined) {
21671
- mkdirSync16(configDir, { recursive: true });
21672
- writeFileSync14(resolve12(configDir, "index.yml"), renderIndexYaml(collectionName, vaultPath), "utf8");
22692
+ mkdirSync18(configDir, { recursive: true });
22693
+ writeFileSync16(resolve12(configDir, "index.yml"), renderIndexYaml(collectionName, vaultPath), "utf8");
21673
22694
  }
21674
- mkdirSync16(cacheDir, { recursive: true });
22695
+ mkdirSync18(cacheDir, { recursive: true });
21675
22696
  } catch (error) {
21676
22697
  return failure3("spawn-error", error instanceof Error ? error.message : "failed to write qmd config");
21677
22698
  }
@@ -21894,7 +22915,7 @@ ${stderr}`;
21894
22915
  }
21895
22916
  try {
21896
22917
  const parsed = JSON.parse(stdout);
21897
- if (isRecord13(parsed)) {
22918
+ if (isRecord14(parsed)) {
21898
22919
  return {
21899
22920
  indexed: numberField(parsed, "indexed") ?? numberField(parsed, "new") ?? 0,
21900
22921
  updated: numberField(parsed, "updated") ?? 0,
@@ -21923,7 +22944,7 @@ function parseSearchHits(stdout) {
21923
22944
  function extractHitRows(parsed) {
21924
22945
  if (Array.isArray(parsed))
21925
22946
  return parsed;
21926
- if (!isRecord13(parsed))
22947
+ if (!isRecord14(parsed))
21927
22948
  return;
21928
22949
  for (const key of ["results", "hits", "documents", "items"]) {
21929
22950
  const value = parsed[key];
@@ -21935,7 +22956,7 @@ function extractHitRows(parsed) {
21935
22956
  return;
21936
22957
  }
21937
22958
  function normalizeHit3(raw) {
21938
- if (!isRecord13(raw))
22959
+ if (!isRecord14(raw))
21939
22960
  return;
21940
22961
  const score = numberField(raw, "score") ?? numberField(raw, "relevance") ?? 0;
21941
22962
  const docid = stringField(raw, "docid") ?? stringField(raw, "id");
@@ -21981,11 +23002,11 @@ function numberField(record, key) {
21981
23002
  const value = record[key];
21982
23003
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
21983
23004
  }
21984
- function isRecord13(value) {
23005
+ function isRecord14(value) {
21985
23006
  return typeof value === "object" && value !== null && !Array.isArray(value);
21986
23007
  }
21987
23008
  var SAFE_INHERITED_ENV_KEYS3, SAFE_QMD_ENV_PREFIX = "QMD_";
21988
- var init_client7 = __esm(() => {
23009
+ var init_client8 = __esm(() => {
21989
23010
  init_portable_spawn();
21990
23011
  init_terminate_tree();
21991
23012
  init_paths5();
@@ -22064,7 +23085,7 @@ async function retrieveObsidian(client, mode, methodName, instanceId, query, opt
22064
23085
  const trimmed = query.trim();
22065
23086
  if (trimmed.length === 0)
22066
23087
  return [];
22067
- const topK = options.topK ?? DEFAULT_TOP_K4;
23088
+ const topK = options.topK ?? DEFAULT_TOP_K5;
22068
23089
  const searchOptions = { topK, signal: options.signal };
22069
23090
  const result = await client.search(mode, trimmed, searchOptions);
22070
23091
  if (!result.ok)
@@ -22106,7 +23127,7 @@ function toRetrievalResult3(hit, source, methodName, instanceId, mode) {
22106
23127
  }
22107
23128
  };
22108
23129
  }
22109
- var OBSIDIAN_DATASOURCE_ID = "obsidian", DEFAULT_OBSIDIAN_TAGS, DEFAULT_TOP_K4 = 20;
23130
+ var OBSIDIAN_DATASOURCE_ID = "obsidian", DEFAULT_OBSIDIAN_TAGS, DEFAULT_TOP_K5 = 20;
22110
23131
  var init_methods5 = __esm(() => {
22111
23132
  init_errors2();
22112
23133
  init_scope2();
@@ -22124,7 +23145,7 @@ class ObsidianSkill {
22124
23145
  lastIndexedAt;
22125
23146
  lastError;
22126
23147
  constructor(options = {}) {
22127
- this.instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID5;
23148
+ this.instanceId = options.instanceId ?? DEFAULT_INSTANCE_ID6;
22128
23149
  this.instances = options.instances !== undefined && options.instances.length > 0 ? options.instances : [this.instanceId];
22129
23150
  this.pollingIntervalMs = options.pollingIntervalMs ?? DEFAULT_POLLING_INTERVAL_MS5;
22130
23151
  this.tags = options.tags ?? DEFAULT_OBSIDIAN_TAGS2;
@@ -22246,7 +23267,7 @@ class ObsidianSkill {
22246
23267
  `Indexing is server-managed and refreshed ${cadence} via \`qmd update\` (+ \`qmd embed\` for semantic). You do not trigger indexing; just search.`,
22247
23268
  "",
22248
23269
  "## How to search",
22249
- `Call the dedicated \`${datasourceSearchToolName(OBSIDIAN_DATASOURCE_ID2)}\` tool with a natural-language \`query\`. Optionally pass \`topK\` and a narrowing \`scope\`. Do not use \`search_datasource_documents\` for this datasource — it fans out to every datasource CLI. Available authorized scopes:`,
23270
+ `Call the dedicated \`${datasourceSearchToolName(OBSIDIAN_DATASOURCE_ID2)}\` tool with a natural-language \`query\`. Optionally pass \`topK\` and a narrowing \`scope\`. Available authorized scopes:`,
22250
23271
  instanceScopes.length > 0 ? instanceScopes : "- (no authorized instances)",
22251
23272
  "",
22252
23273
  "`scope` can only narrow within already-authorized scopes; it can never widen access.",
@@ -22293,10 +23314,10 @@ function boundDiagnostic(value) {
22293
23314
  const trimmed = value.trim();
22294
23315
  return trimmed.length > 500 ? `${trimmed.slice(0, 500)}...` : trimmed;
22295
23316
  }
22296
- var OBSIDIAN_DATASOURCE_ID2 = "obsidian", OBSIDIAN_SKILL_TYPE = "obsidian-vault", DEFAULT_INSTANCE_ID5 = "default", DEFAULT_POLLING_INTERVAL_MS5, DEFAULT_OBSIDIAN_TAGS2;
22297
- var init_skill8 = __esm(() => {
23317
+ var OBSIDIAN_DATASOURCE_ID2 = "obsidian", OBSIDIAN_SKILL_TYPE = "obsidian-vault", DEFAULT_INSTANCE_ID6 = "default", DEFAULT_POLLING_INTERVAL_MS5, DEFAULT_OBSIDIAN_TAGS2;
23318
+ var init_skill10 = __esm(() => {
22298
23319
  init_scope2();
22299
- init_client7();
23320
+ init_client8();
22300
23321
  init_methods5();
22301
23322
  init_paths5();
22302
23323
  DEFAULT_POLLING_INTERVAL_MS5 = 15 * 60 * 1000;
@@ -22305,10 +23326,10 @@ var init_skill8 = __esm(() => {
22305
23326
 
22306
23327
  // src/datasource/skills/obsidian/index.ts
22307
23328
  var init_obsidian = __esm(() => {
22308
- init_client7();
23329
+ init_client8();
22309
23330
  init_methods5();
22310
23331
  init_paths5();
22311
- init_skill8();
23332
+ init_skill10();
22312
23333
  });
22313
23334
 
22314
23335
  // src/datasource/skills/rss/connector.ts
@@ -22458,13 +23479,13 @@ function present(item) {
22458
23479
  return item !== undefined;
22459
23480
  }
22460
23481
  var DEFAULT_MAX_DOCUMENTS4 = 500, DEFAULT_MAX_ITEMS_PER_FEED = 100, DEFAULT_FEED_TIMEOUT_MS = 30000, MAX_CONTENT_CHARS2 = 20000, ACCEPT_HEADER = "application/rss+xml, application/atom+xml, application/xml, text/xml";
22461
- var init_connector4 = () => {};
23482
+ var init_connector5 = () => {};
22462
23483
 
22463
23484
  // src/datasource/skills/rss/skill.ts
22464
23485
  var RSS_SKILL_DEFINITION, DEFAULT_DEDUPE_WINDOW_MS, RssSkill;
22465
- var init_skill9 = __esm(() => {
23486
+ var init_skill11 = __esm(() => {
22466
23487
  init_connector_skill();
22467
- init_connector4();
23488
+ init_connector5();
22468
23489
  RSS_SKILL_DEFINITION = {
22469
23490
  skillName: "rss",
22470
23491
  skillType: "rss-feeds",
@@ -22490,8 +23511,8 @@ var init_skill9 = __esm(() => {
22490
23511
 
22491
23512
  // src/datasource/skills/rss/index.ts
22492
23513
  var init_rss = __esm(() => {
22493
- init_connector4();
22494
- init_skill9();
23514
+ init_connector5();
23515
+ init_skill11();
22495
23516
  });
22496
23517
 
22497
23518
  // src/datasource/skills/slack/index.ts
@@ -22508,7 +23529,7 @@ function parseCount2(stdout) {
22508
23529
  return;
22509
23530
  if (Array.isArray(parsed))
22510
23531
  return parsed.length;
22511
- if (!isRecord14(parsed))
23532
+ if (!isRecord15(parsed))
22512
23533
  return;
22513
23534
  for (const key of ["messages", "message_count", "messageCount", "synced", "count"]) {
22514
23535
  const value = parsed[key];
@@ -22521,12 +23542,12 @@ function parseHits2(stdout) {
22521
23542
  const parsed = parseJson5(stdout);
22522
23543
  if (parsed === null)
22523
23544
  return [];
22524
- const rows = Array.isArray(parsed) ? parsed : isRecord14(parsed) && Array.isArray(parsed.messages) ? parsed.messages : undefined;
23545
+ const rows = Array.isArray(parsed) ? parsed : isRecord15(parsed) && Array.isArray(parsed.messages) ? parsed.messages : undefined;
22525
23546
  if (rows === undefined)
22526
23547
  return;
22527
23548
  const hits = [];
22528
23549
  for (const [index, row] of rows.entries()) {
22529
- if (!isRecord14(row))
23550
+ if (!isRecord15(row))
22530
23551
  return;
22531
23552
  const workspaceId = stringValue3(row, ["workspace_id"]);
22532
23553
  const workspaceName = stringValue3(row, ["workspace_name"]);
@@ -22581,7 +23602,7 @@ function parseJson5(stdout) {
22581
23602
  return;
22582
23603
  }
22583
23604
  }
22584
- function isRecord14(value) {
23605
+ function isRecord15(value) {
22585
23606
  return typeof value === "object" && value !== null && !Array.isArray(value);
22586
23607
  }
22587
23608
  function stringValue3(record, keys) {
@@ -22645,7 +23666,7 @@ var init_slack = __esm(() => {
22645
23666
 
22646
23667
  // src/datasource/skills/spotlight/connector.ts
22647
23668
  import { spawn as spawn15 } from "node:child_process";
22648
- import { createHash as createHash7 } from "node:crypto";
23669
+ import { createHash as createHash8 } from "node:crypto";
22649
23670
  import { readFile as fsReadFile, stat as stat6 } from "node:fs/promises";
22650
23671
  import { basename as basename11, extname as extname10 } from "node:path";
22651
23672
 
@@ -22668,7 +23689,7 @@ class SpotlightConnector {
22668
23689
  if (queries.length === 0) {
22669
23690
  return { ok: false, reason: "not-configured", message: "no spotlight queries configured" };
22670
23691
  }
22671
- const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS10;
23692
+ const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS9;
22672
23693
  const status = await this.run("mdutil", ["-s", "/"], timeoutMs);
22673
23694
  if (status.ok && /indexing disabled/iu.test(status.stdout)) {
22674
23695
  return {
@@ -22729,7 +23750,7 @@ class SpotlightConnector {
22729
23750
  if (content.length === 0)
22730
23751
  continue;
22731
23752
  documents.push({
22732
- docId: createHash7("sha256").update(path).digest("hex").slice(0, 16),
23753
+ docId: createHash8("sha256").update(path).digest("hex").slice(0, 16),
22733
23754
  hierarchy: ["files"],
22734
23755
  title: basename11(path),
22735
23756
  content,
@@ -22762,7 +23783,7 @@ class SpotlightConnector {
22762
23783
  return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
22763
23784
  }
22764
23785
  }
22765
- var DEFAULT_MAX_RESULTS_PER_QUERY = 100, DEFAULT_MAX_DOCUMENTS5 = 300, DEFAULT_MAX_BYTES_PER_FILE3, DEFAULT_TIMEOUT_MS10 = 15000, MAX_CONTENT_CHARS3 = 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((resolve) => {
23786
+ var DEFAULT_MAX_RESULTS_PER_QUERY = 100, DEFAULT_MAX_DOCUMENTS5 = 300, DEFAULT_MAX_BYTES_PER_FILE3, DEFAULT_TIMEOUT_MS9 = 15000, MAX_CONTENT_CHARS3 = 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((resolve) => {
22766
23787
  const child = spawn15(command, [...args], { stdio: ["ignore", "pipe", "pipe"] });
22767
23788
  let stdout = "";
22768
23789
  let stderr = "";
@@ -22795,16 +23816,16 @@ var DEFAULT_MAX_RESULTS_PER_QUERY = 100, DEFAULT_MAX_DOCUMENTS5 = 300, DEFAULT_M
22795
23816
  finish({ ok: code === 0, stdout, stderr, code });
22796
23817
  });
22797
23818
  });
22798
- var init_connector5 = __esm(() => {
23819
+ var init_connector6 = __esm(() => {
22799
23820
  DEFAULT_MAX_BYTES_PER_FILE3 = 256 * 1024;
22800
23821
  MAX_BUFFER_BYTES = 4 * 1024 * 1024;
22801
23822
  });
22802
23823
 
22803
23824
  // src/datasource/skills/spotlight/skill.ts
22804
23825
  var SPOTLIGHT_SKILL_DEFINITION, SpotlightSkill;
22805
- var init_skill10 = __esm(() => {
23826
+ var init_skill12 = __esm(() => {
22806
23827
  init_connector_skill();
22807
- init_connector5();
23828
+ init_connector6();
22808
23829
  SPOTLIGHT_SKILL_DEFINITION = {
22809
23830
  skillName: "spotlight",
22810
23831
  skillType: "spotlight-search",
@@ -22841,8 +23862,8 @@ var init_skill10 = __esm(() => {
22841
23862
 
22842
23863
  // src/datasource/skills/spotlight/index.ts
22843
23864
  var init_spotlight = __esm(() => {
22844
- init_connector5();
22845
- init_skill10();
23865
+ init_connector6();
23866
+ init_skill12();
22846
23867
  });
22847
23868
 
22848
23869
  // src/datasource/skills/telecrawl/index.ts
@@ -22859,7 +23880,7 @@ function parseCount3(stdout) {
22859
23880
  return;
22860
23881
  if (Array.isArray(parsed))
22861
23882
  return parsed.length;
22862
- if (!isRecord15(parsed))
23883
+ if (!isRecord16(parsed))
22863
23884
  return;
22864
23885
  for (const key of ["messages", "message_count", "messageCount", "imported", "count"]) {
22865
23886
  const value = parsed[key];
@@ -22872,12 +23893,12 @@ function parseHits3(stdout) {
22872
23893
  const parsed = parseJson6(stdout);
22873
23894
  if (parsed === null)
22874
23895
  return [];
22875
- const rows = Array.isArray(parsed) ? parsed : isRecord15(parsed) && Array.isArray(parsed.messages) ? parsed.messages : undefined;
23896
+ const rows = Array.isArray(parsed) ? parsed : isRecord16(parsed) && Array.isArray(parsed.messages) ? parsed.messages : undefined;
22876
23897
  if (rows === undefined)
22877
23898
  return;
22878
23899
  const hits = [];
22879
23900
  for (const [index, row] of rows.entries()) {
22880
- if (!isRecord15(row))
23901
+ if (!isRecord16(row))
22881
23902
  return;
22882
23903
  const id = stringValue4(row, ["message_id", "event_id", "id"]);
22883
23904
  const content = stringValue4(row, ["snippet", "text", "content"]);
@@ -22916,7 +23937,7 @@ function parseJson6(stdout) {
22916
23937
  return;
22917
23938
  }
22918
23939
  }
22919
- function isRecord15(value) {
23940
+ function isRecord16(value) {
22920
23941
  return typeof value === "object" && value !== null && !Array.isArray(value);
22921
23942
  }
22922
23943
  function stringValue4(record, keys) {
@@ -22979,7 +24000,7 @@ function parseCount4(stdout) {
22979
24000
  return;
22980
24001
  if (Array.isArray(parsed))
22981
24002
  return parsed.length;
22982
- if (!isRecord16(parsed))
24003
+ if (!isRecord17(parsed))
22983
24004
  return;
22984
24005
  for (const key of ["messages", "message_count", "messageCount", "imported", "count"]) {
22985
24006
  const value = parsed[key];
@@ -22992,12 +24013,12 @@ function parseHits4(stdout) {
22992
24013
  const parsed = parseJson7(stdout);
22993
24014
  if (parsed === null)
22994
24015
  return [];
22995
- const rows = Array.isArray(parsed) ? parsed : isRecord16(parsed) && Array.isArray(parsed.messages) ? parsed.messages : undefined;
24016
+ const rows = Array.isArray(parsed) ? parsed : isRecord17(parsed) && Array.isArray(parsed.messages) ? parsed.messages : undefined;
22996
24017
  if (rows === undefined)
22997
24018
  return;
22998
24019
  const hits = [];
22999
24020
  for (const [index, row] of rows.entries()) {
23000
- if (!isRecord16(row))
24021
+ if (!isRecord17(row))
23001
24022
  return;
23002
24023
  const id = stringValue5(row, ["message_id", "event_id", "id"]);
23003
24024
  const content = stringValue5(row, ["snippet", "text", "content"]);
@@ -23034,7 +24055,7 @@ function parseJson7(stdout) {
23034
24055
  return;
23035
24056
  }
23036
24057
  }
23037
- function isRecord16(value) {
24058
+ function isRecord17(value) {
23038
24059
  return typeof value === "object" && value !== null && !Array.isArray(value);
23039
24060
  }
23040
24061
  function stringValue5(record, keys) {
@@ -23092,6 +24113,16 @@ var init_wacrawl = __esm(() => {
23092
24113
  });
23093
24114
 
23094
24115
  // src/datasource/skills/factory.ts
24116
+ function larkConnectorOptions(connector) {
24117
+ if (connector === undefined)
24118
+ return {};
24119
+ const binaryPath = connector.binaryPath;
24120
+ const embedderBaseUrl = connector.embedderBaseUrl;
24121
+ return {
24122
+ ...typeof binaryPath === "string" ? { binaryPath } : {},
24123
+ ...typeof embedderBaseUrl === "string" ? { embedderBaseUrl } : {}
24124
+ };
24125
+ }
23095
24126
  function common(config, workspaceRoot) {
23096
24127
  return {
23097
24128
  ...config.instanceId !== undefined ? { instanceId: config.instanceId } : {},
@@ -23125,13 +24156,14 @@ function buildDatasourceSkills(config, workspaceRoot, embeddingRuntime) {
23125
24156
  unknown.push(name);
23126
24157
  continue;
23127
24158
  }
23128
- const hasChannelFilter = (entry.channels?.ids?.length ?? 0) > 0 || (entry.channels?.names?.length ?? 0) > 0;
24159
+ const larkOwnsChannels = templateName === "lark";
24160
+ const hasChannelFilter = !larkOwnsChannels && ((entry.channels?.ids?.length ?? 0) > 0 || (entry.channels?.names?.length ?? 0) > 0);
23129
24161
  let aliased = skill;
23130
24162
  if (name !== templateName || hasChannelFilter) {
23131
24163
  aliased = new AliasedDatasourceSkill(skill, {
23132
24164
  alias: name,
23133
- ...entry.channels?.ids !== undefined ? { channelIds: entry.channels.ids } : {},
23134
- ...entry.channels?.names !== undefined ? { channelNames: entry.channels.names } : {}
24165
+ ...!larkOwnsChannels && entry.channels?.ids !== undefined ? { channelIds: entry.channels.ids } : {},
24166
+ ...!larkOwnsChannels && entry.channels?.names !== undefined ? { channelNames: entry.channels.names } : {}
23135
24167
  });
23136
24168
  }
23137
24169
  skills.push(typeof entry.description === "string" && entry.description.trim().length > 0 ? new DescribedDatasourceSkill(aliased, entry.description.trim()) : aliased);
@@ -23145,7 +24177,9 @@ var init_factory = __esm(() => {
23145
24177
  init_cloud_drive();
23146
24178
  init_discrawl();
23147
24179
  init_github();
23148
- init_katok();
24180
+ init_github_gist();
24181
+ init_lark();
24182
+ init_lazykatok();
23149
24183
  init_mail_export();
23150
24184
  init_mailcrawl();
23151
24185
  init_notion();
@@ -23240,8 +24274,15 @@ var init_factory = __esm(() => {
23240
24274
  ...workspaceRoot === undefined ? {} : { workspacePath: workspaceRoot }
23241
24275
  }
23242
24276
  }),
23243
- kakao: (config, _workspaceRoot, _registrationName) => new KatokSkill({
23244
- client: new KatokClient({
24277
+ lark: (config, _workspaceRoot, registrationName) => new LarkSkill({
24278
+ datasourceId: registrationName,
24279
+ ...config.instanceId !== undefined ? { instanceId: config.instanceId } : {},
24280
+ ...config.tags !== undefined ? { tags: config.tags } : {},
24281
+ ...config.channels?.ids !== undefined ? { chatIds: config.channels.ids } : {},
24282
+ ...larkConnectorOptions(config.connector)
24283
+ }),
24284
+ kakao: (config, _workspaceRoot, _registrationName) => new LazykatokSkill({
24285
+ client: new LazykatokClient({
23245
24286
  ...config.connector
23246
24287
  }),
23247
24288
  ...config.instanceId !== undefined ? { instanceId: config.instanceId } : {},
@@ -23253,6 +24294,11 @@ var init_factory = __esm(() => {
23253
24294
  skillName: registrationName,
23254
24295
  connectorOptions: config.connector
23255
24296
  }),
24297
+ "github-gist": (config, workspaceRoot, registrationName) => new GitHubGistSkill({
24298
+ ...common(config, workspaceRoot),
24299
+ skillName: registrationName,
24300
+ connectorOptions: config.connector
24301
+ }),
23256
24302
  "cloud-drive": (config, workspaceRoot, registrationName) => new CloudDriveSkill({
23257
24303
  ...common(config, workspaceRoot),
23258
24304
  skillName: registrationName,
@@ -23298,8 +24344,8 @@ var init_factory = __esm(() => {
23298
24344
 
23299
24345
  // src/cli/config.ts
23300
24346
  import { randomUUID as randomUUID7 } from "node:crypto";
23301
- import { existsSync as existsSync20, mkdirSync as mkdirSync17, readFileSync as readFileSync16, renameSync as renameSync8, unlinkSync as unlinkSync4, writeFileSync as writeFileSync15 } from "node:fs";
23302
- import { basename as basename12, dirname as dirname17, isAbsolute as isAbsolute7, join as join27, resolve as resolve13 } from "node:path";
24347
+ import { existsSync as existsSync23, mkdirSync as mkdirSync19, readFileSync as readFileSync18, renameSync as renameSync8, unlinkSync as unlinkSync4, writeFileSync as writeFileSync17 } from "node:fs";
24348
+ import { basename as basename12, dirname as dirname19, isAbsolute as isAbsolute7, join as join29, resolve as resolve13 } from "node:path";
23303
24349
  import { findEnvKeys, getEnvApiKey, getModel, getProviders } from "@earendil-works/pi-ai/compat";
23304
24350
  function resolveConfigPath(input) {
23305
24351
  const flags = input.flags;
@@ -23314,15 +24360,15 @@ function resolveConfigPath(input) {
23314
24360
  return { configPath: envConfig, explicit: true };
23315
24361
  }
23316
24362
  return {
23317
- configPath: join27(resolveAutoRAGHome(env), DEFAULT_CONFIG_FILENAME),
24363
+ configPath: join29(resolveAutoRAGHome(env), DEFAULT_CONFIG_FILENAME),
23318
24364
  explicit: false,
23319
- legacyPath: join27(cwd, LEGACY_CONFIG_FILENAME)
24365
+ legacyPath: join29(cwd, LEGACY_CONFIG_FILENAME)
23320
24366
  };
23321
24367
  }
23322
24368
  function readConfigFile(configPath, explicit) {
23323
24369
  let exists;
23324
24370
  try {
23325
- exists = existsSync20(configPath);
24371
+ exists = existsSync23(configPath);
23326
24372
  } catch {
23327
24373
  exists = false;
23328
24374
  }
@@ -23334,7 +24380,7 @@ function readConfigFile(configPath, explicit) {
23334
24380
  }
23335
24381
  let text;
23336
24382
  try {
23337
- text = readFileSync16(configPath, "utf8");
24383
+ text = readFileSync18(configPath, "utf8");
23338
24384
  } catch (err) {
23339
24385
  throw new ConfigError(`Failed to read config file: ${err.message}`);
23340
24386
  }
@@ -23372,9 +24418,9 @@ function acquireConfigWriteLock(configPath) {
23372
24418
  });
23373
24419
  }
23374
24420
  function replaceFileAtomically(path, contents, assertCommitAllowed) {
23375
- const temporaryPath = join27(dirname17(path), `.${basename12(path)}.${process.pid}.${randomUUID7()}.tmp`);
24421
+ const temporaryPath = join29(dirname19(path), `.${basename12(path)}.${process.pid}.${randomUUID7()}.tmp`);
23376
24422
  try {
23377
- writeFileSync15(temporaryPath, contents, { encoding: "utf8", flag: "wx", flush: true, mode: 384 });
24423
+ writeFileSync17(temporaryPath, contents, { encoding: "utf8", flag: "wx", flush: true, mode: 384 });
23378
24424
  assertCommitAllowed?.();
23379
24425
  renameSync8(temporaryPath, path);
23380
24426
  } finally {
@@ -23382,15 +24428,15 @@ function replaceFileAtomically(path, contents, assertCommitAllowed) {
23382
24428
  }
23383
24429
  }
23384
24430
  function migrateLegacyConfig(configPath, legacyPath) {
23385
- if (existsSync20(configPath) || !existsSync20(legacyPath))
24431
+ if (existsSync23(configPath) || !existsSync23(legacyPath))
23386
24432
  return;
23387
24433
  const legacy = readConfigFile(legacyPath, true);
23388
- const legacyBytes = readFileSync16(legacyPath);
23389
- const migrated = normalizeLegacyConfigPaths(legacy ?? {}, dirname17(legacyPath));
24434
+ const legacyBytes = readFileSync18(legacyPath);
24435
+ const migrated = normalizeLegacyConfigPaths(legacy ?? {}, dirname19(legacyPath));
23390
24436
  const migratedPathsUnchanged = legacy?.workspacePath === migrated.workspacePath && legacy?.memoryPath === migrated.memoryPath && JSON.stringify(legacy?.searchPaths) === JSON.stringify(migrated.searchPaths);
23391
24437
  const migratedBytes = migratedPathsUnchanged ? legacyBytes : `${JSON.stringify(migrated, null, 2)}
23392
24438
  `;
23393
- mkdirSync17(dirname17(configPath), { recursive: true });
24439
+ mkdirSync19(dirname19(configPath), { recursive: true });
23394
24440
  const lock = acquireConfigWriteLock(configPath);
23395
24441
  try {
23396
24442
  const winner = readConfigFile(configPath, false);
@@ -23420,7 +24466,7 @@ function resolveConfigFileReadOnly(configPath, explicit, legacyPath) {
23420
24466
  const legacy = readConfigFile(legacyPath, false);
23421
24467
  if (legacy === undefined)
23422
24468
  return {};
23423
- return normalizeLegacyConfigPaths(legacy, dirname17(legacyPath));
24469
+ return normalizeLegacyConfigPaths(legacy, dirname19(legacyPath));
23424
24470
  }
23425
24471
  function resolveSearchPaths(searchPaths, origin) {
23426
24472
  return searchPaths.map((searchPath) => resolvePersistedPath(searchPath, origin));
@@ -23865,10 +24911,10 @@ function resolveConfig(input) {
23865
24911
  const file = readOnly ? resolveConfigFileReadOnly(configPath, explicit, legacyPath) : !explicit && legacyPath ? migrateLegacyConfig(configPath, legacyPath) ?? readConfigFile(configPath, explicit) ?? {} : readConfigFile(configPath, explicit) ?? {};
23866
24912
  const defaultSearchPaths = ["."];
23867
24913
  const defaultWorkspacePath = cwd;
23868
- const defaultMemoryPath = join27(resolveAutoRAGHome(env), "memory.json");
24914
+ const defaultMemoryPath = join29(resolveAutoRAGHome(env), "memory.json");
23869
24915
  const flagSearchPaths = parseCsv(flagString(flags, "search-paths"));
23870
24916
  const envSearchPaths = parseCsv(envString(env, "AUTORAG_SEARCH_PATHS"));
23871
- const configOrigin = dirname17(resolve13(configPath));
24917
+ const configOrigin = dirname19(resolve13(configPath));
23872
24918
  const fileWorkspacePath = typeof file.workspacePath === "string" ? resolvePersistedPath(file.workspacePath, configOrigin) : undefined;
23873
24919
  const fileSearchPaths = file.searchPaths ? resolveSearchPaths(file.searchPaths, fileWorkspacePath ?? configOrigin) : undefined;
23874
24920
  const searchPaths = flagSearchPaths ?? envSearchPaths ?? fileSearchPaths ?? defaultSearchPaths;
@@ -24225,7 +25271,7 @@ function writeConfigObject(path, config) {
24225
25271
  if (config === null || typeof config !== "object" || Array.isArray(config)) {
24226
25272
  throw new ConfigError("Config file must be a JSON object");
24227
25273
  }
24228
- mkdirSync17(dirname17(path), { recursive: true });
25274
+ mkdirSync19(dirname19(path), { recursive: true });
24229
25275
  const contents = `${JSON.stringify(config, null, 2)}
24230
25276
  `;
24231
25277
  const lock = acquireConfigWriteLock(path);
@@ -24238,7 +25284,7 @@ function writeConfigObject(path, config) {
24238
25284
  function writeDefaultConfig(path, partial, opts = {}) {
24239
25285
  const cwd = resolve13(opts.cwd ?? process.cwd());
24240
25286
  const workspacePath = resolvePersistedPath(partial.workspacePath ?? ".", cwd);
24241
- const memoryPath = partial.memoryPath === undefined ? join27(resolveAutoRAGHome(opts.env), "memory.json") : resolvePersistedPath(partial.memoryPath, workspacePath);
25287
+ const memoryPath = partial.memoryPath === undefined ? join29(resolveAutoRAGHome(opts.env), "memory.json") : resolvePersistedPath(partial.memoryPath, workspacePath);
24242
25288
  const full = {
24243
25289
  searchPaths: resolveSearchPaths(partial.searchPaths ?? ["."], cwd),
24244
25290
  workspacePath,
@@ -24261,19 +25307,19 @@ function writeDefaultConfig(path, partial, opts = {}) {
24261
25307
  full.p2p = normalizeP2pConfig(partial.p2p);
24262
25308
  else
24263
25309
  full.p2p = { enabled: false };
24264
- mkdirSync17(dirname17(path), { recursive: true });
25310
+ mkdirSync19(dirname19(path), { recursive: true });
24265
25311
  const contents = `${JSON.stringify(full, null, 2)}
24266
25312
  `;
24267
25313
  const lock = acquireConfigWriteLock(path);
24268
25314
  try {
24269
- if (!opts.force && existsSync20(path)) {
25315
+ if (!opts.force && existsSync23(path)) {
24270
25316
  throw new ConfigError(`Config file already exists: ${path}`);
24271
25317
  }
24272
25318
  if (opts.force || opts.atomicCreate)
24273
25319
  replaceFileAtomically(path, contents, lock.assertOwned);
24274
25320
  else {
24275
25321
  lock.assertOwned();
24276
- writeFileSync15(path, contents, { encoding: "utf8", flag: "wx", flush: true, mode: 384 });
25322
+ writeFileSync17(path, contents, { encoding: "utf8", flag: "wx", flush: true, mode: 384 });
24277
25323
  }
24278
25324
  } catch (error) {
24279
25325
  if (!opts.force && isEexistError(error)) {
@@ -24449,7 +25495,6 @@ init_dupey_tool();
24449
25495
  init_emit_results_tool();
24450
25496
  init_jikji_find_tool();
24451
25497
  init_search_all_tool();
24452
- init_search_datasource_tool();
24453
25498
  init_search_minsync_tool();
24454
25499
  init_search_single_datasource_tool();
24455
25500
  init_system_prompt();
@@ -24638,7 +25683,8 @@ init_cloud_drive();
24638
25683
  init_discrawl();
24639
25684
  init_factory();
24640
25685
  init_github();
24641
- init_katok();
25686
+ init_lark();
25687
+ init_lazykatok();
24642
25688
  init_mail_export();
24643
25689
  init_mailcrawl();
24644
25690
  init_notion();
@@ -24707,10 +25753,12 @@ export {
24707
25753
  JIKJI_CRATE_NAME,
24708
25754
  JIKJI_INSTALL_TIMEOUT_MS,
24709
25755
  JikjiClient,
24710
- KatokBm25Method,
24711
- KatokClient,
24712
- KatokSemanticMethod,
24713
- KatokSkill,
25756
+ LarkClient,
25757
+ LarkSkill,
25758
+ LazykatokBm25Method,
25759
+ LazykatokClient,
25760
+ LazykatokSemanticMethod,
25761
+ LazykatokSkill,
24714
25762
  LocalEmbedderError,
24715
25763
  MAILCRAWL_MODES,
24716
25764
  MINSYNC_CONFIG_DIR,