@autorag/librarian 2.10.3 → 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/NOTICE +3 -0
- package/README.md +39 -5
- package/dist/agent/agent.d.ts +19 -4
- package/dist/agent/answer-guidelines.d.ts +17 -0
- package/dist/agent/pi-session.d.ts +21 -0
- package/dist/agent/search-documents.d.ts +1 -1
- package/dist/agent/web-search-tool.d.ts +2 -2
- package/dist/cli/commands/types.d.ts +2 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1218 -725
- package/dist/cli/whatsapp-extension.d.ts +21 -0
- package/dist/core.js +1903 -1482
- package/dist/datasource/sidecar/descriptors.d.ts +1 -0
- package/dist/datasource/skills/lazykatok/skill.d.ts +13 -0
- package/dist/everything/client.d.ts +5 -0
- package/dist/filesystem/path-exclusions.d.ts +2 -0
- package/dist/fsearch/client.d.ts +3 -2
- package/dist/fsearch/walk.d.ts +2 -0
- package/dist/index.js +1887 -1466
- package/dist/mcp/index.js +1887 -1466
- package/dist/minsync/client.d.ts +8 -0
- package/dist/minsync/embedder-config.d.ts +11 -1
- package/dist/minsync/installer.d.ts +1 -1
- package/dist/minsync/kiwi.d.ts +17 -0
- package/dist/minsync/lexical-language.d.ts +18 -0
- package/dist/minsync/types.d.ts +9 -0
- package/dist/web/search/types.d.ts +22 -18
- package/package.json +2 -1
- package/skills/autorag-doctor/SKILL.md +1 -0
- package/skills/autorag-lite-setup/SKILL.md +1 -1
- package/skills/autorag-setup/SKILL.md +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2657,7 +2657,7 @@ function chatCrawler(id) {
|
|
|
2657
2657
|
hierarchy: (metadata) => ["chats", str(metadata.chatName) ?? str(metadata.chatId) ?? "unknown"]
|
|
2658
2658
|
};
|
|
2659
2659
|
}
|
|
2660
|
-
var str = (value) => typeof value === "string" && value.length > 0 ? value : undefined, slack, CRAWLER_DESCRIPTORS;
|
|
2660
|
+
var str = (value) => typeof value === "string" && value.length > 0 ? value : undefined, slack, kakao, CRAWLER_DESCRIPTORS;
|
|
2661
2661
|
var init_descriptors = __esm(() => {
|
|
2662
2662
|
slack = {
|
|
2663
2663
|
id: "slack",
|
|
@@ -2688,10 +2688,32 @@ var init_descriptors = __esm(() => {
|
|
|
2688
2688
|
str(metadata.channelName) ?? str(metadata.channelId) ?? "unknown"
|
|
2689
2689
|
]
|
|
2690
2690
|
};
|
|
2691
|
+
kakao = {
|
|
2692
|
+
id: "kakao",
|
|
2693
|
+
keyExpression: "m.chunk_id",
|
|
2694
|
+
chunkId: (key) => key,
|
|
2695
|
+
fromClause: "chunks m",
|
|
2696
|
+
liveWhere: "length(trim(coalesce(m.text, ''))) > 0 and not (json_valid(m.text) and (json_extract(m.text, '$.feedType') is not null or json_extract(m.text, '$.csIP') is not null))",
|
|
2697
|
+
textExpression: "coalesce(m.text, '')",
|
|
2698
|
+
titleExpression: "m.chat_name",
|
|
2699
|
+
epochSecondsExpression: "unixepoch(m.started_at)",
|
|
2700
|
+
fts: { table: "chunks_fts", joinOn: "chunks_fts.rowid = m.rowid" },
|
|
2701
|
+
metadata: [
|
|
2702
|
+
{ sql: "m.chat_id", key: "chatId" },
|
|
2703
|
+
{ sql: "m.chat_name", key: "chatName" },
|
|
2704
|
+
{ sql: "m.sender_nickname", key: "senderNickname" },
|
|
2705
|
+
{ sql: "m.started_at", key: "startedAt" },
|
|
2706
|
+
{ sql: "m.ended_at", key: "endedAt" }
|
|
2707
|
+
],
|
|
2708
|
+
channelIdExpression: "m.chat_id",
|
|
2709
|
+
channelNameExpression: "m.chat_name",
|
|
2710
|
+
hierarchy: (metadata) => ["chats", str(metadata.chatName) ?? str(metadata.chatId) ?? "unknown"]
|
|
2711
|
+
};
|
|
2691
2712
|
CRAWLER_DESCRIPTORS = {
|
|
2692
2713
|
slack,
|
|
2693
2714
|
telegram: chatCrawler("telegram"),
|
|
2694
|
-
whatsapp: chatCrawler("whatsapp")
|
|
2715
|
+
whatsapp: chatCrawler("whatsapp"),
|
|
2716
|
+
kakao
|
|
2695
2717
|
};
|
|
2696
2718
|
});
|
|
2697
2719
|
|
|
@@ -8054,6 +8076,504 @@ var init_skip = __esm(() => {
|
|
|
8054
8076
|
MINSYNC_METHOD_NAMES = new Set(["minsync", "hybrid", "bm25"]);
|
|
8055
8077
|
});
|
|
8056
8078
|
|
|
8079
|
+
// src/datasource/sidecar/fusion.ts
|
|
8080
|
+
function encodeVector(vector) {
|
|
8081
|
+
const floats = Float32Array.from(vector);
|
|
8082
|
+
return new Uint8Array(floats.buffer, floats.byteOffset, floats.byteLength);
|
|
8083
|
+
}
|
|
8084
|
+
function decodeVector(blob) {
|
|
8085
|
+
if (blob.byteLength % Float32Array.BYTES_PER_ELEMENT !== 0)
|
|
8086
|
+
return;
|
|
8087
|
+
return new Float32Array(new Uint8Array(blob).buffer);
|
|
8088
|
+
}
|
|
8089
|
+
function cosine2(a, b) {
|
|
8090
|
+
if (a.length === 0 || a.length !== b.length)
|
|
8091
|
+
return 0;
|
|
8092
|
+
let dot = 0;
|
|
8093
|
+
let normA = 0;
|
|
8094
|
+
let normB = 0;
|
|
8095
|
+
for (let index = 0;index < a.length; index += 1) {
|
|
8096
|
+
const x = a[index] ?? 0;
|
|
8097
|
+
const y = b[index] ?? 0;
|
|
8098
|
+
dot += x * y;
|
|
8099
|
+
normA += x * x;
|
|
8100
|
+
normB += y * y;
|
|
8101
|
+
}
|
|
8102
|
+
if (normA === 0 || normB === 0)
|
|
8103
|
+
return 0;
|
|
8104
|
+
return dot / Math.sqrt(normA * normB);
|
|
8105
|
+
}
|
|
8106
|
+
function topKByCosine(query, rows, k) {
|
|
8107
|
+
if (k <= 0)
|
|
8108
|
+
return [];
|
|
8109
|
+
const scored = [];
|
|
8110
|
+
for (const row of rows) {
|
|
8111
|
+
const score = cosine2(query, row.vector);
|
|
8112
|
+
if (score > 0)
|
|
8113
|
+
scored.push({ key: row.key, score });
|
|
8114
|
+
}
|
|
8115
|
+
scored.sort((a, b) => b.score - a.score || a.key.localeCompare(b.key));
|
|
8116
|
+
return scored.slice(0, k);
|
|
8117
|
+
}
|
|
8118
|
+
function reciprocalRankFusion(lists) {
|
|
8119
|
+
const scores = new Map;
|
|
8120
|
+
for (const list of lists) {
|
|
8121
|
+
const seen = new Set;
|
|
8122
|
+
for (const [index, key] of list.entries()) {
|
|
8123
|
+
if (seen.has(key))
|
|
8124
|
+
continue;
|
|
8125
|
+
seen.add(key);
|
|
8126
|
+
scores.set(key, (scores.get(key) ?? 0) + 1 / (RRF_K + index + 1));
|
|
8127
|
+
}
|
|
8128
|
+
}
|
|
8129
|
+
return [...scores.entries()].map(([key, score]) => ({ key, score })).sort((a, b) => b.score - a.score);
|
|
8130
|
+
}
|
|
8131
|
+
var RRF_K = 60;
|
|
8132
|
+
|
|
8133
|
+
// src/datasource/sidecar/index.ts
|
|
8134
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
8135
|
+
import { mkdirSync as mkdirSync7, statSync } from "node:fs";
|
|
8136
|
+
import { dirname as dirname11 } from "node:path";
|
|
8137
|
+
import { DatabaseSync } from "node:sqlite";
|
|
8138
|
+
import { pathToFileURL } from "node:url";
|
|
8139
|
+
|
|
8140
|
+
class CrawlerSidecar {
|
|
8141
|
+
descriptor;
|
|
8142
|
+
options;
|
|
8143
|
+
select;
|
|
8144
|
+
where;
|
|
8145
|
+
opened;
|
|
8146
|
+
attached;
|
|
8147
|
+
lastSemantic;
|
|
8148
|
+
constructor(options) {
|
|
8149
|
+
this.options = options;
|
|
8150
|
+
const { descriptor } = options;
|
|
8151
|
+
this.descriptor = descriptor;
|
|
8152
|
+
const metadataColumns = descriptor.metadata.map((column, index) => `${column.sql} as m${index}`);
|
|
8153
|
+
this.select = [
|
|
8154
|
+
`${descriptor.keyExpression} as k`,
|
|
8155
|
+
`${descriptor.textExpression} as content`,
|
|
8156
|
+
`${descriptor.titleExpression ?? "null"} as title`,
|
|
8157
|
+
`${descriptor.epochSecondsExpression ?? "null"} as epoch`,
|
|
8158
|
+
...metadataColumns
|
|
8159
|
+
].join(", ");
|
|
8160
|
+
const scope = descriptor.scopeExpression !== undefined && options.workspaceId !== undefined;
|
|
8161
|
+
const allowlist = [
|
|
8162
|
+
...(options.channelIds?.length ?? 0) > 0 ? [`${descriptor.channelIdExpression} in (${options.channelIds?.map(() => "?").join(", ")})`] : [],
|
|
8163
|
+
...(options.channelNames?.length ?? 0) > 0 ? [`${descriptor.channelNameExpression} in (${options.channelNames?.map(() => "?").join(", ")})`] : []
|
|
8164
|
+
];
|
|
8165
|
+
this.where = [
|
|
8166
|
+
descriptor.liveWhere,
|
|
8167
|
+
...scope ? [`${descriptor.scopeExpression} = ?`] : [],
|
|
8168
|
+
...allowlist.length > 0 ? [`(${allowlist.join(" or ")})`] : []
|
|
8169
|
+
].join(" and ");
|
|
8170
|
+
}
|
|
8171
|
+
get db() {
|
|
8172
|
+
if (this.opened !== undefined)
|
|
8173
|
+
return this.opened;
|
|
8174
|
+
mkdirSync7(dirname11(this.options.sidecarDbPath), { recursive: true });
|
|
8175
|
+
const db = new DatabaseSync(this.options.sidecarDbPath);
|
|
8176
|
+
db.exec(`pragma busy_timeout = ${BUSY_TIMEOUT_MS}`);
|
|
8177
|
+
db.exec("pragma journal_mode = wal");
|
|
8178
|
+
db.exec(`
|
|
8179
|
+
create table if not exists sidecar_meta (k text primary key, v text not null);
|
|
8180
|
+
create table if not exists sidecar_vec (message_key text primary key, hash text not null, vec blob not null);
|
|
8181
|
+
`);
|
|
8182
|
+
this.opened = db;
|
|
8183
|
+
return db;
|
|
8184
|
+
}
|
|
8185
|
+
attachCrawler() {
|
|
8186
|
+
let file;
|
|
8187
|
+
try {
|
|
8188
|
+
const { dev, ino } = statSync(this.options.crawlerDbPath);
|
|
8189
|
+
file = { dev, ino };
|
|
8190
|
+
} catch {
|
|
8191
|
+
this.detachCrawler();
|
|
8192
|
+
return false;
|
|
8193
|
+
}
|
|
8194
|
+
if (this.attached !== undefined) {
|
|
8195
|
+
if (this.attached.dev === file.dev && this.attached.ino === file.ino)
|
|
8196
|
+
return true;
|
|
8197
|
+
this.detachCrawler();
|
|
8198
|
+
}
|
|
8199
|
+
const crawlerUri = `${pathToFileURL(this.options.crawlerDbPath).href}?mode=ro`.replaceAll("'", "''");
|
|
8200
|
+
try {
|
|
8201
|
+
this.db.exec(`attach database '${crawlerUri}' as crawler`);
|
|
8202
|
+
} catch {
|
|
8203
|
+
return false;
|
|
8204
|
+
}
|
|
8205
|
+
const { descriptor } = this.options;
|
|
8206
|
+
try {
|
|
8207
|
+
this.db.prepare(`select ${this.select} from ${descriptor.fromClause} where ${this.where} limit 0`).all(...this.whereParams());
|
|
8208
|
+
this.db.prepare(`select 1 from ${descriptor.fts.table} limit 0`).all();
|
|
8209
|
+
} catch {
|
|
8210
|
+
this.db.exec("detach database crawler");
|
|
8211
|
+
return false;
|
|
8212
|
+
}
|
|
8213
|
+
this.attached = file;
|
|
8214
|
+
this.lastSemantic = undefined;
|
|
8215
|
+
return true;
|
|
8216
|
+
}
|
|
8217
|
+
detachCrawler() {
|
|
8218
|
+
if (this.attached === undefined)
|
|
8219
|
+
return;
|
|
8220
|
+
this.attached = undefined;
|
|
8221
|
+
this.lastSemantic = undefined;
|
|
8222
|
+
try {
|
|
8223
|
+
this.opened?.exec("detach database crawler");
|
|
8224
|
+
} catch {}
|
|
8225
|
+
}
|
|
8226
|
+
get semanticEnabled() {
|
|
8227
|
+
return this.options.embedder !== undefined;
|
|
8228
|
+
}
|
|
8229
|
+
close() {
|
|
8230
|
+
this.opened?.close();
|
|
8231
|
+
this.opened = undefined;
|
|
8232
|
+
this.attached = undefined;
|
|
8233
|
+
this.lastSemantic = undefined;
|
|
8234
|
+
}
|
|
8235
|
+
whereParams() {
|
|
8236
|
+
const { descriptor, workspaceId, channelIds = [], channelNames = [] } = this.options;
|
|
8237
|
+
return [
|
|
8238
|
+
...descriptor.scopeExpression !== undefined && workspaceId !== undefined ? [workspaceId] : [],
|
|
8239
|
+
...channelIds,
|
|
8240
|
+
...channelNames
|
|
8241
|
+
];
|
|
8242
|
+
}
|
|
8243
|
+
searchLexical(query, topK) {
|
|
8244
|
+
const match = toMatchQuery(query);
|
|
8245
|
+
if (match === undefined || topK <= 0 || !this.attachCrawler())
|
|
8246
|
+
return [];
|
|
8247
|
+
const { descriptor } = this.options;
|
|
8248
|
+
const rows = this.db.prepare(`select ${this.select}, bm25(${descriptor.fts.table}) as rank
|
|
8249
|
+
from ${descriptor.fromClause} join ${descriptor.fts.table} on ${descriptor.fts.joinOn}
|
|
8250
|
+
where ${descriptor.fts.table} match ? and ${this.where}
|
|
8251
|
+
order by rank limit ?`).all(match, ...this.whereParams(), topK);
|
|
8252
|
+
return rows.map((row) => this.toHit(this.toRow(row), -Number(row.rank)));
|
|
8253
|
+
}
|
|
8254
|
+
async searchSemantic(query, topK) {
|
|
8255
|
+
const trimmed = query.trim();
|
|
8256
|
+
if (this.options.embedder === undefined || trimmed.length === 0 || topK <= 0 || !this.attachCrawler())
|
|
8257
|
+
return [];
|
|
8258
|
+
const ranked = await this.rankSemantic(trimmed, topK * POOL_FACTOR);
|
|
8259
|
+
if (ranked.length === 0)
|
|
8260
|
+
return [];
|
|
8261
|
+
const live = this.hydrate(ranked.map((entry) => entry.key));
|
|
8262
|
+
const hits = [];
|
|
8263
|
+
for (const entry of ranked) {
|
|
8264
|
+
const row = live.get(entry.key);
|
|
8265
|
+
if (row !== undefined)
|
|
8266
|
+
hits.push(this.toHit(row, entry.score));
|
|
8267
|
+
if (hits.length >= topK)
|
|
8268
|
+
break;
|
|
8269
|
+
}
|
|
8270
|
+
return hits;
|
|
8271
|
+
}
|
|
8272
|
+
async rankSemantic(query, pool) {
|
|
8273
|
+
const now = Date.now();
|
|
8274
|
+
let cached = this.lastSemantic;
|
|
8275
|
+
if (cached === undefined || cached.query !== query || now - cached.at > SEMANTIC_REUSE_MS) {
|
|
8276
|
+
const embedder = this.options.embedder;
|
|
8277
|
+
if (embedder === undefined)
|
|
8278
|
+
return [];
|
|
8279
|
+
cached = {
|
|
8280
|
+
query,
|
|
8281
|
+
at: now,
|
|
8282
|
+
vector: embedder.embed([query]).then((vectors) => vectors[0], () => {
|
|
8283
|
+
return;
|
|
8284
|
+
}),
|
|
8285
|
+
ranked: undefined
|
|
8286
|
+
};
|
|
8287
|
+
this.lastSemantic = cached;
|
|
8288
|
+
}
|
|
8289
|
+
const vector = await cached.vector;
|
|
8290
|
+
if (vector === undefined) {
|
|
8291
|
+
if (this.lastSemantic === cached)
|
|
8292
|
+
this.lastSemantic = undefined;
|
|
8293
|
+
return [];
|
|
8294
|
+
}
|
|
8295
|
+
if (cached.ranked === undefined || cached.ranked.pool < pool) {
|
|
8296
|
+
const wide = pool * POOL_FACTOR;
|
|
8297
|
+
cached.ranked = { pool: wide, keys: topKByCosine(vector, this.storedVectors(), wide) };
|
|
8298
|
+
}
|
|
8299
|
+
return cached.ranked.keys.slice(0, pool);
|
|
8300
|
+
}
|
|
8301
|
+
async searchHybrid(query, topK) {
|
|
8302
|
+
if (topK <= 0)
|
|
8303
|
+
return [];
|
|
8304
|
+
const pool = topK * POOL_FACTOR;
|
|
8305
|
+
const lexical = this.searchLexical(query, pool);
|
|
8306
|
+
const semantic = await this.searchSemantic(query, pool);
|
|
8307
|
+
if (semantic.length === 0)
|
|
8308
|
+
return lexical.slice(0, topK);
|
|
8309
|
+
const byKey = new Map;
|
|
8310
|
+
for (const hit of [...semantic, ...lexical])
|
|
8311
|
+
byKey.set(hit.key, hit);
|
|
8312
|
+
const fused = [];
|
|
8313
|
+
for (const entry of reciprocalRankFusion([lexical.map((hit) => hit.key), semantic.map((hit) => hit.key)])) {
|
|
8314
|
+
const hit = byKey.get(entry.key);
|
|
8315
|
+
if (hit !== undefined)
|
|
8316
|
+
fused.push({ ...hit, score: entry.score });
|
|
8317
|
+
if (fused.length >= topK)
|
|
8318
|
+
break;
|
|
8319
|
+
}
|
|
8320
|
+
return fused;
|
|
8321
|
+
}
|
|
8322
|
+
async sync() {
|
|
8323
|
+
const { embedder } = this.options;
|
|
8324
|
+
if (embedder === undefined)
|
|
8325
|
+
return { ok: false, message: "no embedder configured" };
|
|
8326
|
+
if (!this.attachCrawler())
|
|
8327
|
+
return { ok: false, message: "crawler archive not available yet" };
|
|
8328
|
+
let identity;
|
|
8329
|
+
try {
|
|
8330
|
+
identity = await embedder.identity();
|
|
8331
|
+
} catch (error) {
|
|
8332
|
+
return { ok: false, message: errorMessage(error) };
|
|
8333
|
+
}
|
|
8334
|
+
this.adoptIdentity(identity);
|
|
8335
|
+
const pruned = this.prune();
|
|
8336
|
+
const pending = this.pendingKeys();
|
|
8337
|
+
const batchSize = Math.max(1, this.options.batchSize ?? DEFAULT_BATCH_SIZE);
|
|
8338
|
+
const budget = Math.max(1, this.options.maxEmbedPerSync ?? DEFAULT_MAX_EMBED_PER_SYNC);
|
|
8339
|
+
const target = Math.min(pending.length, budget);
|
|
8340
|
+
const embed = async (rows) => {
|
|
8341
|
+
const vectors = await embedder.embed(rows.map((row) => row.input));
|
|
8342
|
+
if (vectors.length !== rows.length) {
|
|
8343
|
+
throw new Error(`embedder returned ${vectors.length} vectors for ${rows.length} messages`);
|
|
8344
|
+
}
|
|
8345
|
+
const wrong = vectors.find((vector) => vector.length !== identity.dimension);
|
|
8346
|
+
if (wrong !== undefined) {
|
|
8347
|
+
throw new Error(`embedder returned a ${wrong.length}-dimension vector, expected ${identity.dimension}`);
|
|
8348
|
+
}
|
|
8349
|
+
return vectors;
|
|
8350
|
+
};
|
|
8351
|
+
let embedded = 0;
|
|
8352
|
+
let skipped = 0;
|
|
8353
|
+
for (let offset = 0;offset < target; offset += batchSize) {
|
|
8354
|
+
const rows = [...this.hydrate(pending.slice(offset, Math.min(offset + batchSize, target))).values()];
|
|
8355
|
+
if (rows.length === 0)
|
|
8356
|
+
continue;
|
|
8357
|
+
try {
|
|
8358
|
+
this.store(rows, await embed(rows));
|
|
8359
|
+
embedded += rows.length;
|
|
8360
|
+
} catch (batchError) {
|
|
8361
|
+
const rejected = [];
|
|
8362
|
+
let lastError = batchError;
|
|
8363
|
+
for (const row of rows) {
|
|
8364
|
+
try {
|
|
8365
|
+
this.store([row], await embed([row]));
|
|
8366
|
+
embedded += 1;
|
|
8367
|
+
} catch (error) {
|
|
8368
|
+
rejected.push(row);
|
|
8369
|
+
lastError = error;
|
|
8370
|
+
}
|
|
8371
|
+
}
|
|
8372
|
+
if (rejected.length === 0)
|
|
8373
|
+
continue;
|
|
8374
|
+
if (embedded === 0)
|
|
8375
|
+
return { ok: false, message: errorMessage(lastError) };
|
|
8376
|
+
this.store(rejected, undefined);
|
|
8377
|
+
skipped += rejected.length;
|
|
8378
|
+
}
|
|
8379
|
+
}
|
|
8380
|
+
return { ok: true, embedded, skipped, pruned, remaining: pending.length - embedded - skipped };
|
|
8381
|
+
}
|
|
8382
|
+
adoptIdentity(identity) {
|
|
8383
|
+
const serialized = JSON.stringify(identity);
|
|
8384
|
+
const current = this.db.prepare("select v from sidecar_meta where k = ?").get(IDENTITY_KEY);
|
|
8385
|
+
if (current?.v === serialized)
|
|
8386
|
+
return;
|
|
8387
|
+
this.transaction(() => {
|
|
8388
|
+
this.db.exec("delete from sidecar_vec");
|
|
8389
|
+
this.db.prepare("insert or replace into sidecar_meta (k, v) values (?, ?)").run(IDENTITY_KEY, serialized);
|
|
8390
|
+
});
|
|
8391
|
+
this.lastSemantic = undefined;
|
|
8392
|
+
}
|
|
8393
|
+
prune() {
|
|
8394
|
+
const { descriptor } = this.options;
|
|
8395
|
+
const result = this.db.prepare(`delete from sidecar_vec where message_key not in (
|
|
8396
|
+
select ${descriptor.keyExpression} from ${descriptor.fromClause} where ${this.where}
|
|
8397
|
+
)`).run(...this.whereParams());
|
|
8398
|
+
if (Number(result.changes) > 0)
|
|
8399
|
+
this.lastSemantic = undefined;
|
|
8400
|
+
return Number(result.changes);
|
|
8401
|
+
}
|
|
8402
|
+
pendingKeys() {
|
|
8403
|
+
const { descriptor } = this.options;
|
|
8404
|
+
const rows = this.db.prepare(`select ${this.select}, v.hash as stored_hash
|
|
8405
|
+
from ${descriptor.fromClause}
|
|
8406
|
+
left join sidecar_vec v on v.message_key = ${descriptor.keyExpression}
|
|
8407
|
+
where ${this.where}`).iterate(...this.whereParams());
|
|
8408
|
+
const pending = [];
|
|
8409
|
+
for (const row of rows) {
|
|
8410
|
+
if (row.stored_hash !== this.toRow(row).hash)
|
|
8411
|
+
pending.push(String(row.k));
|
|
8412
|
+
}
|
|
8413
|
+
return pending;
|
|
8414
|
+
}
|
|
8415
|
+
hydrate(keys) {
|
|
8416
|
+
const { descriptor } = this.options;
|
|
8417
|
+
const out = new Map;
|
|
8418
|
+
if (keys.length === 0)
|
|
8419
|
+
return out;
|
|
8420
|
+
const rows = this.db.prepare(`select ${this.select} from ${descriptor.fromClause}
|
|
8421
|
+
where ${descriptor.keyExpression} in (${keys.map(() => "?").join(", ")}) and ${this.where}`).all(...keys, ...this.whereParams());
|
|
8422
|
+
const byKey = new Map(rows.map((row) => [String(row.k), this.toRow(row)]));
|
|
8423
|
+
for (const key of keys) {
|
|
8424
|
+
const row = byKey.get(key);
|
|
8425
|
+
if (row !== undefined)
|
|
8426
|
+
out.set(key, row);
|
|
8427
|
+
}
|
|
8428
|
+
return out;
|
|
8429
|
+
}
|
|
8430
|
+
store(rows, vectors) {
|
|
8431
|
+
const upsert = this.db.prepare("insert or replace into sidecar_vec (message_key, hash, vec) values (?, ?, ?)");
|
|
8432
|
+
this.transaction(() => {
|
|
8433
|
+
for (const [index, row] of rows.entries()) {
|
|
8434
|
+
upsert.run(row.key, row.hash, encodeVector(vectors?.[index] ?? []));
|
|
8435
|
+
}
|
|
8436
|
+
});
|
|
8437
|
+
this.lastSemantic = undefined;
|
|
8438
|
+
}
|
|
8439
|
+
*storedVectors() {
|
|
8440
|
+
const rows = this.db.prepare("select message_key, vec from sidecar_vec where length(vec) > 0").iterate();
|
|
8441
|
+
for (const row of rows) {
|
|
8442
|
+
const vector = row.vec instanceof Uint8Array ? decodeVector(row.vec) : undefined;
|
|
8443
|
+
if (vector !== undefined)
|
|
8444
|
+
yield { key: String(row.message_key), vector };
|
|
8445
|
+
}
|
|
8446
|
+
}
|
|
8447
|
+
transaction(work) {
|
|
8448
|
+
this.db.exec("begin immediate");
|
|
8449
|
+
try {
|
|
8450
|
+
work();
|
|
8451
|
+
this.db.exec("commit");
|
|
8452
|
+
} catch (error) {
|
|
8453
|
+
this.db.exec("rollback");
|
|
8454
|
+
throw error;
|
|
8455
|
+
}
|
|
8456
|
+
}
|
|
8457
|
+
toRow(row) {
|
|
8458
|
+
const metadata = {};
|
|
8459
|
+
for (const [index, column] of this.options.descriptor.metadata.entries()) {
|
|
8460
|
+
const value = row[`m${index}`];
|
|
8461
|
+
if (typeof value === "string" && value.length > 0 || typeof value === "number")
|
|
8462
|
+
metadata[column.key] = String(value);
|
|
8463
|
+
}
|
|
8464
|
+
const title = typeof row.title === "string" && row.title.length > 0 ? row.title : undefined;
|
|
8465
|
+
const epoch = typeof row.epoch === "number" && Number.isFinite(row.epoch) ? row.epoch : undefined;
|
|
8466
|
+
const content = String(row.content ?? "");
|
|
8467
|
+
const input = `${title ?? ""}
|
|
8468
|
+
${content}`.slice(0, MAX_EMBED_INPUT_CHARS);
|
|
8469
|
+
return {
|
|
8470
|
+
key: String(row.k),
|
|
8471
|
+
content,
|
|
8472
|
+
title,
|
|
8473
|
+
epoch,
|
|
8474
|
+
metadata,
|
|
8475
|
+
input,
|
|
8476
|
+
hash: createHash6("sha1").update(input).digest("hex")
|
|
8477
|
+
};
|
|
8478
|
+
}
|
|
8479
|
+
toHit(row, score) {
|
|
8480
|
+
return {
|
|
8481
|
+
key: row.key,
|
|
8482
|
+
content: row.content,
|
|
8483
|
+
score,
|
|
8484
|
+
...row.title !== undefined ? { title: row.title } : {},
|
|
8485
|
+
...row.epoch !== undefined ? { publishedAt: Math.round(row.epoch * 1000) } : {},
|
|
8486
|
+
metadata: row.metadata
|
|
8487
|
+
};
|
|
8488
|
+
}
|
|
8489
|
+
}
|
|
8490
|
+
function toMatchQuery(text) {
|
|
8491
|
+
const terms = [...new Set(text.match(/[\p{L}\p{N}_]+/gu) ?? [])];
|
|
8492
|
+
return terms.length === 0 ? undefined : terms.map((term) => `"${term}"`).join(" OR ");
|
|
8493
|
+
}
|
|
8494
|
+
function errorMessage(error) {
|
|
8495
|
+
return error instanceof Error ? error.message : String(error);
|
|
8496
|
+
}
|
|
8497
|
+
var DEFAULT_BATCH_SIZE = 256, DEFAULT_MAX_EMBED_PER_SYNC = 2048, MAX_EMBED_INPUT_CHARS = 8192, BUSY_TIMEOUT_MS = 5000, POOL_FACTOR = 3, SEMANTIC_REUSE_MS = 1e4, IDENTITY_KEY = "identity";
|
|
8498
|
+
var init_sidecar = () => {};
|
|
8499
|
+
|
|
8500
|
+
// src/datasource/sidecar/methods.ts
|
|
8501
|
+
function sidecarMethodName(datasourceId, mode) {
|
|
8502
|
+
return mode === "bm25" ? `${datasourceId}-sidecar-bm25` : `${datasourceId}-${mode}`;
|
|
8503
|
+
}
|
|
8504
|
+
|
|
8505
|
+
class SidecarRetrievalMethod {
|
|
8506
|
+
options;
|
|
8507
|
+
constructor(options) {
|
|
8508
|
+
this.options = options;
|
|
8509
|
+
}
|
|
8510
|
+
describe() {
|
|
8511
|
+
const { datasourceId, skillType, mode, tags } = this.options;
|
|
8512
|
+
const { type, text, capabilities } = DESCRIPTIONS[mode];
|
|
8513
|
+
return {
|
|
8514
|
+
name: sidecarMethodName(datasourceId, mode),
|
|
8515
|
+
type,
|
|
8516
|
+
description: `${text} over ${skillType} via a read-only sidecar index`,
|
|
8517
|
+
status: "active",
|
|
8518
|
+
capabilities: [...capabilities, "scoped", "path-opaque-sources"],
|
|
8519
|
+
datasourceId,
|
|
8520
|
+
tags
|
|
8521
|
+
};
|
|
8522
|
+
}
|
|
8523
|
+
async retrieve(query, options) {
|
|
8524
|
+
const trimmed = query.trim();
|
|
8525
|
+
if (trimmed.length === 0)
|
|
8526
|
+
return [];
|
|
8527
|
+
const topK = options.topK ?? DEFAULT_TOP_K5;
|
|
8528
|
+
const { sidecar, mode } = this.options;
|
|
8529
|
+
const hits = mode === "bm25" ? sidecar.searchLexical(trimmed, topK) : mode === "semantic" ? await sidecar.searchSemantic(trimmed, topK) : await sidecar.searchHybrid(trimmed, topK);
|
|
8530
|
+
const results = [];
|
|
8531
|
+
for (const hit of hits) {
|
|
8532
|
+
const result = this.toResult(hit, options);
|
|
8533
|
+
if (result !== undefined)
|
|
8534
|
+
results.push(result);
|
|
8535
|
+
}
|
|
8536
|
+
return results;
|
|
8537
|
+
}
|
|
8538
|
+
toResult(hit, options) {
|
|
8539
|
+
const { descriptor, datasourceId, instanceId, mode } = this.options;
|
|
8540
|
+
const chunkId = descriptor.chunkId(hit.key, hit.metadata);
|
|
8541
|
+
const source = datasourceSourcePath(datasourceId, instanceId, chunkId);
|
|
8542
|
+
if (!matchesDatasourceScope(source, options.scope))
|
|
8543
|
+
return;
|
|
8544
|
+
const method = sidecarMethodName(datasourceId, mode);
|
|
8545
|
+
return {
|
|
8546
|
+
id: `${datasourceId}:${instanceId}:${chunkId}`,
|
|
8547
|
+
content: hit.content,
|
|
8548
|
+
source,
|
|
8549
|
+
score: hit.score,
|
|
8550
|
+
metadata: {
|
|
8551
|
+
...hit.metadata,
|
|
8552
|
+
method,
|
|
8553
|
+
datasourceId,
|
|
8554
|
+
instanceId,
|
|
8555
|
+
backend: "sidecar",
|
|
8556
|
+
...hit.title !== undefined ? { title: hit.title } : {},
|
|
8557
|
+
hierarchy: descriptor.hierarchy(hit.metadata).join("/"),
|
|
8558
|
+
...hit.publishedAt !== undefined ? { publishedAt: hit.publishedAt } : {}
|
|
8559
|
+
}
|
|
8560
|
+
};
|
|
8561
|
+
}
|
|
8562
|
+
}
|
|
8563
|
+
var DEFAULT_TOP_K5 = 20, DESCRIPTIONS;
|
|
8564
|
+
var init_methods4 = __esm(() => {
|
|
8565
|
+
init_scope2();
|
|
8566
|
+
DESCRIPTIONS = {
|
|
8567
|
+
bm25: { type: "bm25", text: "BM25-ranked FTS5 retrieval", capabilities: ["lexical", "fts5", "bm25"] },
|
|
8568
|
+
semantic: { type: "vector", text: "Semantic (embedding) retrieval", capabilities: ["semantic"] },
|
|
8569
|
+
hybrid: {
|
|
8570
|
+
type: "hybrid",
|
|
8571
|
+
text: "Hybrid BM25 + semantic retrieval (reciprocal rank fusion)",
|
|
8572
|
+
capabilities: ["lexical", "semantic", "hybrid"]
|
|
8573
|
+
}
|
|
8574
|
+
};
|
|
8575
|
+
});
|
|
8576
|
+
|
|
8057
8577
|
// src/datasource/skills/lazykatok/skill.ts
|
|
8058
8578
|
class LazykatokSkill {
|
|
8059
8579
|
client;
|
|
@@ -8061,6 +8581,7 @@ class LazykatokSkill {
|
|
|
8061
8581
|
instances;
|
|
8062
8582
|
pollingIntervalMs;
|
|
8063
8583
|
tags;
|
|
8584
|
+
sidecar;
|
|
8064
8585
|
lastIndexedAt;
|
|
8065
8586
|
constructor(options) {
|
|
8066
8587
|
this.client = options.client;
|
|
@@ -8069,6 +8590,11 @@ class LazykatokSkill {
|
|
|
8069
8590
|
this.pollingIntervalMs = options.pollingIntervalMs ?? DEFAULT_POLLING_INTERVAL_MS3;
|
|
8070
8591
|
this.tags = options.tags ?? DEFAULT_KAKAO_TAGS2;
|
|
8071
8592
|
this.lastIndexedAt = options.lastIndexedAt;
|
|
8593
|
+
this.sidecar = options.sidecar === undefined ? undefined : new CrawlerSidecar({
|
|
8594
|
+
...options.sidecar,
|
|
8595
|
+
channelIds: options.channelIds ?? options.sidecar.channelIds ?? [],
|
|
8596
|
+
channelNames: options.channelNames ?? options.sidecar.channelNames ?? []
|
|
8597
|
+
});
|
|
8072
8598
|
}
|
|
8073
8599
|
describe() {
|
|
8074
8600
|
return {
|
|
@@ -8100,6 +8626,17 @@ class LazykatokSkill {
|
|
|
8100
8626
|
const sync = await this.client.sync(options?.signal);
|
|
8101
8627
|
if (!sync.ok)
|
|
8102
8628
|
return this.fail("datasource-index-failed", sync);
|
|
8629
|
+
if (this.sidecar !== undefined) {
|
|
8630
|
+
this.lastIndexedAt = Date.now();
|
|
8631
|
+
return {
|
|
8632
|
+
ok: true,
|
|
8633
|
+
instanceId: this.instanceId,
|
|
8634
|
+
skill: KAKAO_DATASOURCE_ID2,
|
|
8635
|
+
chunkCount: syncChunkCount(sync),
|
|
8636
|
+
indexedAt: this.lastIndexedAt,
|
|
8637
|
+
diagnostics: await this.syncSidecar()
|
|
8638
|
+
};
|
|
8639
|
+
}
|
|
8103
8640
|
const indexResult = await this.client.index(options?.signal);
|
|
8104
8641
|
if (!indexResult.ok)
|
|
8105
8642
|
return this.fail("datasource-index-failed", indexResult);
|
|
@@ -8123,12 +8660,75 @@ class LazykatokSkill {
|
|
|
8123
8660
|
});
|
|
8124
8661
|
}
|
|
8125
8662
|
}
|
|
8663
|
+
close() {
|
|
8664
|
+
this.sidecar?.close();
|
|
8665
|
+
}
|
|
8126
8666
|
retrievalMethods() {
|
|
8667
|
+
const cliBm25 = new LazykatokBm25Method({ client: this.client, instanceId: this.instanceId, tags: this.tags });
|
|
8668
|
+
const { sidecar } = this;
|
|
8669
|
+
if (sidecar === undefined) {
|
|
8670
|
+
return [
|
|
8671
|
+
cliBm25,
|
|
8672
|
+
new LazykatokSemanticMethod({ client: this.client, instanceId: this.instanceId, tags: this.tags })
|
|
8673
|
+
];
|
|
8674
|
+
}
|
|
8675
|
+
const shared = {
|
|
8676
|
+
sidecar,
|
|
8677
|
+
descriptor: sidecar.descriptor,
|
|
8678
|
+
datasourceId: KAKAO_DATASOURCE_ID2,
|
|
8679
|
+
skillType: KAKAO_SKILL_TYPE,
|
|
8680
|
+
instanceId: this.instanceId,
|
|
8681
|
+
tags: this.tags
|
|
8682
|
+
};
|
|
8683
|
+
const bm25 = new SidecarRetrievalMethod({ ...shared, mode: "bm25" });
|
|
8684
|
+
if (!sidecar.semanticEnabled)
|
|
8685
|
+
return [cliBm25, bm25];
|
|
8127
8686
|
return [
|
|
8128
|
-
|
|
8129
|
-
|
|
8687
|
+
cliBm25,
|
|
8688
|
+
bm25,
|
|
8689
|
+
new SidecarRetrievalMethod({ ...shared, mode: "semantic" }),
|
|
8690
|
+
new SidecarRetrievalMethod({ ...shared, mode: "hybrid" })
|
|
8130
8691
|
];
|
|
8131
8692
|
}
|
|
8693
|
+
async syncSidecar() {
|
|
8694
|
+
if (this.sidecar === undefined || !this.sidecar.semanticEnabled)
|
|
8695
|
+
return [];
|
|
8696
|
+
const base = { instanceId: this.instanceId, source: KAKAO_DATASOURCE_ID2 };
|
|
8697
|
+
let result;
|
|
8698
|
+
try {
|
|
8699
|
+
result = await this.sidecar.sync();
|
|
8700
|
+
} catch (error) {
|
|
8701
|
+
result = { ok: false, message: error instanceof Error ? error.message : String(error) };
|
|
8702
|
+
}
|
|
8703
|
+
if (!result.ok) {
|
|
8704
|
+
return [
|
|
8705
|
+
{
|
|
8706
|
+
code: "datasource-index-failed",
|
|
8707
|
+
severity: "warning",
|
|
8708
|
+
message: `semantic-unavailable: ${result.message}`.slice(0, 300),
|
|
8709
|
+
...base
|
|
8710
|
+
}
|
|
8711
|
+
];
|
|
8712
|
+
}
|
|
8713
|
+
const diagnostics = [];
|
|
8714
|
+
if (result.skipped > 0) {
|
|
8715
|
+
diagnostics.push({
|
|
8716
|
+
code: "datasource-index-partial",
|
|
8717
|
+
severity: "warning",
|
|
8718
|
+
message: `semantic index skipped ${result.skipped} message(s) the embedder rejected; they stay searchable by BM25 and are retried when edited`,
|
|
8719
|
+
...base
|
|
8720
|
+
});
|
|
8721
|
+
}
|
|
8722
|
+
if (result.remaining > 0) {
|
|
8723
|
+
diagnostics.push({
|
|
8724
|
+
code: "datasource-index-partial",
|
|
8725
|
+
severity: "info",
|
|
8726
|
+
message: `semantic index catching up: ${result.embedded} embedded this refresh, ${result.remaining} remaining; hybrid search uses BM25 for the rest until a later refresh`,
|
|
8727
|
+
...base
|
|
8728
|
+
});
|
|
8729
|
+
}
|
|
8730
|
+
return diagnostics;
|
|
8731
|
+
}
|
|
8132
8732
|
describeSources() {
|
|
8133
8733
|
return this.instances.map((instanceId) => ({
|
|
8134
8734
|
source: datasourceSourcePath(KAKAO_DATASOURCE_ID2, instanceId),
|
|
@@ -8202,10 +8802,22 @@ class LazykatokSkill {
|
|
|
8202
8802
|
};
|
|
8203
8803
|
}
|
|
8204
8804
|
}
|
|
8805
|
+
function syncChunkCount(sync) {
|
|
8806
|
+
if (!("data" in sync) || typeof sync.data !== "object" || sync.data === null)
|
|
8807
|
+
return 0;
|
|
8808
|
+
const meta = "metadata" in sync.data && typeof sync.data.metadata === "object" && sync.data.metadata !== null ? sync.data.metadata : undefined;
|
|
8809
|
+
if (typeof meta?.chunks === "number")
|
|
8810
|
+
return meta.chunks;
|
|
8811
|
+
if ("messageCount" in sync.data && typeof sync.data.messageCount === "number")
|
|
8812
|
+
return sync.data.messageCount;
|
|
8813
|
+
return 0;
|
|
8814
|
+
}
|
|
8205
8815
|
var KAKAO_DATASOURCE_ID2 = "kakao", KAKAO_SKILL_TYPE = "kakaotalk", DEFAULT_INSTANCE_ID4 = "default", DEFAULT_POLLING_INTERVAL_MS3, DEFAULT_KAKAO_TAGS2;
|
|
8206
8816
|
var init_skill7 = __esm(() => {
|
|
8207
8817
|
init_skip();
|
|
8208
8818
|
init_scope2();
|
|
8819
|
+
init_sidecar();
|
|
8820
|
+
init_methods4();
|
|
8209
8821
|
init_methods3();
|
|
8210
8822
|
DEFAULT_POLLING_INTERVAL_MS3 = 15 * 60 * 1000;
|
|
8211
8823
|
DEFAULT_KAKAO_TAGS2 = ["kakaotalk", "personal", "pii"];
|
|
@@ -8221,7 +8833,7 @@ var init_lazykatok = __esm(() => {
|
|
|
8221
8833
|
|
|
8222
8834
|
// src/datasource/skills/mail-export/connector.ts
|
|
8223
8835
|
import { readdir as readdir2, readFile as readFile4, stat as stat3 } from "node:fs/promises";
|
|
8224
|
-
import { basename as basename5, dirname as
|
|
8836
|
+
import { basename as basename5, dirname as dirname12, extname as extname3, join as join14 } from "node:path";
|
|
8225
8837
|
import { simpleParser as simpleParser2 } from "mailparser";
|
|
8226
8838
|
|
|
8227
8839
|
class MailExportConnector {
|
|
@@ -8282,7 +8894,7 @@ class MailExportConnector {
|
|
|
8282
8894
|
continue;
|
|
8283
8895
|
}
|
|
8284
8896
|
const stem = basename5(file, extname3(file));
|
|
8285
|
-
const mailboxDir = basename5(
|
|
8897
|
+
const mailboxDir = basename5(dirname12(file));
|
|
8286
8898
|
const hierarchy = ["mailboxes", mailboxDir, stem];
|
|
8287
8899
|
const extension = extname3(file).toLowerCase();
|
|
8288
8900
|
const rawMessages = extension === ".mbox" ? splitMbox(bytes.toString("utf8")) : [bytes.toString("utf8")];
|
|
@@ -8392,14 +9004,14 @@ var init_mail_export = __esm(() => {
|
|
|
8392
9004
|
});
|
|
8393
9005
|
|
|
8394
9006
|
// src/datasource/skills/mailcrawl/config.ts
|
|
8395
|
-
import { chmodSync, mkdirSync as
|
|
9007
|
+
import { chmodSync, mkdirSync as mkdirSync8 } from "node:fs";
|
|
8396
9008
|
function validateMailcrawlInstanceId(instanceId) {
|
|
8397
9009
|
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(instanceId)) {
|
|
8398
9010
|
throw new Error("mailcrawl instanceId must be a safe single path segment");
|
|
8399
9011
|
}
|
|
8400
9012
|
}
|
|
8401
9013
|
function ensurePrivateMailcrawlDataDir(path) {
|
|
8402
|
-
|
|
9014
|
+
mkdirSync8(path, { recursive: true, mode: 448 });
|
|
8403
9015
|
if (process.platform !== "win32")
|
|
8404
9016
|
chmodSync(path, 448);
|
|
8405
9017
|
}
|
|
@@ -8827,7 +9439,7 @@ class MailcrawlMethod {
|
|
|
8827
9439
|
}
|
|
8828
9440
|
}
|
|
8829
9441
|
var modes, MAILCRAWL_MODES;
|
|
8830
|
-
var
|
|
9442
|
+
var init_methods5 = __esm(() => {
|
|
8831
9443
|
init_errors2();
|
|
8832
9444
|
init_scope2();
|
|
8833
9445
|
modes = ["bm25", "semantic", "hybrid"];
|
|
@@ -8977,515 +9589,17 @@ var init_skill9 = __esm(() => {
|
|
|
8977
9589
|
init_scope2();
|
|
8978
9590
|
init_client5();
|
|
8979
9591
|
init_config3();
|
|
8980
|
-
|
|
9592
|
+
init_methods5();
|
|
8981
9593
|
TAGS = ["mailcrawl", "email", "pii"];
|
|
8982
9594
|
});
|
|
8983
9595
|
|
|
8984
9596
|
// src/datasource/skills/mailcrawl/index.ts
|
|
8985
9597
|
var init_mailcrawl = __esm(() => {
|
|
8986
9598
|
init_client5();
|
|
8987
|
-
|
|
9599
|
+
init_methods5();
|
|
8988
9600
|
init_skill9();
|
|
8989
9601
|
});
|
|
8990
9602
|
|
|
8991
|
-
// src/datasource/sidecar/fusion.ts
|
|
8992
|
-
function encodeVector(vector) {
|
|
8993
|
-
const floats = Float32Array.from(vector);
|
|
8994
|
-
return new Uint8Array(floats.buffer, floats.byteOffset, floats.byteLength);
|
|
8995
|
-
}
|
|
8996
|
-
function decodeVector(blob) {
|
|
8997
|
-
if (blob.byteLength % Float32Array.BYTES_PER_ELEMENT !== 0)
|
|
8998
|
-
return;
|
|
8999
|
-
return new Float32Array(new Uint8Array(blob).buffer);
|
|
9000
|
-
}
|
|
9001
|
-
function cosine2(a, b) {
|
|
9002
|
-
if (a.length === 0 || a.length !== b.length)
|
|
9003
|
-
return 0;
|
|
9004
|
-
let dot = 0;
|
|
9005
|
-
let normA = 0;
|
|
9006
|
-
let normB = 0;
|
|
9007
|
-
for (let index = 0;index < a.length; index += 1) {
|
|
9008
|
-
const x = a[index] ?? 0;
|
|
9009
|
-
const y = b[index] ?? 0;
|
|
9010
|
-
dot += x * y;
|
|
9011
|
-
normA += x * x;
|
|
9012
|
-
normB += y * y;
|
|
9013
|
-
}
|
|
9014
|
-
if (normA === 0 || normB === 0)
|
|
9015
|
-
return 0;
|
|
9016
|
-
return dot / Math.sqrt(normA * normB);
|
|
9017
|
-
}
|
|
9018
|
-
function topKByCosine(query, rows, k) {
|
|
9019
|
-
if (k <= 0)
|
|
9020
|
-
return [];
|
|
9021
|
-
const scored = [];
|
|
9022
|
-
for (const row of rows) {
|
|
9023
|
-
const score = cosine2(query, row.vector);
|
|
9024
|
-
if (score > 0)
|
|
9025
|
-
scored.push({ key: row.key, score });
|
|
9026
|
-
}
|
|
9027
|
-
scored.sort((a, b) => b.score - a.score || a.key.localeCompare(b.key));
|
|
9028
|
-
return scored.slice(0, k);
|
|
9029
|
-
}
|
|
9030
|
-
function reciprocalRankFusion(lists) {
|
|
9031
|
-
const scores = new Map;
|
|
9032
|
-
for (const list of lists) {
|
|
9033
|
-
const seen = new Set;
|
|
9034
|
-
for (const [index, key] of list.entries()) {
|
|
9035
|
-
if (seen.has(key))
|
|
9036
|
-
continue;
|
|
9037
|
-
seen.add(key);
|
|
9038
|
-
scores.set(key, (scores.get(key) ?? 0) + 1 / (RRF_K + index + 1));
|
|
9039
|
-
}
|
|
9040
|
-
}
|
|
9041
|
-
return [...scores.entries()].map(([key, score]) => ({ key, score })).sort((a, b) => b.score - a.score);
|
|
9042
|
-
}
|
|
9043
|
-
var RRF_K = 60;
|
|
9044
|
-
|
|
9045
|
-
// src/datasource/sidecar/index.ts
|
|
9046
|
-
import { createHash as createHash6 } from "node:crypto";
|
|
9047
|
-
import { mkdirSync as mkdirSync8, statSync } from "node:fs";
|
|
9048
|
-
import { dirname as dirname12 } from "node:path";
|
|
9049
|
-
import { DatabaseSync } from "node:sqlite";
|
|
9050
|
-
import { pathToFileURL } from "node:url";
|
|
9051
|
-
|
|
9052
|
-
class CrawlerSidecar {
|
|
9053
|
-
descriptor;
|
|
9054
|
-
options;
|
|
9055
|
-
select;
|
|
9056
|
-
where;
|
|
9057
|
-
opened;
|
|
9058
|
-
attached;
|
|
9059
|
-
lastSemantic;
|
|
9060
|
-
constructor(options) {
|
|
9061
|
-
this.options = options;
|
|
9062
|
-
const { descriptor } = options;
|
|
9063
|
-
this.descriptor = descriptor;
|
|
9064
|
-
const metadataColumns = descriptor.metadata.map((column, index) => `${column.sql} as m${index}`);
|
|
9065
|
-
this.select = [
|
|
9066
|
-
`${descriptor.keyExpression} as k`,
|
|
9067
|
-
`${descriptor.textExpression} as content`,
|
|
9068
|
-
`${descriptor.titleExpression ?? "null"} as title`,
|
|
9069
|
-
`${descriptor.epochSecondsExpression ?? "null"} as epoch`,
|
|
9070
|
-
...metadataColumns
|
|
9071
|
-
].join(", ");
|
|
9072
|
-
const scope = descriptor.scopeExpression !== undefined && options.workspaceId !== undefined;
|
|
9073
|
-
const allowlist = [
|
|
9074
|
-
...(options.channelIds?.length ?? 0) > 0 ? [`${descriptor.channelIdExpression} in (${options.channelIds?.map(() => "?").join(", ")})`] : [],
|
|
9075
|
-
...(options.channelNames?.length ?? 0) > 0 ? [`${descriptor.channelNameExpression} in (${options.channelNames?.map(() => "?").join(", ")})`] : []
|
|
9076
|
-
];
|
|
9077
|
-
this.where = [
|
|
9078
|
-
descriptor.liveWhere,
|
|
9079
|
-
...scope ? [`${descriptor.scopeExpression} = ?`] : [],
|
|
9080
|
-
...allowlist.length > 0 ? [`(${allowlist.join(" or ")})`] : []
|
|
9081
|
-
].join(" and ");
|
|
9082
|
-
}
|
|
9083
|
-
get db() {
|
|
9084
|
-
if (this.opened !== undefined)
|
|
9085
|
-
return this.opened;
|
|
9086
|
-
mkdirSync8(dirname12(this.options.sidecarDbPath), { recursive: true });
|
|
9087
|
-
const db = new DatabaseSync(this.options.sidecarDbPath);
|
|
9088
|
-
db.exec(`pragma busy_timeout = ${BUSY_TIMEOUT_MS}`);
|
|
9089
|
-
db.exec("pragma journal_mode = wal");
|
|
9090
|
-
db.exec(`
|
|
9091
|
-
create table if not exists sidecar_meta (k text primary key, v text not null);
|
|
9092
|
-
create table if not exists sidecar_vec (message_key text primary key, hash text not null, vec blob not null);
|
|
9093
|
-
`);
|
|
9094
|
-
this.opened = db;
|
|
9095
|
-
return db;
|
|
9096
|
-
}
|
|
9097
|
-
attachCrawler() {
|
|
9098
|
-
let file;
|
|
9099
|
-
try {
|
|
9100
|
-
const { dev, ino } = statSync(this.options.crawlerDbPath);
|
|
9101
|
-
file = { dev, ino };
|
|
9102
|
-
} catch {
|
|
9103
|
-
this.detachCrawler();
|
|
9104
|
-
return false;
|
|
9105
|
-
}
|
|
9106
|
-
if (this.attached !== undefined) {
|
|
9107
|
-
if (this.attached.dev === file.dev && this.attached.ino === file.ino)
|
|
9108
|
-
return true;
|
|
9109
|
-
this.detachCrawler();
|
|
9110
|
-
}
|
|
9111
|
-
const crawlerUri = `${pathToFileURL(this.options.crawlerDbPath).href}?mode=ro`.replaceAll("'", "''");
|
|
9112
|
-
try {
|
|
9113
|
-
this.db.exec(`attach database '${crawlerUri}' as crawler`);
|
|
9114
|
-
} catch {
|
|
9115
|
-
return false;
|
|
9116
|
-
}
|
|
9117
|
-
const { descriptor } = this.options;
|
|
9118
|
-
try {
|
|
9119
|
-
this.db.prepare(`select ${this.select} from ${descriptor.fromClause} where ${this.where} limit 0`).all(...this.whereParams());
|
|
9120
|
-
this.db.prepare(`select 1 from ${descriptor.fts.table} limit 0`).all();
|
|
9121
|
-
} catch {
|
|
9122
|
-
this.db.exec("detach database crawler");
|
|
9123
|
-
return false;
|
|
9124
|
-
}
|
|
9125
|
-
this.attached = file;
|
|
9126
|
-
this.lastSemantic = undefined;
|
|
9127
|
-
return true;
|
|
9128
|
-
}
|
|
9129
|
-
detachCrawler() {
|
|
9130
|
-
if (this.attached === undefined)
|
|
9131
|
-
return;
|
|
9132
|
-
this.attached = undefined;
|
|
9133
|
-
this.lastSemantic = undefined;
|
|
9134
|
-
try {
|
|
9135
|
-
this.opened?.exec("detach database crawler");
|
|
9136
|
-
} catch {}
|
|
9137
|
-
}
|
|
9138
|
-
get semanticEnabled() {
|
|
9139
|
-
return this.options.embedder !== undefined;
|
|
9140
|
-
}
|
|
9141
|
-
close() {
|
|
9142
|
-
this.opened?.close();
|
|
9143
|
-
this.opened = undefined;
|
|
9144
|
-
this.attached = undefined;
|
|
9145
|
-
this.lastSemantic = undefined;
|
|
9146
|
-
}
|
|
9147
|
-
whereParams() {
|
|
9148
|
-
const { descriptor, workspaceId, channelIds = [], channelNames = [] } = this.options;
|
|
9149
|
-
return [
|
|
9150
|
-
...descriptor.scopeExpression !== undefined && workspaceId !== undefined ? [workspaceId] : [],
|
|
9151
|
-
...channelIds,
|
|
9152
|
-
...channelNames
|
|
9153
|
-
];
|
|
9154
|
-
}
|
|
9155
|
-
searchLexical(query, topK) {
|
|
9156
|
-
const match = toMatchQuery(query);
|
|
9157
|
-
if (match === undefined || topK <= 0 || !this.attachCrawler())
|
|
9158
|
-
return [];
|
|
9159
|
-
const { descriptor } = this.options;
|
|
9160
|
-
const rows = this.db.prepare(`select ${this.select}, bm25(${descriptor.fts.table}) as rank
|
|
9161
|
-
from ${descriptor.fromClause} join ${descriptor.fts.table} on ${descriptor.fts.joinOn}
|
|
9162
|
-
where ${descriptor.fts.table} match ? and ${this.where}
|
|
9163
|
-
order by rank limit ?`).all(match, ...this.whereParams(), topK);
|
|
9164
|
-
return rows.map((row) => this.toHit(this.toRow(row), -Number(row.rank)));
|
|
9165
|
-
}
|
|
9166
|
-
async searchSemantic(query, topK) {
|
|
9167
|
-
const trimmed = query.trim();
|
|
9168
|
-
if (this.options.embedder === undefined || trimmed.length === 0 || topK <= 0 || !this.attachCrawler())
|
|
9169
|
-
return [];
|
|
9170
|
-
const ranked = await this.rankSemantic(trimmed, topK * POOL_FACTOR);
|
|
9171
|
-
if (ranked.length === 0)
|
|
9172
|
-
return [];
|
|
9173
|
-
const live = this.hydrate(ranked.map((entry) => entry.key));
|
|
9174
|
-
const hits = [];
|
|
9175
|
-
for (const entry of ranked) {
|
|
9176
|
-
const row = live.get(entry.key);
|
|
9177
|
-
if (row !== undefined)
|
|
9178
|
-
hits.push(this.toHit(row, entry.score));
|
|
9179
|
-
if (hits.length >= topK)
|
|
9180
|
-
break;
|
|
9181
|
-
}
|
|
9182
|
-
return hits;
|
|
9183
|
-
}
|
|
9184
|
-
async rankSemantic(query, pool) {
|
|
9185
|
-
const now = Date.now();
|
|
9186
|
-
let cached = this.lastSemantic;
|
|
9187
|
-
if (cached === undefined || cached.query !== query || now - cached.at > SEMANTIC_REUSE_MS) {
|
|
9188
|
-
const embedder = this.options.embedder;
|
|
9189
|
-
if (embedder === undefined)
|
|
9190
|
-
return [];
|
|
9191
|
-
cached = {
|
|
9192
|
-
query,
|
|
9193
|
-
at: now,
|
|
9194
|
-
vector: embedder.embed([query]).then((vectors) => vectors[0], () => {
|
|
9195
|
-
return;
|
|
9196
|
-
}),
|
|
9197
|
-
ranked: undefined
|
|
9198
|
-
};
|
|
9199
|
-
this.lastSemantic = cached;
|
|
9200
|
-
}
|
|
9201
|
-
const vector = await cached.vector;
|
|
9202
|
-
if (vector === undefined) {
|
|
9203
|
-
if (this.lastSemantic === cached)
|
|
9204
|
-
this.lastSemantic = undefined;
|
|
9205
|
-
return [];
|
|
9206
|
-
}
|
|
9207
|
-
if (cached.ranked === undefined || cached.ranked.pool < pool) {
|
|
9208
|
-
const wide = pool * POOL_FACTOR;
|
|
9209
|
-
cached.ranked = { pool: wide, keys: topKByCosine(vector, this.storedVectors(), wide) };
|
|
9210
|
-
}
|
|
9211
|
-
return cached.ranked.keys.slice(0, pool);
|
|
9212
|
-
}
|
|
9213
|
-
async searchHybrid(query, topK) {
|
|
9214
|
-
if (topK <= 0)
|
|
9215
|
-
return [];
|
|
9216
|
-
const pool = topK * POOL_FACTOR;
|
|
9217
|
-
const lexical = this.searchLexical(query, pool);
|
|
9218
|
-
const semantic = await this.searchSemantic(query, pool);
|
|
9219
|
-
if (semantic.length === 0)
|
|
9220
|
-
return lexical.slice(0, topK);
|
|
9221
|
-
const byKey = new Map;
|
|
9222
|
-
for (const hit of [...semantic, ...lexical])
|
|
9223
|
-
byKey.set(hit.key, hit);
|
|
9224
|
-
const fused = [];
|
|
9225
|
-
for (const entry of reciprocalRankFusion([lexical.map((hit) => hit.key), semantic.map((hit) => hit.key)])) {
|
|
9226
|
-
const hit = byKey.get(entry.key);
|
|
9227
|
-
if (hit !== undefined)
|
|
9228
|
-
fused.push({ ...hit, score: entry.score });
|
|
9229
|
-
if (fused.length >= topK)
|
|
9230
|
-
break;
|
|
9231
|
-
}
|
|
9232
|
-
return fused;
|
|
9233
|
-
}
|
|
9234
|
-
async sync() {
|
|
9235
|
-
const { embedder } = this.options;
|
|
9236
|
-
if (embedder === undefined)
|
|
9237
|
-
return { ok: false, message: "no embedder configured" };
|
|
9238
|
-
if (!this.attachCrawler())
|
|
9239
|
-
return { ok: false, message: "crawler archive not available yet" };
|
|
9240
|
-
let identity;
|
|
9241
|
-
try {
|
|
9242
|
-
identity = await embedder.identity();
|
|
9243
|
-
} catch (error) {
|
|
9244
|
-
return { ok: false, message: errorMessage(error) };
|
|
9245
|
-
}
|
|
9246
|
-
this.adoptIdentity(identity);
|
|
9247
|
-
const pruned = this.prune();
|
|
9248
|
-
const pending = this.pendingKeys();
|
|
9249
|
-
const batchSize = Math.max(1, this.options.batchSize ?? DEFAULT_BATCH_SIZE);
|
|
9250
|
-
const budget = Math.max(1, this.options.maxEmbedPerSync ?? DEFAULT_MAX_EMBED_PER_SYNC);
|
|
9251
|
-
const target = Math.min(pending.length, budget);
|
|
9252
|
-
const embed = async (rows) => {
|
|
9253
|
-
const vectors = await embedder.embed(rows.map((row) => row.input));
|
|
9254
|
-
if (vectors.length !== rows.length) {
|
|
9255
|
-
throw new Error(`embedder returned ${vectors.length} vectors for ${rows.length} messages`);
|
|
9256
|
-
}
|
|
9257
|
-
const wrong = vectors.find((vector) => vector.length !== identity.dimension);
|
|
9258
|
-
if (wrong !== undefined) {
|
|
9259
|
-
throw new Error(`embedder returned a ${wrong.length}-dimension vector, expected ${identity.dimension}`);
|
|
9260
|
-
}
|
|
9261
|
-
return vectors;
|
|
9262
|
-
};
|
|
9263
|
-
let embedded = 0;
|
|
9264
|
-
let skipped = 0;
|
|
9265
|
-
for (let offset = 0;offset < target; offset += batchSize) {
|
|
9266
|
-
const rows = [...this.hydrate(pending.slice(offset, Math.min(offset + batchSize, target))).values()];
|
|
9267
|
-
if (rows.length === 0)
|
|
9268
|
-
continue;
|
|
9269
|
-
try {
|
|
9270
|
-
this.store(rows, await embed(rows));
|
|
9271
|
-
embedded += rows.length;
|
|
9272
|
-
} catch (batchError) {
|
|
9273
|
-
const rejected = [];
|
|
9274
|
-
let lastError = batchError;
|
|
9275
|
-
for (const row of rows) {
|
|
9276
|
-
try {
|
|
9277
|
-
this.store([row], await embed([row]));
|
|
9278
|
-
embedded += 1;
|
|
9279
|
-
} catch (error) {
|
|
9280
|
-
rejected.push(row);
|
|
9281
|
-
lastError = error;
|
|
9282
|
-
}
|
|
9283
|
-
}
|
|
9284
|
-
if (rejected.length === 0)
|
|
9285
|
-
continue;
|
|
9286
|
-
if (embedded === 0)
|
|
9287
|
-
return { ok: false, message: errorMessage(lastError) };
|
|
9288
|
-
this.store(rejected, undefined);
|
|
9289
|
-
skipped += rejected.length;
|
|
9290
|
-
}
|
|
9291
|
-
}
|
|
9292
|
-
return { ok: true, embedded, skipped, pruned, remaining: pending.length - embedded - skipped };
|
|
9293
|
-
}
|
|
9294
|
-
adoptIdentity(identity) {
|
|
9295
|
-
const serialized = JSON.stringify(identity);
|
|
9296
|
-
const current = this.db.prepare("select v from sidecar_meta where k = ?").get(IDENTITY_KEY);
|
|
9297
|
-
if (current?.v === serialized)
|
|
9298
|
-
return;
|
|
9299
|
-
this.transaction(() => {
|
|
9300
|
-
this.db.exec("delete from sidecar_vec");
|
|
9301
|
-
this.db.prepare("insert or replace into sidecar_meta (k, v) values (?, ?)").run(IDENTITY_KEY, serialized);
|
|
9302
|
-
});
|
|
9303
|
-
this.lastSemantic = undefined;
|
|
9304
|
-
}
|
|
9305
|
-
prune() {
|
|
9306
|
-
const { descriptor } = this.options;
|
|
9307
|
-
const result = this.db.prepare(`delete from sidecar_vec where message_key not in (
|
|
9308
|
-
select ${descriptor.keyExpression} from ${descriptor.fromClause} where ${this.where}
|
|
9309
|
-
)`).run(...this.whereParams());
|
|
9310
|
-
if (Number(result.changes) > 0)
|
|
9311
|
-
this.lastSemantic = undefined;
|
|
9312
|
-
return Number(result.changes);
|
|
9313
|
-
}
|
|
9314
|
-
pendingKeys() {
|
|
9315
|
-
const { descriptor } = this.options;
|
|
9316
|
-
const rows = this.db.prepare(`select ${this.select}, v.hash as stored_hash
|
|
9317
|
-
from ${descriptor.fromClause}
|
|
9318
|
-
left join sidecar_vec v on v.message_key = ${descriptor.keyExpression}
|
|
9319
|
-
where ${this.where}`).iterate(...this.whereParams());
|
|
9320
|
-
const pending = [];
|
|
9321
|
-
for (const row of rows) {
|
|
9322
|
-
if (row.stored_hash !== this.toRow(row).hash)
|
|
9323
|
-
pending.push(String(row.k));
|
|
9324
|
-
}
|
|
9325
|
-
return pending;
|
|
9326
|
-
}
|
|
9327
|
-
hydrate(keys) {
|
|
9328
|
-
const { descriptor } = this.options;
|
|
9329
|
-
const out = new Map;
|
|
9330
|
-
if (keys.length === 0)
|
|
9331
|
-
return out;
|
|
9332
|
-
const rows = this.db.prepare(`select ${this.select} from ${descriptor.fromClause}
|
|
9333
|
-
where ${descriptor.keyExpression} in (${keys.map(() => "?").join(", ")}) and ${this.where}`).all(...keys, ...this.whereParams());
|
|
9334
|
-
const byKey = new Map(rows.map((row) => [String(row.k), this.toRow(row)]));
|
|
9335
|
-
for (const key of keys) {
|
|
9336
|
-
const row = byKey.get(key);
|
|
9337
|
-
if (row !== undefined)
|
|
9338
|
-
out.set(key, row);
|
|
9339
|
-
}
|
|
9340
|
-
return out;
|
|
9341
|
-
}
|
|
9342
|
-
store(rows, vectors) {
|
|
9343
|
-
const upsert = this.db.prepare("insert or replace into sidecar_vec (message_key, hash, vec) values (?, ?, ?)");
|
|
9344
|
-
this.transaction(() => {
|
|
9345
|
-
for (const [index, row] of rows.entries()) {
|
|
9346
|
-
upsert.run(row.key, row.hash, encodeVector(vectors?.[index] ?? []));
|
|
9347
|
-
}
|
|
9348
|
-
});
|
|
9349
|
-
this.lastSemantic = undefined;
|
|
9350
|
-
}
|
|
9351
|
-
*storedVectors() {
|
|
9352
|
-
const rows = this.db.prepare("select message_key, vec from sidecar_vec where length(vec) > 0").iterate();
|
|
9353
|
-
for (const row of rows) {
|
|
9354
|
-
const vector = row.vec instanceof Uint8Array ? decodeVector(row.vec) : undefined;
|
|
9355
|
-
if (vector !== undefined)
|
|
9356
|
-
yield { key: String(row.message_key), vector };
|
|
9357
|
-
}
|
|
9358
|
-
}
|
|
9359
|
-
transaction(work) {
|
|
9360
|
-
this.db.exec("begin immediate");
|
|
9361
|
-
try {
|
|
9362
|
-
work();
|
|
9363
|
-
this.db.exec("commit");
|
|
9364
|
-
} catch (error) {
|
|
9365
|
-
this.db.exec("rollback");
|
|
9366
|
-
throw error;
|
|
9367
|
-
}
|
|
9368
|
-
}
|
|
9369
|
-
toRow(row) {
|
|
9370
|
-
const metadata = {};
|
|
9371
|
-
for (const [index, column] of this.options.descriptor.metadata.entries()) {
|
|
9372
|
-
const value = row[`m${index}`];
|
|
9373
|
-
if (typeof value === "string" && value.length > 0 || typeof value === "number")
|
|
9374
|
-
metadata[column.key] = String(value);
|
|
9375
|
-
}
|
|
9376
|
-
const title = typeof row.title === "string" && row.title.length > 0 ? row.title : undefined;
|
|
9377
|
-
const epoch = typeof row.epoch === "number" && Number.isFinite(row.epoch) ? row.epoch : undefined;
|
|
9378
|
-
const content = String(row.content ?? "");
|
|
9379
|
-
const input = `${title ?? ""}
|
|
9380
|
-
${content}`.slice(0, MAX_EMBED_INPUT_CHARS);
|
|
9381
|
-
return {
|
|
9382
|
-
key: String(row.k),
|
|
9383
|
-
content,
|
|
9384
|
-
title,
|
|
9385
|
-
epoch,
|
|
9386
|
-
metadata,
|
|
9387
|
-
input,
|
|
9388
|
-
hash: createHash6("sha1").update(input).digest("hex")
|
|
9389
|
-
};
|
|
9390
|
-
}
|
|
9391
|
-
toHit(row, score) {
|
|
9392
|
-
return {
|
|
9393
|
-
key: row.key,
|
|
9394
|
-
content: row.content,
|
|
9395
|
-
score,
|
|
9396
|
-
...row.title !== undefined ? { title: row.title } : {},
|
|
9397
|
-
...row.epoch !== undefined ? { publishedAt: Math.round(row.epoch * 1000) } : {},
|
|
9398
|
-
metadata: row.metadata
|
|
9399
|
-
};
|
|
9400
|
-
}
|
|
9401
|
-
}
|
|
9402
|
-
function toMatchQuery(text) {
|
|
9403
|
-
const terms = [...new Set(text.match(/[\p{L}\p{N}_]+/gu) ?? [])];
|
|
9404
|
-
return terms.length === 0 ? undefined : terms.map((term) => `"${term}"`).join(" OR ");
|
|
9405
|
-
}
|
|
9406
|
-
function errorMessage(error) {
|
|
9407
|
-
return error instanceof Error ? error.message : String(error);
|
|
9408
|
-
}
|
|
9409
|
-
var DEFAULT_BATCH_SIZE = 256, DEFAULT_MAX_EMBED_PER_SYNC = 2048, MAX_EMBED_INPUT_CHARS = 8192, BUSY_TIMEOUT_MS = 5000, POOL_FACTOR = 3, SEMANTIC_REUSE_MS = 1e4, IDENTITY_KEY = "identity";
|
|
9410
|
-
var init_sidecar = () => {};
|
|
9411
|
-
|
|
9412
|
-
// src/datasource/sidecar/methods.ts
|
|
9413
|
-
function sidecarMethodName(datasourceId, mode) {
|
|
9414
|
-
return mode === "bm25" ? `${datasourceId}-sidecar-bm25` : `${datasourceId}-${mode}`;
|
|
9415
|
-
}
|
|
9416
|
-
|
|
9417
|
-
class SidecarRetrievalMethod {
|
|
9418
|
-
options;
|
|
9419
|
-
constructor(options) {
|
|
9420
|
-
this.options = options;
|
|
9421
|
-
}
|
|
9422
|
-
describe() {
|
|
9423
|
-
const { datasourceId, skillType, mode, tags } = this.options;
|
|
9424
|
-
const { type, text, capabilities } = DESCRIPTIONS[mode];
|
|
9425
|
-
return {
|
|
9426
|
-
name: sidecarMethodName(datasourceId, mode),
|
|
9427
|
-
type,
|
|
9428
|
-
description: `${text} over ${skillType} via a read-only sidecar index`,
|
|
9429
|
-
status: "active",
|
|
9430
|
-
capabilities: [...capabilities, "scoped", "path-opaque-sources"],
|
|
9431
|
-
datasourceId,
|
|
9432
|
-
tags
|
|
9433
|
-
};
|
|
9434
|
-
}
|
|
9435
|
-
async retrieve(query, options) {
|
|
9436
|
-
const trimmed = query.trim();
|
|
9437
|
-
if (trimmed.length === 0)
|
|
9438
|
-
return [];
|
|
9439
|
-
const topK = options.topK ?? DEFAULT_TOP_K5;
|
|
9440
|
-
const { sidecar, mode } = this.options;
|
|
9441
|
-
const hits = mode === "bm25" ? sidecar.searchLexical(trimmed, topK) : mode === "semantic" ? await sidecar.searchSemantic(trimmed, topK) : await sidecar.searchHybrid(trimmed, topK);
|
|
9442
|
-
const results = [];
|
|
9443
|
-
for (const hit of hits) {
|
|
9444
|
-
const result = this.toResult(hit, options);
|
|
9445
|
-
if (result !== undefined)
|
|
9446
|
-
results.push(result);
|
|
9447
|
-
}
|
|
9448
|
-
return results;
|
|
9449
|
-
}
|
|
9450
|
-
toResult(hit, options) {
|
|
9451
|
-
const { descriptor, datasourceId, instanceId, mode } = this.options;
|
|
9452
|
-
const chunkId = descriptor.chunkId(hit.key, hit.metadata);
|
|
9453
|
-
const source = datasourceSourcePath(datasourceId, instanceId, chunkId);
|
|
9454
|
-
if (!matchesDatasourceScope(source, options.scope))
|
|
9455
|
-
return;
|
|
9456
|
-
const method = sidecarMethodName(datasourceId, mode);
|
|
9457
|
-
return {
|
|
9458
|
-
id: `${datasourceId}:${instanceId}:${chunkId}`,
|
|
9459
|
-
content: hit.content,
|
|
9460
|
-
source,
|
|
9461
|
-
score: hit.score,
|
|
9462
|
-
metadata: {
|
|
9463
|
-
...hit.metadata,
|
|
9464
|
-
method,
|
|
9465
|
-
datasourceId,
|
|
9466
|
-
instanceId,
|
|
9467
|
-
backend: "sidecar",
|
|
9468
|
-
...hit.title !== undefined ? { title: hit.title } : {},
|
|
9469
|
-
hierarchy: descriptor.hierarchy(hit.metadata).join("/"),
|
|
9470
|
-
...hit.publishedAt !== undefined ? { publishedAt: hit.publishedAt } : {}
|
|
9471
|
-
}
|
|
9472
|
-
};
|
|
9473
|
-
}
|
|
9474
|
-
}
|
|
9475
|
-
var DEFAULT_TOP_K5 = 20, DESCRIPTIONS;
|
|
9476
|
-
var init_methods5 = __esm(() => {
|
|
9477
|
-
init_scope2();
|
|
9478
|
-
DESCRIPTIONS = {
|
|
9479
|
-
bm25: { type: "bm25", text: "BM25-ranked FTS5 retrieval", capabilities: ["lexical", "fts5", "bm25"] },
|
|
9480
|
-
semantic: { type: "vector", text: "Semantic (embedding) retrieval", capabilities: ["semantic"] },
|
|
9481
|
-
hybrid: {
|
|
9482
|
-
type: "hybrid",
|
|
9483
|
-
text: "Hybrid BM25 + semantic retrieval (reciprocal rank fusion)",
|
|
9484
|
-
capabilities: ["lexical", "semantic", "hybrid"]
|
|
9485
|
-
}
|
|
9486
|
-
};
|
|
9487
|
-
});
|
|
9488
|
-
|
|
9489
9603
|
// src/datasource/crawler-skill.ts
|
|
9490
9604
|
class CrawlerDatasourceSkill {
|
|
9491
9605
|
definition;
|
|
@@ -9766,7 +9880,7 @@ var init_crawler_skill = __esm(() => {
|
|
|
9766
9880
|
init_errors2();
|
|
9767
9881
|
init_scope2();
|
|
9768
9882
|
init_sidecar();
|
|
9769
|
-
|
|
9883
|
+
init_methods4();
|
|
9770
9884
|
DEFAULT_POLLING_INTERVAL_MS4 = 15 * 60 * 1000;
|
|
9771
9885
|
});
|
|
9772
9886
|
|
|
@@ -11555,24 +11669,42 @@ var init_factory = __esm(() => {
|
|
|
11555
11669
|
...config.channels?.ids !== undefined ? { chatIds: config.channels.ids } : {},
|
|
11556
11670
|
...larkConnectorOptions(config.connector)
|
|
11557
11671
|
}),
|
|
11558
|
-
kakao: (config,
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11672
|
+
kakao: (config, workspaceRoot, registrationName) => {
|
|
11673
|
+
const connector = config.connector ?? {};
|
|
11674
|
+
const dataDir = typeof connector.workspacePath === "string" && connector.workspacePath.length > 0 ? connector.workspacePath : join16(homedir4(), "Library", "Application Support", "katok");
|
|
11675
|
+
const sidecar = resolveSidecarOptions({
|
|
11676
|
+
descriptor: CRAWLER_DESCRIPTORS.kakao,
|
|
11677
|
+
connector: config.connector,
|
|
11678
|
+
workspaceRoot,
|
|
11679
|
+
registrationName,
|
|
11680
|
+
instanceId: config.instanceId ?? "default",
|
|
11681
|
+
defaultCrawlerDb: join16(dataDir, "archive.sqlite3")
|
|
11682
|
+
});
|
|
11683
|
+
return new LazykatokSkill({
|
|
11684
|
+
client: new LazykatokClient(connector),
|
|
11685
|
+
...config.instanceId !== undefined ? { instanceId: config.instanceId } : {},
|
|
11686
|
+
...config.pollingIntervalMs !== undefined ? { pollingIntervalMs: config.pollingIntervalMs } : {},
|
|
11687
|
+
...config.tags !== undefined ? { tags: config.tags } : {},
|
|
11688
|
+
channelIds: config.channels?.ids,
|
|
11689
|
+
channelNames: config.channels?.names,
|
|
11690
|
+
...sidecar === undefined ? {} : { sidecar }
|
|
11691
|
+
});
|
|
11692
|
+
},
|
|
11566
11693
|
github: (config, workspaceRoot, registrationName) => new GitHubSkill({
|
|
11567
11694
|
...common(config, workspaceRoot),
|
|
11568
11695
|
skillName: registrationName,
|
|
11569
11696
|
connectorOptions: config.connector
|
|
11570
11697
|
}),
|
|
11571
|
-
"github-gist": (config, workspaceRoot, registrationName) =>
|
|
11572
|
-
...
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
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
|
+
},
|
|
11576
11708
|
"cloud-drive": (config, workspaceRoot, registrationName) => new CloudDriveSkill({
|
|
11577
11709
|
...common(config, workspaceRoot),
|
|
11578
11710
|
skillName: registrationName,
|
|
@@ -12073,11 +12205,6 @@ var init_types4 = __esm(() => {
|
|
|
12073
12205
|
label: "xAI",
|
|
12074
12206
|
description: "xAI Responses web_search via the agent's xAI model credentials (XAI_API_KEY)"
|
|
12075
12207
|
},
|
|
12076
|
-
{
|
|
12077
|
-
value: "perplexity",
|
|
12078
|
-
label: "Perplexity",
|
|
12079
|
-
description: "Anonymous Perplexity ask endpoint; no key or signup required"
|
|
12080
|
-
},
|
|
12081
12208
|
{
|
|
12082
12209
|
value: "parallel",
|
|
12083
12210
|
label: "Parallel",
|
|
@@ -12088,16 +12215,21 @@ var init_types4 = __esm(() => {
|
|
|
12088
12215
|
label: "SearXNG",
|
|
12089
12216
|
description: "Advanced: requires a self-hosted SEARXNG_ENDPOINT; never part of the default experience"
|
|
12090
12217
|
},
|
|
12091
|
-
{
|
|
12092
|
-
value: "startpage",
|
|
12093
|
-
label: "Startpage",
|
|
12094
|
-
description: "Credential-free scrape of Startpage (Google-backed) results; may be bot-challenged"
|
|
12095
|
-
},
|
|
12096
12218
|
{
|
|
12097
12219
|
value: "duckduckgo",
|
|
12098
12220
|
label: "DuckDuckGo",
|
|
12099
12221
|
description: "Credential-free best-effort fallback; may be bot-challenged on datacenter/shared-egress IPs"
|
|
12100
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
|
+
},
|
|
12101
12233
|
{
|
|
12102
12234
|
value: "ecosia",
|
|
12103
12235
|
label: "Ecosia",
|
|
@@ -12227,7 +12359,7 @@ function migrateLegacyConfig(configPath, legacyPath) {
|
|
|
12227
12359
|
const legacy = readConfigFile(legacyPath, true);
|
|
12228
12360
|
const legacyBytes = readFileSync14(legacyPath);
|
|
12229
12361
|
const migrated = normalizeLegacyConfigPaths(legacy ?? {}, dirname13(legacyPath));
|
|
12230
|
-
const migratedPathsUnchanged = legacy?.workspacePath === migrated.workspacePath && legacy?.memoryPath === migrated.memoryPath && JSON.stringify(legacy?.searchPaths) === JSON.stringify(migrated.searchPaths);
|
|
12362
|
+
const migratedPathsUnchanged = legacy?.workspacePath === migrated.workspacePath && legacy?.memoryPath === migrated.memoryPath && JSON.stringify(legacy?.searchPaths) === JSON.stringify(migrated.searchPaths) && JSON.stringify(legacy?.excludePaths) === JSON.stringify(migrated.excludePaths);
|
|
12231
12363
|
const migratedBytes = migratedPathsUnchanged ? legacyBytes : `${JSON.stringify(migrated, null, 2)}
|
|
12232
12364
|
`;
|
|
12233
12365
|
mkdirSync11(dirname13(configPath), { recursive: true });
|
|
@@ -12265,15 +12397,25 @@ function resolveConfigFileReadOnly(configPath, explicit, legacyPath) {
|
|
|
12265
12397
|
function resolveSearchPaths(searchPaths, origin) {
|
|
12266
12398
|
return searchPaths.map((searchPath) => resolvePersistedPath(searchPath, origin));
|
|
12267
12399
|
}
|
|
12400
|
+
function resolveExcludePaths(value, origin) {
|
|
12401
|
+
if (value === undefined)
|
|
12402
|
+
return;
|
|
12403
|
+
if (!Array.isArray(value) || value.some((path) => typeof path !== "string" || path.length === 0)) {
|
|
12404
|
+
throw new ConfigError("Config field 'excludePaths' must be a non-empty string array");
|
|
12405
|
+
}
|
|
12406
|
+
return resolveSearchPaths(value, origin);
|
|
12407
|
+
}
|
|
12268
12408
|
function resolvePersistedPath(path, origin) {
|
|
12269
12409
|
return isAbsolute5(path) ? path : resolve7(origin, path);
|
|
12270
12410
|
}
|
|
12271
12411
|
function normalizeLegacyConfigPaths(partial, origin) {
|
|
12272
12412
|
const workspacePath = resolvePersistedPath(partial.workspacePath ?? ".", origin);
|
|
12413
|
+
const excludePaths = resolveExcludePaths(partial.excludePaths, workspacePath);
|
|
12273
12414
|
return {
|
|
12274
12415
|
...partial,
|
|
12275
12416
|
workspacePath,
|
|
12276
12417
|
...partial.searchPaths === undefined ? {} : { searchPaths: resolveSearchPaths(partial.searchPaths, workspacePath) },
|
|
12418
|
+
...excludePaths === undefined ? {} : { excludePaths },
|
|
12277
12419
|
...partial.memoryPath === undefined ? {} : { memoryPath: resolvePersistedPath(partial.memoryPath, workspacePath) }
|
|
12278
12420
|
};
|
|
12279
12421
|
}
|
|
@@ -12728,10 +12870,7 @@ function resolveConfig(input) {
|
|
|
12728
12870
|
const configOrigin = dirname13(resolve7(configPath));
|
|
12729
12871
|
const fileWorkspacePath = typeof file.workspacePath === "string" ? resolvePersistedPath(file.workspacePath, configOrigin) : undefined;
|
|
12730
12872
|
const fileSearchPaths = file.searchPaths ? resolveSearchPaths(file.searchPaths, fileWorkspacePath ?? configOrigin) : undefined;
|
|
12731
|
-
|
|
12732
|
-
throw new ConfigError("Config field 'excludePaths' must be a non-empty string array");
|
|
12733
|
-
}
|
|
12734
|
-
const fileExcludePaths = file.excludePaths ? resolveSearchPaths(file.excludePaths, fileWorkspacePath ?? configOrigin) : undefined;
|
|
12873
|
+
const fileExcludePaths = resolveExcludePaths(file.excludePaths, fileWorkspacePath ?? configOrigin);
|
|
12735
12874
|
const searchPaths = flagSearchPaths ?? envSearchPaths ?? fileSearchPaths ?? defaultSearchPaths;
|
|
12736
12875
|
const flagWorkspacePath = flagString(flags, "workspace");
|
|
12737
12876
|
const envWorkspacePath = envString(env, "AUTORAG_WORKSPACE");
|
|
@@ -13467,7 +13606,7 @@ function writeDefaultConfig(path, partial, opts = {}) {
|
|
|
13467
13606
|
full.dupey = partial.dupey ?? { enabled: true };
|
|
13468
13607
|
full.excludeExactDuplicates = partial.excludeExactDuplicates ?? true;
|
|
13469
13608
|
if (partial.excludePaths !== undefined)
|
|
13470
|
-
full.excludePaths =
|
|
13609
|
+
full.excludePaths = resolveExcludePaths(partial.excludePaths, workspacePath);
|
|
13471
13610
|
if (partial.limits !== undefined)
|
|
13472
13611
|
full.limits = normalizeLimitsConfig(partial.limits);
|
|
13473
13612
|
if (partial.refresh !== undefined)
|
|
@@ -13611,7 +13750,7 @@ var init_config4 = __esm(() => {
|
|
|
13611
13750
|
|
|
13612
13751
|
// src/cli/commands/init.ts
|
|
13613
13752
|
import { existsSync as existsSync11, readFileSync as readFileSync15 } from "node:fs";
|
|
13614
|
-
import { dirname as dirname14 } from "node:path";
|
|
13753
|
+
import { dirname as dirname14, resolve as resolve8 } from "node:path";
|
|
13615
13754
|
async function runInit(ctx) {
|
|
13616
13755
|
const flags = ctx.flags;
|
|
13617
13756
|
const partial = {};
|
|
@@ -13619,6 +13758,14 @@ async function runInit(ctx) {
|
|
|
13619
13758
|
if (searchPathsFlag) {
|
|
13620
13759
|
partial.searchPaths = searchPathsFlag.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
13621
13760
|
}
|
|
13761
|
+
const excludedPaths = ctx.repeatedFlags?.exclude ?? (typeof flags.exclude === "string" ? [flags.exclude] : undefined);
|
|
13762
|
+
if (flags.exclude !== undefined || excludedPaths !== undefined) {
|
|
13763
|
+
if (excludedPaths === undefined || excludedPaths.length === 0 || excludedPaths.some((path) => path.trim().length === 0)) {
|
|
13764
|
+
ctx.stderr(renderError(new ConfigError("--exclude requires a non-empty path"), { json: ctx.json, debug: ctx.debug }));
|
|
13765
|
+
return 2;
|
|
13766
|
+
}
|
|
13767
|
+
partial.excludePaths = excludedPaths.map((path) => resolve8(ctx.cwd, path));
|
|
13768
|
+
}
|
|
13622
13769
|
if (typeof flags.workspace === "string")
|
|
13623
13770
|
partial.workspacePath = flags.workspace;
|
|
13624
13771
|
if (typeof flags["memory-path"] === "string")
|
|
@@ -13683,6 +13830,7 @@ async function runInit(ctx) {
|
|
|
13683
13830
|
const legacy = JSON.parse(readFileSync15(legacyPath, "utf8"));
|
|
13684
13831
|
const normalizedLegacy = normalizeLegacyConfigPaths(legacy, dirname14(legacyPath));
|
|
13685
13832
|
partial.searchPaths ??= normalizedLegacy.searchPaths;
|
|
13833
|
+
partial.excludePaths ??= normalizedLegacy.excludePaths;
|
|
13686
13834
|
partial.workspacePath ??= normalizedLegacy.workspacePath;
|
|
13687
13835
|
partial.memoryPath ??= normalizedLegacy.memoryPath;
|
|
13688
13836
|
partial.model ??= legacy.model;
|
|
@@ -13809,7 +13957,20 @@ function configuredVectorDimension(workspacePath) {
|
|
|
13809
13957
|
const dimension = options.dimension;
|
|
13810
13958
|
return typeof dimension === "number" ? dimension : undefined;
|
|
13811
13959
|
}
|
|
13812
|
-
function
|
|
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 = {}) {
|
|
13813
13974
|
const configPath = minSyncConfigPath(workspacePath);
|
|
13814
13975
|
let raw;
|
|
13815
13976
|
try {
|
|
@@ -13852,8 +14013,13 @@ function rewriteEmbedderConfig(workspacePath, embedder, chunkerConfig = {}) {
|
|
|
13852
14013
|
}
|
|
13853
14014
|
if (embedder.dimension !== undefined)
|
|
13854
14015
|
options.dimension = embedder.dimension;
|
|
13855
|
-
if (
|
|
13856
|
-
chunkerOptions.max_chunk_size =
|
|
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
|
+
}
|
|
13857
14023
|
writeFileSync9(configPath, stringify(parsed));
|
|
13858
14024
|
return true;
|
|
13859
14025
|
}
|
|
@@ -14265,9 +14431,9 @@ var init_retry = __esm(() => {
|
|
|
14265
14431
|
|
|
14266
14432
|
// src/dupey/cli.ts
|
|
14267
14433
|
import { spawn as spawn10 } from "node:child_process";
|
|
14268
|
-
import { resolve as
|
|
14434
|
+
import { resolve as resolve9 } from "node:path";
|
|
14269
14435
|
async function scanWithDupey(dir, options = {}) {
|
|
14270
|
-
const absoluteDir =
|
|
14436
|
+
const absoluteDir = resolve9(dir);
|
|
14271
14437
|
const stdout = options.run ? await options.run(["scan", absoluteDir, "--json"]) : await spawnDupey(options.executable ?? "dupey", ["scan", absoluteDir, "--json"], options);
|
|
14272
14438
|
let parsed;
|
|
14273
14439
|
try {
|
|
@@ -14337,13 +14503,13 @@ var init_cli = __esm(() => {
|
|
|
14337
14503
|
|
|
14338
14504
|
// src/dupey/filter.ts
|
|
14339
14505
|
import { stat as stat5 } from "node:fs/promises";
|
|
14340
|
-
import { resolve as
|
|
14506
|
+
import { resolve as resolve10 } from "node:path";
|
|
14341
14507
|
async function selectExactDuplicateExclusions(root, scan, isUnavailable) {
|
|
14342
14508
|
const byHash = new Map;
|
|
14343
14509
|
for (const file of scan.files) {
|
|
14344
14510
|
if (typeof file.content_hash !== "string" || file.content_hash.length === 0)
|
|
14345
14511
|
continue;
|
|
14346
|
-
const path =
|
|
14512
|
+
const path = resolve10(root, file.path);
|
|
14347
14513
|
const group = byHash.get(file.content_hash) ?? [];
|
|
14348
14514
|
group.push(path);
|
|
14349
14515
|
byHash.set(file.content_hash, group);
|
|
@@ -14380,7 +14546,7 @@ function dupeyExactRanking(scan) {
|
|
|
14380
14546
|
return;
|
|
14381
14547
|
const path = entry.path;
|
|
14382
14548
|
if (typeof path === "string")
|
|
14383
|
-
ranking.set(
|
|
14549
|
+
ranking.set(resolve10(path), index);
|
|
14384
14550
|
});
|
|
14385
14551
|
}
|
|
14386
14552
|
return ranking;
|
|
@@ -14536,14 +14702,33 @@ function errorMessage2(error) {
|
|
|
14536
14702
|
var MANIFEST_FILENAME = "manifest.json";
|
|
14537
14703
|
var init_bundle = () => {};
|
|
14538
14704
|
|
|
14705
|
+
// src/filesystem/path-exclusions.ts
|
|
14706
|
+
import { posix as posix2, win32 as win322 } from "node:path";
|
|
14707
|
+
function createPathExclusionMatcher(excludedPaths, platform = process.platform) {
|
|
14708
|
+
const paths = platform === "win32" ? win322 : posix2;
|
|
14709
|
+
const normalize = (path) => {
|
|
14710
|
+
const absolute = paths.resolve(path);
|
|
14711
|
+
return platform === "win32" ? absolute.toLowerCase() : absolute;
|
|
14712
|
+
};
|
|
14713
|
+
const excluded = excludedPaths.map((path) => {
|
|
14714
|
+
const absolute = normalize(path);
|
|
14715
|
+
return { absolute, prefix: absolute.endsWith(paths.sep) ? absolute : `${absolute}${paths.sep}` };
|
|
14716
|
+
});
|
|
14717
|
+
return (path) => {
|
|
14718
|
+
const absolute = normalize(path);
|
|
14719
|
+
return excluded.some((entry) => absolute === entry.absolute || absolute.startsWith(entry.prefix));
|
|
14720
|
+
};
|
|
14721
|
+
}
|
|
14722
|
+
var init_path_exclusions = () => {};
|
|
14723
|
+
|
|
14539
14724
|
// src/everything/client.ts
|
|
14540
14725
|
import { spawn as spawn11 } from "node:child_process";
|
|
14541
14726
|
import { createHash as createHash9 } from "node:crypto";
|
|
14542
14727
|
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "node:fs";
|
|
14543
|
-
import { join as join22, resolve as
|
|
14728
|
+
import { join as join22, resolve as resolve11 } from "node:path";
|
|
14544
14729
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
14545
14730
|
function everythingInstanceName(root) {
|
|
14546
|
-
return `autorag-${createHash9("sha256").update(
|
|
14731
|
+
return `autorag-${createHash9("sha256").update(resolve11(root).toLowerCase()).digest("hex").slice(0, 12)}`;
|
|
14547
14732
|
}
|
|
14548
14733
|
function buildEverythingIni(input) {
|
|
14549
14734
|
const list = (values) => values.map((value) => `"${value.replace(/\\/g, "\\\\")}"`).join(",");
|
|
@@ -14653,6 +14838,7 @@ class EverythingClient {
|
|
|
14653
14838
|
run;
|
|
14654
14839
|
launch;
|
|
14655
14840
|
stateDir;
|
|
14841
|
+
isExcludedPath;
|
|
14656
14842
|
binaries;
|
|
14657
14843
|
operation = Promise.resolve();
|
|
14658
14844
|
constructor(options) {
|
|
@@ -14662,6 +14848,7 @@ class EverythingClient {
|
|
|
14662
14848
|
this.run = options.run ?? runProcess;
|
|
14663
14849
|
this.launch = options.launch ?? launchDetached;
|
|
14664
14850
|
this.stateDir = join22(options.root, ".autorag", "everything");
|
|
14851
|
+
this.isExcludedPath = createPathExclusionMatcher(options.excludeFolders ?? [], "win32");
|
|
14665
14852
|
}
|
|
14666
14853
|
isSupported() {
|
|
14667
14854
|
return this.platform === "win32";
|
|
@@ -14681,6 +14868,9 @@ class EverythingClient {
|
|
|
14681
14868
|
}
|
|
14682
14869
|
const es = { ok: true, esPath: binaries.esPath };
|
|
14683
14870
|
const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS6;
|
|
14871
|
+
if ((this.options.excludeFolders?.length ?? 0) > 0) {
|
|
14872
|
+
return this.searchWithExclusions(es.esPath, request, timeoutMs);
|
|
14873
|
+
}
|
|
14684
14874
|
const args = buildEverythingSearchArgs(this.instanceName, request, timeoutMs);
|
|
14685
14875
|
const result = await this.run(es.esPath, args, timeoutMs + 5000);
|
|
14686
14876
|
if (result.code !== 0)
|
|
@@ -14692,6 +14882,50 @@ class EverythingClient {
|
|
|
14692
14882
|
}
|
|
14693
14883
|
});
|
|
14694
14884
|
}
|
|
14885
|
+
async searchWithExclusions(esPath, request, timeoutMs) {
|
|
14886
|
+
const deadline = Date.now() + timeoutMs;
|
|
14887
|
+
const offset = request.offset ?? 0;
|
|
14888
|
+
const maxResults = request.maxResults ?? DEFAULT_MAX_RESULTS;
|
|
14889
|
+
const results = [];
|
|
14890
|
+
let skipped = 0;
|
|
14891
|
+
let nativeOffset = 0;
|
|
14892
|
+
const timedOut = () => ({
|
|
14893
|
+
ok: false,
|
|
14894
|
+
reason: "search-failed",
|
|
14895
|
+
message: `Everything search exceeded ${timeoutMs}ms while filtering excluded paths; narrow the query or increase the Everything timeout.`
|
|
14896
|
+
});
|
|
14897
|
+
for (;; ) {
|
|
14898
|
+
const remainingMs = deadline - Date.now();
|
|
14899
|
+
if (remainingMs <= 0)
|
|
14900
|
+
return timedOut();
|
|
14901
|
+
const args = buildEverythingSearchArgs(this.instanceName, { ...request, offset: nativeOffset, maxResults: MAX_FILTER_CANDIDATES }, remainingMs);
|
|
14902
|
+
const response = await this.run(esPath, args, remainingMs + 5000);
|
|
14903
|
+
if (response.code !== 0)
|
|
14904
|
+
return this.failure("search-failed", esPath, args, response);
|
|
14905
|
+
if (Date.now() >= deadline)
|
|
14906
|
+
return timedOut();
|
|
14907
|
+
let entries;
|
|
14908
|
+
try {
|
|
14909
|
+
entries = parseEverythingJson(response.stdout);
|
|
14910
|
+
} catch (error) {
|
|
14911
|
+
return { ok: false, reason: "search-failed", message: error.message };
|
|
14912
|
+
}
|
|
14913
|
+
for (const entry of entries) {
|
|
14914
|
+
if (this.isExcludedPath(entry.path))
|
|
14915
|
+
continue;
|
|
14916
|
+
if (skipped < offset) {
|
|
14917
|
+
skipped += 1;
|
|
14918
|
+
continue;
|
|
14919
|
+
}
|
|
14920
|
+
results.push(entry);
|
|
14921
|
+
if (results.length >= maxResults)
|
|
14922
|
+
return { ok: true, results };
|
|
14923
|
+
}
|
|
14924
|
+
if (entries.length < MAX_FILTER_CANDIDATES)
|
|
14925
|
+
return { ok: true, results };
|
|
14926
|
+
nativeOffset += entries.length;
|
|
14927
|
+
}
|
|
14928
|
+
}
|
|
14695
14929
|
index() {
|
|
14696
14930
|
return this.serialize(async () => {
|
|
14697
14931
|
const es = await this.ensureRunning(true);
|
|
@@ -14765,7 +14999,8 @@ class EverythingClient {
|
|
|
14765
14999
|
excludeFolders: [
|
|
14766
15000
|
...new Set([
|
|
14767
15001
|
join22(this.options.root, ".autorag"),
|
|
14768
|
-
...this.options.folders.map((folder) => join22(folder, ".autorag"))
|
|
15002
|
+
...this.options.folders.map((folder) => join22(folder, ".autorag")),
|
|
15003
|
+
...this.options.excludeFolders ?? []
|
|
14769
15004
|
])
|
|
14770
15005
|
]
|
|
14771
15006
|
}));
|
|
@@ -14859,8 +15094,9 @@ function launchDetached(command, args) {
|
|
|
14859
15094
|
child.on("error", () => {});
|
|
14860
15095
|
child.unref();
|
|
14861
15096
|
}
|
|
14862
|
-
var DEFAULT_TIMEOUT_MS6 = 30000, DEFAULT_STARTUP_TIMEOUT_MS = 20000, DEFAULT_INDEX_TIMEOUT_MS2, DEFAULT_POLL_INTERVAL_MS = 250, DEFAULT_MAX_RESULTS = 100, MAX_OUTPUT_BYTES, VERSION_PATTERN;
|
|
15097
|
+
var DEFAULT_TIMEOUT_MS6 = 30000, DEFAULT_STARTUP_TIMEOUT_MS = 20000, DEFAULT_INDEX_TIMEOUT_MS2, DEFAULT_POLL_INTERVAL_MS = 250, DEFAULT_MAX_RESULTS = 100, MAX_FILTER_CANDIDATES = 1000, MAX_OUTPUT_BYTES, VERSION_PATTERN;
|
|
14863
15098
|
var init_client7 = __esm(() => {
|
|
15099
|
+
init_path_exclusions();
|
|
14864
15100
|
init_bundle();
|
|
14865
15101
|
DEFAULT_INDEX_TIMEOUT_MS2 = 10 * 60000;
|
|
14866
15102
|
MAX_OUTPUT_BYTES = 8 * 1024 * 1024;
|
|
@@ -14880,6 +15116,7 @@ function compare(a, b) {
|
|
|
14880
15116
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
14881
15117
|
}
|
|
14882
15118
|
async function walkFileSearch(folders, request, options = {}) {
|
|
15119
|
+
const isExcluded = createPathExclusionMatcher(options.excludeFolders ?? []);
|
|
14883
15120
|
const maxVisited = options.maxVisited ?? DEFAULT_MAX_VISITED;
|
|
14884
15121
|
const maxMatches = options.maxMatches ?? DEFAULT_MAX_MATCHES;
|
|
14885
15122
|
const deadline = Date.now() + (options.deadlineMs ?? DEFAULT_DEADLINE_MS);
|
|
@@ -14902,6 +15139,8 @@ async function walkFileSearch(folders, request, options = {}) {
|
|
|
14902
15139
|
outer:
|
|
14903
15140
|
while (pending.length > 0) {
|
|
14904
15141
|
const dir = pending.pop();
|
|
15142
|
+
if (isExcluded(dir))
|
|
15143
|
+
continue;
|
|
14905
15144
|
if (Date.now() >= deadline) {
|
|
14906
15145
|
truncated = true;
|
|
14907
15146
|
break;
|
|
@@ -14919,6 +15158,8 @@ async function walkFileSearch(folders, request, options = {}) {
|
|
|
14919
15158
|
}
|
|
14920
15159
|
visited += 1;
|
|
14921
15160
|
const path = join23(dir, dirent.name);
|
|
15161
|
+
if (isExcluded(path))
|
|
15162
|
+
continue;
|
|
14922
15163
|
if (dirent.isDirectory()) {
|
|
14923
15164
|
if (dirent.name === ".autorag")
|
|
14924
15165
|
continue;
|
|
@@ -14953,6 +15194,8 @@ async function walkFileSearch(folders, request, options = {}) {
|
|
|
14953
15194
|
}
|
|
14954
15195
|
}
|
|
14955
15196
|
for (const folder of folders) {
|
|
15197
|
+
if (isExcluded(folder))
|
|
15198
|
+
continue;
|
|
14956
15199
|
if (!underPath(folder) || !matches(folder))
|
|
14957
15200
|
continue;
|
|
14958
15201
|
if (request.kind === "files")
|
|
@@ -14979,6 +15222,7 @@ async function walkFileSearch(folders, request, options = {}) {
|
|
|
14979
15222
|
}
|
|
14980
15223
|
var DEFAULT_MAX_VISITED = 500000, DEFAULT_DEADLINE_MS = 20000, DEFAULT_MAX_MATCHES = 1e4, SORT_ORDERS;
|
|
14981
15224
|
var init_walk = __esm(() => {
|
|
15225
|
+
init_path_exclusions();
|
|
14982
15226
|
SORT_ORDERS = {
|
|
14983
15227
|
"name-ascending": (a, b) => compare(a.name, b.name),
|
|
14984
15228
|
"name-descending": (a, b) => compare(b.name, a.name),
|
|
@@ -14995,14 +15239,14 @@ var init_walk = __esm(() => {
|
|
|
14995
15239
|
import { spawn as spawn12 } from "node:child_process";
|
|
14996
15240
|
import { createHash as createHash10 } from "node:crypto";
|
|
14997
15241
|
import { existsSync as existsSync15, mkdirSync as mkdirSync14, readFileSync as readFileSync18, realpathSync as realpathSync2, rmSync as rmSync4, writeFileSync as writeFileSync12 } from "node:fs";
|
|
14998
|
-
import { basename as basename9, join as join24, resolve as
|
|
15242
|
+
import { basename as basename9, join as join24, resolve as resolve12, sep as sep4 } from "node:path";
|
|
14999
15243
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
15000
15244
|
function fsearchSocketPath(dbPath) {
|
|
15001
15245
|
let canonical;
|
|
15002
15246
|
try {
|
|
15003
15247
|
canonical = realpathSync2(dbPath);
|
|
15004
15248
|
} catch {
|
|
15005
|
-
canonical =
|
|
15249
|
+
canonical = resolve12(dbPath);
|
|
15006
15250
|
}
|
|
15007
15251
|
const hash = createHash10("sha256").update(canonical).digest("hex").slice(0, 12);
|
|
15008
15252
|
return `/tmp/autorag-fsearch-${process.getuid?.() ?? 0}-${hash}.sock`;
|
|
@@ -15088,6 +15332,7 @@ class FSearchClient {
|
|
|
15088
15332
|
launch;
|
|
15089
15333
|
isAlive;
|
|
15090
15334
|
kill;
|
|
15335
|
+
isExcludedPath;
|
|
15091
15336
|
stateDir;
|
|
15092
15337
|
dbPath;
|
|
15093
15338
|
pidPath;
|
|
@@ -15100,6 +15345,7 @@ class FSearchClient {
|
|
|
15100
15345
|
this.launch = options.launch ?? launchDetached2;
|
|
15101
15346
|
this.isAlive = options.isProcessAlive ?? defaultIsProcessAlive;
|
|
15102
15347
|
this.kill = options.killProcess ?? ((pid) => process.kill(pid, "SIGTERM"));
|
|
15348
|
+
this.isExcludedPath = createPathExclusionMatcher(options.excludeFolders ?? []);
|
|
15103
15349
|
this.stateDir = join24(options.root, ".autorag", "fsearch");
|
|
15104
15350
|
this.dbPath = join24(this.stateDir, "fsearch.db");
|
|
15105
15351
|
this.pidPath = join24(this.stateDir, "watch.pid");
|
|
@@ -15117,6 +15363,7 @@ class FSearchClient {
|
|
|
15117
15363
|
if (unavailable !== undefined || !binary.ok) {
|
|
15118
15364
|
try {
|
|
15119
15365
|
const walked = await walkFileSearch(this.options.folders, request, {
|
|
15366
|
+
excludeFolders: this.options.excludeFolders,
|
|
15120
15367
|
maxVisited: this.options.walkMaxVisited,
|
|
15121
15368
|
deadlineMs: this.options.walkDeadlineMs
|
|
15122
15369
|
});
|
|
@@ -15131,7 +15378,9 @@ class FSearchClient {
|
|
|
15131
15378
|
return { ok: false, reason: "search-failed", message: error.message };
|
|
15132
15379
|
}
|
|
15133
15380
|
}
|
|
15134
|
-
const
|
|
15381
|
+
const hasExclusions = (this.options.excludeFolders?.length ?? 0) > 0;
|
|
15382
|
+
const nativeRequest = hasExclusions ? { ...request, offset: 0, maxResults: MAX_RESULTS_CAP } : request;
|
|
15383
|
+
const args = buildFSearchSearchArgs(this.dbPath, this.socketPath(), nativeRequest);
|
|
15135
15384
|
const result = await this.run(binary.path, args, this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS7);
|
|
15136
15385
|
if (result.code !== 0)
|
|
15137
15386
|
return this.failure("search-failed", binary.path, args, result);
|
|
@@ -15142,6 +15391,10 @@ class FSearchClient {
|
|
|
15142
15391
|
return { ok: false, reason: "search-failed", message: error.message };
|
|
15143
15392
|
}
|
|
15144
15393
|
let entries = parsed.entries;
|
|
15394
|
+
if (hasExclusions) {
|
|
15395
|
+
const isExcluded = this.isExcludedPath;
|
|
15396
|
+
entries = entries.filter((entry) => !isExcluded(entry.path));
|
|
15397
|
+
}
|
|
15145
15398
|
if (request.path !== undefined) {
|
|
15146
15399
|
const prefix = request.path.endsWith(sep4) ? request.path : `${request.path}${sep4}`;
|
|
15147
15400
|
entries = entries.filter((entry) => entry.path === request.path || entry.path.startsWith(prefix));
|
|
@@ -15151,7 +15404,16 @@ class FSearchClient {
|
|
|
15151
15404
|
const maxResults = Math.min(request.maxResults ?? DEFAULT_MAX_RESULTS2, MAX_RESULTS_CAP);
|
|
15152
15405
|
if (entries.length > maxResults)
|
|
15153
15406
|
entries = entries.slice(0, maxResults);
|
|
15154
|
-
return {
|
|
15407
|
+
return {
|
|
15408
|
+
ok: true,
|
|
15409
|
+
backend: "fsearch-cli",
|
|
15410
|
+
results: entries,
|
|
15411
|
+
total: parsed.total,
|
|
15412
|
+
...hasExclusions && parsed.total !== undefined && parsed.total > parsed.entries.length && entries.length < maxResults ? {
|
|
15413
|
+
truncated: true,
|
|
15414
|
+
note: `FSearch checked ${parsed.entries.length} of ${parsed.total} native matches before exclusions (candidate limit ${MAX_RESULTS_CAP}); results may be incomplete. Narrow the query.`
|
|
15415
|
+
} : {}
|
|
15416
|
+
};
|
|
15155
15417
|
});
|
|
15156
15418
|
}
|
|
15157
15419
|
index() {
|
|
@@ -15345,6 +15607,7 @@ function defaultIsProcessAlive(pid) {
|
|
|
15345
15607
|
}
|
|
15346
15608
|
var DEFAULT_TIMEOUT_MS7 = 30000, DEFAULT_STARTUP_TIMEOUT_MS2 = 1e4, STOP_TIMEOUT_MS = 2000, DEFAULT_INDEX_TIMEOUT_MS3, DEFAULT_POLL_INTERVAL_MS2 = 250, DEFAULT_MAX_RESULTS2 = 100, MAX_RESULTS_CAP = 1000, MAX_OUTPUT_BYTES2, SORT_KEYS;
|
|
15347
15609
|
var init_client8 = __esm(() => {
|
|
15610
|
+
init_path_exclusions();
|
|
15348
15611
|
init_walk();
|
|
15349
15612
|
DEFAULT_INDEX_TIMEOUT_MS3 = 10 * 60000;
|
|
15350
15613
|
MAX_OUTPUT_BYTES2 = 8 * 1024 * 1024;
|
|
@@ -15416,7 +15679,7 @@ var init_diagnostics = __esm(() => {
|
|
|
15416
15679
|
|
|
15417
15680
|
// src/jikji/answer-pack.ts
|
|
15418
15681
|
import { realpathSync as realpathSync3 } from "node:fs";
|
|
15419
|
-
import { isAbsolute as isAbsolute7, join as join25, relative as relative3, resolve as
|
|
15682
|
+
import { isAbsolute as isAbsolute7, join as join25, relative as relative3, resolve as resolve13 } from "node:path";
|
|
15420
15683
|
function isRecord10(value) {
|
|
15421
15684
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15422
15685
|
}
|
|
@@ -15546,7 +15809,7 @@ function normalizeJikjiAnswerPath(rawPath, sourceRoots) {
|
|
|
15546
15809
|
} else {
|
|
15547
15810
|
if (normalized.split("/").includes(".."))
|
|
15548
15811
|
continue;
|
|
15549
|
-
candidate =
|
|
15812
|
+
candidate = resolve13(realRoot, normalized);
|
|
15550
15813
|
}
|
|
15551
15814
|
let realCandidate;
|
|
15552
15815
|
try {
|
|
@@ -15983,7 +16246,7 @@ var init_client9 = __esm(() => {
|
|
|
15983
16246
|
});
|
|
15984
16247
|
|
|
15985
16248
|
// src/jikji/path-map.ts
|
|
15986
|
-
import { isAbsolute as isAbsolute8, relative as relative4, resolve as
|
|
16249
|
+
import { isAbsolute as isAbsolute8, relative as relative4, resolve as resolve14, sep as sep5 } from "node:path";
|
|
15987
16250
|
function planJikjiSourceRoots(searchPaths) {
|
|
15988
16251
|
return planSourceRoots(searchPaths).map((sourceRoot) => ({
|
|
15989
16252
|
...sourceRoot,
|
|
@@ -15991,7 +16254,7 @@ function planJikjiSourceRoots(searchPaths) {
|
|
|
15991
16254
|
}));
|
|
15992
16255
|
}
|
|
15993
16256
|
function outermostJikjiRoots(searchPaths) {
|
|
15994
|
-
const unique = [...new Set(searchPaths.map((searchPath) =>
|
|
16257
|
+
const unique = [...new Set(searchPaths.map((searchPath) => resolve14(searchPath)))];
|
|
15995
16258
|
return unique.filter((candidate) => !unique.some((other) => {
|
|
15996
16259
|
if (other === candidate)
|
|
15997
16260
|
return false;
|
|
@@ -16012,13 +16275,13 @@ function resolveReturnedPath(rootPath, returnedPath) {
|
|
|
16012
16275
|
return;
|
|
16013
16276
|
if (normalized.split("/").includes(".."))
|
|
16014
16277
|
return;
|
|
16015
|
-
return
|
|
16278
|
+
return resolve14(rootPath, normalized);
|
|
16016
16279
|
}
|
|
16017
16280
|
function isUnsafeReturnedPath(normalizedPath) {
|
|
16018
16281
|
return normalizedPath.startsWith("/") || /^[A-Za-z]:\//.test(normalizedPath) || normalizedPath.startsWith("//") || /^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(normalizedPath);
|
|
16019
16282
|
}
|
|
16020
16283
|
function isWithinRoot(rootPath, sourcePath) {
|
|
16021
|
-
const rel = relative4(
|
|
16284
|
+
const rel = relative4(resolve14(rootPath), resolve14(sourcePath));
|
|
16022
16285
|
return rel === "" || !rel.startsWith("..") && !rel.includes(`..${sep5}`) && !isAbsolute8(rel);
|
|
16023
16286
|
}
|
|
16024
16287
|
var init_path_map = __esm(() => {
|
|
@@ -16976,6 +17239,66 @@ var init_errors4 = __esm(() => {
|
|
|
16976
17239
|
};
|
|
16977
17240
|
});
|
|
16978
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
|
+
|
|
16979
17302
|
// src/minsync/local-embedder.ts
|
|
16980
17303
|
import { spawn as spawn15 } from "node:child_process";
|
|
16981
17304
|
import { request } from "node:http";
|
|
@@ -17164,7 +17487,7 @@ var init_process = __esm(() => {
|
|
|
17164
17487
|
});
|
|
17165
17488
|
|
|
17166
17489
|
// src/minsync/client.ts
|
|
17167
|
-
import { existsSync as
|
|
17490
|
+
import { existsSync as existsSync20, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "node:fs";
|
|
17168
17491
|
import { join as join28 } from "node:path";
|
|
17169
17492
|
|
|
17170
17493
|
class MinSyncClient {
|
|
@@ -17259,7 +17582,7 @@ class MinSyncClient {
|
|
|
17259
17582
|
return previous === undefined || JSON.stringify(previous) !== JSON.stringify(identity);
|
|
17260
17583
|
}
|
|
17261
17584
|
async sync(force = false, signal) {
|
|
17262
|
-
if (!
|
|
17585
|
+
if (!existsSync20(this.binaryPath))
|
|
17263
17586
|
throw minSyncBinaryMissingError();
|
|
17264
17587
|
let effective = { config: {} };
|
|
17265
17588
|
try {
|
|
@@ -17297,14 +17620,16 @@ class MinSyncClient {
|
|
|
17297
17620
|
}
|
|
17298
17621
|
}
|
|
17299
17622
|
const spawnOptions = signal === undefined ? {} : { signal };
|
|
17300
|
-
const
|
|
17623
|
+
const lexical = this.resolveLexicalLanguage();
|
|
17624
|
+
const initialized = existsSync20(minSyncConfigPath(this.workspacePath));
|
|
17301
17625
|
if (!initialized) {
|
|
17302
17626
|
const initArgs = ["init", "--format", "json"];
|
|
17303
17627
|
if (this.embedder?.id) {
|
|
17304
17628
|
initArgs.push("--embedder", this.embedder.id);
|
|
17305
17629
|
}
|
|
17630
|
+
initArgs.push("--language", lexical.language);
|
|
17306
17631
|
const init = await this.spawn(initArgs, spawnOptions);
|
|
17307
|
-
if (!init.ok || !
|
|
17632
|
+
if (!init.ok || !existsSync20(minSyncConfigPath(this.workspacePath))) {
|
|
17308
17633
|
return {
|
|
17309
17634
|
ok: false,
|
|
17310
17635
|
synced: 0,
|
|
@@ -17316,20 +17641,29 @@ class MinSyncClient {
|
|
|
17316
17641
|
const chunkSizeChanged = this.maxChunkSize !== undefined && configuredMaxChunkSize(this.workspacePath) !== this.maxChunkSize;
|
|
17317
17642
|
const dimensionChanged = embedder.dimension !== undefined && configuredVectorDimension(this.workspacePath) !== embedder.dimension;
|
|
17318
17643
|
const configPath = minSyncConfigPath(this.workspacePath);
|
|
17319
|
-
const
|
|
17320
|
-
const
|
|
17321
|
-
|
|
17644
|
+
const originalConfig = readConfigSnapshot(configPath);
|
|
17645
|
+
const configRewritten = rewriteEmbedderConfig(this.workspacePath, embedder, {
|
|
17646
|
+
maxChunkSize: this.maxChunkSize,
|
|
17647
|
+
lexicalLanguage: lexical.language
|
|
17648
|
+
});
|
|
17322
17649
|
const restoreConfig = () => {
|
|
17323
17650
|
if (configRewritten && originalConfig !== undefined)
|
|
17324
17651
|
writeFileSync14(configPath, originalConfig);
|
|
17325
17652
|
};
|
|
17326
17653
|
try {
|
|
17327
|
-
|
|
17654
|
+
const result = await this.checkAndSync(force || chunkSizeChanged || dimensionChanged, signal, effective, restoreConfig);
|
|
17655
|
+
return result.ok && lexical.fallback !== undefined ? { ...result, lexicalFallback: lexical.fallback } : result;
|
|
17328
17656
|
} catch (error) {
|
|
17329
17657
|
restoreConfig();
|
|
17330
17658
|
throw error;
|
|
17331
17659
|
}
|
|
17332
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
|
+
}
|
|
17333
17667
|
lockHeldResult(stderr) {
|
|
17334
17668
|
return {
|
|
17335
17669
|
ok: false,
|
|
@@ -17382,7 +17716,7 @@ class MinSyncClient {
|
|
|
17382
17716
|
}
|
|
17383
17717
|
const identityChanged = effective.identity !== undefined && this.identityMismatch(effective.identity);
|
|
17384
17718
|
const fullReindex = forceFull || identityChanged;
|
|
17385
|
-
const syncArgs =
|
|
17719
|
+
const syncArgs = existsSync20(cursorPath) && !fullReindex ? ["sync", "--format", "json"] : ["sync", "--full", "--format", "json"];
|
|
17386
17720
|
const result = await this.spawn(syncArgs, spawnOptions);
|
|
17387
17721
|
if (!result.ok && result.code === MINSYNC_LOCK_HELD_EXIT_CODE) {
|
|
17388
17722
|
restoreConfig();
|
|
@@ -17403,7 +17737,7 @@ class MinSyncClient {
|
|
|
17403
17737
|
}
|
|
17404
17738
|
};
|
|
17405
17739
|
}
|
|
17406
|
-
if (!
|
|
17740
|
+
if (!existsSync20(cursorPath)) {
|
|
17407
17741
|
restoreConfig();
|
|
17408
17742
|
return { ok: false, synced: 0, workspacePath: this.workspacePath, reason: "not-ready: missing cursor" };
|
|
17409
17743
|
}
|
|
@@ -17424,7 +17758,7 @@ class MinSyncClient {
|
|
|
17424
17758
|
};
|
|
17425
17759
|
}
|
|
17426
17760
|
async query(text, topK, mode = "vector", signal) {
|
|
17427
|
-
if (!
|
|
17761
|
+
if (!existsSync20(this.binaryPath))
|
|
17428
17762
|
throw minSyncBinaryMissingError();
|
|
17429
17763
|
const result = await this.spawn(["query", "--format", "json", "--mode", mode, "-k", String(topK), text], {
|
|
17430
17764
|
...signal === undefined ? {} : { signal }
|
|
@@ -17518,6 +17852,7 @@ var MINSYNC_OLLAMA_MIGRATION_MESSAGE = "This workspace uses the legacy 768-dimen
|
|
|
17518
17852
|
var init_client10 = __esm(() => {
|
|
17519
17853
|
init_embedder_config();
|
|
17520
17854
|
init_errors4();
|
|
17855
|
+
init_kiwi();
|
|
17521
17856
|
init_local_embedder();
|
|
17522
17857
|
init_process();
|
|
17523
17858
|
API_KEY_ENV_PATTERN2 = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -17540,14 +17875,14 @@ var init_client10 = __esm(() => {
|
|
|
17540
17875
|
|
|
17541
17876
|
// src/minsync/installer.ts
|
|
17542
17877
|
import { createHash as createHash14 } from "node:crypto";
|
|
17543
|
-
import { copyFileSync, createWriteStream as createWriteStream2, existsSync as
|
|
17878
|
+
import { copyFileSync, createWriteStream as createWriteStream2, existsSync as existsSync21, mkdirSync as mkdirSync17, renameSync as renameSync6, rmSync as rmSync6 } from "node:fs";
|
|
17544
17879
|
import { chmod as chmod2, mkdtemp } from "node:fs/promises";
|
|
17545
17880
|
import { get } from "node:https";
|
|
17546
17881
|
import { basename as basename10, delimiter as delimiter4, dirname as dirname18, join as join29 } from "node:path";
|
|
17547
17882
|
import { pipeline } from "node:stream/promises";
|
|
17548
17883
|
async function ensureMinSyncBinary(options) {
|
|
17549
17884
|
const binaryPath = join29(options.root, ".autorag", "bin", executableName(options.platform ?? process.platform));
|
|
17550
|
-
if (
|
|
17885
|
+
if (existsSync21(binaryPath))
|
|
17551
17886
|
return { binaryPath, version: "cached" };
|
|
17552
17887
|
mkdirSync17(dirname18(binaryPath), { recursive: true });
|
|
17553
17888
|
const cargoInstaller = options.cargoInstaller ?? ((destination) => installMinSyncFromCargoIfAvailable(options, destination));
|
|
@@ -17595,7 +17930,7 @@ async function installMinSyncFromCargo(options, destination, cargo) {
|
|
|
17595
17930
|
throw new MinSyncReleaseError(result.stderr || `Could not install MinSync ${MINSYNC_VERSION} from crates.io`);
|
|
17596
17931
|
}
|
|
17597
17932
|
const installedBinary = join29(cargoRoot, "bin", executableName(options.platform ?? process.platform));
|
|
17598
|
-
if (!
|
|
17933
|
+
if (!existsSync21(installedBinary)) {
|
|
17599
17934
|
throw new MinSyncReleaseError(`Cargo did not produce the MinSync ${MINSYNC_VERSION} binary`);
|
|
17600
17935
|
}
|
|
17601
17936
|
copyFileSync(installedBinary, destination);
|
|
@@ -17727,7 +18062,7 @@ function lookupCargo(env, platform) {
|
|
|
17727
18062
|
if (dir.length === 0)
|
|
17728
18063
|
continue;
|
|
17729
18064
|
const candidate = join29(dir, execName);
|
|
17730
|
-
if (
|
|
18065
|
+
if (existsSync21(candidate))
|
|
17731
18066
|
return candidate;
|
|
17732
18067
|
}
|
|
17733
18068
|
return;
|
|
@@ -17758,7 +18093,7 @@ function isGitHubAsset(value) {
|
|
|
17758
18093
|
function isRecord12(value) {
|
|
17759
18094
|
return typeof value === "object" && value !== null;
|
|
17760
18095
|
}
|
|
17761
|
-
var LATEST_RELEASE_URL = "https://api.github.com/repos/NomaDamas/MinSync/releases/latest", SHA256_HEX_PATTERN, MINSYNC_VERSION = "0.4.
|
|
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;
|
|
17762
18097
|
var init_installer2 = __esm(() => {
|
|
17763
18098
|
init_process();
|
|
17764
18099
|
SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/;
|
|
@@ -17786,7 +18121,7 @@ var init_paths5 = __esm(() => {
|
|
|
17786
18121
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
17787
18122
|
import {
|
|
17788
18123
|
copyFileSync as copyFileSync2,
|
|
17789
|
-
existsSync as
|
|
18124
|
+
existsSync as existsSync22,
|
|
17790
18125
|
lstatSync as lstatSync2,
|
|
17791
18126
|
mkdirSync as mkdirSync18,
|
|
17792
18127
|
readdirSync as readdirSync2,
|
|
@@ -17801,8 +18136,8 @@ function syncMinSyncWorkspace(root, options = {}) {
|
|
|
17801
18136
|
const filesRoot = join31(workspacePath, "files");
|
|
17802
18137
|
const index = loadMirrorIndex(root);
|
|
17803
18138
|
const candidates = Object.values(index.entries).sort((a, b) => a.virtualPath.localeCompare(b.virtualPath));
|
|
17804
|
-
const excluded = candidates.filter((entry) => normalizeVirtualPath(entry.virtualPath) !== entry.virtualPath || !
|
|
17805
|
-
const entries = candidates.filter((entry) => normalizeVirtualPath(entry.virtualPath) === entry.virtualPath &&
|
|
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) => {
|
|
17806
18141
|
const minSyncPath = minSyncDocumentPath(workspacePath, entry.virtualPath);
|
|
17807
18142
|
return {
|
|
17808
18143
|
virtualPath: entry.virtualPath,
|
|
@@ -17939,7 +18274,7 @@ var init_workspace = __esm(() => {
|
|
|
17939
18274
|
});
|
|
17940
18275
|
|
|
17941
18276
|
// src/minsync/method.ts
|
|
17942
|
-
import { accessSync as accessSync2, constants as constants2, existsSync as
|
|
18277
|
+
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync23, realpathSync as realpathSync4 } from "node:fs";
|
|
17943
18278
|
import { basename as basename12, delimiter as delimiter5, join as join32, normalize as normalize3 } from "node:path";
|
|
17944
18279
|
function lookupInPath2(env) {
|
|
17945
18280
|
const pathEnv = env.PATH;
|
|
@@ -18035,19 +18370,19 @@ class MinSyncVectorMethod {
|
|
|
18035
18370
|
}
|
|
18036
18371
|
isBinaryMissing() {
|
|
18037
18372
|
if (this.binaryPath !== undefined)
|
|
18038
|
-
return !
|
|
18373
|
+
return !existsSync23(this.binaryPath);
|
|
18039
18374
|
if (lookupInPath2(process.env) !== undefined)
|
|
18040
18375
|
return false;
|
|
18041
18376
|
const cachedBinary = join32(this.root, ".autorag", "bin", executableName(process.platform));
|
|
18042
|
-
if (
|
|
18377
|
+
if (existsSync23(cachedBinary))
|
|
18043
18378
|
return false;
|
|
18044
18379
|
return true;
|
|
18045
18380
|
}
|
|
18046
18381
|
isExplicitBinaryMissing() {
|
|
18047
|
-
return this.binaryPath !== undefined && !
|
|
18382
|
+
return this.binaryPath !== undefined && !existsSync23(this.binaryPath);
|
|
18048
18383
|
}
|
|
18049
18384
|
isReady() {
|
|
18050
|
-
return
|
|
18385
|
+
return existsSync23(join32(this.workspacePath, ".minsync", "cursor.json"));
|
|
18051
18386
|
}
|
|
18052
18387
|
async retrieve(query, options) {
|
|
18053
18388
|
if ((workspaceSyncInFlight.get(this.workspacePath) ?? 0) > 0) {
|
|
@@ -18100,7 +18435,7 @@ class MinSyncVectorMethod {
|
|
|
18100
18435
|
return results;
|
|
18101
18436
|
}
|
|
18102
18437
|
async resolveBinary(options = {}) {
|
|
18103
|
-
if (this.binaryPath &&
|
|
18438
|
+
if (this.binaryPath && existsSync23(this.binaryPath))
|
|
18104
18439
|
return this.binaryPath;
|
|
18105
18440
|
if (this.binaryPath !== undefined)
|
|
18106
18441
|
throw minSyncBinaryMissingError();
|
|
@@ -18108,7 +18443,7 @@ class MinSyncVectorMethod {
|
|
|
18108
18443
|
if (pathBinary)
|
|
18109
18444
|
return pathBinary;
|
|
18110
18445
|
const cachedBinary = join32(this.root, ".autorag", "bin", executableName(process.platform));
|
|
18111
|
-
if (
|
|
18446
|
+
if (existsSync23(cachedBinary))
|
|
18112
18447
|
return cachedBinary;
|
|
18113
18448
|
if (this.autoInstall && options.install !== false) {
|
|
18114
18449
|
try {
|
|
@@ -18161,7 +18496,7 @@ var init_minsync = __esm(() => {
|
|
|
18161
18496
|
});
|
|
18162
18497
|
|
|
18163
18498
|
// src/parser/isolated.ts
|
|
18164
|
-
import { existsSync as
|
|
18499
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
18165
18500
|
import { dirname as dirname20, join as join33 } from "node:path";
|
|
18166
18501
|
import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL2 } from "node:url";
|
|
18167
18502
|
import { Worker } from "node:worker_threads";
|
|
@@ -18300,7 +18635,7 @@ function defaultWorkerUrl() {
|
|
|
18300
18635
|
join33(here, "parser", "parse-worker.js"),
|
|
18301
18636
|
join33(here, "..", "parser", "parse-worker.js")
|
|
18302
18637
|
]) {
|
|
18303
|
-
if (
|
|
18638
|
+
if (existsSync24(candidate))
|
|
18304
18639
|
return pathToFileURL2(candidate);
|
|
18305
18640
|
}
|
|
18306
18641
|
throw new Error(`The AutoRAG parse worker entry was not found near ${here}.`);
|
|
@@ -18312,9 +18647,9 @@ var init_isolated = __esm(() => {
|
|
|
18312
18647
|
});
|
|
18313
18648
|
|
|
18314
18649
|
// src/mirror/sync.ts
|
|
18315
|
-
import { existsSync as
|
|
18650
|
+
import { existsSync as existsSync25, rmSync as rmSync8 } from "node:fs";
|
|
18316
18651
|
import { opendir, readFile as readFile5, stat as stat7 } from "node:fs/promises";
|
|
18317
|
-
import { extname as extname5, resolve as
|
|
18652
|
+
import { extname as extname5, resolve as resolve15, sep as sep7 } from "node:path";
|
|
18318
18653
|
import { setTimeout as sleep3 } from "node:timers/promises";
|
|
18319
18654
|
function errorCode(error) {
|
|
18320
18655
|
if (!(error instanceof Error))
|
|
@@ -18504,7 +18839,7 @@ async function syncWithRegistry(options, registry, isolated) {
|
|
|
18504
18839
|
}
|
|
18505
18840
|
const previousEntry = previous.entries[entry.virtualPath];
|
|
18506
18841
|
const outputPath = parsedOutputPath(options.root, entry.virtualPath);
|
|
18507
|
-
const unchanged = !options.force && previousEntry?.sourceMtimeNs === entry.mtimeNs && previousEntry.sourceSizeBytes === entry.sizeBytes && previousEntry.parserName === parser.name &&
|
|
18842
|
+
const unchanged = !options.force && previousEntry?.sourceMtimeNs === entry.mtimeNs && previousEntry.sourceSizeBytes === entry.sizeBytes && previousEntry.parserName === parser.name && existsSync25(outputPath);
|
|
18508
18843
|
if (!unchanged) {
|
|
18509
18844
|
let bytes;
|
|
18510
18845
|
try {
|
|
@@ -18832,7 +19167,7 @@ async function collectFiles(state, directory, depth) {
|
|
|
18832
19167
|
continue;
|
|
18833
19168
|
if (PRODUCT_ARTIFACT_FILE_NAMES.has(entry.name))
|
|
18834
19169
|
continue;
|
|
18835
|
-
const sourcePath =
|
|
19170
|
+
const sourcePath = resolve15(directory, entry.name);
|
|
18836
19171
|
if (entry.isDirectory()) {
|
|
18837
19172
|
if (depth + 1 > state.limits.maxDepth) {
|
|
18838
19173
|
listing.unreliablePrefixes.push(sourceIdentifier(sourceRoot, sourcePath));
|
|
@@ -18885,7 +19220,7 @@ function removePrevious(root, index, virtualPath) {
|
|
|
18885
19220
|
return 1;
|
|
18886
19221
|
}
|
|
18887
19222
|
function removeFile(path) {
|
|
18888
|
-
if (
|
|
19223
|
+
if (existsSync25(path))
|
|
18889
19224
|
rmSync8(path, { force: true });
|
|
18890
19225
|
}
|
|
18891
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;
|
|
@@ -20015,12 +20350,43 @@ function digitsEndAt(text, from) {
|
|
|
20015
20350
|
end += 1;
|
|
20016
20351
|
return end;
|
|
20017
20352
|
}
|
|
20018
|
-
function continuesAsProse(answer, from) {
|
|
20019
|
-
|
|
20353
|
+
function continuesAsProse(answer, openIndex, from) {
|
|
20354
|
+
let cursor = from;
|
|
20355
|
+
let leadingEmphasis = 0;
|
|
20356
|
+
while (openIndex - 1 - leadingEmphasis >= 0 && (answer[openIndex - 1 - leadingEmphasis] === "*" || answer[openIndex - 1 - leadingEmphasis] === "_")) {
|
|
20357
|
+
leadingEmphasis += 1;
|
|
20358
|
+
}
|
|
20359
|
+
if (leadingEmphasis > 0) {
|
|
20360
|
+
let trailingEmphasis = 0;
|
|
20361
|
+
while (cursor + trailingEmphasis < answer.length && trailingEmphasis < leadingEmphasis && answer[cursor + trailingEmphasis] === answer[openIndex - 1 - trailingEmphasis]) {
|
|
20362
|
+
trailingEmphasis += 1;
|
|
20363
|
+
}
|
|
20364
|
+
if (trailingEmphasis === leadingEmphasis)
|
|
20365
|
+
cursor += trailingEmphasis;
|
|
20366
|
+
}
|
|
20367
|
+
const tokenStart = openIndex - leadingEmphasis;
|
|
20368
|
+
const linePrefix = answer.slice(answer.lastIndexOf(`
|
|
20369
|
+
`, tokenStart - 1) + 1, tokenStart);
|
|
20370
|
+
const atHeadingStart = /^[ \t>*-]*$/u.test(linePrefix);
|
|
20371
|
+
if (atHeadingStart && answer[cursor] === ":")
|
|
20372
|
+
cursor += 1;
|
|
20373
|
+
if (answer[cursor] !== " " && answer[cursor] !== " ")
|
|
20020
20374
|
return false;
|
|
20021
|
-
|
|
20375
|
+
cursor += 1;
|
|
20376
|
+
while (cursor < answer.length && (answer[cursor] === "*" || answer[cursor] === "_" || answer[cursor] === '"' || answer[cursor] === "'" || answer[cursor] === "“" || answer[cursor] === "‘")) {
|
|
20377
|
+
cursor += 1;
|
|
20378
|
+
}
|
|
20379
|
+
const next = answer.codePointAt(cursor);
|
|
20022
20380
|
return next !== undefined && /[\p{L}\p{N}]/u.test(String.fromCodePoint(next));
|
|
20023
20381
|
}
|
|
20382
|
+
function isLegalPinpoint(answer, openIndex, afterCloseIndex) {
|
|
20383
|
+
if (PINPOINT_RANGE_AHEAD.test(answer.slice(afterCloseIndex, afterCloseIndex + 16)))
|
|
20384
|
+
return true;
|
|
20385
|
+
if (PINPOINT_RANGE_BEHIND.test(answer.slice(Math.max(0, openIndex - 16), openIndex)))
|
|
20386
|
+
return true;
|
|
20387
|
+
const prefixWindow = answer.slice(Math.max(0, openIndex - 120), openIndex);
|
|
20388
|
+
return PARA_PINPOINT_PREFIX.test(prefixWindow) || CLAUSE_START_AT_PREFIX.test(prefixWindow) || CASE_CITATION_AT_PREFIX.test(prefixWindow);
|
|
20389
|
+
}
|
|
20024
20390
|
function evidenceMarkerAt(answer, index) {
|
|
20025
20391
|
const ids = [];
|
|
20026
20392
|
let cursor = index + 1;
|
|
@@ -20086,7 +20452,7 @@ function answerMarkers(answer, isFile = () => false) {
|
|
|
20086
20452
|
}
|
|
20087
20453
|
if (char === "[") {
|
|
20088
20454
|
const digitsEnd = digitsEndAt(answer, index + 1);
|
|
20089
|
-
if (digitsEnd > index + 1 && answer[digitsEnd] === "]" && answer[digitsEnd + 1] !== "(" && !continuesAsProse(answer, digitsEnd + 1)) {
|
|
20455
|
+
if (digitsEnd > index + 1 && answer[digitsEnd] === "]" && answer[digitsEnd + 1] !== "(" && !continuesAsProse(answer, index, digitsEnd + 1) && !isLegalPinpoint(answer, index, digitsEnd + 1)) {
|
|
20090
20456
|
markers.push({
|
|
20091
20457
|
kind: "number",
|
|
20092
20458
|
start: leadingStart(index),
|
|
@@ -20158,7 +20524,14 @@ function assertResultsMappingOneToOne(label, results, mapping) {
|
|
|
20158
20524
|
].filter((note) => note !== undefined);
|
|
20159
20525
|
throw new Error(`${label}: result numbers and mapping numbers must be one-to-one, but results contain ${formatCitationList(resultNumbers)} ` + `and mapping contains ${formatCitationList(mappingNumbers)}` + (duplicateNotes.length > 0 ? ` (${duplicateNotes.join("; ")})` : "") + ". Give every result a unique number with exactly one mapping entry carrying the same number and no mapping entry without a result.");
|
|
20160
20526
|
}
|
|
20161
|
-
var MAX_FILE_MARKER_CLOSERS = 8, FILE_MARKER_PREFIX = "[file:";
|
|
20527
|
+
var MAX_FILE_MARKER_CLOSERS = 8, FILE_MARKER_PREFIX = "[file:", PARA_PINPOINT_PREFIX, CLAUSE_START_AT_PREFIX, CASE_CITATION_AT_PREFIX, PINPOINT_RANGE_AHEAD, PINPOINT_RANGE_BEHIND;
|
|
20528
|
+
var init_citations = __esm(() => {
|
|
20529
|
+
PARA_PINPOINT_PREFIX = /(?:^|[^\p{L}\p{N}])(?:paras?\.?|paragraphs?)(?:[ \t\u00a0]+\[\d+\][ \t\u00a0]*(?:[-–—]|,(?:[ \t\u00a0]*(?:and|or)\b)?|and\b|or\b))*[ \t\u00a0]*$/iu;
|
|
20530
|
+
CLAUSE_START_AT_PREFIX = /(?:^|[\n.!?؛;:()]|[>*-])[ \t\u00a0]*At(?:[ \t\u00a0]+\[\d+\][ \t\u00a0]*(?:[-–—]|,(?:[ \t\u00a0]*(?:and|or)\b)?|and\b|or\b))*[ \t\u00a0]+$/u;
|
|
20531
|
+
CASE_CITATION_AT_PREFIX = /(?:\[\d{4}\][^\n.;]*?|\b(?:v|vs\.?)\s+[^\n.;]{1,60}?|\bs(?:ection)?[ \t\u00a0]+\d+[A-Za-z]?(?:\(\d+\))?[^\n.;]{0,30}?)[ \t\u00a0]+at(?:[ \t\u00a0]+\[\d+\][ \t\u00a0]*(?:[-–—]|,(?:[ \t\u00a0]*(?:and|or)\b)?|and\b|or\b))*[ \t\u00a0]+$/iu;
|
|
20532
|
+
PINPOINT_RANGE_AHEAD = /^[ \t\u00a0]*[-–—][ \t\u00a0]*\[\d+\]/u;
|
|
20533
|
+
PINPOINT_RANGE_BEHIND = /\[\d+\][ \t\u00a0]*[-–—][ \t\u00a0]*$/u;
|
|
20534
|
+
});
|
|
20162
20535
|
|
|
20163
20536
|
// src/agent/evidence-ledger.ts
|
|
20164
20537
|
import { createHash as createHash15 } from "node:crypto";
|
|
@@ -20442,6 +20815,7 @@ function deriveResultsFromAnswer(text, ledger, options) {
|
|
|
20442
20815
|
}
|
|
20443
20816
|
var MAX_EXCERPT_CHARS = 400, MAX_CLAIM_CHARS = 280, CLAIM_LOOKBACK_CHARS = 600, SENTENCE_END, LEADING_LIST_MARK, EVIDENCE_MARKER_TEXT, LEADING_BLANK;
|
|
20444
20817
|
var init_answer_citations = __esm(() => {
|
|
20818
|
+
init_citations();
|
|
20445
20819
|
init_evidence_ledger();
|
|
20446
20820
|
SENTENCE_END = /[.!?。!?]/u;
|
|
20447
20821
|
LEADING_LIST_MARK = /^[\s\-*•>]+/u;
|
|
@@ -20450,7 +20824,7 @@ var init_answer_citations = __esm(() => {
|
|
|
20450
20824
|
});
|
|
20451
20825
|
|
|
20452
20826
|
// src/agent/answer-guidelines.ts
|
|
20453
|
-
var ANSWER_IMAGE_EMBED_RULE = "**Images**: when a retrieved result is itself an image file (png, jpg, jpeg, gif, webp, svg, bmp, avif, heic, tiff) and showing it answers the question, embed it in the answer as a markdown image on its own line: ``, followed by its citation, e.g. ` [e2]`. Use only the real source path of retrieved evidence. Never invent a path, use relative paths, or embed remote URLs. Apart from these image embeds, the no-file-paths rule stands.", ANSWER_IMAGE_DELTA_RULE = "An image already embedded in the first answer must not be embedded again.", ANSWER_CITATION_RULE = "**Citations**: cite supporting evidence inline with the evidence id shown next to each retrieved result, in square brackets (e.g. [e3], or [e3][e7] for two). Copy ids exactly; never invent one. For a local file you opened yourself with `bash`, cite `[file:<absolute path>]`. Cite the evidence that backs a sentence at the end of that sentence. Do not cite numbers, paths, or anything else.";
|
|
20827
|
+
var ANSWER_IMAGE_EMBED_RULE = "**Images**: when a retrieved result is itself an image file (png, jpg, jpeg, gif, webp, svg, bmp, avif, heic, tiff) and showing it answers the question, embed it in the answer as a markdown image on its own line: ``, followed by its citation, e.g. ` [e2]`. Use only the real source path of retrieved evidence. Never invent a path, use relative paths, or embed remote URLs. Apart from these image embeds, the no-file-paths rule stands.", ANSWER_IMAGE_DELTA_RULE = "An image already embedded in the first answer must not be embedded again.", ANSWER_CITATION_RULE = "**Citations**: cite supporting evidence inline with the evidence id shown next to each retrieved result, in square brackets (e.g. [e3], or [e3][e7] for two). Copy ids exactly; never invent one. For a local file you opened yourself with `bash`, cite `[file:<absolute path>]`. Cite the evidence that backs a sentence at the end of that sentence. Do not cite numbers, paths, or anything else.", ANSWER_DELIVERABLE_RULE = '**Deliverables and artifacts**: if the user asks you to draft, write, generate, or prepare a deliverable or artifact (a reply email, letter, legal opinion, summary document, standard-format case citation block, checklist, report, or workflow document — including requests phrased as "how should I reply..." to a specific message), produce the finished, ready-to-use artifact as a file (using `write`, `edit`, or `bash` in local sessions, without overwriting existing source files) in a file format, extension, and structure aligned with the context and domain (e.g. `.eml` or `.md`/`.txt` with headers for an email reply; `.md`, `.txt`, `.docx`, `.hwp`, or `.pdf` matching workspace conventions for a legal opinion, insertable case-citation block, or summary document; `.csv`, `.xlsx`, or `.json` for structured tabular data), containing the complete unabridged artifact (including required headers, precedent passages, pinpoint paragraphs, and standard-format citations) inside the file instead of merely describing how to produce it. Do NOT dump the full artifact text into your reply: unless the user explicitly requests otherwise, keep your reply concise within the at-most-5-bullet direct-answer format, clearly stating the created file\'s exact path/location and briefly summarizing what was generated with inline citations to the underlying source evidence (`[eN]` or `[file:<source path>]`); never cite the newly created deliverable file itself as `[file:...]`.', ANSWER_DELIVERABLE_DELTA_RULE = "If a deliverable or artifact was requested and the first answer could not write the file yet (the fast phase runs without tool calls) or only gave advice about it, create the complete context-aligned deliverable file now during verification and report the created file's location and a brief summary concisely.";
|
|
20454
20828
|
|
|
20455
20829
|
// src/agent/datasource-skill.ts
|
|
20456
20830
|
import { Type as Type3 } from "typebox";
|
|
@@ -21801,6 +22175,20 @@ function interactiveKeyOf(data) {
|
|
|
21801
22175
|
return "followUp";
|
|
21802
22176
|
return;
|
|
21803
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
|
+
}
|
|
21804
22192
|
function createAutoRAGInteractiveExtension(getSystemPrompt, contextTransform, options, updateNotice) {
|
|
21805
22193
|
return (pi) => {
|
|
21806
22194
|
pi.on("before_agent_start", () => ({ systemPrompt: getSystemPrompt() }));
|
|
@@ -21831,8 +22219,8 @@ function createAutoRAGInteractiveExtension(getSystemPrompt, contextTransform, op
|
|
|
21831
22219
|
});
|
|
21832
22220
|
}
|
|
21833
22221
|
pi.on("input", (event) => {
|
|
21834
|
-
const text = event
|
|
21835
|
-
if (
|
|
22222
|
+
const text = userInputText(event);
|
|
22223
|
+
if (text === undefined || text.length === 0)
|
|
21836
22224
|
return;
|
|
21837
22225
|
if (text.startsWith("/") && isSearchRunning?.() !== true)
|
|
21838
22226
|
return;
|
|
@@ -21929,7 +22317,9 @@ async function createAutoRAGPiInteractiveRuntime(options) {
|
|
|
21929
22317
|
agentDir: runtimeAgentDir,
|
|
21930
22318
|
modelRuntime,
|
|
21931
22319
|
settingsManager,
|
|
22320
|
+
...options.extensionFlags === undefined ? {} : { extensionFlagValues: new Map(Object.entries(options.extensionFlags)) },
|
|
21932
22321
|
resourceLoaderOptions: {
|
|
22322
|
+
...options.extensionPaths === undefined ? {} : { additionalExtensionPaths: [...options.extensionPaths] },
|
|
21933
22323
|
extensionFactories: [
|
|
21934
22324
|
createAutoRAGInteractiveExtension(options.getSystemPrompt, options.contextTransform, { onInput: options.onInput, isSearchRunning: options.isSearchRunning, onKey: options.onKey }, options.updateNotice),
|
|
21935
22325
|
...options.extensionFactories ?? []
|
|
@@ -21963,7 +22353,7 @@ async function createAutoRAGPiInteractiveRuntime(options) {
|
|
|
21963
22353
|
});
|
|
21964
22354
|
return { runtime, dispose: () => runtime.dispose() };
|
|
21965
22355
|
}
|
|
21966
|
-
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 ";
|
|
21967
22357
|
var init_pi_session = __esm(() => {
|
|
21968
22358
|
init_provider();
|
|
21969
22359
|
init_context_token_guard();
|
|
@@ -22219,7 +22609,7 @@ var init_jikji_find_tool = __esm(() => {
|
|
|
22219
22609
|
|
|
22220
22610
|
// src/p2p/approval-store.ts
|
|
22221
22611
|
import {
|
|
22222
|
-
existsSync as
|
|
22612
|
+
existsSync as existsSync26,
|
|
22223
22613
|
mkdirSync as mkdirSync20,
|
|
22224
22614
|
readdirSync as readdirSync3,
|
|
22225
22615
|
readFileSync as readFileSync22,
|
|
@@ -22284,7 +22674,7 @@ function savePendingPeerRequest(workspacePath, request) {
|
|
|
22284
22674
|
}
|
|
22285
22675
|
function loadPendingPeerRequest(workspacePath, id) {
|
|
22286
22676
|
const path = requestPath(workspacePath, id);
|
|
22287
|
-
if (!
|
|
22677
|
+
if (!existsSync26(path))
|
|
22288
22678
|
return;
|
|
22289
22679
|
try {
|
|
22290
22680
|
return parsePending(JSON.parse(readFileSync22(path, "utf8")));
|
|
@@ -22294,7 +22684,7 @@ function loadPendingPeerRequest(workspacePath, id) {
|
|
|
22294
22684
|
}
|
|
22295
22685
|
function listPendingPeerRequests(workspacePath) {
|
|
22296
22686
|
const dir = requestsDir(workspacePath);
|
|
22297
|
-
if (!
|
|
22687
|
+
if (!existsSync26(dir))
|
|
22298
22688
|
return [];
|
|
22299
22689
|
const requests = [];
|
|
22300
22690
|
for (const name of readdirSync3(dir)) {
|
|
@@ -22316,7 +22706,7 @@ function writePeerRequestDecision(workspacePath, id, decision) {
|
|
|
22316
22706
|
}
|
|
22317
22707
|
function loadPeerRequestDecision(workspacePath, id) {
|
|
22318
22708
|
const path = decisionPath(workspacePath, id);
|
|
22319
|
-
if (!
|
|
22709
|
+
if (!existsSync26(path))
|
|
22320
22710
|
return;
|
|
22321
22711
|
try {
|
|
22322
22712
|
return parseDecision(JSON.parse(readFileSync22(path, "utf8")));
|
|
@@ -22650,8 +23040,8 @@ var init_egress_gate = __esm(() => {
|
|
|
22650
23040
|
});
|
|
22651
23041
|
|
|
22652
23042
|
// src/p2p/policy.ts
|
|
22653
|
-
import { existsSync as
|
|
22654
|
-
import { basename as basename13, dirname as dirname23, join as join36, resolve as
|
|
23043
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync21, readFileSync as readFileSync23, writeFileSync as writeFileSync17 } from "node:fs";
|
|
23044
|
+
import { basename as basename13, dirname as dirname23, join as join36, resolve as resolve16 } from "node:path";
|
|
22655
23045
|
import { parse as parseToml2 } from "smol-toml";
|
|
22656
23046
|
function isRecord17(value) {
|
|
22657
23047
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -22730,7 +23120,7 @@ function normalizePolicyEntries(value, workspacePath, context) {
|
|
|
22730
23120
|
return entries;
|
|
22731
23121
|
}
|
|
22732
23122
|
function readJsonFile(path) {
|
|
22733
|
-
if (!
|
|
23123
|
+
if (!existsSync27(path))
|
|
22734
23124
|
return;
|
|
22735
23125
|
let raw;
|
|
22736
23126
|
try {
|
|
@@ -22745,7 +23135,7 @@ function readJsonFile(path) {
|
|
|
22745
23135
|
}
|
|
22746
23136
|
}
|
|
22747
23137
|
function readTomlFile(path) {
|
|
22748
|
-
if (!
|
|
23138
|
+
if (!existsSync27(path))
|
|
22749
23139
|
return;
|
|
22750
23140
|
let raw;
|
|
22751
23141
|
try {
|
|
@@ -22886,7 +23276,7 @@ function globToRegExp(pattern) {
|
|
|
22886
23276
|
return new RegExp(`${expression}$`, "u");
|
|
22887
23277
|
}
|
|
22888
23278
|
function loadSeenSources(path, workspacePath) {
|
|
22889
|
-
if (!
|
|
23279
|
+
if (!existsSync27(path))
|
|
22890
23280
|
return { promoted: new Set, pending: new Set };
|
|
22891
23281
|
let parsed;
|
|
22892
23282
|
try {
|
|
@@ -22934,7 +23324,7 @@ class PolicyStore {
|
|
|
22934
23324
|
if (typeof normalizedOptions.workspacePath !== "string" || normalizedOptions.workspacePath.length === 0) {
|
|
22935
23325
|
throw new PolicyError("workspacePath must be a non-empty path");
|
|
22936
23326
|
}
|
|
22937
|
-
this.workspacePath =
|
|
23327
|
+
this.workspacePath = resolve16(normalizedOptions.workspacePath);
|
|
22938
23328
|
this.sourceRoots = normalizedOptions.sourceRoots;
|
|
22939
23329
|
const globalConfigPath = normalizedOptions.globalConfigPath ?? resolveGlobalConfigPath(normalizedOptions.homePath);
|
|
22940
23330
|
const workspacePolicyPath = normalizedOptions.workspacePolicyPath ?? join36(this.workspacePath, ".autorag", "p2p", "policy.toml");
|
|
@@ -23193,7 +23583,7 @@ var init_wire = __esm(() => {
|
|
|
23193
23583
|
|
|
23194
23584
|
// src/p2p/simplex-query-store.ts
|
|
23195
23585
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
23196
|
-
import { existsSync as
|
|
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";
|
|
23197
23587
|
import { dirname as dirname24, join as join37 } from "node:path";
|
|
23198
23588
|
import { Value as Value2 } from "typebox/value";
|
|
23199
23589
|
function statePath(workspacePath, directory, id) {
|
|
@@ -23229,7 +23619,7 @@ function parseState(value) {
|
|
|
23229
23619
|
return value;
|
|
23230
23620
|
}
|
|
23231
23621
|
function readState(path) {
|
|
23232
|
-
if (!
|
|
23622
|
+
if (!existsSync28(path))
|
|
23233
23623
|
return;
|
|
23234
23624
|
try {
|
|
23235
23625
|
return parseState(JSON.parse(readFileSync24(path, "utf8")));
|
|
@@ -23256,14 +23646,14 @@ function moveState(workspacePath, from, to, state) {
|
|
|
23256
23646
|
}
|
|
23257
23647
|
function recoverClaims(workspacePath) {
|
|
23258
23648
|
const directory = join37(workspacePath, QUERY_DIR, PENDING_DIR);
|
|
23259
|
-
if (!
|
|
23649
|
+
if (!existsSync28(directory))
|
|
23260
23650
|
return;
|
|
23261
23651
|
for (const name of readdirSync4(directory)) {
|
|
23262
23652
|
if (!name.endsWith(".json.claim"))
|
|
23263
23653
|
continue;
|
|
23264
23654
|
const claim = join37(directory, name);
|
|
23265
23655
|
const restored = join37(directory, name.slice(0, -".claim".length));
|
|
23266
|
-
if (!
|
|
23656
|
+
if (!existsSync28(restored))
|
|
23267
23657
|
renameSync9(claim, restored);
|
|
23268
23658
|
}
|
|
23269
23659
|
}
|
|
@@ -23287,7 +23677,7 @@ function loadSimplexQueryState(workspacePath, id) {
|
|
|
23287
23677
|
}
|
|
23288
23678
|
function listPendingSimplexQueryStates(workspacePath) {
|
|
23289
23679
|
const directory = join37(workspacePath, QUERY_DIR, PENDING_DIR);
|
|
23290
|
-
if (!
|
|
23680
|
+
if (!existsSync28(directory))
|
|
23291
23681
|
return [];
|
|
23292
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));
|
|
23293
23683
|
}
|
|
@@ -23430,7 +23820,7 @@ var init_simplex_query_store = __esm(() => {
|
|
|
23430
23820
|
});
|
|
23431
23821
|
|
|
23432
23822
|
// src/p2p/simplex-server.ts
|
|
23433
|
-
import { existsSync as
|
|
23823
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync23, readFileSync as readFileSync25, writeFileSync as writeFileSync19 } from "node:fs";
|
|
23434
23824
|
import { join as join38 } from "node:path";
|
|
23435
23825
|
import { Type as Type10 } from "typebox";
|
|
23436
23826
|
import { Value as Value3 } from "typebox/value";
|
|
@@ -23459,7 +23849,7 @@ function rankSimplexPeerTargets(query, registry) {
|
|
|
23459
23849
|
}
|
|
23460
23850
|
function loadSimplexPeerRegistry(workspacePath) {
|
|
23461
23851
|
const path = join38(workspacePath, PEERS_DIR, PEERS_FILENAME);
|
|
23462
|
-
if (!
|
|
23852
|
+
if (!existsSync29(path))
|
|
23463
23853
|
return {};
|
|
23464
23854
|
try {
|
|
23465
23855
|
const parsed = JSON.parse(readFileSync25(path, "utf8"));
|
|
@@ -24365,6 +24755,7 @@ function recordStructuredResultsSession(sessionId, query, details, memory, compo
|
|
|
24365
24755
|
}
|
|
24366
24756
|
var init_search_documents = __esm(() => {
|
|
24367
24757
|
init_memory();
|
|
24758
|
+
init_citations();
|
|
24368
24759
|
});
|
|
24369
24760
|
|
|
24370
24761
|
// src/agent/search-all-tool.ts
|
|
@@ -24570,14 +24961,14 @@ var init_search_single_datasource_tool = __esm(() => {
|
|
|
24570
24961
|
});
|
|
24571
24962
|
|
|
24572
24963
|
// src/agent/self-config.ts
|
|
24573
|
-
import { existsSync as
|
|
24964
|
+
import { existsSync as existsSync30, readFileSync as readFileSync27, rmSync as rmSync10, writeFileSync as writeFileSync21 } from "node:fs";
|
|
24574
24965
|
import { dirname as dirname26, join as join39 } from "node:path";
|
|
24575
24966
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
24576
24967
|
function findPackagedSetupSkill() {
|
|
24577
24968
|
let dir = dirname26(fileURLToPath3(import.meta.url));
|
|
24578
24969
|
for (let depth = 0;depth < 6; depth += 1) {
|
|
24579
24970
|
const candidate = join39(dir, SETUP_SKILL_RELATIVE_PATH);
|
|
24580
|
-
if (
|
|
24971
|
+
if (existsSync30(candidate))
|
|
24581
24972
|
return candidate;
|
|
24582
24973
|
const parent = dirname26(dir);
|
|
24583
24974
|
if (parent === dir)
|
|
@@ -24600,7 +24991,7 @@ function loadSetupSkill(skillPath) {
|
|
|
24600
24991
|
return text.replace(/^---\r?\n[\s\S]*?\r?\n---(?:\r?\n)+/u, "");
|
|
24601
24992
|
}
|
|
24602
24993
|
function snapshotConfigFile(configPath) {
|
|
24603
|
-
return
|
|
24994
|
+
return existsSync30(configPath) ? readFileSync27(configPath, "utf8") : undefined;
|
|
24604
24995
|
}
|
|
24605
24996
|
async function rollbackIfBroken(options, before) {
|
|
24606
24997
|
if (options.validate === undefined)
|
|
@@ -24668,6 +25059,10 @@ function buildSystemPrompt(config) {
|
|
|
24668
25059
|
const modelId = config.modelId ?? "the configured model";
|
|
24669
25060
|
const toolLines = [
|
|
24670
25061
|
toolLine(config, "bash", "read and inspect configured document collections with ls, find, grep, cat, and similar tools, and — in local sessions — organize local files when the user asks (rename, move, copy, create folders, move to trash)"),
|
|
25062
|
+
...config.remoteSession ? [] : [
|
|
25063
|
+
toolLine(config, "write", "create a new file in local sessions — use this to produce requested deliverables and artifacts in a file format aligned with the request and context without overwriting existing source files"),
|
|
25064
|
+
toolLine(config, "edit", "surgically update an existing deliverable or config file when the user asks (local sessions only)")
|
|
25065
|
+
],
|
|
24671
25066
|
toolLine(config, "jikji_find", "local discovery through Jikji answer packs"),
|
|
24672
25067
|
toolLine(config, "everything_search", "instant Windows file/folder name search (voidtools Everything) across the configured search folders"),
|
|
24673
25068
|
toolLine(config, "fsearch_search", "instant macOS/Linux file/folder name search (fsearch-cli/FSearch) across the configured search folders, with a slow filesystem-walk fallback"),
|
|
@@ -24684,6 +25079,12 @@ function buildSystemPrompt(config) {
|
|
|
24684
25079
|
...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`),
|
|
24685
25080
|
...config.toolNames.filter((name) => ![
|
|
24686
25081
|
"bash",
|
|
25082
|
+
"read",
|
|
25083
|
+
"write",
|
|
25084
|
+
"edit",
|
|
25085
|
+
"grep",
|
|
25086
|
+
"find",
|
|
25087
|
+
"ls",
|
|
24687
25088
|
"jikji_find",
|
|
24688
25089
|
"everything_search",
|
|
24689
25090
|
"fsearch_search",
|
|
@@ -24816,8 +25217,9 @@ Your answer is your final reply, written as plain text, never as a tool call. Ci
|
|
|
24816
25217
|
## Answer Guidelines
|
|
24817
25218
|
|
|
24818
25219
|
- **Complete vs. delta answer**: When no first answer reached the caller, give the complete core answer. When a first answer was already delivered, return only the delta against it — corrections and newly verified findings — and never repeat its unchanged content; if nothing changed, confirm the first answer in one short line.
|
|
24819
|
-
- **Bullet-point core answer**:
|
|
24820
|
-
- **Direct answer only**:
|
|
25220
|
+
- **Bullet-point core answer**: 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 or context is necessary, append it after the bullet points.
|
|
25221
|
+
- **Direct answer only**: Unless the user explicitly requests otherwise, the caller only needs the direct answer to their question. Never include specific file paths, datasource descriptions, or retrieval mechanics/principles in the answer (source paths are attached to each citation from the evidence ids you cite). Do not quote raw chunk text.
|
|
25222
|
+
- ${ANSWER_DELIVERABLE_RULE} ${ANSWER_DELIVERABLE_DELTA_RULE}
|
|
24821
25223
|
- ${ANSWER_CITATION_RULE}
|
|
24822
25224
|
- ${ANSWER_IMAGE_EMBED_RULE} ${ANSWER_IMAGE_DELTA_RULE}
|
|
24823
25225
|
- **No per-source negative reports**: Never report individual negative findings per source (e.g. "no information found in Slack" or "checked Drive but found nothing"). Simply omit unproductive sources from the answer and focus on what was found or provide a concise overall conclusion.
|
|
@@ -28390,12 +28792,11 @@ function eventAnswer(event) {
|
|
|
28390
28792
|
}
|
|
28391
28793
|
return "";
|
|
28392
28794
|
}
|
|
28393
|
-
var PERPLEXITY_ASK_URL = "https://www.perplexity.ai/rest/sse/perplexity_ask", API_VERSION2 = "2.18",
|
|
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;
|
|
28394
28796
|
var init_perplexity = __esm(() => {
|
|
28395
28797
|
init_query();
|
|
28396
28798
|
init_types4();
|
|
28397
28799
|
init_utils();
|
|
28398
|
-
DEFLECTION_PATTERN = /sign up|log in|create an account|verify you are human|are you a robot|가입|로그인|회원 ?가입/i;
|
|
28399
28800
|
MAX_ERROR_BYTES4 = 8 * 1024;
|
|
28400
28801
|
PERPLEXITY_QUERY_SYNTAX = {
|
|
28401
28802
|
phrases: true,
|
|
@@ -28509,8 +28910,8 @@ var init_perplexity = __esm(() => {
|
|
|
28509
28910
|
break;
|
|
28510
28911
|
}
|
|
28511
28912
|
const sources = [...sourcesByUrl.values()];
|
|
28512
|
-
if (sources.length === 0
|
|
28513
|
-
throw new SearchProviderError("perplexity",
|
|
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);
|
|
28514
28915
|
}
|
|
28515
28916
|
const numResults = params.numSearchResults ?? params.limit;
|
|
28516
28917
|
return {
|
|
@@ -28928,7 +29329,7 @@ var init_browser_headers = __esm(() => {
|
|
|
28928
29329
|
});
|
|
28929
29330
|
|
|
28930
29331
|
// src/web/search/browser-loader.ts
|
|
28931
|
-
import { existsSync as
|
|
29332
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
28932
29333
|
async function closeBounded(close) {
|
|
28933
29334
|
let timer;
|
|
28934
29335
|
try {
|
|
@@ -29007,7 +29408,7 @@ function ensureWebSearchBrowserLoader(options = {}) {
|
|
|
29007
29408
|
return true;
|
|
29008
29409
|
const exists = options.exists ?? ((path) => {
|
|
29009
29410
|
try {
|
|
29010
|
-
return
|
|
29411
|
+
return existsSync31(path);
|
|
29011
29412
|
} catch {
|
|
29012
29413
|
return false;
|
|
29013
29414
|
}
|
|
@@ -29112,6 +29513,8 @@ function normalizeText(value) {
|
|
|
29112
29513
|
function isChallengeResponse(page) {
|
|
29113
29514
|
if (/\/(?:errors|captcha)\//.test(page.url) || page.url.includes("/sp/captcha"))
|
|
29114
29515
|
return true;
|
|
29516
|
+
if (ANUBIS_CHALLENGE_PATTERN.test(page.html))
|
|
29517
|
+
return true;
|
|
29115
29518
|
return page.html.includes("component---src-pages-captcha") || page.html.includes("/sp/captcha");
|
|
29116
29519
|
}
|
|
29117
29520
|
function parseSearchFormInputs(html) {
|
|
@@ -29216,7 +29619,7 @@ async function callStartpageHtml(params) {
|
|
|
29216
29619
|
});
|
|
29217
29620
|
}
|
|
29218
29621
|
if (isChallengeResponse(page)) {
|
|
29219
|
-
throw new SearchProviderError("startpage", "Startpage blocked the request with a CAPTCHA
|
|
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);
|
|
29220
29623
|
}
|
|
29221
29624
|
if (page.status < 200 || page.status >= 300) {
|
|
29222
29625
|
const classified = classifyProviderHttpError("startpage", page.status, page.html);
|
|
@@ -29242,7 +29645,7 @@ async function searchStartpage(params) {
|
|
|
29242
29645
|
}
|
|
29243
29646
|
return { provider: "startpage", sources };
|
|
29244
29647
|
}
|
|
29245
|
-
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;
|
|
29246
29649
|
var init_startpage = __esm(() => {
|
|
29247
29650
|
init_entities();
|
|
29248
29651
|
init_browser_page();
|
|
@@ -29255,6 +29658,7 @@ var init_startpage = __esm(() => {
|
|
|
29255
29658
|
month: "m",
|
|
29256
29659
|
year: "y"
|
|
29257
29660
|
};
|
|
29661
|
+
ANUBIS_CHALLENGE_PATTERN = /<script\b[^>]*\bid\s*=\s*["']anubis_(?:challenge|version)["']/i;
|
|
29258
29662
|
StartpageProvider = class StartpageProvider extends SearchProvider {
|
|
29259
29663
|
id = "startpage";
|
|
29260
29664
|
label = "Startpage";
|
|
@@ -30472,8 +30876,8 @@ var init_web_search_tool = __esm(() => {
|
|
|
30472
30876
|
|
|
30473
30877
|
// src/agent/agent.ts
|
|
30474
30878
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
30475
|
-
import { existsSync as
|
|
30476
|
-
import { basename as basename14, dirname as dirname27, join as join41, resolve as
|
|
30879
|
+
import { existsSync as existsSync32, watch as fsWatch, mkdirSync as mkdirSync25, realpathSync as realpathSync5, statSync as statSync3, writeFileSync as writeFileSync22 } from "node:fs";
|
|
30880
|
+
import { basename as basename14, dirname as dirname27, join as join41, resolve as resolve17 } from "node:path";
|
|
30477
30881
|
import { clampThinkingLevel } from "@earendil-works/pi-ai/compat";
|
|
30478
30882
|
import { getAgentDir as getAgentDir4 } from "@earendil-works/pi-coding-agent";
|
|
30479
30883
|
function positiveLimit(value, fallback, path) {
|
|
@@ -30603,6 +31007,9 @@ class AutoRAGAgent {
|
|
|
30603
31007
|
piSessionDir;
|
|
30604
31008
|
persistPiSessions;
|
|
30605
31009
|
updateNotice;
|
|
31010
|
+
piExtensionPaths;
|
|
31011
|
+
piExtensionFlags;
|
|
31012
|
+
telegram;
|
|
30606
31013
|
boundPiRuntime;
|
|
30607
31014
|
memoryEmbedder;
|
|
30608
31015
|
memoryVectorPath;
|
|
@@ -30628,12 +31035,15 @@ class AutoRAGAgent {
|
|
|
30628
31035
|
this.piSessionDir = options.piSessionDir;
|
|
30629
31036
|
this.persistPiSessions = options.persistPiSessions ?? true;
|
|
30630
31037
|
this.updateNotice = options.updateNotice;
|
|
31038
|
+
this.piExtensionPaths = options.piExtensionPaths;
|
|
31039
|
+
this.piExtensionFlags = options.piExtensionFlags;
|
|
31040
|
+
this.telegram = options.telegram === true;
|
|
30631
31041
|
this.datasourceSkills = options.datasourceSkills ?? [];
|
|
30632
31042
|
this.datasourceRetry = options.datasourceRetry ?? {};
|
|
30633
31043
|
this.datasourceVirtualScopePrefixes = this.datasourceSkills.map((skill) => normalizeVirtualPath2(`/${skill.describe().name}`));
|
|
30634
31044
|
this.startupDiagnostics = options.startupDiagnostics ?? [];
|
|
30635
31045
|
this.datasourceAgentSkills = this.buildDatasourceAgentSkills();
|
|
30636
|
-
this.configuredSearchPaths = options.searchPaths.map((searchPath) =>
|
|
31046
|
+
this.configuredSearchPaths = options.searchPaths.map((searchPath) => resolve17(searchPath));
|
|
30637
31047
|
this.languages = options.languages ?? DEFAULT_LANGUAGES;
|
|
30638
31048
|
this.searchPaths = options.searchPaths.map(pinSearchRoot);
|
|
30639
31049
|
this.jikjiRoots = outermostJikjiRoots(this.searchPaths);
|
|
@@ -30680,7 +31090,8 @@ class AutoRAGAgent {
|
|
|
30680
31090
|
const everythingClient = new EverythingClient({
|
|
30681
31091
|
...options.everything ?? {},
|
|
30682
31092
|
root: this.workspaceProjectRoot,
|
|
30683
|
-
folders: this.searchPaths
|
|
31093
|
+
folders: this.searchPaths,
|
|
31094
|
+
excludeFolders: this.excludePaths
|
|
30684
31095
|
});
|
|
30685
31096
|
if (everythingClient.isSupported())
|
|
30686
31097
|
this.everythingClient = everythingClient;
|
|
@@ -30786,8 +31197,9 @@ class AutoRAGAgent {
|
|
|
30786
31197
|
return true;
|
|
30787
31198
|
});
|
|
30788
31199
|
this.tools = tools;
|
|
31200
|
+
const builtinToolNames = this.remoteSession ? PI_BUILTIN_TOOL_NAMES.filter((name) => name !== "write" && name !== "edit") : PI_BUILTIN_TOOL_NAMES;
|
|
30789
31201
|
const toolNames = [
|
|
30790
|
-
...
|
|
31202
|
+
...builtinToolNames,
|
|
30791
31203
|
...tools.map((tool) => tool.name),
|
|
30792
31204
|
...this.jevExtension !== undefined ? [JEV_TOOL_NAME] : []
|
|
30793
31205
|
];
|
|
@@ -30803,7 +31215,7 @@ class AutoRAGAgent {
|
|
|
30803
31215
|
this.innerAgent = {
|
|
30804
31216
|
state: {
|
|
30805
31217
|
systemPrompt,
|
|
30806
|
-
tools: [...
|
|
31218
|
+
tools: [...builtinToolNames.map((name) => ({ name })), ...tools],
|
|
30807
31219
|
messages: []
|
|
30808
31220
|
},
|
|
30809
31221
|
transformContext: (messages) => this.withMemoryContext(messages)
|
|
@@ -31097,6 +31509,7 @@ ${context.text}
|
|
|
31097
31509
|
}
|
|
31098
31510
|
async createPiInteractiveRuntime() {
|
|
31099
31511
|
const resolved = this.configuredModel === undefined ? undefined : await this.resolveSessionModel();
|
|
31512
|
+
const telegramBridge = this.telegram ? (await import("@llblab/pi-telegram")).default : undefined;
|
|
31100
31513
|
const runtime = await createAutoRAGPiInteractiveRuntime({
|
|
31101
31514
|
cwd: this.workspaceProjectRoot,
|
|
31102
31515
|
agentDir: this.piAgentDir,
|
|
@@ -31117,10 +31530,14 @@ ${context.text}
|
|
|
31117
31530
|
onKey: (key) => this.handleInteractiveKey(key),
|
|
31118
31531
|
extensionFactories: [
|
|
31119
31532
|
createEvidencePersistence(this.evidenceLedger),
|
|
31120
|
-
...this.jevExtension !== undefined ? [this.jevExtension] : []
|
|
31533
|
+
...this.jevExtension !== undefined ? [this.jevExtension] : [],
|
|
31534
|
+
...telegramBridge !== undefined ? [telegramBridge] : []
|
|
31121
31535
|
],
|
|
31122
31536
|
...this.jevExtension !== undefined ? { extensionToolNames: [JEV_TOOL_NAME] } : {},
|
|
31123
|
-
|
|
31537
|
+
remoteSession: this.remoteSession,
|
|
31538
|
+
...this.updateNotice === undefined ? {} : { updateNotice: this.updateNotice },
|
|
31539
|
+
...this.piExtensionPaths === undefined ? {} : { extensionPaths: this.piExtensionPaths },
|
|
31540
|
+
...this.piExtensionFlags === undefined ? {} : { extensionFlags: this.piExtensionFlags }
|
|
31124
31541
|
});
|
|
31125
31542
|
this.boundPiRuntime = runtime.runtime;
|
|
31126
31543
|
const dispose = runtime.dispose;
|
|
@@ -31574,7 +31991,7 @@ Warning: the edited config did not validate, so the previous config was restored
|
|
|
31574
31991
|
result.summary,
|
|
31575
31992
|
...result.evidence.map((evidence) => evidence.excerpt)
|
|
31576
31993
|
])
|
|
31577
|
-
], [this.workspaceProjectRoot, ...this.searchPaths].map((root) =>
|
|
31994
|
+
], [this.workspaceProjectRoot, ...this.searchPaths].map((root) => resolve17(root)));
|
|
31578
31995
|
if (!scan.ok)
|
|
31579
31996
|
throw new RemoteSessionRejectedError(scan.code);
|
|
31580
31997
|
}
|
|
@@ -32040,21 +32457,22 @@ ${result.content.replace(/\s+/gu, " ")}`).join(`
|
|
|
32040
32457
|
const scope = options.scope ? ` Restrict search to virtual path scope ${options.scope}.` : "";
|
|
32041
32458
|
return `Answer this original query immediately: ${query}${limit}${scope}
|
|
32042
32459
|
|
|
32043
|
-
|
|
32460
|
+
Baseline retrieval evidence (already gathered for you):
|
|
32044
32461
|
${baseline}
|
|
32045
32462
|
|
|
32046
|
-
|
|
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.
|
|
32047
32464
|
|
|
32048
|
-
|
|
32049
|
-
|
|
32050
|
-
|
|
32051
|
-
|
|
32052
|
-
|
|
32053
|
-
|
|
32054
|
-
|
|
32055
|
-
|
|
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.
|
|
32056
32474
|
|
|
32057
|
-
|
|
32475
|
+
Write the answer as your reply, then stop.`;
|
|
32058
32476
|
}
|
|
32059
32477
|
isConversationTurn() {
|
|
32060
32478
|
if (this.remoteSession || this.boundPiRuntime === undefined)
|
|
@@ -32064,7 +32482,7 @@ ${baseline}
|
|
|
32064
32482
|
buildDirectAnswerPrompt(query) {
|
|
32065
32483
|
return `Answer this query directly from your own general knowledge: ${query}
|
|
32066
32484
|
|
|
32067
|
-
|
|
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.`;
|
|
32068
32486
|
}
|
|
32069
32487
|
discoveryHint(sentence) {
|
|
32070
32488
|
const names = this.tools.map((tool) => tool.name).filter((name) => name === JIKJI_FIND_TOOL_NAME || name === EVERYTHING_SEARCH_TOOL_NAME);
|
|
@@ -32082,52 +32500,55 @@ ${baseline}
|
|
|
32082
32500
|
const scope = options.scope ? ` Restrict search to virtual path scope ${options.scope}.` : "";
|
|
32083
32501
|
const firstAnswer = formatFirstAnswerContext(fastAnswer, fastAnswerDelivered);
|
|
32084
32502
|
const answerRules = fastAnswerDelivered ? `Formatting and content rules for your final answer (DELTA ONLY):
|
|
32085
|
-
|
|
32086
|
-
|
|
32087
|
-
|
|
32088
|
-
|
|
32089
|
-
|
|
32090
|
-
|
|
32091
|
-
|
|
32092
|
-
|
|
32093
|
-
|
|
32094
|
-
` + `-
|
|
32095
|
-
|
|
32096
|
-
|
|
32097
|
-
|
|
32098
|
-
|
|
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):
|
|
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.
|
|
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.`;
|
|
32099
32519
|
return `Original query: ${query}${limit}${scope}
|
|
32100
32520
|
|
|
32101
|
-
|
|
32521
|
+
${firstAnswer}
|
|
32102
32522
|
|
|
32103
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()}
|
|
32104
32524
|
|
|
32105
|
-
|
|
32525
|
+
${answerRules}
|
|
32106
32526
|
|
|
32107
|
-
|
|
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.`;
|
|
32108
32528
|
}
|
|
32109
32529
|
buildSearchPrompt(query, options, initialRetrievalContext) {
|
|
32110
32530
|
const limit = typeof options.topK === "number" ? ` Cite at most ${options.topK} sources.` : "";
|
|
32111
32531
|
const scope = options.scope ? ` Restrict search to virtual path scope ${options.scope}.` : "";
|
|
32112
32532
|
return `Find and curate information for this original query: ${query}${limit}${scope}
|
|
32113
32533
|
|
|
32114
|
-
|
|
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.
|
|
32115
32535
|
|
|
32116
|
-
|
|
32536
|
+
Baseline retrieval context:
|
|
32117
32537
|
${initialRetrievalContext ?? "Pending; continue only with a brief progress statement."}
|
|
32118
32538
|
|
|
32119
|
-
|
|
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.
|
|
32120
32540
|
|
|
32121
|
-
|
|
32122
|
-
|
|
32123
|
-
|
|
32124
|
-
|
|
32125
|
-
|
|
32126
|
-
|
|
32127
|
-
|
|
32128
|
-
|
|
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.
|
|
32129
32550
|
|
|
32130
|
-
|
|
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.`;
|
|
32131
32552
|
}
|
|
32132
32553
|
async refresh(force = false, opts) {
|
|
32133
32554
|
const signal = opts?.signal;
|
|
@@ -32363,7 +32784,7 @@ ${initialRetrievalContext ?? "Pending; continue only with a brief progress state
|
|
|
32363
32784
|
});
|
|
32364
32785
|
}
|
|
32365
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;
|
|
32366
|
-
const parsedMirrorReady = this.refreshState.lastOutcome === "success" || persistedProgress?.state === "success" ||
|
|
32787
|
+
const parsedMirrorReady = this.refreshState.lastOutcome === "success" || persistedProgress?.state === "success" || existsSync32(refreshReadinessPath(this.workspaceProjectRoot));
|
|
32367
32788
|
const effectiveProgress = this.refreshState.inFlight || persistedRunning ? persistedProgress : undefined;
|
|
32368
32789
|
return {
|
|
32369
32790
|
state,
|
|
@@ -32405,7 +32826,7 @@ ${initialRetrievalContext ?? "Pending; continue only with a brief progress state
|
|
|
32405
32826
|
}
|
|
32406
32827
|
startWatchRefresh(options = {}) {
|
|
32407
32828
|
this.refreshState = { ...this.refreshState, watchLimited: false, watchFailed: false };
|
|
32408
|
-
const dirs = this.searchPaths.map((searchPath) =>
|
|
32829
|
+
const dirs = this.searchPaths.map((searchPath) => resolve17(searchPath));
|
|
32409
32830
|
const watcherFactory = options.watcherFactory ?? this.defaultWatcherFactory();
|
|
32410
32831
|
return createWatchRefresh({
|
|
32411
32832
|
dirs,
|
|
@@ -32582,7 +33003,7 @@ ${initialRetrievalContext ?? "Pending; continue only with a brief progress state
|
|
|
32582
33003
|
const evidencePack = [];
|
|
32583
33004
|
const allPaths = [];
|
|
32584
33005
|
for (const entry of entries) {
|
|
32585
|
-
const originRoot = sourceRoots.find((sr) => sr.rootPath ===
|
|
33006
|
+
const originRoot = sourceRoots.find((sr) => sr.rootPath === resolve17(entry.root));
|
|
32586
33007
|
if (originRoot === undefined)
|
|
32587
33008
|
continue;
|
|
32588
33009
|
const originRoots = [originRoot];
|
|
@@ -32823,7 +33244,7 @@ function buildSteerMessage(text) {
|
|
|
32823
33244
|
|
|
32824
33245
|
${text}
|
|
32825
33246
|
|
|
32826
|
-
|
|
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.`;
|
|
32827
33248
|
}
|
|
32828
33249
|
function frameSteers(messages, steerTexts) {
|
|
32829
33250
|
if (steerTexts.size === 0)
|
|
@@ -32843,12 +33264,12 @@ function formatSteerContext(steers) {
|
|
|
32843
33264
|
return `While the search ran, the user corrected it:
|
|
32844
33265
|
${steers.map((steer) => `- ${steer}`).join(`
|
|
32845
33266
|
`)}
|
|
32846
|
-
|
|
33267
|
+
The first answer predates these corrections; follow them over it.
|
|
32847
33268
|
|
|
32848
33269
|
`;
|
|
32849
33270
|
}
|
|
32850
33271
|
function buildSteerContinuationPrompt(steers) {
|
|
32851
|
-
return `${formatSteerContext(steers)}Apply the latest correction: search or verify further if it needs that,
|
|
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.`;
|
|
32852
33273
|
}
|
|
32853
33274
|
function formatFirstAnswerContext(fastAnswer, delivered) {
|
|
32854
33275
|
if (fastAnswer === undefined) {
|
|
@@ -32971,11 +33392,20 @@ function fsearchRefreshDiagnostics(fsearch) {
|
|
|
32971
33392
|
function minSyncRefreshDiagnostics(minsync) {
|
|
32972
33393
|
if (!minsync)
|
|
32973
33394
|
return [];
|
|
33395
|
+
const diagnostics = [];
|
|
32974
33396
|
if (minsync.diagnostic)
|
|
32975
|
-
|
|
32976
|
-
if (!minsync.ok && minsync.reason)
|
|
32977
|
-
|
|
32978
|
-
|
|
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;
|
|
32979
33409
|
}
|
|
32980
33410
|
function toMinSyncDiagnostic(diag, ok) {
|
|
32981
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";
|
|
@@ -32995,7 +33425,7 @@ function toMinSyncReasonDiagnostic(reason) {
|
|
|
32995
33425
|
};
|
|
32996
33426
|
}
|
|
32997
33427
|
function pinExcludedPath(path) {
|
|
32998
|
-
const resolvedPath =
|
|
33428
|
+
const resolvedPath = resolve17(path);
|
|
32999
33429
|
try {
|
|
33000
33430
|
return join41(realpathSync5(dirname27(resolvedPath)), basename14(resolvedPath));
|
|
33001
33431
|
} catch {
|
|
@@ -33003,7 +33433,7 @@ function pinExcludedPath(path) {
|
|
|
33003
33433
|
}
|
|
33004
33434
|
}
|
|
33005
33435
|
function pinSearchRoot(searchPath) {
|
|
33006
|
-
const resolvedPath =
|
|
33436
|
+
const resolvedPath = resolve17(searchPath);
|
|
33007
33437
|
let canonicalPath;
|
|
33008
33438
|
try {
|
|
33009
33439
|
canonicalPath = realpathSync5(resolvedPath);
|
|
@@ -33455,8 +33885,8 @@ var init_memory2 = __esm(() => {
|
|
|
33455
33885
|
});
|
|
33456
33886
|
|
|
33457
33887
|
// src/cli/commands/index.ts
|
|
33458
|
-
import { existsSync as
|
|
33459
|
-
import { join as join42, resolve as
|
|
33888
|
+
import { existsSync as existsSync33, lstatSync as lstatSync3, rmSync as rmSync11 } from "node:fs";
|
|
33889
|
+
import { join as join42, resolve as resolve18, sep as sep8 } from "node:path";
|
|
33460
33890
|
async function runIndex2(ctx) {
|
|
33461
33891
|
const sub = ctx.positionals[0];
|
|
33462
33892
|
if (sub !== "reset" && sub !== "rebuild") {
|
|
@@ -33472,10 +33902,10 @@ async function runIndex2(ctx) {
|
|
|
33472
33902
|
}
|
|
33473
33903
|
const methods = parseMethodFlag(ctx.flags.method);
|
|
33474
33904
|
const { targetNames, targetSubdirs, refreshMethods } = resolveResetScope(methods);
|
|
33475
|
-
const autoragDir =
|
|
33476
|
-
const targets = targetSubdirs.map((subdir) =>
|
|
33905
|
+
const autoragDir = resolve18(config.workspacePath, ".autorag");
|
|
33906
|
+
const targets = targetSubdirs.map((subdir) => resolve18(config.workspacePath, subdir));
|
|
33477
33907
|
for (const dir of [autoragDir, ...targets]) {
|
|
33478
|
-
if (
|
|
33908
|
+
if (existsSync33(dir) && lstatSync3(dir).isSymbolicLink()) {
|
|
33479
33909
|
ctx.stderr(renderError(new Error("Refusing to reset: an index path is a symlink, not a real directory."), {
|
|
33480
33910
|
json: ctx.json
|
|
33481
33911
|
}));
|
|
@@ -33835,12 +34265,12 @@ var init_health = __esm(() => {
|
|
|
33835
34265
|
});
|
|
33836
34266
|
|
|
33837
34267
|
// src/cli/commands/duplicates.ts
|
|
33838
|
-
import { resolve as
|
|
34268
|
+
import { resolve as resolve19 } from "node:path";
|
|
33839
34269
|
async function runDuplicates(ctx, scanner = (path) => scanWithDupey(path)) {
|
|
33840
34270
|
try {
|
|
33841
34271
|
const config = resolveConfig({ flags: ctx.flags, cwd: ctx.cwd });
|
|
33842
34272
|
const requested = ctx.positionals[0];
|
|
33843
|
-
const roots = requested ? [
|
|
34273
|
+
const roots = requested ? [resolve19(ctx.cwd, requested)] : config.searchPaths.map((path) => resolve19(path));
|
|
33844
34274
|
const scans = await Promise.all(roots.map((root) => scanner(root)));
|
|
33845
34275
|
const exactGroups = scans.flatMap(exactGroupsFromScan);
|
|
33846
34276
|
const extractionErrors = scans.flatMap((scan) => scan.errors);
|
|
@@ -33965,6 +34395,40 @@ var init_update_check = __esm(() => {
|
|
|
33965
34395
|
SEMVER_PATTERN = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
33966
34396
|
});
|
|
33967
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
|
+
|
|
33968
34432
|
// src/cli/commands/tui.ts
|
|
33969
34433
|
import { InteractiveMode } from "@earendil-works/pi-coding-agent";
|
|
33970
34434
|
function parseThinkingFlags(flags) {
|
|
@@ -33985,6 +34449,8 @@ async function createTuiAgent(ctx) {
|
|
|
33985
34449
|
const config = resolveConfig({ flags: ctx.flags, cwd: ctx.cwd });
|
|
33986
34450
|
const options = { ...buildAgentOptions(config) };
|
|
33987
34451
|
options.updateNotice = async () => renderAutoRAGUpdateNotice(await checkAutoRAGUpdate({ currentVersion: readPackageVersion() }));
|
|
34452
|
+
if (ctx.flags.telegram === true)
|
|
34453
|
+
options.telegram = true;
|
|
33988
34454
|
const thinking = parseThinkingFlags(ctx.flags);
|
|
33989
34455
|
if (thinking !== undefined)
|
|
33990
34456
|
options.thinking = thinking;
|
|
@@ -33996,6 +34462,11 @@ async function createTuiAgent(ctx) {
|
|
|
33996
34462
|
if (resolved.providerApiKeys !== undefined)
|
|
33997
34463
|
options.providerApiKeys = resolved.providerApiKeys;
|
|
33998
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
|
+
}
|
|
33999
34470
|
const decompositionModel = await resolveQueryDecompositionModel(config);
|
|
34000
34471
|
if (decompositionModel !== undefined)
|
|
34001
34472
|
options.queryDecomposition = decompositionModel;
|
|
@@ -34036,11 +34507,12 @@ var init_tui = __esm(() => {
|
|
|
34036
34507
|
init_config4();
|
|
34037
34508
|
init_update_check();
|
|
34038
34509
|
init_version();
|
|
34510
|
+
init_whatsapp_extension();
|
|
34039
34511
|
THINKING_LEVELS2 = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
34040
34512
|
});
|
|
34041
34513
|
|
|
34042
34514
|
// src/cli/commands/serve.ts
|
|
34043
|
-
import { existsSync as
|
|
34515
|
+
import { existsSync as existsSync34 } from "node:fs";
|
|
34044
34516
|
import { completeSimple as completeSimple3 } from "@earendil-works/pi-ai/compat";
|
|
34045
34517
|
function classifierText(message) {
|
|
34046
34518
|
return message.content.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
@@ -34058,7 +34530,7 @@ function createInjectionClassifierModel(resolved) {
|
|
|
34058
34530
|
async function runServe(ctx, deps = {}) {
|
|
34059
34531
|
const flags = ctx.flags;
|
|
34060
34532
|
const resolved = resolveConfigPath({ flags, cwd: ctx.cwd });
|
|
34061
|
-
if (!
|
|
34533
|
+
if (!existsSync34(resolved.configPath)) {
|
|
34062
34534
|
ctx.stderr(renderError(new ConfigError(`Config file not found: ${resolved.configPath}. Run autorag init first.`), {
|
|
34063
34535
|
json: ctx.json
|
|
34064
34536
|
}));
|
|
@@ -34439,7 +34911,7 @@ var init_p2p = __esm(() => {
|
|
|
34439
34911
|
});
|
|
34440
34912
|
|
|
34441
34913
|
// src/cli/commands/p2p-policy.ts
|
|
34442
|
-
import { existsSync as
|
|
34914
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync26, readFileSync as readFileSync28, writeFileSync as writeFileSync23 } from "node:fs";
|
|
34443
34915
|
import { dirname as dirname28, join as join43 } from "node:path";
|
|
34444
34916
|
import { parse as parseToml3, stringify as stringifyToml } from "smol-toml";
|
|
34445
34917
|
function workspacePolicyPath(cwd) {
|
|
@@ -34470,7 +34942,7 @@ function normalizeEntry(value, key) {
|
|
|
34470
34942
|
throw new Error(`Invalid policy entry for ${key}: ${JSON.stringify(value)}`);
|
|
34471
34943
|
}
|
|
34472
34944
|
function readPolicyToml(policyPath) {
|
|
34473
|
-
if (!
|
|
34945
|
+
if (!existsSync35(policyPath))
|
|
34474
34946
|
return {};
|
|
34475
34947
|
const parsed = parseToml3(readFileSync28(policyPath, "utf8"));
|
|
34476
34948
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
@@ -34505,7 +34977,7 @@ function writePolicyToml(policyPath, entries) {
|
|
|
34505
34977
|
function getGlobalPolicy() {
|
|
34506
34978
|
const home = resolveAutoRAGHome();
|
|
34507
34979
|
const configPath = join43(home, "config.json");
|
|
34508
|
-
if (!
|
|
34980
|
+
if (!existsSync35(configPath))
|
|
34509
34981
|
return {};
|
|
34510
34982
|
try {
|
|
34511
34983
|
const raw = readFileSync28(configPath, "utf8");
|
|
@@ -34589,9 +35061,9 @@ var init_p2p_policy = __esm(() => {
|
|
|
34589
35061
|
|
|
34590
35062
|
// src/cli/commands/models.ts
|
|
34591
35063
|
import { join as join44 } from "node:path";
|
|
34592
|
-
import { getAgentDir as
|
|
35064
|
+
import { getAgentDir as getAgentDir6, ModelRuntime as ModelRuntime3 } from "@earendil-works/pi-coding-agent";
|
|
34593
35065
|
async function listModelCatalog(options) {
|
|
34594
|
-
const agentDir = options.agentDir ??
|
|
35066
|
+
const agentDir = options.agentDir ?? getAgentDir6();
|
|
34595
35067
|
const runtime = await ModelRuntime3.create({
|
|
34596
35068
|
authPath: join44(agentDir, "auth.json"),
|
|
34597
35069
|
modelsPath: join44(agentDir, "models.json"),
|
|
@@ -35136,7 +35608,7 @@ var init_results = __esm(() => {
|
|
|
35136
35608
|
confidence: Type18.Optional(Type18.Number({ description: "Evidence confidence, 0..1", minimum: 0, maximum: 1 }))
|
|
35137
35609
|
});
|
|
35138
35610
|
answerSchema = Type18.String({
|
|
35139
|
-
description: `Answer for the caller.
|
|
35611
|
+
description: `Answer for the caller. Unless the user explicitly requests otherwise, provide at most 5 bullet points (plus optional explanation) and answer directly; reference results by bracketed number (e.g. [1], [2]) without file paths or raw chunk text, except the image-embed exception below. ${ANSWER_DELIVERABLE_RULE} ${ANSWER_CITATION_RULE} ${ANSWER_IMAGE_EMBED_RULE}`
|
|
35140
35612
|
});
|
|
35141
35613
|
warningsSchema = Type18.Optional(Type18.Array(Type18.String(), { description: "Optional warnings about this result set" }));
|
|
35142
35614
|
curatedResultFields = {
|
|
@@ -35313,7 +35785,8 @@ Usage: autorag lite <subcommand> [args] [flags]
|
|
|
35313
35785
|
Subcommands:
|
|
35314
35786
|
autorag lite init
|
|
35315
35787
|
Write the configured model-free lifecycle config
|
|
35316
|
-
(--search-paths PATHS --workspace DIR --memory-path FILE --force)
|
|
35788
|
+
(--search-paths PATHS --exclude PATH --workspace DIR --memory-path FILE --force)
|
|
35789
|
+
--exclude may be repeated; relative paths use the current directory
|
|
35317
35790
|
autorag lite refresh Run an incremental index refresh (no model required)
|
|
35318
35791
|
(--full --force --method minsync,parsed,datasources,jikji,everything,fsearch,all)
|
|
35319
35792
|
autorag lite watch Watch configured roots (or --once for one refresh tick)
|
|
@@ -35389,6 +35862,7 @@ var COMMAND_USAGE = {
|
|
|
35389
35862
|
"",
|
|
35390
35863
|
"Flags:",
|
|
35391
35864
|
" --search-paths <csv> Folders to index/search",
|
|
35865
|
+
" --exclude <path> Exclude a file or folder (repeatable; relative to the current directory)",
|
|
35392
35866
|
" --languages <csv> OCR/parser languages",
|
|
35393
35867
|
" --workspace <dir> Workspace that owns .autorag state",
|
|
35394
35868
|
" --memory-path <file> Retrieval memory file",
|
|
@@ -35549,7 +36023,14 @@ var COMMAND_USAGE = {
|
|
|
35549
36023
|
"",
|
|
35550
36024
|
"Flags:",
|
|
35551
36025
|
" --fast-thinking <lvl> Thinking level for the fast first answer",
|
|
35552
|
-
" --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"
|
|
35553
36034
|
]),
|
|
35554
36035
|
serve: usage([
|
|
35555
36036
|
"autorag serve - P2P peer query server over SimpleX",
|
|
@@ -35652,11 +36133,14 @@ var BOOLEAN_FLAGS = new Set([
|
|
|
35652
36133
|
"retry-failed",
|
|
35653
36134
|
"strict",
|
|
35654
36135
|
"refresh",
|
|
35655
|
-
"available"
|
|
36136
|
+
"available",
|
|
36137
|
+
"whatsapp",
|
|
36138
|
+
"telegram"
|
|
35656
36139
|
]);
|
|
35657
36140
|
var VALUE_FLAGS = new Set([
|
|
35658
36141
|
"config",
|
|
35659
36142
|
"search-paths",
|
|
36143
|
+
"exclude",
|
|
35660
36144
|
"languages",
|
|
35661
36145
|
"workspace",
|
|
35662
36146
|
"memory-path",
|
|
@@ -35694,7 +36178,8 @@ var VALUE_FLAGS = new Set([
|
|
|
35694
36178
|
"format",
|
|
35695
36179
|
"provider",
|
|
35696
36180
|
"fast-thinking",
|
|
35697
|
-
"final-thinking"
|
|
36181
|
+
"final-thinking",
|
|
36182
|
+
"whatsapp-group"
|
|
35698
36183
|
]);
|
|
35699
36184
|
var COMMANDS = [
|
|
35700
36185
|
"init",
|
|
@@ -35742,6 +36227,7 @@ Commands:
|
|
|
35742
36227
|
lite report <query> Persist a structured report (--input FILE)
|
|
35743
36228
|
duplicates [DIR] Scan exact/near duplicate document families; never deletes files
|
|
35744
36229
|
tui Open an interactive Pi-powered librarian terminal UI
|
|
36230
|
+
(--whatsapp [--whatsapp-group JID]: also take questions from WhatsApp)
|
|
35745
36231
|
serve Start the P2P peer query server over SimpleX
|
|
35746
36232
|
(--port N --force)
|
|
35747
36233
|
p2p SimpleX peer trust management
|
|
@@ -35786,6 +36272,7 @@ Run: autorag <command> --help for command-specific usage.
|
|
|
35786
36272
|
function parseArgs(argv) {
|
|
35787
36273
|
const positionals = [];
|
|
35788
36274
|
const flags = {};
|
|
36275
|
+
const repeatedFlags = {};
|
|
35789
36276
|
for (let i = 0;i < argv.length; i++) {
|
|
35790
36277
|
const token = argv[i];
|
|
35791
36278
|
if (token === "-h") {
|
|
@@ -35806,22 +36293,27 @@ function parseArgs(argv) {
|
|
|
35806
36293
|
if (!BOOLEAN_FLAGS.has(key) && !VALUE_FLAGS.has(key)) {
|
|
35807
36294
|
return { error: `Unknown flag: --${key}` };
|
|
35808
36295
|
}
|
|
35809
|
-
if (eq
|
|
35810
|
-
flags[key] = body.slice(eq + 1);
|
|
35811
|
-
continue;
|
|
35812
|
-
}
|
|
35813
|
-
if (BOOLEAN_FLAGS.has(key)) {
|
|
36296
|
+
if (eq === -1 && BOOLEAN_FLAGS.has(key)) {
|
|
35814
36297
|
flags[key] = true;
|
|
35815
36298
|
continue;
|
|
35816
36299
|
}
|
|
35817
|
-
const
|
|
35818
|
-
if (
|
|
36300
|
+
const value = eq === -1 ? argv[++i] : body.slice(eq + 1);
|
|
36301
|
+
if (value === undefined || eq === -1 && value.startsWith("--")) {
|
|
35819
36302
|
return { error: `Flag --${key} requires a value` };
|
|
35820
36303
|
}
|
|
35821
|
-
|
|
35822
|
-
|
|
36304
|
+
if (key === "exclude") {
|
|
36305
|
+
if (value.trim().length === 0)
|
|
36306
|
+
return { error: "Flag --exclude requires a non-empty path" };
|
|
36307
|
+
repeatedFlags[key] ??= [];
|
|
36308
|
+
repeatedFlags[key].push(value);
|
|
36309
|
+
}
|
|
36310
|
+
flags[key] = value;
|
|
35823
36311
|
}
|
|
35824
|
-
return {
|
|
36312
|
+
return {
|
|
36313
|
+
positionals,
|
|
36314
|
+
flags,
|
|
36315
|
+
...Object.keys(repeatedFlags).length > 0 ? { repeatedFlags } : {}
|
|
36316
|
+
};
|
|
35825
36317
|
}
|
|
35826
36318
|
function promptYesNo(question) {
|
|
35827
36319
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -35943,6 +36435,7 @@ ${USAGE2}`);
|
|
|
35943
36435
|
const ctx = {
|
|
35944
36436
|
positionals: positionals.slice(1),
|
|
35945
36437
|
flags,
|
|
36438
|
+
...parsed.repeatedFlags === undefined ? {} : { repeatedFlags: parsed.repeatedFlags },
|
|
35946
36439
|
json,
|
|
35947
36440
|
debug,
|
|
35948
36441
|
cwd: process.cwd(),
|