@autorag/librarian 2.13.2 → 2.13.4
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 +0 -1
- package/dist/agent/context-token-guard.d.ts +11 -6
- package/dist/agent/token-count.d.ts +4 -0
- package/dist/cli/index.js +287 -632
- package/dist/cloud/context-overflow.d.ts +29 -0
- package/dist/core.js +203 -547
- package/dist/datasource/connector-skill.d.ts +1 -1
- package/dist/datasource/connector.d.ts +1 -1
- package/dist/datasource/index.d.ts +0 -1
- package/dist/index.js +205 -552
- package/dist/mcp/index.js +205 -549
- package/dist/profiles.js +288 -632
- package/package.json +2 -1
- package/skills/autorag-doctor/SKILL.md +1 -2
- package/skills/autorag-lite-setup/SKILL.md +1 -1
- package/skills/autorag-setup/SKILL.md +3 -4
- package/dist/datasource/skills/spotlight/connector.d.ts +0 -53
- package/dist/datasource/skills/spotlight/index.d.ts +0 -2
- package/dist/datasource/skills/spotlight/skill.d.ts +0 -33
package/dist/mcp/index.js
CHANGED
|
@@ -15099,6 +15099,51 @@ var init_permission_gate = __esm(() => {
|
|
|
15099
15099
|
};
|
|
15100
15100
|
});
|
|
15101
15101
|
|
|
15102
|
+
// src/cloud/context-overflow.ts
|
|
15103
|
+
import {
|
|
15104
|
+
createAssistantMessageEventStream,
|
|
15105
|
+
isContextOverflow
|
|
15106
|
+
} from "@earendil-works/pi-ai";
|
|
15107
|
+
import { getApiProvider } from "@earendil-works/pi-ai/compat";
|
|
15108
|
+
function recordRequestEstimate(model, estimatedTokens) {
|
|
15109
|
+
lastEstimates.set(modelKey(model), estimatedTokens);
|
|
15110
|
+
}
|
|
15111
|
+
function noteContextOverflow(model) {
|
|
15112
|
+
const key = modelKey(model);
|
|
15113
|
+
const failed = lastEstimates.get(key);
|
|
15114
|
+
if (failed === undefined)
|
|
15115
|
+
return;
|
|
15116
|
+
ceilings.set(key, Math.min(Math.floor(failed / 2), ceilings.get(key) ?? Number.POSITIVE_INFINITY));
|
|
15117
|
+
}
|
|
15118
|
+
function contextOverflowCeiling(model) {
|
|
15119
|
+
return ceilings.get(modelKey(model));
|
|
15120
|
+
}
|
|
15121
|
+
function streamDazziSimple(model, context, options) {
|
|
15122
|
+
const api = getApiProvider(model.api);
|
|
15123
|
+
if (api === undefined)
|
|
15124
|
+
throw new Error(`No API provider registered for api: ${model.api}`);
|
|
15125
|
+
const inner = api.streamSimple(model, context, options);
|
|
15126
|
+
const stream = createAssistantMessageEventStream();
|
|
15127
|
+
(async () => {
|
|
15128
|
+
try {
|
|
15129
|
+
for await (const event of inner) {
|
|
15130
|
+
if (event.type === "error" && event.error.stopReason === "error" && isContextOverflow(event.error)) {
|
|
15131
|
+
noteContextOverflow(model);
|
|
15132
|
+
}
|
|
15133
|
+
stream.push(event);
|
|
15134
|
+
}
|
|
15135
|
+
} finally {
|
|
15136
|
+
stream.end();
|
|
15137
|
+
}
|
|
15138
|
+
})();
|
|
15139
|
+
return stream;
|
|
15140
|
+
}
|
|
15141
|
+
var lastEstimates, ceilings, modelKey = (model) => `${model.provider}/${model.id}`;
|
|
15142
|
+
var init_context_overflow = __esm(() => {
|
|
15143
|
+
lastEstimates = new Map;
|
|
15144
|
+
ceilings = new Map;
|
|
15145
|
+
});
|
|
15146
|
+
|
|
15102
15147
|
// src/cloud/authorize.ts
|
|
15103
15148
|
function buildAuthorizeUrl(params) {
|
|
15104
15149
|
const url = new URL(`${normalizeBaseUrl(params.baseUrl)}${AUTHORIZE_PATH}`);
|
|
@@ -15454,6 +15499,7 @@ function createDazziProviderConfig(env = process.env) {
|
|
|
15454
15499
|
name: PROVIDER_NAME,
|
|
15455
15500
|
baseUrl: endpoints.apiBaseUrl,
|
|
15456
15501
|
api: "openai-responses",
|
|
15502
|
+
streamSimple: streamDazziSimple,
|
|
15457
15503
|
oauth: {
|
|
15458
15504
|
name: PROVIDER_NAME,
|
|
15459
15505
|
login: (callbacks) => loginDazzi(callbacks, { baseUrl }),
|
|
@@ -15469,6 +15515,7 @@ async function registerDazziProvider(runtime, env = process.env) {
|
|
|
15469
15515
|
}
|
|
15470
15516
|
var init_provider2 = __esm(() => {
|
|
15471
15517
|
init_config();
|
|
15518
|
+
init_context_overflow();
|
|
15472
15519
|
init_models();
|
|
15473
15520
|
init_oauth_login();
|
|
15474
15521
|
});
|
|
@@ -24288,413 +24335,6 @@ var init_slack = __esm(() => {
|
|
|
24288
24335
|
};
|
|
24289
24336
|
});
|
|
24290
24337
|
|
|
24291
|
-
// src/datasource/skills/spotlight/connector.ts
|
|
24292
|
-
import { spawn as spawn19 } from "node:child_process";
|
|
24293
|
-
import { createHash as createHash19 } from "node:crypto";
|
|
24294
|
-
import { readFile as fsReadFile, stat as stat5 } from "node:fs/promises";
|
|
24295
|
-
import { basename as basename11, extname as extname7 } from "node:path";
|
|
24296
|
-
|
|
24297
|
-
class SpotlightConnector {
|
|
24298
|
-
options;
|
|
24299
|
-
run;
|
|
24300
|
-
readFile;
|
|
24301
|
-
platform;
|
|
24302
|
-
constructor(options = {}) {
|
|
24303
|
-
this.options = options;
|
|
24304
|
-
this.run = options.run ?? spawnRunner;
|
|
24305
|
-
this.readFile = options.readFile ?? ((path) => fsReadFile(path, "utf8"));
|
|
24306
|
-
this.platform = options.platform ?? process.platform;
|
|
24307
|
-
}
|
|
24308
|
-
async probe() {
|
|
24309
|
-
const queries = (this.options.queries ?? []).map((query) => query.trim()).filter((query) => query.length > 0);
|
|
24310
|
-
const failure = await this.readinessFailure(queries);
|
|
24311
|
-
return failure ?? { ok: true, documents: [] };
|
|
24312
|
-
}
|
|
24313
|
-
async search(query, limit = DEFAULT_MAX_DOCUMENTS4, acceptDocument) {
|
|
24314
|
-
const trimmed = query.trim();
|
|
24315
|
-
if (trimmed.length === 0 || limit <= 0)
|
|
24316
|
-
return { ok: true, documents: [] };
|
|
24317
|
-
const queries = (this.options.queries ?? []).map((query) => query.trim()).filter((query) => query.length > 0);
|
|
24318
|
-
const failure = await this.readinessFailure(queries);
|
|
24319
|
-
if (failure !== undefined)
|
|
24320
|
-
return failure;
|
|
24321
|
-
const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS11;
|
|
24322
|
-
const maxPerQuery = this.options.maxResultsPerQuery ?? DEFAULT_MAX_RESULTS_PER_QUERY;
|
|
24323
|
-
const maxDocuments = Math.min(this.options.maxDocuments ?? DEFAULT_MAX_DOCUMENTS4, limit);
|
|
24324
|
-
const seen = new Set;
|
|
24325
|
-
const paths = [];
|
|
24326
|
-
const textQuery = trimmed.split(/\s+/u).map((term) => `"${term.replace(/\\/gu, "\\\\").replace(/"/gu, "\\\"")}"`).join(" AND ");
|
|
24327
|
-
let mdfindMissing = false;
|
|
24328
|
-
let failedQueries = 0;
|
|
24329
|
-
const failures = [];
|
|
24330
|
-
for (const filter of queries) {
|
|
24331
|
-
const query = `(${filter}) AND (${textQuery})`;
|
|
24332
|
-
const args = this.options.onlyIn !== undefined ? ["-onlyin", this.options.onlyIn, query] : [query];
|
|
24333
|
-
const result = await this.run("mdfind", args, timeoutMs);
|
|
24334
|
-
if (!result.ok) {
|
|
24335
|
-
if (result.error === "ENOENT") {
|
|
24336
|
-
mdfindMissing = true;
|
|
24337
|
-
break;
|
|
24338
|
-
}
|
|
24339
|
-
failedQueries += 1;
|
|
24340
|
-
failures.push(result);
|
|
24341
|
-
continue;
|
|
24342
|
-
}
|
|
24343
|
-
let kept = 0;
|
|
24344
|
-
for (const line of result.stdout.split(`
|
|
24345
|
-
`)) {
|
|
24346
|
-
const path = line.trim();
|
|
24347
|
-
if (path.length === 0 || seen.has(path))
|
|
24348
|
-
continue;
|
|
24349
|
-
if (kept >= maxPerQuery)
|
|
24350
|
-
break;
|
|
24351
|
-
seen.add(path);
|
|
24352
|
-
paths.push(path);
|
|
24353
|
-
kept += 1;
|
|
24354
|
-
}
|
|
24355
|
-
}
|
|
24356
|
-
if (mdfindMissing) {
|
|
24357
|
-
return { ok: false, reason: "unavailable", message: "mdfind binary not found; Spotlight requires macOS" };
|
|
24358
|
-
}
|
|
24359
|
-
if (failedQueries === queries.length) {
|
|
24360
|
-
return {
|
|
24361
|
-
ok: false,
|
|
24362
|
-
reason: "unavailable",
|
|
24363
|
-
message: "every configured mdfind query failed",
|
|
24364
|
-
stderr: failures[0]?.stderr,
|
|
24365
|
-
stdout: failures[0]?.stdout,
|
|
24366
|
-
code: failures[0]?.code
|
|
24367
|
-
};
|
|
24368
|
-
}
|
|
24369
|
-
const documents = [];
|
|
24370
|
-
let permissionDenied = 0;
|
|
24371
|
-
let readFailures = 0;
|
|
24372
|
-
const maxBytes = this.options.maxBytesPerFile ?? DEFAULT_MAX_BYTES_PER_FILE2;
|
|
24373
|
-
const maxContentChars = this.options.maxContentChars ?? MAX_CONTENT_CHARS4;
|
|
24374
|
-
for (const path of paths) {
|
|
24375
|
-
if (documents.length >= maxDocuments)
|
|
24376
|
-
break;
|
|
24377
|
-
const docId = createHash19("sha256").update(path).digest("hex").slice(0, 16);
|
|
24378
|
-
if (acceptDocument !== undefined && !acceptDocument(docId))
|
|
24379
|
-
continue;
|
|
24380
|
-
try {
|
|
24381
|
-
const info = await stat5(path);
|
|
24382
|
-
if (!info.isFile() || info.size > maxBytes)
|
|
24383
|
-
continue;
|
|
24384
|
-
const text = await this.readFile(path);
|
|
24385
|
-
const content = text.trim().slice(0, maxContentChars);
|
|
24386
|
-
if (content.length === 0)
|
|
24387
|
-
continue;
|
|
24388
|
-
documents.push({
|
|
24389
|
-
docId,
|
|
24390
|
-
hierarchy: ["files"],
|
|
24391
|
-
title: basename11(path),
|
|
24392
|
-
content,
|
|
24393
|
-
publishedAt: info.mtimeMs,
|
|
24394
|
-
metadata: { path, extension: extname7(path).toLowerCase() }
|
|
24395
|
-
});
|
|
24396
|
-
} catch (error) {
|
|
24397
|
-
const code = error.code;
|
|
24398
|
-
if (code === "EPERM" || code === "EACCES")
|
|
24399
|
-
permissionDenied += 1;
|
|
24400
|
-
else
|
|
24401
|
-
readFailures += 1;
|
|
24402
|
-
}
|
|
24403
|
-
}
|
|
24404
|
-
if (documents.length === 0 && permissionDenied > 0) {
|
|
24405
|
-
return {
|
|
24406
|
-
ok: false,
|
|
24407
|
-
reason: "permission",
|
|
24408
|
-
message: `macOS denied access to all Spotlight hits; ${FDA_GUIDANCE}`
|
|
24409
|
-
};
|
|
24410
|
-
}
|
|
24411
|
-
if (documents.length === 0 && failedQueries > 0) {
|
|
24412
|
-
return {
|
|
24413
|
-
ok: false,
|
|
24414
|
-
reason: "unavailable",
|
|
24415
|
-
message: "some mdfind queries failed and no readable hits remained",
|
|
24416
|
-
stderr: failures[0]?.stderr,
|
|
24417
|
-
stdout: failures[0]?.stdout,
|
|
24418
|
-
code: failures[0]?.code
|
|
24419
|
-
};
|
|
24420
|
-
}
|
|
24421
|
-
const warnings = [];
|
|
24422
|
-
if (failedQueries > 0)
|
|
24423
|
-
warnings.push(`${failedQueries} mdfind quer(y/ies) failed`);
|
|
24424
|
-
if (permissionDenied > 0) {
|
|
24425
|
-
warnings.push(`${permissionDenied} Spotlight hit(s) were denied by macOS privacy protections; ${FDA_GUIDANCE}`);
|
|
24426
|
-
}
|
|
24427
|
-
if (readFailures > 0)
|
|
24428
|
-
warnings.push(`${readFailures} Spotlight hit(s) failed to read`);
|
|
24429
|
-
return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
|
|
24430
|
-
}
|
|
24431
|
-
async readinessFailure(queries) {
|
|
24432
|
-
if (this.platform !== "darwin") {
|
|
24433
|
-
return { ok: false, reason: "unavailable", message: "spotlight datasource is supported on macOS only" };
|
|
24434
|
-
}
|
|
24435
|
-
if (queries.length === 0) {
|
|
24436
|
-
return { ok: false, reason: "not-configured", message: "no spotlight queries configured" };
|
|
24437
|
-
}
|
|
24438
|
-
const timeoutMs = this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS11;
|
|
24439
|
-
const status = await this.run("mdutil", ["-s", "/"], timeoutMs);
|
|
24440
|
-
if (status.ok && /indexing disabled/iu.test(status.stdout)) {
|
|
24441
|
-
return {
|
|
24442
|
-
ok: false,
|
|
24443
|
-
reason: "unavailable",
|
|
24444
|
-
message: "Spotlight indexing is disabled; enable it with `sudo mdutil -i on /`"
|
|
24445
|
-
};
|
|
24446
|
-
}
|
|
24447
|
-
return;
|
|
24448
|
-
}
|
|
24449
|
-
}
|
|
24450
|
-
var DEFAULT_MAX_RESULTS_PER_QUERY = 100, DEFAULT_MAX_DOCUMENTS4 = 300, DEFAULT_MAX_BYTES_PER_FILE2, DEFAULT_TIMEOUT_MS11 = 15000, MAX_CONTENT_CHARS4 = 1e5, MAX_BUFFER_BYTES, FDA_GUIDANCE = "grant Full Disk Access to the terminal or app running AutoRAG (System Settings -> Privacy & Security -> Full Disk Access), then restart it", spawnRunner = (command, args, timeoutMs) => new Promise((resolve) => {
|
|
24451
|
-
const child = spawn19(command, [...args], { stdio: ["ignore", "pipe", "pipe"] });
|
|
24452
|
-
let stdout = "";
|
|
24453
|
-
let stderr = "";
|
|
24454
|
-
let settled = false;
|
|
24455
|
-
const finish = (result) => {
|
|
24456
|
-
if (settled)
|
|
24457
|
-
return;
|
|
24458
|
-
settled = true;
|
|
24459
|
-
clearTimeout(timer);
|
|
24460
|
-
resolve(result);
|
|
24461
|
-
};
|
|
24462
|
-
const timer = setTimeout(() => {
|
|
24463
|
-
child.kill("SIGKILL");
|
|
24464
|
-
finish({ ok: false, stdout, stderr: "process timed out", code: null });
|
|
24465
|
-
}, timeoutMs);
|
|
24466
|
-
child.stdout.setEncoding("utf8");
|
|
24467
|
-
child.stderr.setEncoding("utf8");
|
|
24468
|
-
child.stdout.on("data", (chunk) => {
|
|
24469
|
-
if (stdout.length < MAX_BUFFER_BYTES)
|
|
24470
|
-
stdout += chunk;
|
|
24471
|
-
});
|
|
24472
|
-
child.stderr.on("data", (chunk) => {
|
|
24473
|
-
if (stderr.length < MAX_BUFFER_BYTES)
|
|
24474
|
-
stderr += chunk;
|
|
24475
|
-
});
|
|
24476
|
-
child.on("error", (error) => {
|
|
24477
|
-
finish({ ok: false, stdout, stderr: error.message, code: null, error: error.code });
|
|
24478
|
-
});
|
|
24479
|
-
child.on("close", (code) => {
|
|
24480
|
-
finish({ ok: code === 0, stdout, stderr, code });
|
|
24481
|
-
});
|
|
24482
|
-
});
|
|
24483
|
-
var init_connector5 = __esm(() => {
|
|
24484
|
-
DEFAULT_MAX_BYTES_PER_FILE2 = 256 * 1024;
|
|
24485
|
-
MAX_BUFFER_BYTES = 4 * 1024 * 1024;
|
|
24486
|
-
});
|
|
24487
|
-
|
|
24488
|
-
// src/datasource/skills/spotlight/skill.ts
|
|
24489
|
-
class SpotlightSkill {
|
|
24490
|
-
connector;
|
|
24491
|
-
skillName;
|
|
24492
|
-
instanceId;
|
|
24493
|
-
instances;
|
|
24494
|
-
tags;
|
|
24495
|
-
lastIndexedAt;
|
|
24496
|
-
lastError;
|
|
24497
|
-
constructor(options = {}) {
|
|
24498
|
-
this.skillName = options.skillName ?? "spotlight";
|
|
24499
|
-
this.instanceId = options.instanceId ?? "default";
|
|
24500
|
-
this.instances = options.instances !== undefined && options.instances.length > 0 ? options.instances : [this.instanceId];
|
|
24501
|
-
this.tags = options.tags ?? DEFAULT_TAGS4;
|
|
24502
|
-
this.connector = options.connector ?? new SpotlightConnector(options.connectorOptions ?? {});
|
|
24503
|
-
}
|
|
24504
|
-
describe() {
|
|
24505
|
-
return {
|
|
24506
|
-
name: this.skillName,
|
|
24507
|
-
id: this.skillName,
|
|
24508
|
-
type: "spotlight-search",
|
|
24509
|
-
description: "Live macOS Spotlight search via the built-in mdfind CLI",
|
|
24510
|
-
capabilities: ["files", "macos-only", "local-search", "live-search", "scoped", "external-cli"],
|
|
24511
|
-
tags: this.tags,
|
|
24512
|
-
status: "active",
|
|
24513
|
-
requiresExternalCli: true,
|
|
24514
|
-
datasourceId: this.skillName,
|
|
24515
|
-
instanceId: this.instanceId,
|
|
24516
|
-
instances: this.instances
|
|
24517
|
-
};
|
|
24518
|
-
}
|
|
24519
|
-
polling() {
|
|
24520
|
-
return {
|
|
24521
|
-
mode: "none",
|
|
24522
|
-
...this.lastIndexedAt !== undefined ? { lastIndexedAt: this.lastIndexedAt } : {},
|
|
24523
|
-
...this.lastError !== undefined ? { lastError: this.lastError } : {}
|
|
24524
|
-
};
|
|
24525
|
-
}
|
|
24526
|
-
async index() {
|
|
24527
|
-
try {
|
|
24528
|
-
const result = await this.connector.probe();
|
|
24529
|
-
if (!result.ok) {
|
|
24530
|
-
const code = connectorFailureToDiagnosticCode(result.reason);
|
|
24531
|
-
const message = boundDiagnosticText(result.message ?? result.reason);
|
|
24532
|
-
this.lastError = message;
|
|
24533
|
-
return this.fail(code, message);
|
|
24534
|
-
}
|
|
24535
|
-
this.lastError = undefined;
|
|
24536
|
-
this.lastIndexedAt = Date.now();
|
|
24537
|
-
return {
|
|
24538
|
-
ok: true,
|
|
24539
|
-
instanceId: this.instanceId,
|
|
24540
|
-
skill: this.skillName,
|
|
24541
|
-
chunkCount: 0,
|
|
24542
|
-
indexedAt: this.lastIndexedAt,
|
|
24543
|
-
diagnostics: [this.note(LIVE_NOTE, "datasource-empty", "info")]
|
|
24544
|
-
};
|
|
24545
|
-
} catch (error) {
|
|
24546
|
-
const message = boundDiagnosticText(error instanceof Error ? error.message : "spotlight probe failed");
|
|
24547
|
-
this.lastError = message;
|
|
24548
|
-
return this.fail("datasource-unavailable", message);
|
|
24549
|
-
}
|
|
24550
|
-
}
|
|
24551
|
-
retrievalMethods() {
|
|
24552
|
-
return [new SpotlightLiveMethod(this)];
|
|
24553
|
-
}
|
|
24554
|
-
describeSources() {
|
|
24555
|
-
return this.instances.map((instanceId) => ({
|
|
24556
|
-
source: datasourceSourcePath(this.skillName, instanceId),
|
|
24557
|
-
datasourceId: this.skillName,
|
|
24558
|
-
skill: this.skillName,
|
|
24559
|
-
instanceId,
|
|
24560
|
-
contentType: "document",
|
|
24561
|
-
metadata: { datasourceId: this.skillName, instanceId, tags: this.tags }
|
|
24562
|
-
}));
|
|
24563
|
-
}
|
|
24564
|
-
skillManifest() {
|
|
24565
|
-
const scopes = this.instances.map((instanceId) => `- \`${datasourceSourcePath(this.skillName, instanceId)}\``).join(`
|
|
24566
|
-
`);
|
|
24567
|
-
return {
|
|
24568
|
-
name: `datasource-${this.skillName}`,
|
|
24569
|
-
description: "Search the current macOS Spotlight index directly. AutoRAG keeps no file snapshot.",
|
|
24570
|
-
content: [
|
|
24571
|
-
"# macOS Spotlight (live mdfind search)",
|
|
24572
|
-
"",
|
|
24573
|
-
"Search files indexed by macOS Spotlight via the built-in `mdfind` CLI. macOS owns and refreshes the index; AutoRAG does not copy files into a local snapshot.",
|
|
24574
|
-
"",
|
|
24575
|
-
"## Platform requirements",
|
|
24576
|
-
"- macOS only: this datasource is unavailable on any other platform.",
|
|
24577
|
-
"- Spotlight indexing must be enabled (`sudo mdutil -i on /`; check with `mdutil -s /`).",
|
|
24578
|
-
"- Full Disk Access may be required to read protected hits. Grant it under System Settings -> Privacy & Security -> Full Disk Access, then restart the host app.",
|
|
24579
|
-
"",
|
|
24580
|
-
"## How to search",
|
|
24581
|
-
`Call the dedicated \`${datasourceSearchToolName(this.skillName)}\` tool with a query and optional narrowing scope. Available scopes:`,
|
|
24582
|
-
scopes,
|
|
24583
|
-
"Each configured Spotlight filter is AND-ed with the user query. Only the requested hits are read; refresh performs a readiness probe without running `mdfind`.",
|
|
24584
|
-
"Result metadata carries the real absolute file path (`path`) so hits are traceable back to disk. If paths must not leave this machine, run AutoRAG with a local LLM.",
|
|
24585
|
-
"",
|
|
24586
|
-
"## Native CLI",
|
|
24587
|
-
"- `mdfind <query>` searches Spotlight; `mdfind -onlyin <dir> <query>` scopes to a directory; `mdfind -name <text>` matches file names.",
|
|
24588
|
-
"- `mdls <file>` shows Spotlight metadata for one file.",
|
|
24589
|
-
"- `mdutil -s /` checks index status; `sudo mdutil -i on /` enables indexing."
|
|
24590
|
-
].join(`
|
|
24591
|
-
`)
|
|
24592
|
-
};
|
|
24593
|
-
}
|
|
24594
|
-
async search(query, options) {
|
|
24595
|
-
const trimmed = query.trim();
|
|
24596
|
-
if (trimmed.length === 0)
|
|
24597
|
-
return [];
|
|
24598
|
-
const scope = options.scope?.trim() ? normalizeVirtualPath2(options.scope) : undefined;
|
|
24599
|
-
if (scope !== undefined) {
|
|
24600
|
-
const chunksRoot = normalizeVirtualPath2(`/${this.skillName}/${this.instanceId}/chunks`);
|
|
24601
|
-
const scopeProbe = datasourceSourcePath(this.skillName, this.instanceId, "__scope_probe__");
|
|
24602
|
-
if (!matchesDatasourceScope(chunksRoot, scope) && !matchesDatasourceScope(scopeProbe, scope) && !scope.startsWith(`${chunksRoot}/`)) {
|
|
24603
|
-
return [];
|
|
24604
|
-
}
|
|
24605
|
-
}
|
|
24606
|
-
const topK = options.topK ?? DEFAULT_TOP_K9;
|
|
24607
|
-
const result = await this.connector.search(trimmed, topK, (docId) => matchesDatasourceScope(datasourceSourcePath(this.skillName, this.instanceId, docId), scope));
|
|
24608
|
-
if (!result.ok) {
|
|
24609
|
-
throw datasourceCliError(this.skillName, "search", {
|
|
24610
|
-
reason: result.reason,
|
|
24611
|
-
stderr: result.stderr ?? result.message,
|
|
24612
|
-
stdout: result.stdout,
|
|
24613
|
-
code: result.code
|
|
24614
|
-
});
|
|
24615
|
-
}
|
|
24616
|
-
const hits = [];
|
|
24617
|
-
for (const [index, document] of result.documents.entries()) {
|
|
24618
|
-
const source = datasourceSourcePath(this.skillName, this.instanceId, document.docId);
|
|
24619
|
-
if (!matchesDatasourceScope(source, options.scope))
|
|
24620
|
-
continue;
|
|
24621
|
-
hits.push({
|
|
24622
|
-
id: `${this.skillName}:${this.instanceId}:${document.docId}`,
|
|
24623
|
-
content: document.content,
|
|
24624
|
-
source,
|
|
24625
|
-
score: 1 / (index + 1),
|
|
24626
|
-
metadata: {
|
|
24627
|
-
...document.metadata,
|
|
24628
|
-
method: `${this.skillName}-live`,
|
|
24629
|
-
datasourceId: this.skillName,
|
|
24630
|
-
instanceId: this.instanceId,
|
|
24631
|
-
chunkId: document.docId,
|
|
24632
|
-
...document.title !== undefined ? { title: document.title } : {},
|
|
24633
|
-
...document.hierarchy !== undefined && document.hierarchy.length > 0 ? { hierarchy: document.hierarchy.join("/") } : {},
|
|
24634
|
-
...document.publishedAt !== undefined ? { publishedAt: document.publishedAt } : {},
|
|
24635
|
-
...result.warnings !== undefined && result.warnings.length > 0 ? { warnings: result.warnings } : {}
|
|
24636
|
-
}
|
|
24637
|
-
});
|
|
24638
|
-
if (hits.length >= topK)
|
|
24639
|
-
break;
|
|
24640
|
-
}
|
|
24641
|
-
return hits;
|
|
24642
|
-
}
|
|
24643
|
-
fail(code, message) {
|
|
24644
|
-
return {
|
|
24645
|
-
ok: false,
|
|
24646
|
-
instanceId: this.instanceId,
|
|
24647
|
-
skill: this.skillName,
|
|
24648
|
-
indexedAt: Date.now(),
|
|
24649
|
-
diagnostics: [
|
|
24650
|
-
this.note(message, code, code === "datasource-unavailable" || code === "datasource-empty" ? "warning" : "error")
|
|
24651
|
-
],
|
|
24652
|
-
error: message,
|
|
24653
|
-
code,
|
|
24654
|
-
message
|
|
24655
|
-
};
|
|
24656
|
-
}
|
|
24657
|
-
note(message, code, severity) {
|
|
24658
|
-
return { code, severity, message, instanceId: this.instanceId, source: this.skillName };
|
|
24659
|
-
}
|
|
24660
|
-
}
|
|
24661
|
-
|
|
24662
|
-
class SpotlightLiveMethod {
|
|
24663
|
-
skill;
|
|
24664
|
-
constructor(skill) {
|
|
24665
|
-
this.skill = skill;
|
|
24666
|
-
}
|
|
24667
|
-
describe() {
|
|
24668
|
-
const descriptor = this.skill.describe();
|
|
24669
|
-
return {
|
|
24670
|
-
name: `${descriptor.datasourceId ?? "spotlight"}-live`,
|
|
24671
|
-
type: "remote",
|
|
24672
|
-
description: "Live macOS Spotlight search via mdfind",
|
|
24673
|
-
status: "active",
|
|
24674
|
-
capabilities: ["live-search", "scoped", "external-cli", "path-opaque-sources"],
|
|
24675
|
-
datasourceId: descriptor.datasourceId,
|
|
24676
|
-
tags: descriptor.tags
|
|
24677
|
-
};
|
|
24678
|
-
}
|
|
24679
|
-
retrieve(query, options) {
|
|
24680
|
-
return this.skill.search(query, options);
|
|
24681
|
-
}
|
|
24682
|
-
}
|
|
24683
|
-
var DEFAULT_TAGS4, DEFAULT_TOP_K9 = 20, LIVE_NOTE = "live-search datasource: macOS owns the Spotlight index; AutoRAG stores no snapshot";
|
|
24684
|
-
var init_skill12 = __esm(() => {
|
|
24685
|
-
init_connector();
|
|
24686
|
-
init_errors4();
|
|
24687
|
-
init_scope2();
|
|
24688
|
-
init_connector5();
|
|
24689
|
-
DEFAULT_TAGS4 = ["spotlight", "files"];
|
|
24690
|
-
});
|
|
24691
|
-
|
|
24692
|
-
// src/datasource/skills/spotlight/index.ts
|
|
24693
|
-
var init_spotlight = __esm(() => {
|
|
24694
|
-
init_connector5();
|
|
24695
|
-
init_skill12();
|
|
24696
|
-
});
|
|
24697
|
-
|
|
24698
24338
|
// src/datasource/skills/telecrawl/index.ts
|
|
24699
24339
|
function globalArgs3(options) {
|
|
24700
24340
|
return [
|
|
@@ -25099,7 +24739,6 @@ var init_factory = __esm(() => {
|
|
|
25099
24739
|
init_obsidian();
|
|
25100
24740
|
init_rss();
|
|
25101
24741
|
init_slack();
|
|
25102
|
-
init_spotlight();
|
|
25103
24742
|
init_telecrawl();
|
|
25104
24743
|
init_wacrawl();
|
|
25105
24744
|
BUILDERS = {
|
|
@@ -25236,12 +24875,6 @@ var init_factory = __esm(() => {
|
|
|
25236
24875
|
...common(config, workspaceRoot),
|
|
25237
24876
|
skillName: registrationName,
|
|
25238
24877
|
connectorOptions: config.connector
|
|
25239
|
-
}),
|
|
25240
|
-
spotlight: (config, _workspaceRoot, registrationName) => new SpotlightSkill({
|
|
25241
|
-
...config.instanceId !== undefined ? { instanceId: config.instanceId } : {},
|
|
25242
|
-
...config.tags !== undefined ? { tags: config.tags } : {},
|
|
25243
|
-
skillName: registrationName,
|
|
25244
|
-
connectorOptions: config.connector
|
|
25245
24878
|
})
|
|
25246
24879
|
};
|
|
25247
24880
|
PLATFORM_ONLY_SKILLS = { kakao: "darwin" };
|
|
@@ -25251,7 +24884,7 @@ var init_factory = __esm(() => {
|
|
|
25251
24884
|
// src/cli/config.ts
|
|
25252
24885
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
25253
24886
|
import { existsSync as existsSync27, mkdirSync as mkdirSync22, readFileSync as readFileSync24, unlinkSync as unlinkSync3, writeFileSync as writeFileSync15 } from "node:fs";
|
|
25254
|
-
import { basename as
|
|
24887
|
+
import { basename as basename11, dirname as dirname23, isAbsolute as isAbsolute11, join as join42, resolve as resolve15 } from "node:path";
|
|
25255
24888
|
import { findEnvKeys, getEnvApiKey } from "@earendil-works/pi-ai/compat";
|
|
25256
24889
|
import { getAgentDir as getAgentDir2, ModelRuntime, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
25257
24890
|
function resolveConfigPath(input) {
|
|
@@ -25325,7 +24958,7 @@ function acquireConfigWriteLock(configPath) {
|
|
|
25325
24958
|
});
|
|
25326
24959
|
}
|
|
25327
24960
|
function replaceFileAtomically(path, contents, assertCommitAllowed) {
|
|
25328
|
-
const temporaryPath = join42(dirname23(path), `.${
|
|
24961
|
+
const temporaryPath = join42(dirname23(path), `.${basename11(path)}.${process.pid}.${randomUUID6()}.tmp`);
|
|
25329
24962
|
try {
|
|
25330
24963
|
writeFileSync15(temporaryPath, contents, { encoding: "utf8", flag: "wx", flush: true, mode: 384 });
|
|
25331
24964
|
assertCommitAllowed?.();
|
|
@@ -26836,6 +26469,42 @@ function loopGuardPrompt(rule) {
|
|
|
26836
26469
|
`);
|
|
26837
26470
|
}
|
|
26838
26471
|
|
|
26472
|
+
// src/agent/token-count.ts
|
|
26473
|
+
import { createHash as createHash19 } from "node:crypto";
|
|
26474
|
+
import { countTokens } from "gpt-tokenizer/encoding/o200k_base";
|
|
26475
|
+
function estimateTextTokens(text) {
|
|
26476
|
+
return Math.max(Math.ceil(text.length / 4), countText(text));
|
|
26477
|
+
}
|
|
26478
|
+
function tokensBeyondCharGuess(parts) {
|
|
26479
|
+
let excess = 0;
|
|
26480
|
+
for (const part of parts)
|
|
26481
|
+
excess += Math.max(0, countText(part) - Math.ceil(part.length / 4));
|
|
26482
|
+
return excess;
|
|
26483
|
+
}
|
|
26484
|
+
function countText(text) {
|
|
26485
|
+
if (text.length === 0)
|
|
26486
|
+
return 0;
|
|
26487
|
+
if (text.length < CACHE_MIN_CHARS)
|
|
26488
|
+
return countTokens(text, PLAIN_TEXT);
|
|
26489
|
+
const key = `${text.length}:${createHash19("sha1").update(text).digest("base64")}`;
|
|
26490
|
+
const hit = cache.get(key);
|
|
26491
|
+
if (hit !== undefined) {
|
|
26492
|
+
cache.delete(key);
|
|
26493
|
+
cache.set(key, hit);
|
|
26494
|
+
return hit;
|
|
26495
|
+
}
|
|
26496
|
+
const count = countTokens(text, PLAIN_TEXT);
|
|
26497
|
+
cache.set(key, count);
|
|
26498
|
+
if (cache.size > CACHE_ENTRIES)
|
|
26499
|
+
cache.delete(cache.keys().next().value);
|
|
26500
|
+
return count;
|
|
26501
|
+
}
|
|
26502
|
+
var PLAIN_TEXT, CACHE_MIN_CHARS = 2000, CACHE_ENTRIES = 64, cache;
|
|
26503
|
+
var init_token_count = __esm(() => {
|
|
26504
|
+
PLAIN_TEXT = { disallowedSpecial: new Set };
|
|
26505
|
+
cache = new Map;
|
|
26506
|
+
});
|
|
26507
|
+
|
|
26839
26508
|
// src/agent/context-token-guard.ts
|
|
26840
26509
|
import { calculateContextTokens, estimateTokens } from "@earendil-works/pi-coding-agent";
|
|
26841
26510
|
function createContextTokenGuardedTransform(model, base, fixedOverheadTokens = () => 0) {
|
|
@@ -26851,12 +26520,20 @@ function withBoundedOutput(model) {
|
|
|
26851
26520
|
return { ...model, maxTokens: MAX_OUTPUT_TOKENS };
|
|
26852
26521
|
}
|
|
26853
26522
|
function guardAgentContextMessages(model, messages, useReportedUsage = true, fixedOverheadTokens = 0) {
|
|
26523
|
+
const guarded = guardWithinBudget(model, messages, useReportedUsage, fixedOverheadTokens);
|
|
26524
|
+
if (model !== undefined && guarded.length > 0) {
|
|
26525
|
+
recordRequestEstimate(model, estimateContextMessageTokens(guarded, false) + fixedOverheadTokens);
|
|
26526
|
+
}
|
|
26527
|
+
return guarded;
|
|
26528
|
+
}
|
|
26529
|
+
function guardWithinBudget(model, messages, useReportedUsage = true, fixedOverheadTokens = 0) {
|
|
26854
26530
|
if (model === undefined || !Number.isFinite(model.contextWindow) || model.contextWindow <= 0 || messages.length === 0) {
|
|
26855
26531
|
return messages;
|
|
26856
26532
|
}
|
|
26857
26533
|
const usableContext = Math.floor(model.contextWindow / CONTEXT_HEADROOM);
|
|
26858
26534
|
const outputReserve = Math.min(Math.floor(usableContext / 4), MAX_OUTPUT_TOKENS, Number.isFinite(model.maxTokens) ? Math.max(0, model.maxTokens) : 0);
|
|
26859
|
-
const
|
|
26535
|
+
const overflowCeiling = contextOverflowCeiling(model);
|
|
26536
|
+
const inputBudget = Math.min(Math.max(0, usableContext - outputReserve), overflowCeiling ?? Number.POSITIVE_INFINITY);
|
|
26860
26537
|
const budget = Math.max(0, inputBudget - fixedOverheadTokens);
|
|
26861
26538
|
const reported = useReportedUsage && findLastAssistantUsage(messages) !== undefined;
|
|
26862
26539
|
if (estimateContextMessageTokens(messages, useReportedUsage) <= (reported ? inputBudget : budget)) {
|
|
@@ -26878,50 +26555,40 @@ function guardAgentContextMessages(model, messages, useReportedUsage = true, fix
|
|
|
26878
26555
|
}
|
|
26879
26556
|
let guarded = messages;
|
|
26880
26557
|
if (candidates.length > 0) {
|
|
26881
|
-
const
|
|
26882
|
-
const maximumOmitted = candidates.reduce((total, section) => total + section.blocks.length, 0);
|
|
26558
|
+
const entries = candidates.flatMap((section, sectionIndex) => section.blocks.map((block, blockIndex) => ({ block, sectionIndex, blockIndex })));
|
|
26883
26559
|
const omissionMarker = (count) => `
|
|
26884
26560
|
|
|
26885
26561
|
${GUARD_MARKER} Retrieval candidates omitted: ${count}.
|
|
26886
26562
|
`;
|
|
26887
|
-
|
|
26888
|
-
|
|
26889
|
-
|
|
26890
|
-
|
|
26891
|
-
for (const
|
|
26892
|
-
|
|
26893
|
-
|
|
26894
|
-
if (tokens <= remaining) {
|
|
26895
|
-
kept[sectionIndex]?.push(block);
|
|
26896
|
-
remaining -= tokens;
|
|
26897
|
-
continue;
|
|
26898
|
-
}
|
|
26899
|
-
omitted += section.blocks.length - (kept[sectionIndex]?.length ?? 0);
|
|
26900
|
-
for (const later of candidates.slice(sectionIndex + 1))
|
|
26901
|
-
omitted += later.blocks.length;
|
|
26902
|
-
firstOmitted = sectionIndex;
|
|
26903
|
-
break;
|
|
26904
|
-
}
|
|
26905
|
-
if (firstOmitted !== -1)
|
|
26563
|
+
const markerTokens = candidates.length * Math.ceil(omissionMarker(entries.length).length / 4);
|
|
26564
|
+
let remaining = Math.max(0, budget - estimateContextMessageTokens(base, false) - markerTokens);
|
|
26565
|
+
entries.sort((a, b) => a.block.rank - b.block.rank || a.sectionIndex - b.sectionIndex || a.blockIndex - b.blockIndex);
|
|
26566
|
+
const kept = new Set;
|
|
26567
|
+
for (const { block } of entries) {
|
|
26568
|
+
const tokens = Math.max(1, estimateTextTokens(block.text));
|
|
26569
|
+
if (tokens > remaining)
|
|
26906
26570
|
break;
|
|
26571
|
+
remaining -= tokens;
|
|
26572
|
+
kept.add(block);
|
|
26907
26573
|
}
|
|
26908
|
-
if (
|
|
26909
|
-
const
|
|
26910
|
-
for (const [sectionIndex, messageIndex] of candidateIndexes.entries())
|
|
26911
|
-
candidateByMessage.set(messageIndex, sectionIndex);
|
|
26574
|
+
if (kept.size < entries.length) {
|
|
26575
|
+
const sectionByMessage = new Map(candidateIndexes.map((messageIndex, i) => [messageIndex, candidates[i]]));
|
|
26912
26576
|
guarded = base.map((message, index) => {
|
|
26913
|
-
const
|
|
26914
|
-
if (
|
|
26577
|
+
const section = sectionByMessage.get(index);
|
|
26578
|
+
if (section === undefined)
|
|
26915
26579
|
return message;
|
|
26916
|
-
const
|
|
26917
|
-
const
|
|
26918
|
-
|
|
26580
|
+
const keptBlocks = section.blocks.filter((block) => kept.has(block));
|
|
26581
|
+
const omitted = section.blocks.length - keptBlocks.length;
|
|
26582
|
+
if (omitted === 0)
|
|
26583
|
+
return messages[index] ?? message;
|
|
26584
|
+
const marker = omissionMarker(omitted);
|
|
26585
|
+
return replaceTextContent(message, `${section.prefix}${keptBlocks.map((block) => block.text).join("")}${marker}${section.suffix}`);
|
|
26919
26586
|
});
|
|
26920
26587
|
}
|
|
26921
26588
|
}
|
|
26922
|
-
if (estimateContextMessageTokens(guarded, false) <= budget
|
|
26589
|
+
if (guarded !== messages && estimateContextMessageTokens(guarded, false) <= budget)
|
|
26923
26590
|
return guarded;
|
|
26924
|
-
return
|
|
26591
|
+
return fitNewestToolResult(guarded, budget);
|
|
26925
26592
|
}
|
|
26926
26593
|
function estimateContextMessageTokens(messages, useReportedUsage = false) {
|
|
26927
26594
|
if (useReportedUsage) {
|
|
@@ -26932,82 +26599,37 @@ function estimateContextMessageTokens(messages, useReportedUsage = false) {
|
|
|
26932
26599
|
}
|
|
26933
26600
|
return messages.reduce((total, message) => total + estimateMessageTokens(message), 0);
|
|
26934
26601
|
}
|
|
26935
|
-
function
|
|
26936
|
-
const
|
|
26937
|
-
const
|
|
26938
|
-
|
|
26939
|
-
|
|
26940
|
-
const
|
|
26941
|
-
|
|
26942
|
-
|
|
26943
|
-
|
|
26944
|
-
|
|
26945
|
-
trimmed[index] = fitMessageToTokens(message, Math.max(floorTokens, inputBudget - others));
|
|
26946
|
-
};
|
|
26947
|
-
for (const [index, message] of trimmed.entries()) {
|
|
26948
|
-
if (index === lastIndex || index === queryIndex || message.role === "system")
|
|
26949
|
-
continue;
|
|
26950
|
-
const replacement = replaceMessageWithMarker(message);
|
|
26951
|
-
if (replacement === message || estimateMessageTokens(replacement) >= estimateMessageTokens(message))
|
|
26952
|
-
continue;
|
|
26953
|
-
trimmed[index] = replacement;
|
|
26954
|
-
if (fits())
|
|
26955
|
-
return trimmed;
|
|
26956
|
-
}
|
|
26957
|
-
if (lastIndex !== queryIndex && trimmed[lastIndex]?.role !== "system") {
|
|
26958
|
-
fitInto(lastIndex, 0);
|
|
26959
|
-
if (fits())
|
|
26960
|
-
return trimmed;
|
|
26961
|
-
}
|
|
26962
|
-
for (const [index, message] of trimmed.entries()) {
|
|
26963
|
-
if (message.role !== "system")
|
|
26964
|
-
continue;
|
|
26965
|
-
fitInto(index, 0);
|
|
26966
|
-
if (fits())
|
|
26967
|
-
return trimmed;
|
|
26968
|
-
}
|
|
26969
|
-
if (queryIndex >= 0)
|
|
26970
|
-
fitInto(queryIndex, MIN_QUERY_TOKENS);
|
|
26971
|
-
return trimmed;
|
|
26602
|
+
function fitNewestToolResult(messages, inputBudget) {
|
|
26603
|
+
const lastIndex = messages.length - 1;
|
|
26604
|
+
const last = messages[lastIndex];
|
|
26605
|
+
if (last?.role !== "toolResult")
|
|
26606
|
+
return messages;
|
|
26607
|
+
const others = messages.reduce((total, message, index) => index === lastIndex ? total : total + estimateMessageTokens(message), 0);
|
|
26608
|
+
if (others >= inputBudget)
|
|
26609
|
+
return messages;
|
|
26610
|
+
const fitted = fitMessageToTokens(last, inputBudget - others);
|
|
26611
|
+
return fitted === last ? messages : [...messages.slice(0, lastIndex), fitted];
|
|
26972
26612
|
}
|
|
26973
26613
|
function fitMessageToTokens(message, maxTokens) {
|
|
26974
26614
|
const original = textContent(message);
|
|
26975
26615
|
if (original === undefined)
|
|
26976
|
-
return
|
|
26616
|
+
return message;
|
|
26977
26617
|
const marker = `
|
|
26978
26618
|
|
|
26979
26619
|
${GUARD_MARKER}
|
|
26980
26620
|
`;
|
|
26981
|
-
|
|
26982
|
-
|
|
26983
|
-
let
|
|
26984
|
-
|
|
26985
|
-
const
|
|
26986
|
-
const
|
|
26987
|
-
if (
|
|
26988
|
-
|
|
26989
|
-
|
|
26990
|
-
|
|
26991
|
-
high = middle - 1;
|
|
26992
|
-
}
|
|
26621
|
+
const overhead = estimateMessageTokens(replaceTextContent(message, ""));
|
|
26622
|
+
const density = Math.max(0, estimateMessageTokens(message) - overhead) / Math.max(1, original.length);
|
|
26623
|
+
let chars = density > 0 ? Math.floor((maxTokens - overhead) / density) : original.length;
|
|
26624
|
+
for (let attempt = 0;attempt < MAX_FIT_ATTEMPTS && chars > 0; attempt++) {
|
|
26625
|
+
const candidate = replaceTextContent(message, excerpt(original, chars, marker));
|
|
26626
|
+
const tokens = estimateMessageTokens(candidate);
|
|
26627
|
+
if (tokens <= maxTokens)
|
|
26628
|
+
return candidate;
|
|
26629
|
+
const scaled = Math.floor(chars * Math.max(0, maxTokens - overhead) / Math.max(1, tokens - overhead) * 0.98);
|
|
26630
|
+
chars = Math.min(chars - 1, scaled);
|
|
26993
26631
|
}
|
|
26994
|
-
return
|
|
26995
|
-
}
|
|
26996
|
-
function stripNonTextContent(message) {
|
|
26997
|
-
if (message.role !== "user" || typeof message.content === "string")
|
|
26998
|
-
return replaceMessageWithMarker(message);
|
|
26999
|
-
const marker = `
|
|
27000
|
-
|
|
27001
|
-
${GUARD_MARKER}
|
|
27002
|
-
`;
|
|
27003
|
-
let replacedImage = false;
|
|
27004
|
-
const content = message.content.map((block) => {
|
|
27005
|
-
if (block.type === "text")
|
|
27006
|
-
return block;
|
|
27007
|
-
replacedImage = true;
|
|
27008
|
-
return { type: "text", text: marker };
|
|
27009
|
-
});
|
|
27010
|
-
return replacedImage ? { ...message, content } : message;
|
|
26632
|
+
return replaceTextContent(message, marker);
|
|
27011
26633
|
}
|
|
27012
26634
|
function excerpt(text, maxChars, marker) {
|
|
27013
26635
|
if (text.length <= maxChars)
|
|
@@ -27017,32 +26639,54 @@ function excerpt(text, maxChars, marker) {
|
|
|
27017
26639
|
const tail = Math.max(0, available - head);
|
|
27018
26640
|
return `${text.slice(0, head)}${marker}${tail > 0 ? text.slice(-tail) : ""}`;
|
|
27019
26641
|
}
|
|
27020
|
-
function replaceMessageWithMarker(message) {
|
|
27021
|
-
return replaceTextContent(message, `
|
|
27022
|
-
|
|
27023
|
-
${GUARD_MARKER}
|
|
27024
|
-
`);
|
|
27025
|
-
}
|
|
27026
26642
|
function estimateMessageTokens(message) {
|
|
27027
26643
|
if (message.role === "system")
|
|
27028
26644
|
return estimateSystemMessageTokens(message);
|
|
27029
|
-
const estimate = estimateTokens(message);
|
|
26645
|
+
const estimate = estimateTokens(message) + tokensBeyondCharGuess(messageTextParts(message));
|
|
27030
26646
|
return message.role === "toolResult" ? estimate + Math.ceil(message.toolName.length / 4) : estimate;
|
|
27031
26647
|
}
|
|
27032
26648
|
function estimateSystemMessageTokens(message) {
|
|
27033
|
-
|
|
27034
|
-
|
|
26649
|
+
const parts = [];
|
|
26650
|
+
let chars = 0;
|
|
26651
|
+
if (typeof message.content === "string")
|
|
26652
|
+
parts.push(message.content);
|
|
26653
|
+
else {
|
|
27035
26654
|
for (const block of message.content) {
|
|
27036
26655
|
if (block.type === "text")
|
|
27037
|
-
|
|
26656
|
+
parts.push(block.text);
|
|
27038
26657
|
else if (block.type === "image")
|
|
27039
26658
|
chars += 4800;
|
|
27040
26659
|
}
|
|
27041
26660
|
}
|
|
27042
|
-
|
|
27043
|
-
|
|
27044
|
-
|
|
27045
|
-
return Math.ceil(chars / 4);
|
|
26661
|
+
parts.push(JSON.stringify(message.sections ?? {}), JSON.stringify(message.toolsAdded ?? []), JSON.stringify(message.toolsRemoved ?? []));
|
|
26662
|
+
for (const part of parts)
|
|
26663
|
+
chars += part.length;
|
|
26664
|
+
return Math.ceil(chars / 4) + tokensBeyondCharGuess(parts);
|
|
26665
|
+
}
|
|
26666
|
+
function messageTextParts(message) {
|
|
26667
|
+
switch (message.role) {
|
|
26668
|
+
case "user":
|
|
26669
|
+
case "custom":
|
|
26670
|
+
case "toolResult":
|
|
26671
|
+
return typeof message.content === "string" ? [message.content] : message.content.flatMap((block) => block.type === "text" ? [block.text] : []);
|
|
26672
|
+
case "assistant":
|
|
26673
|
+
return message.content.flatMap((block) => {
|
|
26674
|
+
if (block.type === "text")
|
|
26675
|
+
return [block.text];
|
|
26676
|
+
if (block.type === "thinking")
|
|
26677
|
+
return [block.thinking];
|
|
26678
|
+
if (block.type === "toolCall")
|
|
26679
|
+
return [`${block.name}${JSON.stringify(block.arguments)}`];
|
|
26680
|
+
return [];
|
|
26681
|
+
});
|
|
26682
|
+
case "bashExecution":
|
|
26683
|
+
return [message.command, message.output];
|
|
26684
|
+
case "branchSummary":
|
|
26685
|
+
case "compactionSummary":
|
|
26686
|
+
return [message.summary];
|
|
26687
|
+
default:
|
|
26688
|
+
return [];
|
|
26689
|
+
}
|
|
27046
26690
|
}
|
|
27047
26691
|
function findLastAssistantUsage(messages) {
|
|
27048
26692
|
for (let index = messages.length - 1;index >= 0; index--) {
|
|
@@ -27116,7 +26760,7 @@ function parseCandidateSection(message, text) {
|
|
|
27116
26760
|
const sectionEnd = findBaselineSectionEnd(text, sectionStart);
|
|
27117
26761
|
if (!/^\[e?\d+\] /mu.test(text.slice(sectionStart, sectionEnd)))
|
|
27118
26762
|
return;
|
|
27119
|
-
return splitCandidates(text, sectionStart, sectionEnd);
|
|
26763
|
+
return splitCandidates(text, sectionStart, sectionEnd, { lists: true });
|
|
27120
26764
|
}
|
|
27121
26765
|
if (message.role !== "toolResult")
|
|
27122
26766
|
return;
|
|
@@ -27127,7 +26771,9 @@ function parseCandidateSection(message, text) {
|
|
|
27127
26771
|
const directiveStart = text.indexOf(`
|
|
27128
26772
|
|
|
27129
26773
|
directive:`);
|
|
27130
|
-
return splitCandidates(text, 0, directiveStart >= 0 ? directiveStart : text.length,
|
|
26774
|
+
return splitCandidates(text, 0, directiveStart >= 0 ? directiveStart : text.length, {
|
|
26775
|
+
pattern: /^- [^\n]*(?:\n|$)/gmu
|
|
26776
|
+
});
|
|
27131
26777
|
}
|
|
27132
26778
|
const diagnosticsStart = text.indexOf(`
|
|
27133
26779
|
|
|
@@ -27149,17 +26795,23 @@ Formatting and content rules`
|
|
|
27149
26795
|
const positions = endMarkers.map((marker) => text.indexOf(marker, startIndex)).filter((index) => index >= 0);
|
|
27150
26796
|
return positions.length > 0 ? Math.min(...positions) : text.length;
|
|
27151
26797
|
}
|
|
27152
|
-
function splitCandidates(text, sectionStart, sectionEnd,
|
|
26798
|
+
function splitCandidates(text, sectionStart, sectionEnd, options = {}) {
|
|
27153
26799
|
const region = text.slice(sectionStart, sectionEnd);
|
|
27154
|
-
const matches = [...region.matchAll(pattern)];
|
|
26800
|
+
const matches = [...region.matchAll(options.pattern ?? /^\[e?\d+\] [^\n]*(?:\n|$)/gmu)];
|
|
27155
26801
|
const firstStart = matches[0]?.index;
|
|
27156
26802
|
if (firstStart === undefined)
|
|
27157
26803
|
return;
|
|
27158
26804
|
const blocks = [];
|
|
26805
|
+
let rank = 0;
|
|
26806
|
+
let heading = "";
|
|
27159
26807
|
for (const [index, match] of matches.entries()) {
|
|
27160
26808
|
const start = match.index ?? 0;
|
|
27161
26809
|
const end = index + 1 < matches.length ? matches[index + 1]?.index ?? region.length : region.length;
|
|
27162
|
-
|
|
26810
|
+
const raw = region.slice(start, end);
|
|
26811
|
+
const tail = options.lists === true ? LIST_HEADING_TAIL.exec(raw) : null;
|
|
26812
|
+
blocks.push({ text: heading + (tail === null ? raw : raw.slice(0, tail.index)), rank });
|
|
26813
|
+
rank = tail === null ? rank + 1 : 0;
|
|
26814
|
+
heading = tail === null ? "" : tail[0];
|
|
27163
26815
|
}
|
|
27164
26816
|
return {
|
|
27165
26817
|
prefix: text.slice(0, sectionStart) + region.slice(0, firstStart),
|
|
@@ -27167,8 +26819,10 @@ function splitCandidates(text, sectionStart, sectionEnd, pattern = /^\[e?\d+\] [
|
|
|
27167
26819
|
blocks
|
|
27168
26820
|
};
|
|
27169
26821
|
}
|
|
27170
|
-
var CONTEXT_HEADROOM = 1.2, MAX_OUTPUT_TOKENS = 32000,
|
|
26822
|
+
var CONTEXT_HEADROOM = 1.2, MAX_OUTPUT_TOKENS = 32000, GUARD_MARKER = "[AutoRAG context guard: omitted context to stay within the model context budget.]", RETRIEVAL_TOOL_PREFIX = "search_datasource_", RETRIEVAL_TOOL_NAMES, MAX_FIT_ATTEMPTS = 6, LIST_HEADING_TAIL;
|
|
27171
26823
|
var init_context_token_guard = __esm(() => {
|
|
26824
|
+
init_context_overflow();
|
|
26825
|
+
init_token_count();
|
|
27172
26826
|
RETRIEVAL_TOOL_NAMES = {
|
|
27173
26827
|
jikji_find: true,
|
|
27174
26828
|
search_all_documents: true,
|
|
@@ -27177,6 +26831,7 @@ var init_context_token_guard = __esm(() => {
|
|
|
27177
26831
|
everything_search: true,
|
|
27178
26832
|
fsearch_search: true
|
|
27179
26833
|
};
|
|
26834
|
+
LIST_HEADING_TAIL = /\n[^\n]*initial candidates[^\n]*:\n$/u;
|
|
27180
26835
|
});
|
|
27181
26836
|
|
|
27182
26837
|
// src/agent/current-date.ts
|
|
@@ -27622,7 +27277,7 @@ function guardContext(pi, contextTransform) {
|
|
|
27622
27277
|
pi.on("context", async (event, ctx) => {
|
|
27623
27278
|
const activeTools = new Set(pi.getActiveTools());
|
|
27624
27279
|
const toolDeclarations = pi.getAllTools().filter((tool) => activeTools.has(tool.name)).map(({ name, description, parameters }) => ({ name, description, parameters }));
|
|
27625
|
-
const fixedOverheadTokens =
|
|
27280
|
+
const fixedOverheadTokens = estimateTextTokens(ctx.getSystemPrompt()) + estimateTextTokens(JSON.stringify(toolDeclarations));
|
|
27626
27281
|
return {
|
|
27627
27282
|
messages: await createContextTokenGuardedTransform(ctx.model, contextTransform, () => fixedOverheadTokens)(event.messages)
|
|
27628
27283
|
};
|
|
@@ -27856,6 +27511,7 @@ var init_pi_session = __esm(() => {
|
|
|
27856
27511
|
init_intent_tracing();
|
|
27857
27512
|
init_mcp_client();
|
|
27858
27513
|
init_request_limits();
|
|
27514
|
+
init_token_count();
|
|
27859
27515
|
init_welcome_header();
|
|
27860
27516
|
PI_BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
27861
27517
|
});
|
|
@@ -29044,7 +28700,7 @@ var init_automation_tool = __esm(() => {
|
|
|
29044
28700
|
});
|
|
29045
28701
|
|
|
29046
28702
|
// src/agent/citation-links.ts
|
|
29047
|
-
import { basename as
|
|
28703
|
+
import { basename as basename12, isAbsolute as isAbsolute12 } from "node:path";
|
|
29048
28704
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
29049
28705
|
function openableTarget(source) {
|
|
29050
28706
|
if (/^https?:\/\//iu.test(source)) {
|
|
@@ -29062,7 +28718,7 @@ function linkCitations(markdown, sourceOf, isFile) {
|
|
|
29062
28718
|
for (const marker of answerMarkers(markdown, isFile)) {
|
|
29063
28719
|
if (marker.kind === "number")
|
|
29064
28720
|
continue;
|
|
29065
|
-
const labels = marker.kind === "file" ? [{ label:
|
|
28721
|
+
const labels = marker.kind === "file" ? [{ label: basename12(marker.path), source: marker.path }] : marker.ids.map((id) => ({ label: id, source: sourceOf(id) }));
|
|
29066
28722
|
let linked = false;
|
|
29067
28723
|
const parts = labels.map(({ label, source }) => {
|
|
29068
28724
|
const target = source === undefined ? undefined : openableTarget(source);
|
|
@@ -30301,7 +29957,7 @@ class DazziJevBackend {
|
|
|
30301
29957
|
this.apiKey = apiKey;
|
|
30302
29958
|
this.baseUrl = options.baseUrl ?? resolveBaseUrl(env);
|
|
30303
29959
|
this.defaultModel = options.defaultModel ?? DAZZI_DEFAULT_JEV_MODEL;
|
|
30304
|
-
this.timeoutMs = options.timeoutMs ??
|
|
29960
|
+
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS11;
|
|
30305
29961
|
this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
30306
29962
|
this.fetchImpl = options.fetchImpl;
|
|
30307
29963
|
}
|
|
@@ -30488,7 +30144,7 @@ async function extractErrorMessage(response) {
|
|
|
30488
30144
|
function sleep6(ms) {
|
|
30489
30145
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
30490
30146
|
}
|
|
30491
|
-
var JEV_PATH, DAZZI_DEFAULT_JEV_MODEL = "jev-1.13",
|
|
30147
|
+
var JEV_PATH, DAZZI_DEFAULT_JEV_MODEL = "jev-1.13", DEFAULT_TIMEOUT_MS11 = 1e4, DEFAULT_MAX_RETRIES = 2, RETRY_BASE_MS = 500, RETRY_MAX_MS = 5000;
|
|
30492
30148
|
var init_jev_backend = __esm(() => {
|
|
30493
30149
|
init_config();
|
|
30494
30150
|
init_credential();
|
|
@@ -31972,7 +31628,7 @@ var init_page_loader = __esm(() => {
|
|
|
31972
31628
|
});
|
|
31973
31629
|
|
|
31974
31630
|
// src/web/fetch/url-target.ts
|
|
31975
|
-
import { extname as
|
|
31631
|
+
import { extname as extname7 } from "node:path";
|
|
31976
31632
|
function repairCollapsedScheme(value) {
|
|
31977
31633
|
const m = value.match(/^(https?):\/(?!\/)/i);
|
|
31978
31634
|
return m ? `${m[1]}://${value.slice(m[0].length)}` : value;
|
|
@@ -31991,7 +31647,7 @@ function getFilenameExtensionHint(filename) {
|
|
|
31991
31647
|
const lower = filename.toLowerCase();
|
|
31992
31648
|
if (lower.endsWith(".tar.gz"))
|
|
31993
31649
|
return ".tar.gz";
|
|
31994
|
-
return
|
|
31650
|
+
return extname7(filename).toLowerCase();
|
|
31995
31651
|
}
|
|
31996
31652
|
function getExtensionHint(url, contentDisposition) {
|
|
31997
31653
|
if (contentDisposition) {
|
|
@@ -32168,7 +31824,7 @@ var init_convert2 = __esm(() => {
|
|
|
32168
31824
|
});
|
|
32169
31825
|
|
|
32170
31826
|
// src/web/fetch/feeds.ts
|
|
32171
|
-
import { extname as
|
|
31827
|
+
import { extname as extname8 } from "node:path";
|
|
32172
31828
|
import { XMLParser as XMLParser3 } from "fast-xml-parser";
|
|
32173
31829
|
function getHtmlAttribute(tag, attribute) {
|
|
32174
31830
|
const pattern = new RegExp(`\\b${attribute}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s"'=<>]+))`, "i");
|
|
@@ -32231,7 +31887,7 @@ function extractDocumentLinks(html, baseUrl) {
|
|
|
32231
31887
|
const href = getHtmlAttribute(tag, "href");
|
|
32232
31888
|
if (!href)
|
|
32233
31889
|
continue;
|
|
32234
|
-
const ext =
|
|
31890
|
+
const ext = extname8(href).toLowerCase();
|
|
32235
31891
|
if (!CONVERTIBLE_EXTENSIONS2.has(ext))
|
|
32236
31892
|
continue;
|
|
32237
31893
|
const resolved = href.startsWith("http") ? href : new URL(href, baseUrl).href;
|
|
@@ -33899,7 +33555,7 @@ var init_web_search_tool = __esm(() => {
|
|
|
33899
33555
|
// src/agent/agent.ts
|
|
33900
33556
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
33901
33557
|
import { existsSync as existsSync34, watch as fsWatch, mkdirSync as mkdirSync25, realpathSync as realpathSync7, statSync as statSync9, writeFileSync as writeFileSync18 } from "node:fs";
|
|
33902
|
-
import { basename as
|
|
33558
|
+
import { basename as basename13, dirname as dirname26, join as join52, resolve as resolve18 } from "node:path";
|
|
33903
33559
|
import { clampThinkingLevel } from "@earendil-works/pi-ai/compat";
|
|
33904
33560
|
import { getAgentDir as getAgentDir5, keyText } from "@earendil-works/pi-coding-agent";
|
|
33905
33561
|
function positiveLimit(value, fallback, path) {
|
|
@@ -36822,7 +36478,7 @@ function toMinSyncReasonDiagnostic(reason) {
|
|
|
36822
36478
|
function pinExcludedPath(path) {
|
|
36823
36479
|
const resolvedPath = resolve18(path);
|
|
36824
36480
|
try {
|
|
36825
|
-
return join52(realpathSync7(dirname26(resolvedPath)),
|
|
36481
|
+
return join52(realpathSync7(dirname26(resolvedPath)), basename13(resolvedPath));
|
|
36826
36482
|
} catch {
|
|
36827
36483
|
return resolvedPath;
|
|
36828
36484
|
}
|
|
@@ -37709,7 +37365,7 @@ import { z as z4 } from "zod";
|
|
|
37709
37365
|
// src/filesystem/name-search.ts
|
|
37710
37366
|
import { realpathSync as realpathSync8 } from "node:fs";
|
|
37711
37367
|
import { lstat, readdir as readdir3 } from "node:fs/promises";
|
|
37712
|
-
import { basename as
|
|
37368
|
+
import { basename as basename14, dirname as dirname27, isAbsolute as isAbsolute13, join as join53, relative as relative8, resolve as resolve19, sep as sep10 } from "node:path";
|
|
37713
37369
|
var SKIP_DIR_NAMES2 = {
|
|
37714
37370
|
".git": true,
|
|
37715
37371
|
".autorag": true,
|
|
@@ -37743,7 +37399,7 @@ function isContained(childReal, parentReal) {
|
|
|
37743
37399
|
function pinExcludedPath2(path) {
|
|
37744
37400
|
const resolvedPath = resolve19(path);
|
|
37745
37401
|
try {
|
|
37746
|
-
return join53(realpathSync8(dirname27(resolvedPath)),
|
|
37402
|
+
return join53(realpathSync8(dirname27(resolvedPath)), basename14(resolvedPath));
|
|
37747
37403
|
} catch {
|
|
37748
37404
|
return resolvedPath;
|
|
37749
37405
|
}
|