@autorag/librarian 2.10.4 → 2.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -11695,11 +11695,16 @@ var init_factory = __esm(() => {
11695
11695
  skillName: registrationName,
11696
11696
  connectorOptions: config.connector
11697
11697
  }),
11698
- "github-gist": (config, workspaceRoot, registrationName) => new GitHubGistSkill({
11699
- ...common(config, workspaceRoot),
11700
- skillName: registrationName,
11701
- connectorOptions: config.connector
11702
- }),
11698
+ "github-gist": (config, workspaceRoot, registrationName) => {
11699
+ const { semantic, ...connectorOptions } = config.connector ?? {};
11700
+ const semanticOff = semantic === false || typeof semantic === "object" && semantic !== null && semantic.enabled === false;
11701
+ return new GitHubGistSkill({
11702
+ ...common(config, workspaceRoot),
11703
+ skillName: registrationName,
11704
+ connectorOptions,
11705
+ ...semanticOff ? { semantic: { enabled: false } } : {}
11706
+ });
11707
+ },
11703
11708
  "cloud-drive": (config, workspaceRoot, registrationName) => new CloudDriveSkill({
11704
11709
  ...common(config, workspaceRoot),
11705
11710
  skillName: registrationName,
@@ -12200,11 +12205,6 @@ var init_types4 = __esm(() => {
12200
12205
  label: "xAI",
12201
12206
  description: "xAI Responses web_search via the agent's xAI model credentials (XAI_API_KEY)"
12202
12207
  },
12203
- {
12204
- value: "perplexity",
12205
- label: "Perplexity",
12206
- description: "Anonymous Perplexity ask endpoint; no key or signup required"
12207
- },
12208
12208
  {
12209
12209
  value: "parallel",
12210
12210
  label: "Parallel",
@@ -12215,16 +12215,21 @@ var init_types4 = __esm(() => {
12215
12215
  label: "SearXNG",
12216
12216
  description: "Advanced: requires a self-hosted SEARXNG_ENDPOINT; never part of the default experience"
12217
12217
  },
12218
- {
12219
- value: "startpage",
12220
- label: "Startpage",
12221
- description: "Credential-free scrape of Startpage (Google-backed) results; may be bot-challenged"
12222
- },
12223
12218
  {
12224
12219
  value: "duckduckgo",
12225
12220
  label: "DuckDuckGo",
12226
12221
  description: "Credential-free best-effort fallback; may be bot-challenged on datacenter/shared-egress IPs"
12227
12222
  },
12223
+ {
12224
+ value: "perplexity",
12225
+ label: "Perplexity",
12226
+ description: "Anonymous Perplexity ask endpoint; no key or signup required"
12227
+ },
12228
+ {
12229
+ value: "startpage",
12230
+ label: "Startpage",
12231
+ description: "Credential-free scrape of Startpage (Google-backed) results; may be bot-challenged"
12232
+ },
12228
12233
  {
12229
12234
  value: "ecosia",
12230
12235
  label: "Ecosia",
@@ -13952,7 +13957,20 @@ function configuredVectorDimension(workspacePath) {
13952
13957
  const dimension = options.dimension;
13953
13958
  return typeof dimension === "number" ? dimension : undefined;
13954
13959
  }
13955
- function rewriteEmbedderConfig(workspacePath, embedder, chunkerConfig = {}) {
13960
+ function configuredLexicalLanguage(workspacePath) {
13961
+ let raw;
13962
+ try {
13963
+ raw = readFileSync16(minSyncConfigPath(workspacePath), "utf8");
13964
+ } catch {
13965
+ return;
13966
+ }
13967
+ const lexical = parse2(raw).lexical;
13968
+ if (typeof lexical !== "object" || lexical === null || Array.isArray(lexical))
13969
+ return "simple";
13970
+ const language = lexical.language;
13971
+ return typeof language === "string" ? language : "simple";
13972
+ }
13973
+ function rewriteEmbedderConfig(workspacePath, embedder, managed = {}) {
13956
13974
  const configPath = minSyncConfigPath(workspacePath);
13957
13975
  let raw;
13958
13976
  try {
@@ -13995,8 +14013,13 @@ function rewriteEmbedderConfig(workspacePath, embedder, chunkerConfig = {}) {
13995
14013
  }
13996
14014
  if (embedder.dimension !== undefined)
13997
14015
  options.dimension = embedder.dimension;
13998
- if (chunkerConfig.maxChunkSize !== undefined)
13999
- chunkerOptions.max_chunk_size = chunkerConfig.maxChunkSize;
14016
+ if (managed.maxChunkSize !== undefined)
14017
+ chunkerOptions.max_chunk_size = managed.maxChunkSize;
14018
+ if (managed.lexicalLanguage !== undefined) {
14019
+ const lexical = typeof parsed.lexical === "object" && parsed.lexical !== null && !Array.isArray(parsed.lexical) ? parsed.lexical : {};
14020
+ parsed.lexical = lexical;
14021
+ lexical.language = managed.lexicalLanguage;
14022
+ }
14000
14023
  writeFileSync9(configPath, stringify(parsed));
14001
14024
  return true;
14002
14025
  }
@@ -17216,6 +17239,66 @@ var init_errors4 = __esm(() => {
17216
17239
  };
17217
17240
  });
17218
17241
 
17242
+ // src/minsync/kiwi.ts
17243
+ import { existsSync as existsSync19 } from "node:fs";
17244
+ import { homedir as homedir5 } from "node:os";
17245
+ import { posix as posix3, win32 as win323 } from "node:path";
17246
+ function kiwiAvailable(environment = defaultEnvironment()) {
17247
+ const { env, exists } = environment;
17248
+ const library = env.KIWI_LIBRARY_PATH;
17249
+ const model = env.KIWI_MODEL_PATH;
17250
+ const libraryFound = library !== undefined && library !== "" ? exists(library) : libraryCandidates(environment).some(exists);
17251
+ const modelFound = model !== undefined && model !== "" ? exists(model) : modelCandidates(environment).some(exists);
17252
+ return libraryFound && modelFound;
17253
+ }
17254
+ function defaultEnvironment() {
17255
+ let home;
17256
+ try {
17257
+ home = homedir5();
17258
+ } catch {
17259
+ home = undefined;
17260
+ }
17261
+ return { env: process.env, platform: process.platform, home, exists: existsSync19 };
17262
+ }
17263
+ function userRoots({ env, platform, home }) {
17264
+ if (platform === "win32") {
17265
+ const roots = [];
17266
+ if (env.LOCALAPPDATA)
17267
+ roots.push(win323.join(env.LOCALAPPDATA, "kiwi"));
17268
+ const profile = env.USERPROFILE ?? home;
17269
+ if (profile)
17270
+ roots.push(win323.join(profile, "AppData", "Local", "kiwi"));
17271
+ roots.push("C:\\kiwi", "C:\\Program Files\\Kiwi");
17272
+ return roots;
17273
+ }
17274
+ return home ? [posix3.join(home, ".local", "kiwi")] : [];
17275
+ }
17276
+ function libraryCandidates(environment) {
17277
+ const { platform } = environment;
17278
+ if (platform === "win32")
17279
+ return userRoots(environment).map((root) => win323.join(root, "lib", "kiwi.dll"));
17280
+ const file = platform === "darwin" ? "libkiwi.dylib" : "libkiwi.so";
17281
+ const system = platform === "darwin" ? ["/usr/local/lib", "/opt/homebrew/lib"] : ["/usr/local/lib", "/usr/lib"];
17282
+ return [
17283
+ ...userRoots(environment).map((root) => posix3.join(root, "lib", file)),
17284
+ ...system.map((dir) => posix3.join(dir, file))
17285
+ ];
17286
+ }
17287
+ function modelCandidates(environment) {
17288
+ const { platform } = environment;
17289
+ if (platform === "win32")
17290
+ return userRoots(environment).map((root) => win323.join(root, "models", "cong", "base"));
17291
+ const system = platform === "darwin" ? ["/usr/local/models", "/opt/homebrew/models", "/usr/local/share/kiwi/models"] : ["/usr/local/models", "/usr/local/share/kiwi/models", "/usr/share/kiwi/models"];
17292
+ return [
17293
+ ...userRoots(environment).map((root) => posix3.join(root, "models", "cong", "base")),
17294
+ ...system.map((dir) => posix3.join(dir, "cong", "base"))
17295
+ ];
17296
+ }
17297
+ var init_kiwi = () => {};
17298
+
17299
+ // src/minsync/lexical-language.ts
17300
+ var MINSYNC_LEXICAL_LANGUAGE = "multilingual";
17301
+
17219
17302
  // src/minsync/local-embedder.ts
17220
17303
  import { spawn as spawn15 } from "node:child_process";
17221
17304
  import { request } from "node:http";
@@ -17404,7 +17487,7 @@ var init_process = __esm(() => {
17404
17487
  });
17405
17488
 
17406
17489
  // src/minsync/client.ts
17407
- import { existsSync as existsSync19, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "node:fs";
17490
+ import { existsSync as existsSync20, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "node:fs";
17408
17491
  import { join as join28 } from "node:path";
17409
17492
 
17410
17493
  class MinSyncClient {
@@ -17499,7 +17582,7 @@ class MinSyncClient {
17499
17582
  return previous === undefined || JSON.stringify(previous) !== JSON.stringify(identity);
17500
17583
  }
17501
17584
  async sync(force = false, signal) {
17502
- if (!existsSync19(this.binaryPath))
17585
+ if (!existsSync20(this.binaryPath))
17503
17586
  throw minSyncBinaryMissingError();
17504
17587
  let effective = { config: {} };
17505
17588
  try {
@@ -17537,14 +17620,16 @@ class MinSyncClient {
17537
17620
  }
17538
17621
  }
17539
17622
  const spawnOptions = signal === undefined ? {} : { signal };
17540
- const initialized = existsSync19(minSyncConfigPath(this.workspacePath));
17623
+ const lexical = this.resolveLexicalLanguage();
17624
+ const initialized = existsSync20(minSyncConfigPath(this.workspacePath));
17541
17625
  if (!initialized) {
17542
17626
  const initArgs = ["init", "--format", "json"];
17543
17627
  if (this.embedder?.id) {
17544
17628
  initArgs.push("--embedder", this.embedder.id);
17545
17629
  }
17630
+ initArgs.push("--language", lexical.language);
17546
17631
  const init = await this.spawn(initArgs, spawnOptions);
17547
- if (!init.ok || !existsSync19(minSyncConfigPath(this.workspacePath))) {
17632
+ if (!init.ok || !existsSync20(minSyncConfigPath(this.workspacePath))) {
17548
17633
  return {
17549
17634
  ok: false,
17550
17635
  synced: 0,
@@ -17556,20 +17641,29 @@ class MinSyncClient {
17556
17641
  const chunkSizeChanged = this.maxChunkSize !== undefined && configuredMaxChunkSize(this.workspacePath) !== this.maxChunkSize;
17557
17642
  const dimensionChanged = embedder.dimension !== undefined && configuredVectorDimension(this.workspacePath) !== embedder.dimension;
17558
17643
  const configPath = minSyncConfigPath(this.workspacePath);
17559
- const shouldRewriteConfig = this.embedder !== undefined || this.maxChunkSize !== undefined || effective.identity !== undefined;
17560
- const originalConfig = shouldRewriteConfig ? readConfigSnapshot(configPath) : undefined;
17561
- const configRewritten = shouldRewriteConfig && rewriteEmbedderConfig(this.workspacePath, embedder, { maxChunkSize: this.maxChunkSize });
17644
+ const originalConfig = readConfigSnapshot(configPath);
17645
+ const configRewritten = rewriteEmbedderConfig(this.workspacePath, embedder, {
17646
+ maxChunkSize: this.maxChunkSize,
17647
+ lexicalLanguage: lexical.language
17648
+ });
17562
17649
  const restoreConfig = () => {
17563
17650
  if (configRewritten && originalConfig !== undefined)
17564
17651
  writeFileSync14(configPath, originalConfig);
17565
17652
  };
17566
17653
  try {
17567
- return await this.checkAndSync(force || chunkSizeChanged || dimensionChanged, signal, effective, restoreConfig);
17654
+ const result = await this.checkAndSync(force || chunkSizeChanged || dimensionChanged, signal, effective, restoreConfig);
17655
+ return result.ok && lexical.fallback !== undefined ? { ...result, lexicalFallback: lexical.fallback } : result;
17568
17656
  } catch (error) {
17569
17657
  restoreConfig();
17570
17658
  throw error;
17571
17659
  }
17572
17660
  }
17661
+ resolveLexicalLanguage() {
17662
+ const requested = MINSYNC_LEXICAL_LANGUAGE;
17663
+ if (kiwiAvailable() || configuredLexicalLanguage(this.workspacePath) === requested)
17664
+ return { language: requested };
17665
+ return { language: "simple", fallback: { requested, used: "simple" } };
17666
+ }
17573
17667
  lockHeldResult(stderr) {
17574
17668
  return {
17575
17669
  ok: false,
@@ -17622,7 +17716,7 @@ class MinSyncClient {
17622
17716
  }
17623
17717
  const identityChanged = effective.identity !== undefined && this.identityMismatch(effective.identity);
17624
17718
  const fullReindex = forceFull || identityChanged;
17625
- const syncArgs = existsSync19(cursorPath) && !fullReindex ? ["sync", "--format", "json"] : ["sync", "--full", "--format", "json"];
17719
+ const syncArgs = existsSync20(cursorPath) && !fullReindex ? ["sync", "--format", "json"] : ["sync", "--full", "--format", "json"];
17626
17720
  const result = await this.spawn(syncArgs, spawnOptions);
17627
17721
  if (!result.ok && result.code === MINSYNC_LOCK_HELD_EXIT_CODE) {
17628
17722
  restoreConfig();
@@ -17643,7 +17737,7 @@ class MinSyncClient {
17643
17737
  }
17644
17738
  };
17645
17739
  }
17646
- if (!existsSync19(cursorPath)) {
17740
+ if (!existsSync20(cursorPath)) {
17647
17741
  restoreConfig();
17648
17742
  return { ok: false, synced: 0, workspacePath: this.workspacePath, reason: "not-ready: missing cursor" };
17649
17743
  }
@@ -17664,7 +17758,7 @@ class MinSyncClient {
17664
17758
  };
17665
17759
  }
17666
17760
  async query(text, topK, mode = "vector", signal) {
17667
- if (!existsSync19(this.binaryPath))
17761
+ if (!existsSync20(this.binaryPath))
17668
17762
  throw minSyncBinaryMissingError();
17669
17763
  const result = await this.spawn(["query", "--format", "json", "--mode", mode, "-k", String(topK), text], {
17670
17764
  ...signal === undefined ? {} : { signal }
@@ -17758,6 +17852,7 @@ var MINSYNC_OLLAMA_MIGRATION_MESSAGE = "This workspace uses the legacy 768-dimen
17758
17852
  var init_client10 = __esm(() => {
17759
17853
  init_embedder_config();
17760
17854
  init_errors4();
17855
+ init_kiwi();
17761
17856
  init_local_embedder();
17762
17857
  init_process();
17763
17858
  API_KEY_ENV_PATTERN2 = /^[A-Za-z_][A-Za-z0-9_]*$/;
@@ -17780,14 +17875,14 @@ var init_client10 = __esm(() => {
17780
17875
 
17781
17876
  // src/minsync/installer.ts
17782
17877
  import { createHash as createHash14 } from "node:crypto";
17783
- import { copyFileSync, createWriteStream as createWriteStream2, existsSync as existsSync20, mkdirSync as mkdirSync17, renameSync as renameSync6, rmSync as rmSync6 } from "node:fs";
17878
+ import { copyFileSync, createWriteStream as createWriteStream2, existsSync as existsSync21, mkdirSync as mkdirSync17, renameSync as renameSync6, rmSync as rmSync6 } from "node:fs";
17784
17879
  import { chmod as chmod2, mkdtemp } from "node:fs/promises";
17785
17880
  import { get } from "node:https";
17786
17881
  import { basename as basename10, delimiter as delimiter4, dirname as dirname18, join as join29 } from "node:path";
17787
17882
  import { pipeline } from "node:stream/promises";
17788
17883
  async function ensureMinSyncBinary(options) {
17789
17884
  const binaryPath = join29(options.root, ".autorag", "bin", executableName(options.platform ?? process.platform));
17790
- if (existsSync20(binaryPath))
17885
+ if (existsSync21(binaryPath))
17791
17886
  return { binaryPath, version: "cached" };
17792
17887
  mkdirSync17(dirname18(binaryPath), { recursive: true });
17793
17888
  const cargoInstaller = options.cargoInstaller ?? ((destination) => installMinSyncFromCargoIfAvailable(options, destination));
@@ -17835,7 +17930,7 @@ async function installMinSyncFromCargo(options, destination, cargo) {
17835
17930
  throw new MinSyncReleaseError(result.stderr || `Could not install MinSync ${MINSYNC_VERSION} from crates.io`);
17836
17931
  }
17837
17932
  const installedBinary = join29(cargoRoot, "bin", executableName(options.platform ?? process.platform));
17838
- if (!existsSync20(installedBinary)) {
17933
+ if (!existsSync21(installedBinary)) {
17839
17934
  throw new MinSyncReleaseError(`Cargo did not produce the MinSync ${MINSYNC_VERSION} binary`);
17840
17935
  }
17841
17936
  copyFileSync(installedBinary, destination);
@@ -17967,7 +18062,7 @@ function lookupCargo(env, platform) {
17967
18062
  if (dir.length === 0)
17968
18063
  continue;
17969
18064
  const candidate = join29(dir, execName);
17970
- if (existsSync20(candidate))
18065
+ if (existsSync21(candidate))
17971
18066
  return candidate;
17972
18067
  }
17973
18068
  return;
@@ -17998,7 +18093,7 @@ function isGitHubAsset(value) {
17998
18093
  function isRecord12(value) {
17999
18094
  return typeof value === "object" && value !== null;
18000
18095
  }
18001
- var LATEST_RELEASE_URL = "https://api.github.com/repos/NomaDamas/MinSync/releases/latest", SHA256_HEX_PATTERN, MINSYNC_VERSION = "0.4.8", MINSYNC_CARGO_INSTALL_TIMEOUT_MS, CARGO_PROBE_TIMEOUT_MS = 5000, MinSyncReleaseError;
18096
+ var LATEST_RELEASE_URL = "https://api.github.com/repos/NomaDamas/MinSync/releases/latest", SHA256_HEX_PATTERN, MINSYNC_VERSION = "0.4.10", MINSYNC_CARGO_INSTALL_TIMEOUT_MS, CARGO_PROBE_TIMEOUT_MS = 5000, MinSyncReleaseError;
18002
18097
  var init_installer2 = __esm(() => {
18003
18098
  init_process();
18004
18099
  SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/;
@@ -18026,7 +18121,7 @@ var init_paths5 = __esm(() => {
18026
18121
  import { randomUUID as randomUUID5 } from "node:crypto";
18027
18122
  import {
18028
18123
  copyFileSync as copyFileSync2,
18029
- existsSync as existsSync21,
18124
+ existsSync as existsSync22,
18030
18125
  lstatSync as lstatSync2,
18031
18126
  mkdirSync as mkdirSync18,
18032
18127
  readdirSync as readdirSync2,
@@ -18041,8 +18136,8 @@ function syncMinSyncWorkspace(root, options = {}) {
18041
18136
  const filesRoot = join31(workspacePath, "files");
18042
18137
  const index = loadMirrorIndex(root);
18043
18138
  const candidates = Object.values(index.entries).sort((a, b) => a.virtualPath.localeCompare(b.virtualPath));
18044
- const excluded = candidates.filter((entry) => normalizeVirtualPath(entry.virtualPath) !== entry.virtualPath || !existsSync21(entry.outputPath)).map((entry) => entry.virtualPath);
18045
- const entries = candidates.filter((entry) => normalizeVirtualPath(entry.virtualPath) === entry.virtualPath && existsSync21(entry.outputPath)).map((entry) => {
18139
+ const excluded = candidates.filter((entry) => normalizeVirtualPath(entry.virtualPath) !== entry.virtualPath || !existsSync22(entry.outputPath)).map((entry) => entry.virtualPath);
18140
+ const entries = candidates.filter((entry) => normalizeVirtualPath(entry.virtualPath) === entry.virtualPath && existsSync22(entry.outputPath)).map((entry) => {
18046
18141
  const minSyncPath = minSyncDocumentPath(workspacePath, entry.virtualPath);
18047
18142
  return {
18048
18143
  virtualPath: entry.virtualPath,
@@ -18179,7 +18274,7 @@ var init_workspace = __esm(() => {
18179
18274
  });
18180
18275
 
18181
18276
  // src/minsync/method.ts
18182
- import { accessSync as accessSync2, constants as constants2, existsSync as existsSync22, realpathSync as realpathSync4 } from "node:fs";
18277
+ import { accessSync as accessSync2, constants as constants2, existsSync as existsSync23, realpathSync as realpathSync4 } from "node:fs";
18183
18278
  import { basename as basename12, delimiter as delimiter5, join as join32, normalize as normalize3 } from "node:path";
18184
18279
  function lookupInPath2(env) {
18185
18280
  const pathEnv = env.PATH;
@@ -18275,19 +18370,19 @@ class MinSyncVectorMethod {
18275
18370
  }
18276
18371
  isBinaryMissing() {
18277
18372
  if (this.binaryPath !== undefined)
18278
- return !existsSync22(this.binaryPath);
18373
+ return !existsSync23(this.binaryPath);
18279
18374
  if (lookupInPath2(process.env) !== undefined)
18280
18375
  return false;
18281
18376
  const cachedBinary = join32(this.root, ".autorag", "bin", executableName(process.platform));
18282
- if (existsSync22(cachedBinary))
18377
+ if (existsSync23(cachedBinary))
18283
18378
  return false;
18284
18379
  return true;
18285
18380
  }
18286
18381
  isExplicitBinaryMissing() {
18287
- return this.binaryPath !== undefined && !existsSync22(this.binaryPath);
18382
+ return this.binaryPath !== undefined && !existsSync23(this.binaryPath);
18288
18383
  }
18289
18384
  isReady() {
18290
- return existsSync22(join32(this.workspacePath, ".minsync", "cursor.json"));
18385
+ return existsSync23(join32(this.workspacePath, ".minsync", "cursor.json"));
18291
18386
  }
18292
18387
  async retrieve(query, options) {
18293
18388
  if ((workspaceSyncInFlight.get(this.workspacePath) ?? 0) > 0) {
@@ -18340,7 +18435,7 @@ class MinSyncVectorMethod {
18340
18435
  return results;
18341
18436
  }
18342
18437
  async resolveBinary(options = {}) {
18343
- if (this.binaryPath && existsSync22(this.binaryPath))
18438
+ if (this.binaryPath && existsSync23(this.binaryPath))
18344
18439
  return this.binaryPath;
18345
18440
  if (this.binaryPath !== undefined)
18346
18441
  throw minSyncBinaryMissingError();
@@ -18348,7 +18443,7 @@ class MinSyncVectorMethod {
18348
18443
  if (pathBinary)
18349
18444
  return pathBinary;
18350
18445
  const cachedBinary = join32(this.root, ".autorag", "bin", executableName(process.platform));
18351
- if (existsSync22(cachedBinary))
18446
+ if (existsSync23(cachedBinary))
18352
18447
  return cachedBinary;
18353
18448
  if (this.autoInstall && options.install !== false) {
18354
18449
  try {
@@ -18401,7 +18496,7 @@ var init_minsync = __esm(() => {
18401
18496
  });
18402
18497
 
18403
18498
  // src/parser/isolated.ts
18404
- import { existsSync as existsSync23 } from "node:fs";
18499
+ import { existsSync as existsSync24 } from "node:fs";
18405
18500
  import { dirname as dirname20, join as join33 } from "node:path";
18406
18501
  import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
18407
18502
  import { Worker } from "node:worker_threads";
@@ -18540,7 +18635,7 @@ function defaultWorkerUrl() {
18540
18635
  join33(here, "parser", "parse-worker.js"),
18541
18636
  join33(here, "..", "parser", "parse-worker.js")
18542
18637
  ]) {
18543
- if (existsSync23(candidate))
18638
+ if (existsSync24(candidate))
18544
18639
  return pathToFileURL2(candidate);
18545
18640
  }
18546
18641
  throw new Error(`The AutoRAG parse worker entry was not found near ${here}.`);
@@ -18552,7 +18647,7 @@ var init_isolated = __esm(() => {
18552
18647
  });
18553
18648
 
18554
18649
  // src/mirror/sync.ts
18555
- import { existsSync as existsSync24, rmSync as rmSync8 } from "node:fs";
18650
+ import { existsSync as existsSync25, rmSync as rmSync8 } from "node:fs";
18556
18651
  import { opendir, readFile as readFile5, stat as stat7 } from "node:fs/promises";
18557
18652
  import { extname as extname5, resolve as resolve15, sep as sep7 } from "node:path";
18558
18653
  import { setTimeout as sleep3 } from "node:timers/promises";
@@ -18744,7 +18839,7 @@ async function syncWithRegistry(options, registry, isolated) {
18744
18839
  }
18745
18840
  const previousEntry = previous.entries[entry.virtualPath];
18746
18841
  const outputPath = parsedOutputPath(options.root, entry.virtualPath);
18747
- const unchanged = !options.force && previousEntry?.sourceMtimeNs === entry.mtimeNs && previousEntry.sourceSizeBytes === entry.sizeBytes && previousEntry.parserName === parser.name && existsSync24(outputPath);
18842
+ const unchanged = !options.force && previousEntry?.sourceMtimeNs === entry.mtimeNs && previousEntry.sourceSizeBytes === entry.sizeBytes && previousEntry.parserName === parser.name && existsSync25(outputPath);
18748
18843
  if (!unchanged) {
18749
18844
  let bytes;
18750
18845
  try {
@@ -19125,7 +19220,7 @@ function removePrevious(root, index, virtualPath) {
19125
19220
  return 1;
19126
19221
  }
19127
19222
  function removeFile(path) {
19128
- if (existsSync24(path))
19223
+ if (existsSync25(path))
19129
19224
  rmSync8(path, { force: true });
19130
19225
  }
19131
19226
  var DEFAULT_MAX_SOURCE_BYTES, MIRROR_CHECKPOINT_EVERY = 25, DEFAULT_MAX_DELETE_RATIO = 0.3, MASS_DELETE_FLOOR = 100, DEFAULT_MAX_SCANNED_ENTRIES = 2000000, DEFAULT_MAX_WALK_DEPTH = 64, PRODUCT_ARTIFACT_FILE_NAMES, SKIP_DIR_NAMES, PARSER_FAILURE_MESSAGE = "The registered parser failed on this file; it was skipped during indexing.", PARSER_CRASH_MESSAGE = "The registered parser crashed unexpectedly on this file; it was skipped during indexing.", DEFAULT_TRANSIENT_RETRIES = 3, DEFAULT_TRANSIENT_BACKOFF_MS = 50, TRANSIENT_ERROR_CODES, WORKSPACE_ERROR_CODES, MirrorWorkspaceError, STANDALONE_OCR_IMAGE_EXTENSIONS, RETRYABLE_SKIP_MESSAGES, SEARCH_ROOT_UNAVAILABLE_MESSAGES;
@@ -22080,6 +22175,20 @@ function interactiveKeyOf(data) {
22080
22175
  return "followUp";
22081
22176
  return;
22082
22177
  }
22178
+ function stripTelegramEnvelope(text) {
22179
+ return text.replace(/^\s*\[telegram\]\s?/u, "").replace(/\n*\[time\][^\n]*/gu, "").trim();
22180
+ }
22181
+ function userInputText(event) {
22182
+ if (event.source === "interactive")
22183
+ return event.text.trim();
22184
+ if (event.source === "extension" && event.text.trimStart().startsWith(TELEGRAM_INPUT_PREFIX)) {
22185
+ return stripTelegramEnvelope(event.text);
22186
+ }
22187
+ if (event.source === "extension" && event.text.trimStart().startsWith(WHATSAPP_INPUT_PREFIX)) {
22188
+ return event.text.trim();
22189
+ }
22190
+ return;
22191
+ }
22083
22192
  function createAutoRAGInteractiveExtension(getSystemPrompt, contextTransform, options, updateNotice) {
22084
22193
  return (pi) => {
22085
22194
  pi.on("before_agent_start", () => ({ systemPrompt: getSystemPrompt() }));
@@ -22110,8 +22219,8 @@ function createAutoRAGInteractiveExtension(getSystemPrompt, contextTransform, op
22110
22219
  });
22111
22220
  }
22112
22221
  pi.on("input", (event) => {
22113
- const text = event.text.trim();
22114
- if (event.source !== "interactive" || text.length === 0)
22222
+ const text = userInputText(event);
22223
+ if (text === undefined || text.length === 0)
22115
22224
  return;
22116
22225
  if (text.startsWith("/") && isSearchRunning?.() !== true)
22117
22226
  return;
@@ -22208,7 +22317,9 @@ async function createAutoRAGPiInteractiveRuntime(options) {
22208
22317
  agentDir: runtimeAgentDir,
22209
22318
  modelRuntime,
22210
22319
  settingsManager,
22320
+ ...options.extensionFlags === undefined ? {} : { extensionFlagValues: new Map(Object.entries(options.extensionFlags)) },
22211
22321
  resourceLoaderOptions: {
22322
+ ...options.extensionPaths === undefined ? {} : { additionalExtensionPaths: [...options.extensionPaths] },
22212
22323
  extensionFactories: [
22213
22324
  createAutoRAGInteractiveExtension(options.getSystemPrompt, options.contextTransform, { onInput: options.onInput, isSearchRunning: options.isSearchRunning, onKey: options.onKey }, options.updateNotice),
22214
22325
  ...options.extensionFactories ?? []
@@ -22242,7 +22353,7 @@ async function createAutoRAGPiInteractiveRuntime(options) {
22242
22353
  });
22243
22354
  return { runtime, dispose: () => runtime.dispose() };
22244
22355
  }
22245
- var PI_BUILTIN_TOOL_NAMES, EMBEDDED_AUTH_MARKER = "<autorag-embedded-model>";
22356
+ var PI_BUILTIN_TOOL_NAMES, EMBEDDED_AUTH_MARKER = "<autorag-embedded-model>", TELEGRAM_INPUT_PREFIX = "[telegram]", WHATSAPP_INPUT_PREFIX = "Message from ";
22246
22357
  var init_pi_session = __esm(() => {
22247
22358
  init_provider();
22248
22359
  init_context_token_guard();
@@ -22498,7 +22609,7 @@ var init_jikji_find_tool = __esm(() => {
22498
22609
 
22499
22610
  // src/p2p/approval-store.ts
22500
22611
  import {
22501
- existsSync as existsSync25,
22612
+ existsSync as existsSync26,
22502
22613
  mkdirSync as mkdirSync20,
22503
22614
  readdirSync as readdirSync3,
22504
22615
  readFileSync as readFileSync22,
@@ -22563,7 +22674,7 @@ function savePendingPeerRequest(workspacePath, request) {
22563
22674
  }
22564
22675
  function loadPendingPeerRequest(workspacePath, id) {
22565
22676
  const path = requestPath(workspacePath, id);
22566
- if (!existsSync25(path))
22677
+ if (!existsSync26(path))
22567
22678
  return;
22568
22679
  try {
22569
22680
  return parsePending(JSON.parse(readFileSync22(path, "utf8")));
@@ -22573,7 +22684,7 @@ function loadPendingPeerRequest(workspacePath, id) {
22573
22684
  }
22574
22685
  function listPendingPeerRequests(workspacePath) {
22575
22686
  const dir = requestsDir(workspacePath);
22576
- if (!existsSync25(dir))
22687
+ if (!existsSync26(dir))
22577
22688
  return [];
22578
22689
  const requests = [];
22579
22690
  for (const name of readdirSync3(dir)) {
@@ -22595,7 +22706,7 @@ function writePeerRequestDecision(workspacePath, id, decision) {
22595
22706
  }
22596
22707
  function loadPeerRequestDecision(workspacePath, id) {
22597
22708
  const path = decisionPath(workspacePath, id);
22598
- if (!existsSync25(path))
22709
+ if (!existsSync26(path))
22599
22710
  return;
22600
22711
  try {
22601
22712
  return parseDecision(JSON.parse(readFileSync22(path, "utf8")));
@@ -22929,7 +23040,7 @@ var init_egress_gate = __esm(() => {
22929
23040
  });
22930
23041
 
22931
23042
  // src/p2p/policy.ts
22932
- import { existsSync as existsSync26, mkdirSync as mkdirSync21, readFileSync as readFileSync23, writeFileSync as writeFileSync17 } from "node:fs";
23043
+ import { existsSync as existsSync27, mkdirSync as mkdirSync21, readFileSync as readFileSync23, writeFileSync as writeFileSync17 } from "node:fs";
22933
23044
  import { basename as basename13, dirname as dirname23, join as join36, resolve as resolve16 } from "node:path";
22934
23045
  import { parse as parseToml2 } from "smol-toml";
22935
23046
  function isRecord17(value) {
@@ -23009,7 +23120,7 @@ function normalizePolicyEntries(value, workspacePath, context) {
23009
23120
  return entries;
23010
23121
  }
23011
23122
  function readJsonFile(path) {
23012
- if (!existsSync26(path))
23123
+ if (!existsSync27(path))
23013
23124
  return;
23014
23125
  let raw;
23015
23126
  try {
@@ -23024,7 +23135,7 @@ function readJsonFile(path) {
23024
23135
  }
23025
23136
  }
23026
23137
  function readTomlFile(path) {
23027
- if (!existsSync26(path))
23138
+ if (!existsSync27(path))
23028
23139
  return;
23029
23140
  let raw;
23030
23141
  try {
@@ -23165,7 +23276,7 @@ function globToRegExp(pattern) {
23165
23276
  return new RegExp(`${expression}$`, "u");
23166
23277
  }
23167
23278
  function loadSeenSources(path, workspacePath) {
23168
- if (!existsSync26(path))
23279
+ if (!existsSync27(path))
23169
23280
  return { promoted: new Set, pending: new Set };
23170
23281
  let parsed;
23171
23282
  try {
@@ -23472,7 +23583,7 @@ var init_wire = __esm(() => {
23472
23583
 
23473
23584
  // src/p2p/simplex-query-store.ts
23474
23585
  import { randomUUID as randomUUID6 } from "node:crypto";
23475
- import { existsSync as existsSync27, mkdirSync as mkdirSync22, readdirSync as readdirSync4, readFileSync as readFileSync24, renameSync as renameSync9, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "node:fs";
23586
+ import { existsSync as existsSync28, mkdirSync as mkdirSync22, readdirSync as readdirSync4, readFileSync as readFileSync24, renameSync as renameSync9, unlinkSync as unlinkSync5, writeFileSync as writeFileSync18 } from "node:fs";
23476
23587
  import { dirname as dirname24, join as join37 } from "node:path";
23477
23588
  import { Value as Value2 } from "typebox/value";
23478
23589
  function statePath(workspacePath, directory, id) {
@@ -23508,7 +23619,7 @@ function parseState(value) {
23508
23619
  return value;
23509
23620
  }
23510
23621
  function readState(path) {
23511
- if (!existsSync27(path))
23622
+ if (!existsSync28(path))
23512
23623
  return;
23513
23624
  try {
23514
23625
  return parseState(JSON.parse(readFileSync24(path, "utf8")));
@@ -23535,14 +23646,14 @@ function moveState(workspacePath, from, to, state) {
23535
23646
  }
23536
23647
  function recoverClaims(workspacePath) {
23537
23648
  const directory = join37(workspacePath, QUERY_DIR, PENDING_DIR);
23538
- if (!existsSync27(directory))
23649
+ if (!existsSync28(directory))
23539
23650
  return;
23540
23651
  for (const name of readdirSync4(directory)) {
23541
23652
  if (!name.endsWith(".json.claim"))
23542
23653
  continue;
23543
23654
  const claim = join37(directory, name);
23544
23655
  const restored = join37(directory, name.slice(0, -".claim".length));
23545
- if (!existsSync27(restored))
23656
+ if (!existsSync28(restored))
23546
23657
  renameSync9(claim, restored);
23547
23658
  }
23548
23659
  }
@@ -23566,7 +23677,7 @@ function loadSimplexQueryState(workspacePath, id) {
23566
23677
  }
23567
23678
  function listPendingSimplexQueryStates(workspacePath) {
23568
23679
  const directory = join37(workspacePath, QUERY_DIR, PENDING_DIR);
23569
- if (!existsSync27(directory))
23680
+ if (!existsSync28(directory))
23570
23681
  return [];
23571
23682
  return readdirSync4(directory).filter((name) => name.endsWith(".json")).map((name) => readState(join37(directory, name))).filter((state) => state?.status === "pending").filter((state) => loadSimplexQueryState(workspacePath, state.id)?.status === "pending").sort((left, right) => left.createdAt.localeCompare(right.createdAt));
23572
23683
  }
@@ -23709,7 +23820,7 @@ var init_simplex_query_store = __esm(() => {
23709
23820
  });
23710
23821
 
23711
23822
  // src/p2p/simplex-server.ts
23712
- import { existsSync as existsSync28, mkdirSync as mkdirSync23, readFileSync as readFileSync25, writeFileSync as writeFileSync19 } from "node:fs";
23823
+ import { existsSync as existsSync29, mkdirSync as mkdirSync23, readFileSync as readFileSync25, writeFileSync as writeFileSync19 } from "node:fs";
23713
23824
  import { join as join38 } from "node:path";
23714
23825
  import { Type as Type10 } from "typebox";
23715
23826
  import { Value as Value3 } from "typebox/value";
@@ -23738,7 +23849,7 @@ function rankSimplexPeerTargets(query, registry) {
23738
23849
  }
23739
23850
  function loadSimplexPeerRegistry(workspacePath) {
23740
23851
  const path = join38(workspacePath, PEERS_DIR, PEERS_FILENAME);
23741
- if (!existsSync28(path))
23852
+ if (!existsSync29(path))
23742
23853
  return {};
23743
23854
  try {
23744
23855
  const parsed = JSON.parse(readFileSync25(path, "utf8"));
@@ -24850,14 +24961,14 @@ var init_search_single_datasource_tool = __esm(() => {
24850
24961
  });
24851
24962
 
24852
24963
  // src/agent/self-config.ts
24853
- import { existsSync as existsSync29, readFileSync as readFileSync27, rmSync as rmSync10, writeFileSync as writeFileSync21 } from "node:fs";
24964
+ import { existsSync as existsSync30, readFileSync as readFileSync27, rmSync as rmSync10, writeFileSync as writeFileSync21 } from "node:fs";
24854
24965
  import { dirname as dirname26, join as join39 } from "node:path";
24855
24966
  import { fileURLToPath as fileURLToPath3 } from "node:url";
24856
24967
  function findPackagedSetupSkill() {
24857
24968
  let dir = dirname26(fileURLToPath3(import.meta.url));
24858
24969
  for (let depth = 0;depth < 6; depth += 1) {
24859
24970
  const candidate = join39(dir, SETUP_SKILL_RELATIVE_PATH);
24860
- if (existsSync29(candidate))
24971
+ if (existsSync30(candidate))
24861
24972
  return candidate;
24862
24973
  const parent = dirname26(dir);
24863
24974
  if (parent === dir)
@@ -24880,7 +24991,7 @@ function loadSetupSkill(skillPath) {
24880
24991
  return text.replace(/^---\r?\n[\s\S]*?\r?\n---(?:\r?\n)+/u, "");
24881
24992
  }
24882
24993
  function snapshotConfigFile(configPath) {
24883
- return existsSync29(configPath) ? readFileSync27(configPath, "utf8") : undefined;
24994
+ return existsSync30(configPath) ? readFileSync27(configPath, "utf8") : undefined;
24884
24995
  }
24885
24996
  async function rollbackIfBroken(options, before) {
24886
24997
  if (options.validate === undefined)
@@ -28681,12 +28792,11 @@ function eventAnswer(event) {
28681
28792
  }
28682
28793
  return "";
28683
28794
  }
28684
- var PERPLEXITY_ASK_URL = "https://www.perplexity.ai/rest/sse/perplexity_ask", API_VERSION2 = "2.18", DEFLECTION_PATTERN, MAX_ERROR_BYTES4, ANONYMOUS_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", PERPLEXITY_QUERY_SYNTAX, PerplexityProvider;
28795
+ var PERPLEXITY_ASK_URL = "https://www.perplexity.ai/rest/sse/perplexity_ask", API_VERSION2 = "2.18", MAX_ERROR_BYTES4, ANONYMOUS_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", PERPLEXITY_QUERY_SYNTAX, PerplexityProvider;
28685
28796
  var init_perplexity = __esm(() => {
28686
28797
  init_query();
28687
28798
  init_types4();
28688
28799
  init_utils();
28689
- DEFLECTION_PATTERN = /sign up|log in|create an account|verify you are human|are you a robot|가입|로그인|회원 ?가입/i;
28690
28800
  MAX_ERROR_BYTES4 = 8 * 1024;
28691
28801
  PERPLEXITY_QUERY_SYNTAX = {
28692
28802
  phrases: true,
@@ -28800,8 +28910,8 @@ var init_perplexity = __esm(() => {
28800
28910
  break;
28801
28911
  }
28802
28912
  const sources = [...sourcesByUrl.values()];
28803
- if (sources.length === 0 && DEFLECTION_PATTERN.test(answer)) {
28804
- throw new SearchProviderError("perplexity", "Perplexity anonymous ask was deflected with a sign-up wall", 403);
28913
+ if (sources.length === 0) {
28914
+ throw new SearchProviderError("perplexity", `Perplexity anonymous ask returned no web sources (sign-up wall or conversational deflection): ${answer.slice(0, 120)}`, 403);
28805
28915
  }
28806
28916
  const numResults = params.numSearchResults ?? params.limit;
28807
28917
  return {
@@ -29219,7 +29329,7 @@ var init_browser_headers = __esm(() => {
29219
29329
  });
29220
29330
 
29221
29331
  // src/web/search/browser-loader.ts
29222
- import { existsSync as existsSync30 } from "node:fs";
29332
+ import { existsSync as existsSync31 } from "node:fs";
29223
29333
  async function closeBounded(close) {
29224
29334
  let timer;
29225
29335
  try {
@@ -29298,7 +29408,7 @@ function ensureWebSearchBrowserLoader(options = {}) {
29298
29408
  return true;
29299
29409
  const exists = options.exists ?? ((path) => {
29300
29410
  try {
29301
- return existsSync30(path);
29411
+ return existsSync31(path);
29302
29412
  } catch {
29303
29413
  return false;
29304
29414
  }
@@ -29403,6 +29513,8 @@ function normalizeText(value) {
29403
29513
  function isChallengeResponse(page) {
29404
29514
  if (/\/(?:errors|captcha)\//.test(page.url) || page.url.includes("/sp/captcha"))
29405
29515
  return true;
29516
+ if (ANUBIS_CHALLENGE_PATTERN.test(page.html))
29517
+ return true;
29406
29518
  return page.html.includes("component---src-pages-captcha") || page.html.includes("/sp/captcha");
29407
29519
  }
29408
29520
  function parseSearchFormInputs(html) {
@@ -29507,7 +29619,7 @@ async function callStartpageHtml(params) {
29507
29619
  });
29508
29620
  }
29509
29621
  if (isChallengeResponse(page)) {
29510
- throw new SearchProviderError("startpage", "Startpage blocked the request with a CAPTCHA challenge. Startpage rate-limits automated searches from datacenter/shared-egress IPs; try another provider such as DuckDuckGo or Mojeek, or retry later.", 429);
29622
+ throw new SearchProviderError("startpage", "Startpage blocked the request with a bot challenge (CAPTCHA or Anubis proof-of-work). Startpage rate-limits automated searches from datacenter/shared-egress IPs; try another provider such as DuckDuckGo or Mojeek, or retry later.", 429);
29511
29623
  }
29512
29624
  if (page.status < 200 || page.status >= 300) {
29513
29625
  const classified = classifyProviderHttpError("startpage", page.status, page.html);
@@ -29533,7 +29645,7 @@ async function searchStartpage(params) {
29533
29645
  }
29534
29646
  return { provider: "startpage", sources };
29535
29647
  }
29536
- var STARTPAGE_HOME_URL = "https://www.startpage.com/", STARTPAGE_SEARCH_URL = "https://www.startpage.com/sp/search", DEFAULT_NUM_RESULTS3 = 10, MAX_NUM_RESULTS3 = 20, RECENCY_TO_STARTPAGE_WITH_DATE, StartpageProvider;
29648
+ var STARTPAGE_HOME_URL = "https://www.startpage.com/", STARTPAGE_SEARCH_URL = "https://www.startpage.com/sp/search", DEFAULT_NUM_RESULTS3 = 10, MAX_NUM_RESULTS3 = 20, RECENCY_TO_STARTPAGE_WITH_DATE, ANUBIS_CHALLENGE_PATTERN, StartpageProvider;
29537
29649
  var init_startpage = __esm(() => {
29538
29650
  init_entities();
29539
29651
  init_browser_page();
@@ -29546,6 +29658,7 @@ var init_startpage = __esm(() => {
29546
29658
  month: "m",
29547
29659
  year: "y"
29548
29660
  };
29661
+ ANUBIS_CHALLENGE_PATTERN = /<script\b[^>]*\bid\s*=\s*["']anubis_(?:challenge|version)["']/i;
29549
29662
  StartpageProvider = class StartpageProvider extends SearchProvider {
29550
29663
  id = "startpage";
29551
29664
  label = "Startpage";
@@ -30763,7 +30876,7 @@ var init_web_search_tool = __esm(() => {
30763
30876
 
30764
30877
  // src/agent/agent.ts
30765
30878
  import { randomUUID as randomUUID8 } from "node:crypto";
30766
- import { existsSync as existsSync31, watch as fsWatch, mkdirSync as mkdirSync25, realpathSync as realpathSync5, statSync as statSync3, writeFileSync as writeFileSync22 } from "node:fs";
30879
+ import { existsSync as existsSync32, watch as fsWatch, mkdirSync as mkdirSync25, realpathSync as realpathSync5, statSync as statSync3, writeFileSync as writeFileSync22 } from "node:fs";
30767
30880
  import { basename as basename14, dirname as dirname27, join as join41, resolve as resolve17 } from "node:path";
30768
30881
  import { clampThinkingLevel } from "@earendil-works/pi-ai/compat";
30769
30882
  import { getAgentDir as getAgentDir4 } from "@earendil-works/pi-coding-agent";
@@ -30894,6 +31007,9 @@ class AutoRAGAgent {
30894
31007
  piSessionDir;
30895
31008
  persistPiSessions;
30896
31009
  updateNotice;
31010
+ piExtensionPaths;
31011
+ piExtensionFlags;
31012
+ telegram;
30897
31013
  boundPiRuntime;
30898
31014
  memoryEmbedder;
30899
31015
  memoryVectorPath;
@@ -30919,6 +31035,9 @@ class AutoRAGAgent {
30919
31035
  this.piSessionDir = options.piSessionDir;
30920
31036
  this.persistPiSessions = options.persistPiSessions ?? true;
30921
31037
  this.updateNotice = options.updateNotice;
31038
+ this.piExtensionPaths = options.piExtensionPaths;
31039
+ this.piExtensionFlags = options.piExtensionFlags;
31040
+ this.telegram = options.telegram === true;
30922
31041
  this.datasourceSkills = options.datasourceSkills ?? [];
30923
31042
  this.datasourceRetry = options.datasourceRetry ?? {};
30924
31043
  this.datasourceVirtualScopePrefixes = this.datasourceSkills.map((skill) => normalizeVirtualPath2(`/${skill.describe().name}`));
@@ -31390,6 +31509,7 @@ ${context.text}
31390
31509
  }
31391
31510
  async createPiInteractiveRuntime() {
31392
31511
  const resolved = this.configuredModel === undefined ? undefined : await this.resolveSessionModel();
31512
+ const telegramBridge = this.telegram ? (await import("@llblab/pi-telegram")).default : undefined;
31393
31513
  const runtime = await createAutoRAGPiInteractiveRuntime({
31394
31514
  cwd: this.workspaceProjectRoot,
31395
31515
  agentDir: this.piAgentDir,
@@ -31410,11 +31530,14 @@ ${context.text}
31410
31530
  onKey: (key) => this.handleInteractiveKey(key),
31411
31531
  extensionFactories: [
31412
31532
  createEvidencePersistence(this.evidenceLedger),
31413
- ...this.jevExtension !== undefined ? [this.jevExtension] : []
31533
+ ...this.jevExtension !== undefined ? [this.jevExtension] : [],
31534
+ ...telegramBridge !== undefined ? [telegramBridge] : []
31414
31535
  ],
31415
31536
  ...this.jevExtension !== undefined ? { extensionToolNames: [JEV_TOOL_NAME] } : {},
31416
31537
  remoteSession: this.remoteSession,
31417
- ...this.updateNotice === undefined ? {} : { updateNotice: this.updateNotice }
31538
+ ...this.updateNotice === undefined ? {} : { updateNotice: this.updateNotice },
31539
+ ...this.piExtensionPaths === undefined ? {} : { extensionPaths: this.piExtensionPaths },
31540
+ ...this.piExtensionFlags === undefined ? {} : { extensionFlags: this.piExtensionFlags }
31418
31541
  });
31419
31542
  this.boundPiRuntime = runtime.runtime;
31420
31543
  const dispose = runtime.dispose;
@@ -32334,22 +32457,22 @@ ${result.content.replace(/\s+/gu, " ")}`).join(`
32334
32457
  const scope = options.scope ? ` Restrict search to virtual path scope ${options.scope}.` : "";
32335
32458
  return `Answer this original query immediately: ${query}${limit}${scope}
32336
32459
 
32337
- ` + `Baseline retrieval evidence (already gathered for you):
32460
+ Baseline retrieval evidence (already gathered for you):
32338
32461
  ${baseline}
32339
32462
 
32340
- ` + `Produce the best complete, self-contained answer you can RIGHT NOW from this evidence. Do NOT call any search, retrieval, or file-reading tools and do NOT wait for more evidence. ` + `If the query is answerable from general knowledge alone, answer directly.
32463
+ Produce the best complete, self-contained answer you can RIGHT NOW from this evidence. Do NOT call any search, retrieval, or file-reading tools and do NOT wait for more evidence. If the query is answerable from general knowledge alone, answer directly.
32341
32464
 
32342
- ` + `Formatting and content rules for the answer:
32343
- ` + `- Unless the user explicitly requests a different format or length, provide the core answer to the user's question in at most 5 bullet points. If additional explanation is necessary, append it after the bullet points.
32344
- ` + `- Unless the user explicitly requests otherwise, answer the question directly. Do not include specific file paths, datasource descriptions, or retrieval mechanics in the answer text.
32345
- ` + `- ${ANSWER_DELIVERABLE_RULE}
32346
- ` + `- ${ANSWER_CITATION_RULE}
32347
- ` + `- ${ANSWER_IMAGE_EMBED_RULE}
32348
- ` + `- Do not report per-source negative findings (e.g. "no information found in Slack" or "checked Drive but found nothing").
32349
- ` + `- When evidence conflicts, treat the freshest (most recent) information as the correct source of truth.
32350
- ` + `- If information is incomplete or uncertain, acknowledge it briefly without lengthy explanations, stating that it is difficult to answer fully with the given information and searching continues. If there are partial clues or leads (even if not the exact answer), mention those clues concisely.
32465
+ Formatting and content rules for the answer:
32466
+ - Unless the user explicitly requests a different format or length, provide the core answer to the user's question in at most 5 bullet points. If additional explanation is necessary, append it after the bullet points.
32467
+ - Unless the user explicitly requests otherwise, answer the question directly. Do not include specific file paths, datasource descriptions, or retrieval mechanics in the answer text.
32468
+ - ${ANSWER_DELIVERABLE_RULE}
32469
+ - ${ANSWER_CITATION_RULE}
32470
+ - ${ANSWER_IMAGE_EMBED_RULE}
32471
+ - Do not report per-source negative findings (e.g. "no information found in Slack" or "checked Drive but found nothing").
32472
+ - When evidence conflicts, treat the freshest (most recent) information as the correct source of truth.
32473
+ - If information is incomplete or uncertain, acknowledge it briefly without lengthy explanations, stating that it is difficult to answer fully with the given information and searching continues. If there are partial clues or leads (even if not the exact answer), mention those clues concisely.
32351
32474
 
32352
- ` + `Write the answer as your reply, then stop.`;
32475
+ Write the answer as your reply, then stop.`;
32353
32476
  }
32354
32477
  isConversationTurn() {
32355
32478
  if (this.remoteSession || this.boundPiRuntime === undefined)
@@ -32359,7 +32482,7 @@ ${baseline}
32359
32482
  buildDirectAnswerPrompt(query) {
32360
32483
  return `Answer this query directly from your own general knowledge: ${query}
32361
32484
 
32362
- ` + `It needs no search: it is general knowledge, simple reasoning, or conversation. Do NOT call any search, retrieval, web, or file-reading tools. ` + `Reply naturally and concisely; for small talk, just respond conversationally. Do not cite sources or mention retrieval.`;
32485
+ It needs no search: it is general knowledge, simple reasoning, or conversation. Do NOT call any search, retrieval, web, or file-reading tools. Reply naturally and concisely; for small talk, just respond conversationally. Do not cite sources or mention retrieval.`;
32363
32486
  }
32364
32487
  discoveryHint(sentence) {
32365
32488
  const names = this.tools.map((tool) => tool.name).filter((name) => name === JIKJI_FIND_TOOL_NAME || name === EVERYTHING_SEARCH_TOOL_NAME);
@@ -32377,55 +32500,55 @@ ${baseline}
32377
32500
  const scope = options.scope ? ` Restrict search to virtual path scope ${options.scope}.` : "";
32378
32501
  const firstAnswer = formatFirstAnswerContext(fastAnswer, fastAnswerDelivered);
32379
32502
  const answerRules = fastAnswerDelivered ? `Formatting and content rules for your final answer (DELTA ONLY):
32380
- ` + `- The user already has the first answer above. Your final answer MUST contain only the delta against it: (a) corrections to anything in the first answer that is wrong, unsupported, or outdated, and (b) newly verified findings that were not present in the first answer.
32381
- ` + `- NEVER restate or re-list first-answer facts that remain correct, and never repeat its bullet list.
32382
- ` + `- Mark each item clearly as a correction or as a new finding.
32383
- ` + `- If verification changed nothing and found nothing new, say so in one short line (the first answer is confirmed as-is) instead of restating it.
32384
- ` + `- ${ANSWER_DELIVERABLE_RULE} ${ANSWER_DELIVERABLE_DELTA_RULE}
32385
- ` + `- ${ANSWER_CITATION_RULE} A correction or new finding that relies on evidence the first answer cited must cite that evidence's id again.
32386
- ` + `- ${ANSWER_IMAGE_EMBED_RULE} ${ANSWER_IMAGE_DELTA_RULE}
32387
- ` + `- Do not report per-source negative findings (e.g. "no information found in Slack").
32388
- ` + `- When evidence conflicts, treat the freshest (most recent) information as the correct source of truth.` : `Formatting and content rules for the final answer (COMPLETE — no first answer reached the caller):
32503
+ - The user already has the first answer above. Your final answer MUST contain only the delta against it: (a) corrections to anything in the first answer that is wrong, unsupported, or outdated, and (b) newly verified findings that were not present in the first answer.
32504
+ - NEVER restate or re-list first-answer facts that remain correct, and never repeat its bullet list.
32505
+ - Mark each item clearly as a correction or as a new finding.
32506
+ - If verification changed nothing and found nothing new, say so in one short line (the first answer is confirmed as-is) instead of restating it.
32507
+ - ${ANSWER_DELIVERABLE_RULE} ${ANSWER_DELIVERABLE_DELTA_RULE}
32508
+ - ${ANSWER_CITATION_RULE} A correction or new finding that relies on evidence the first answer cited must cite that evidence's id again.
32509
+ - ${ANSWER_IMAGE_EMBED_RULE} ${ANSWER_IMAGE_DELTA_RULE}
32510
+ - Do not report per-source negative findings (e.g. "no information found in Slack").
32511
+ - When evidence conflicts, treat the freshest (most recent) information as the correct source of truth.` : `Formatting and content rules for the final answer (COMPLETE — no first answer reached the caller):
32389
32512
  ` + `- Unless the user explicitly requests a different format or length, provide the core answer to the user's question in at most 5 bullet points. If additional explanation is necessary, append it after the bullet points.
32390
- ` + `- Unless the user explicitly requests otherwise, answer the question directly. Do not include specific file paths, datasource descriptions, or retrieval mechanics in the answer text.
32391
- ` + `- ${ANSWER_DELIVERABLE_RULE}${fastAnswer !== undefined ? ` ${ANSWER_DELIVERABLE_DELTA_RULE}` : ""}
32392
- ` + `- ${ANSWER_CITATION_RULE}
32393
- ` + `- ${ANSWER_IMAGE_EMBED_RULE}
32394
- ` + `- Do not report per-source negative findings (e.g. "no information found in Slack").
32395
- ` + `- When evidence conflicts, treat the freshest (most recent) information as the correct source of truth.`;
32513
+ - Unless the user explicitly requests otherwise, answer the question directly. Do not include specific file paths, datasource descriptions, or retrieval mechanics in the answer text.
32514
+ - ${ANSWER_DELIVERABLE_RULE}${fastAnswer !== undefined ? ` ${ANSWER_DELIVERABLE_DELTA_RULE}` : ""}
32515
+ - ${ANSWER_CITATION_RULE}
32516
+ - ${ANSWER_IMAGE_EMBED_RULE}
32517
+ - Do not report per-source negative findings (e.g. "no information found in Slack").
32518
+ - When evidence conflicts, treat the freshest (most recent) information as the correct source of truth.`;
32396
32519
  return `Original query: ${query}${limit}${scope}
32397
32520
 
32398
- ` + `${firstAnswer}
32521
+ ${firstAnswer}
32399
32522
 
32400
32523
  ` + formatSteerContext(steers) + `Now verify it rigorously. ${this.discoveryHint((tools) => `Actively use ${tools} when discovering or exploring local files and folders. `)}Check important claims against source files with bash when needed, correct anything wrong or unsupported, fill gaps with retrieval tools, and resolve conflicts and freshness. ${this.webFallbackHint()}
32401
32524
 
32402
- ` + `${answerRules}
32525
+ ${answerRules}
32403
32526
 
32404
- ` + `Do not use broad grep/find or recursive filesystem scans: only inspect a path or narrow neighborhood surfaced by retrieval, and only when evidence clearly points there. ` + `Avoid spinning repeated near-identical queries against the same datasource; once additional attempts stop surfacing new evidence, conclude from the evidence available. ` + `If more search is needed, first write a brief 1–2 line progress update stating the best current hypothesis and what you are checking next, then call retrieval tools. ` + `When finished, reply with the final answer and no tool call: that last reply is what the user receives, so keep progress notes out of it.`;
32527
+ Do not use broad grep/find or recursive filesystem scans: only inspect a path or narrow neighborhood surfaced by retrieval, and only when evidence clearly points there. Avoid spinning repeated near-identical queries against the same datasource; once additional attempts stop surfacing new evidence, conclude from the evidence available. ` + `If more search is needed, first write a brief 1–2 line progress update stating the best current hypothesis and what you are checking next, then call retrieval tools. ` + `When finished, reply with the final answer and no tool call: that last reply is what the user receives, so keep progress notes out of it.`;
32405
32528
  }
32406
32529
  buildSearchPrompt(query, options, initialRetrievalContext) {
32407
32530
  const limit = typeof options.topK === "number" ? ` Cite at most ${options.topK} sources.` : "";
32408
32531
  const scope = options.scope ? ` Restrict search to virtual path scope ${options.scope}.` : "";
32409
32532
  return `Find and curate information for this original query: ${query}${limit}${scope}
32410
32533
 
32411
- ` + `Start by deciding whether this is answerable from general knowledge or memory. If it is a generic, stable question, answer it immediately without retrieval. ` + `Otherwise, baseline MinSync and Jikji retrieval is already running in parallel; do not give the final answer until its next message arrives.
32534
+ Start by deciding whether this is answerable from general knowledge or memory. If it is a generic, stable question, answer it immediately without retrieval. Otherwise, baseline MinSync and Jikji retrieval is already running in parallel; do not give the final answer until its next message arrives.
32412
32535
 
32413
- ` + `Baseline retrieval context:
32536
+ Baseline retrieval context:
32414
32537
  ${initialRetrievalContext ?? "Pending; continue only with a brief progress statement."}
32415
32538
 
32416
- ` + `Treat candidates as unverified evidence, verify important claims against source files when needed, and use additional tools when needed. ` + `Judge relevance, conflicts, freshness, and sufficiency in this agent loop.
32539
+ Treat candidates as unverified evidence, verify important claims against source files when needed, and use additional tools when needed. Judge relevance, conflicts, freshness, and sufficiency in this agent loop.
32417
32540
 
32418
- ` + `Formatting and content rules for the answer:
32419
- ` + `- Unless the user explicitly requests a different format or length, provide the core answer to the user's question in at most 5 bullet points. If additional explanation is necessary, append it after the bullet points.
32420
- ` + `- Unless the user explicitly requests otherwise, answer the question directly. Do not include specific file paths, datasource descriptions, or retrieval mechanics in the answer text.
32421
- ` + `- ${ANSWER_DELIVERABLE_RULE}
32422
- ` + `- ${ANSWER_CITATION_RULE}
32423
- ` + `- ${ANSWER_IMAGE_EMBED_RULE}
32424
- ` + `- Do not report per-source negative findings (e.g. "no information found in Slack").
32425
- ` + `- When evidence conflicts, treat the freshest (most recent) information as the correct source of truth.
32426
- ` + `- If information is incomplete or uncertain, acknowledge it briefly without lengthy explanations. If there are partial clues or leads, mention them concisely.
32541
+ Formatting and content rules for the answer:
32542
+ - Unless the user explicitly requests a different format or length, provide the core answer to the user's question in at most 5 bullet points. If additional explanation is necessary, append it after the bullet points.
32543
+ - Unless the user explicitly requests otherwise, answer the question directly. Do not include specific file paths, datasource descriptions, or retrieval mechanics in the answer text.
32544
+ - ${ANSWER_DELIVERABLE_RULE}
32545
+ - ${ANSWER_CITATION_RULE}
32546
+ - ${ANSWER_IMAGE_EMBED_RULE}
32547
+ - Do not report per-source negative findings (e.g. "no information found in Slack").
32548
+ - When evidence conflicts, treat the freshest (most recent) information as the correct source of truth.
32549
+ - If information is incomplete or uncertain, acknowledge it briefly without lengthy explanations. If there are partial clues or leads, mention them concisely.
32427
32550
 
32428
- ` + `${this.discoveryHint((tools) => `When exploring local files and folders, actively use ${tools} rather than exploratory bash commands. `)}If more search is needed, first write a brief 1–2 line progress update stating the best current hypothesis and what you are checking next, then call retrieval tools. ` + `Never repeat a generic status message. Do not use broad grep/find or recursive filesystem scans: only inspect a path or narrow neighborhood surfaced by retrieval, and only when evidence clearly points there. ` + `Avoid spinning repeated near-identical queries against the same datasource; once additional attempts stop surfacing new evidence, conclude from the evidence available. ` + `When finished, reply with the final answer and no tool call: that last reply is what the user receives, so keep progress notes out of it.`;
32551
+ ${this.discoveryHint((tools) => `When exploring local files and folders, actively use ${tools} rather than exploratory bash commands. `)}If more search is needed, first write a brief 1–2 line progress update stating the best current hypothesis and what you are checking next, then call retrieval tools. ` + `Never repeat a generic status message. Do not use broad grep/find or recursive filesystem scans: only inspect a path or narrow neighborhood surfaced by retrieval, and only when evidence clearly points there. Avoid spinning repeated near-identical queries against the same datasource; once additional attempts stop surfacing new evidence, conclude from the evidence available. When finished, reply with the final answer and no tool call: that last reply is what the user receives, so keep progress notes out of it.`;
32429
32552
  }
32430
32553
  async refresh(force = false, opts) {
32431
32554
  const signal = opts?.signal;
@@ -32661,7 +32784,7 @@ ${initialRetrievalContext ?? "Pending; continue only with a brief progress state
32661
32784
  });
32662
32785
  }
32663
32786
  const state = this.refreshState.inFlight || persistedRunning && ownerAlive ? "indexing" : interrupted ? "failed" : persistedProgress?.state === "success" || persistedProgress?.state === "failed" ? persistedProgress.state : this.refreshState.lastOutcome === "never" ? "idle" : this.refreshState.lastOutcome;
32664
- const parsedMirrorReady = this.refreshState.lastOutcome === "success" || persistedProgress?.state === "success" || existsSync31(refreshReadinessPath(this.workspaceProjectRoot));
32787
+ const parsedMirrorReady = this.refreshState.lastOutcome === "success" || persistedProgress?.state === "success" || existsSync32(refreshReadinessPath(this.workspaceProjectRoot));
32665
32788
  const effectiveProgress = this.refreshState.inFlight || persistedRunning ? persistedProgress : undefined;
32666
32789
  return {
32667
32790
  state,
@@ -33121,7 +33244,7 @@ function buildSteerMessage(text) {
33121
33244
 
33122
33245
  ${text}
33123
33246
 
33124
- ` + `Treat it as direction for the search in progress, not as a new question: adjust which sources, queries, or claims you pursue, then continue. ` + `When finished, reply with the final answer to the original question, updated for this correction, and no tool call.`;
33247
+ Treat it as direction for the search in progress, not as a new question: adjust which sources, queries, or claims you pursue, then continue. When finished, reply with the final answer to the original question, updated for this correction, and no tool call.`;
33125
33248
  }
33126
33249
  function frameSteers(messages, steerTexts) {
33127
33250
  if (steerTexts.size === 0)
@@ -33141,12 +33264,12 @@ function formatSteerContext(steers) {
33141
33264
  return `While the search ran, the user corrected it:
33142
33265
  ${steers.map((steer) => `- ${steer}`).join(`
33143
33266
  `)}
33144
- ` + `The first answer predates these corrections; follow them over it.
33267
+ The first answer predates these corrections; follow them over it.
33145
33268
 
33146
33269
  `;
33147
33270
  }
33148
33271
  function buildSteerContinuationPrompt(steers) {
33149
- return `${formatSteerContext(steers)}Apply the latest correction: search or verify further if it needs that, ` + `then reply with the complete final answer to the original question and no tool call.`;
33272
+ return `${formatSteerContext(steers)}Apply the latest correction: search or verify further if it needs that, then reply with the complete final answer to the original question and no tool call.`;
33150
33273
  }
33151
33274
  function formatFirstAnswerContext(fastAnswer, delivered) {
33152
33275
  if (fastAnswer === undefined) {
@@ -33269,11 +33392,20 @@ function fsearchRefreshDiagnostics(fsearch) {
33269
33392
  function minSyncRefreshDiagnostics(minsync) {
33270
33393
  if (!minsync)
33271
33394
  return [];
33395
+ const diagnostics = [];
33272
33396
  if (minsync.diagnostic)
33273
- return [toMinSyncDiagnostic(minsync.diagnostic, minsync.ok)];
33274
- if (!minsync.ok && minsync.reason)
33275
- return [toMinSyncReasonDiagnostic(minsync.reason)];
33276
- return [];
33397
+ diagnostics.push(toMinSyncDiagnostic(minsync.diagnostic, minsync.ok));
33398
+ else if (!minsync.ok && minsync.reason)
33399
+ diagnostics.push(toMinSyncReasonDiagnostic(minsync.reason));
33400
+ if (minsync.lexicalFallback) {
33401
+ diagnostics.push({
33402
+ code: "lexical-tokenizer-unavailable",
33403
+ severity: "warning",
33404
+ message: `The "${minsync.lexicalFallback.requested}" BM25 tokenizer needs the Kiwi native library and model, which were not found, so MinSync tokenizes on whitespace and a Korean word with a particle (인증서를) does not match its stem (인증서). Install Kiwi (set KIWI_LIBRARY_PATH and KIWI_MODEL_PATH, or install it under ~/.local/kiwi), then refresh again.`,
33405
+ source: "minsync"
33406
+ });
33407
+ }
33408
+ return diagnostics;
33277
33409
  }
33278
33410
  function toMinSyncDiagnostic(diag, ok) {
33279
33411
  const code = diag.code === "embedder-unavailable" ? "embedder-unavailable" : diag.code === "embedding-identity-mismatch" ? "embedding-identity-mismatch" : diag.code === "minsync-lock-held" ? "minsync-lock-held" : "minsync-sync-failed";
@@ -33753,7 +33885,7 @@ var init_memory2 = __esm(() => {
33753
33885
  });
33754
33886
 
33755
33887
  // src/cli/commands/index.ts
33756
- import { existsSync as existsSync32, lstatSync as lstatSync3, rmSync as rmSync11 } from "node:fs";
33888
+ import { existsSync as existsSync33, lstatSync as lstatSync3, rmSync as rmSync11 } from "node:fs";
33757
33889
  import { join as join42, resolve as resolve18, sep as sep8 } from "node:path";
33758
33890
  async function runIndex2(ctx) {
33759
33891
  const sub = ctx.positionals[0];
@@ -33773,7 +33905,7 @@ async function runIndex2(ctx) {
33773
33905
  const autoragDir = resolve18(config.workspacePath, ".autorag");
33774
33906
  const targets = targetSubdirs.map((subdir) => resolve18(config.workspacePath, subdir));
33775
33907
  for (const dir of [autoragDir, ...targets]) {
33776
- if (existsSync32(dir) && lstatSync3(dir).isSymbolicLink()) {
33908
+ if (existsSync33(dir) && lstatSync3(dir).isSymbolicLink()) {
33777
33909
  ctx.stderr(renderError(new Error("Refusing to reset: an index path is a symlink, not a real directory."), {
33778
33910
  json: ctx.json
33779
33911
  }));
@@ -34263,6 +34395,40 @@ var init_update_check = __esm(() => {
34263
34395
  SEMVER_PATTERN = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
34264
34396
  });
34265
34397
 
34398
+ // src/cli/whatsapp-extension.ts
34399
+ import { DefaultPackageManager, getAgentDir as getAgentDir5, SettingsManager as SettingsManager3 } from "@earendil-works/pi-coding-agent";
34400
+ async function installWithPi(cwd, sources) {
34401
+ const agentDir = getAgentDir5();
34402
+ const packageManager = new DefaultPackageManager({
34403
+ cwd,
34404
+ agentDir,
34405
+ settingsManager: SettingsManager3.create(cwd, agentDir)
34406
+ });
34407
+ const resolved = await packageManager.resolveExtensionSources(sources, { local: false, temporary: false });
34408
+ return resolved.extensions.filter((entry) => entry.enabled).map((entry) => entry.path);
34409
+ }
34410
+ async function resolveWhatsAppExtension(input) {
34411
+ const group = input.flags["whatsapp-group"];
34412
+ if (input.flags.whatsapp !== true) {
34413
+ if (group !== undefined)
34414
+ throw new Error("--whatsapp-group requires --whatsapp.");
34415
+ return;
34416
+ }
34417
+ const resolve = input.resolveSources ?? ((sources) => installWithPi(input.cwd ?? process.cwd(), sources));
34418
+ const paths = await resolve([WHATSAPP_PI_SOURCE]);
34419
+ if (paths.length === 0)
34420
+ throw new Error(`${WHATSAPP_PI_SOURCE} exposes no pi extension; cannot enable WhatsApp.`);
34421
+ return {
34422
+ paths,
34423
+ flags: {
34424
+ "whatsapp-pi-online": true,
34425
+ ...typeof group === "string" && group !== "" ? { "whatsapp-group": group } : {}
34426
+ }
34427
+ };
34428
+ }
34429
+ var WHATSAPP_PI_SOURCE = "npm:whatsapp-pi@1.0.70";
34430
+ var init_whatsapp_extension = () => {};
34431
+
34266
34432
  // src/cli/commands/tui.ts
34267
34433
  import { InteractiveMode } from "@earendil-works/pi-coding-agent";
34268
34434
  function parseThinkingFlags(flags) {
@@ -34283,6 +34449,8 @@ async function createTuiAgent(ctx) {
34283
34449
  const config = resolveConfig({ flags: ctx.flags, cwd: ctx.cwd });
34284
34450
  const options = { ...buildAgentOptions(config) };
34285
34451
  options.updateNotice = async () => renderAutoRAGUpdateNotice(await checkAutoRAGUpdate({ currentVersion: readPackageVersion() }));
34452
+ if (ctx.flags.telegram === true)
34453
+ options.telegram = true;
34286
34454
  const thinking = parseThinkingFlags(ctx.flags);
34287
34455
  if (thinking !== undefined)
34288
34456
  options.thinking = thinking;
@@ -34294,6 +34462,11 @@ async function createTuiAgent(ctx) {
34294
34462
  if (resolved.providerApiKeys !== undefined)
34295
34463
  options.providerApiKeys = resolved.providerApiKeys;
34296
34464
  }
34465
+ const whatsapp = await resolveWhatsAppExtension({ flags: ctx.flags, cwd: ctx.cwd });
34466
+ if (whatsapp !== undefined) {
34467
+ options.piExtensionPaths = whatsapp.paths;
34468
+ options.piExtensionFlags = whatsapp.flags;
34469
+ }
34297
34470
  const decompositionModel = await resolveQueryDecompositionModel(config);
34298
34471
  if (decompositionModel !== undefined)
34299
34472
  options.queryDecomposition = decompositionModel;
@@ -34334,11 +34507,12 @@ var init_tui = __esm(() => {
34334
34507
  init_config4();
34335
34508
  init_update_check();
34336
34509
  init_version();
34510
+ init_whatsapp_extension();
34337
34511
  THINKING_LEVELS2 = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
34338
34512
  });
34339
34513
 
34340
34514
  // src/cli/commands/serve.ts
34341
- import { existsSync as existsSync33 } from "node:fs";
34515
+ import { existsSync as existsSync34 } from "node:fs";
34342
34516
  import { completeSimple as completeSimple3 } from "@earendil-works/pi-ai/compat";
34343
34517
  function classifierText(message) {
34344
34518
  return message.content.filter((part) => part.type === "text").map((part) => part.text).join("");
@@ -34356,7 +34530,7 @@ function createInjectionClassifierModel(resolved) {
34356
34530
  async function runServe(ctx, deps = {}) {
34357
34531
  const flags = ctx.flags;
34358
34532
  const resolved = resolveConfigPath({ flags, cwd: ctx.cwd });
34359
- if (!existsSync33(resolved.configPath)) {
34533
+ if (!existsSync34(resolved.configPath)) {
34360
34534
  ctx.stderr(renderError(new ConfigError(`Config file not found: ${resolved.configPath}. Run autorag init first.`), {
34361
34535
  json: ctx.json
34362
34536
  }));
@@ -34737,7 +34911,7 @@ var init_p2p = __esm(() => {
34737
34911
  });
34738
34912
 
34739
34913
  // src/cli/commands/p2p-policy.ts
34740
- import { existsSync as existsSync34, mkdirSync as mkdirSync26, readFileSync as readFileSync28, writeFileSync as writeFileSync23 } from "node:fs";
34914
+ import { existsSync as existsSync35, mkdirSync as mkdirSync26, readFileSync as readFileSync28, writeFileSync as writeFileSync23 } from "node:fs";
34741
34915
  import { dirname as dirname28, join as join43 } from "node:path";
34742
34916
  import { parse as parseToml3, stringify as stringifyToml } from "smol-toml";
34743
34917
  function workspacePolicyPath(cwd) {
@@ -34768,7 +34942,7 @@ function normalizeEntry(value, key) {
34768
34942
  throw new Error(`Invalid policy entry for ${key}: ${JSON.stringify(value)}`);
34769
34943
  }
34770
34944
  function readPolicyToml(policyPath) {
34771
- if (!existsSync34(policyPath))
34945
+ if (!existsSync35(policyPath))
34772
34946
  return {};
34773
34947
  const parsed = parseToml3(readFileSync28(policyPath, "utf8"));
34774
34948
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
@@ -34803,7 +34977,7 @@ function writePolicyToml(policyPath, entries) {
34803
34977
  function getGlobalPolicy() {
34804
34978
  const home = resolveAutoRAGHome();
34805
34979
  const configPath = join43(home, "config.json");
34806
- if (!existsSync34(configPath))
34980
+ if (!existsSync35(configPath))
34807
34981
  return {};
34808
34982
  try {
34809
34983
  const raw = readFileSync28(configPath, "utf8");
@@ -34887,9 +35061,9 @@ var init_p2p_policy = __esm(() => {
34887
35061
 
34888
35062
  // src/cli/commands/models.ts
34889
35063
  import { join as join44 } from "node:path";
34890
- import { getAgentDir as getAgentDir5, ModelRuntime as ModelRuntime3 } from "@earendil-works/pi-coding-agent";
35064
+ import { getAgentDir as getAgentDir6, ModelRuntime as ModelRuntime3 } from "@earendil-works/pi-coding-agent";
34891
35065
  async function listModelCatalog(options) {
34892
- const agentDir = options.agentDir ?? getAgentDir5();
35066
+ const agentDir = options.agentDir ?? getAgentDir6();
34893
35067
  const runtime = await ModelRuntime3.create({
34894
35068
  authPath: join44(agentDir, "auth.json"),
34895
35069
  modelsPath: join44(agentDir, "models.json"),
@@ -35849,7 +36023,14 @@ var COMMAND_USAGE = {
35849
36023
  "",
35850
36024
  "Flags:",
35851
36025
  " --fast-thinking <lvl> Thinking level for the fast first answer",
35852
- " --final-thinking <lvl> Thinking level for the verified answer"
36026
+ " --final-thinking <lvl> Thinking level for the verified answer",
36027
+ " --telegram Load the Telegram bridge (@llblab/pi-telegram) so a paired",
36028
+ " Telegram chat can ask, steer, and read answers. In-session:",
36029
+ " /telegram-setup (bot token), /telegram-connect, /telegram-status",
36030
+ " --whatsapp Control this session from WhatsApp (installs whatsapp-pi;",
36031
+ " pair once with /whatsapp, allow your contact there). Messages",
36032
+ " from allowed chats run as questions; answers go back to the chat",
36033
+ " --whatsapp-group <jid> With --whatsapp, answer only this WhatsApp group"
35853
36034
  ]),
35854
36035
  serve: usage([
35855
36036
  "autorag serve - P2P peer query server over SimpleX",
@@ -35952,7 +36133,9 @@ var BOOLEAN_FLAGS = new Set([
35952
36133
  "retry-failed",
35953
36134
  "strict",
35954
36135
  "refresh",
35955
- "available"
36136
+ "available",
36137
+ "whatsapp",
36138
+ "telegram"
35956
36139
  ]);
35957
36140
  var VALUE_FLAGS = new Set([
35958
36141
  "config",
@@ -35995,7 +36178,8 @@ var VALUE_FLAGS = new Set([
35995
36178
  "format",
35996
36179
  "provider",
35997
36180
  "fast-thinking",
35998
- "final-thinking"
36181
+ "final-thinking",
36182
+ "whatsapp-group"
35999
36183
  ]);
36000
36184
  var COMMANDS = [
36001
36185
  "init",
@@ -36043,6 +36227,7 @@ Commands:
36043
36227
  lite report <query> Persist a structured report (--input FILE)
36044
36228
  duplicates [DIR] Scan exact/near duplicate document families; never deletes files
36045
36229
  tui Open an interactive Pi-powered librarian terminal UI
36230
+ (--whatsapp [--whatsapp-group JID]: also take questions from WhatsApp)
36046
36231
  serve Start the P2P peer query server over SimpleX
36047
36232
  (--port N --force)
36048
36233
  p2p SimpleX peer trust management