@autorag/librarian 2.9.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +57 -2
  2. package/dist/agent/agent.d.ts +27 -21
  3. package/dist/agent/answer-citations.d.ts +22 -0
  4. package/dist/agent/answer-guidelines.d.ts +9 -9
  5. package/dist/agent/citations.d.ts +35 -23
  6. package/dist/agent/evidence-ledger.d.ts +7 -12
  7. package/dist/agent/index.d.ts +2 -2
  8. package/dist/agent/{emit-results-tool.d.ts → results.d.ts} +5 -40
  9. package/dist/agent/search-documents.d.ts +14 -19
  10. package/dist/agent/self-config.d.ts +1 -1
  11. package/dist/cli/commands/report.d.ts +1 -1
  12. package/dist/cli/commands/tui.d.ts +3 -2
  13. package/dist/cli/index.js +1466 -984
  14. package/dist/cloud/authorize.d.ts +12 -0
  15. package/dist/cloud/callback-server.d.ts +33 -0
  16. package/dist/cloud/callback.d.ts +23 -0
  17. package/dist/cloud/config.d.ts +23 -0
  18. package/dist/cloud/errors.d.ts +46 -0
  19. package/dist/cloud/index.d.ts +12 -0
  20. package/dist/cloud/models.d.ts +32 -0
  21. package/dist/cloud/oauth-login.d.ts +22 -0
  22. package/dist/cloud/pkce.d.ts +20 -0
  23. package/dist/cloud/provider.d.ts +40 -0
  24. package/dist/cloud/token.d.ts +22 -0
  25. package/dist/core.d.ts +2 -5
  26. package/dist/core.js +1341 -918
  27. package/dist/index.d.ts +2 -1
  28. package/dist/index.js +1463 -928
  29. package/dist/mcp/index.js +1404 -925
  30. package/dist/minsync/client.d.ts +1 -1
  31. package/dist/minsync/installer.d.ts +1 -1
  32. package/dist/observability/run-log.d.ts +1 -1
  33. package/dist/retrieval/index.d.ts +1 -1
  34. package/dist/retrieval/types.d.ts +0 -23
  35. package/package.json +1 -1
  36. package/skills/autorag-lite-setup/SKILL.md +1 -1
  37. package/skills/autorag-setup/SKILL.md +2 -2
  38. package/dist/agent/fast-answer-tool.d.ts +0 -56
package/dist/core.js CHANGED
@@ -5606,7 +5606,7 @@ function isGitHubAsset(value) {
5606
5606
  function isRecord5(value) {
5607
5607
  return typeof value === "object" && value !== null;
5608
5608
  }
5609
- var LATEST_RELEASE_URL = "https://api.github.com/repos/NomaDamas/MinSync/releases/latest", SHA256_HEX_PATTERN, MINSYNC_VERSION = "0.4.5", MINSYNC_CARGO_INSTALL_TIMEOUT_MS, CARGO_PROBE_TIMEOUT_MS = 5000, MinSyncReleaseError;
5609
+ var LATEST_RELEASE_URL = "https://api.github.com/repos/NomaDamas/MinSync/releases/latest", SHA256_HEX_PATTERN, MINSYNC_VERSION = "0.4.6", MINSYNC_CARGO_INSTALL_TIMEOUT_MS, CARGO_PROBE_TIMEOUT_MS = 5000, MinSyncReleaseError;
5610
5610
  var init_installer2 = __esm(() => {
5611
5611
  init_process();
5612
5612
  SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/;
@@ -8040,8 +8040,398 @@ var init_model_auth = __esm(() => {
8040
8040
  };
8041
8041
  });
8042
8042
 
8043
+ // src/agent/citations.ts
8044
+ function isAsciiDigit(code) {
8045
+ return code >= 48 && code <= 57;
8046
+ }
8047
+ function digitsEndAt(text, from) {
8048
+ let end = from;
8049
+ while (end < text.length && isAsciiDigit(text.charCodeAt(end)))
8050
+ end += 1;
8051
+ return end;
8052
+ }
8053
+ function continuesAsProse(answer, from) {
8054
+ if (answer[from] !== " " && answer[from] !== " ")
8055
+ return false;
8056
+ const next = answer.codePointAt(from + 1);
8057
+ return next !== undefined && /[\p{L}\p{N}]/u.test(String.fromCodePoint(next));
8058
+ }
8059
+ function evidenceMarkerAt(answer, index) {
8060
+ const ids = [];
8061
+ let cursor = index + 1;
8062
+ for (;; ) {
8063
+ if (answer[cursor] !== "e")
8064
+ return;
8065
+ const digitsEnd = digitsEndAt(answer, cursor + 1);
8066
+ if (digitsEnd === cursor + 1)
8067
+ return;
8068
+ ids.push(answer.slice(cursor, digitsEnd));
8069
+ cursor = digitsEnd;
8070
+ if (answer[cursor] === "]")
8071
+ return { ids, end: cursor + 1 };
8072
+ if (answer[cursor] !== ",")
8073
+ return;
8074
+ cursor += 1;
8075
+ while (answer[cursor] === " " || answer[cursor] === "\t")
8076
+ cursor += 1;
8077
+ }
8078
+ }
8079
+ function filePathFrom(raw) {
8080
+ const trimmed = raw.trim();
8081
+ return trimmed.startsWith("<") && trimmed.endsWith(">") ? trimmed.slice(1, -1).trim() : trimmed;
8082
+ }
8083
+ function fileMarkerAt(answer, index, isFile) {
8084
+ if (!answer.startsWith(FILE_MARKER_PREFIX, index))
8085
+ return;
8086
+ const bodyStart = index + FILE_MARKER_PREFIX.length;
8087
+ let closer = answer.indexOf("]", bodyStart);
8088
+ for (let attempt = 0;closer !== -1 && attempt < MAX_FILE_MARKER_CLOSERS; attempt++) {
8089
+ const path = filePathFrom(answer.slice(bodyStart, closer));
8090
+ if (path.length > 0 && isFile(path))
8091
+ return { path, end: closer + 1 };
8092
+ closer = answer.indexOf("]", closer + 1);
8093
+ }
8094
+ return;
8095
+ }
8096
+ function answerMarkers(answer, isFile = () => false) {
8097
+ const markers = [];
8098
+ let floor = 0;
8099
+ let closeIndex = -1;
8100
+ let index = 0;
8101
+ const leadingStart = (from) => {
8102
+ let start = from;
8103
+ while (start > floor && (answer[start - 1] === " " || answer[start - 1] === "\t"))
8104
+ start -= 1;
8105
+ return start;
8106
+ };
8107
+ while (index < answer.length) {
8108
+ const char = answer[index];
8109
+ if (char === "(" && answer[index + 1] === "<") {
8110
+ if (closeIndex < index + 2) {
8111
+ const found = answer.indexOf(">", index + 2);
8112
+ closeIndex = found === -1 ? answer.length : found;
8113
+ }
8114
+ if (answer[closeIndex + 1] === ")") {
8115
+ index = closeIndex + 2;
8116
+ floor = index;
8117
+ continue;
8118
+ }
8119
+ index += 1;
8120
+ continue;
8121
+ }
8122
+ if (char === "[") {
8123
+ const digitsEnd = digitsEndAt(answer, index + 1);
8124
+ if (digitsEnd > index + 1 && answer[digitsEnd] === "]" && answer[digitsEnd + 1] !== "(" && !continuesAsProse(answer, digitsEnd + 1)) {
8125
+ markers.push({
8126
+ kind: "number",
8127
+ start: leadingStart(index),
8128
+ end: digitsEnd + 1,
8129
+ number: Number(answer.slice(index + 1, digitsEnd))
8130
+ });
8131
+ index = digitsEnd + 1;
8132
+ floor = index;
8133
+ continue;
8134
+ }
8135
+ const evidence = evidenceMarkerAt(answer, index);
8136
+ if (evidence !== undefined && answer[evidence.end] !== "(") {
8137
+ markers.push({ kind: "evidence", start: leadingStart(index), end: evidence.end, ids: evidence.ids });
8138
+ index = evidence.end;
8139
+ floor = index;
8140
+ continue;
8141
+ }
8142
+ const file = fileMarkerAt(answer, index, isFile);
8143
+ if (file !== undefined && answer[file.end] !== "(") {
8144
+ markers.push({ kind: "file", start: leadingStart(index), end: file.end, path: file.path });
8145
+ index = file.end;
8146
+ floor = index;
8147
+ continue;
8148
+ }
8149
+ }
8150
+ index += 1;
8151
+ }
8152
+ return markers;
8153
+ }
8154
+ function stripUnresolvedCitations(answer, results) {
8155
+ const known = new Set(results.map((result) => result.number));
8156
+ const unresolved = new Set;
8157
+ let stripped = "";
8158
+ let cursor = 0;
8159
+ for (const marker of answerMarkers(answer)) {
8160
+ if (marker.kind !== "number" || known.has(marker.number))
8161
+ continue;
8162
+ unresolved.add(marker.number);
8163
+ stripped += answer.slice(cursor, marker.start);
8164
+ cursor = marker.end;
8165
+ }
8166
+ if (unresolved.size === 0)
8167
+ return { answer, unresolved: [] };
8168
+ return { answer: stripped + answer.slice(cursor), unresolved: [...unresolved].sort((a, b) => a - b) };
8169
+ }
8170
+ function formatCitationList(numbers) {
8171
+ return numbers.length === 0 ? "none" : numbers.map((number) => `[${number}]`).join(", ");
8172
+ }
8173
+ function duplicateNumbers(sortedNumbers) {
8174
+ const duplicates = [];
8175
+ for (let index = 1;index < sortedNumbers.length; index++) {
8176
+ const number = sortedNumbers[index];
8177
+ if (number === sortedNumbers[index - 1] && duplicates[duplicates.length - 1] !== number)
8178
+ duplicates.push(number);
8179
+ }
8180
+ return duplicates;
8181
+ }
8182
+ function assertResultsMappingOneToOne(label, results, mapping) {
8183
+ const resultNumbers = results.map((result) => result.number).sort((a, b) => a - b);
8184
+ const mappingNumbers = mapping.map((entry) => entry.number).sort((a, b) => a - b);
8185
+ const resultDuplicates = duplicateNumbers(resultNumbers);
8186
+ const mappingDuplicates = duplicateNumbers(mappingNumbers);
8187
+ const sameNumbers = resultNumbers.length === mappingNumbers.length && resultNumbers.every((number, index) => number === mappingNumbers[index]);
8188
+ if (sameNumbers && resultDuplicates.length === 0 && mappingDuplicates.length === 0)
8189
+ return;
8190
+ const duplicateNotes = [
8191
+ resultDuplicates.length > 0 ? `results repeat ${formatCitationList(resultDuplicates)}` : undefined,
8192
+ mappingDuplicates.length > 0 ? `mapping repeats ${formatCitationList(mappingDuplicates)}` : undefined
8193
+ ].filter((note) => note !== undefined);
8194
+ 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.");
8195
+ }
8196
+ var MAX_FILE_MARKER_CLOSERS = 8, FILE_MARKER_PREFIX = "[file:";
8197
+
8198
+ // src/agent/evidence-ledger.ts
8199
+ import { createHash as createHash10 } from "node:crypto";
8200
+ import { closeSync as closeSync2, openSync as openSync2, readSync, statSync } from "node:fs";
8201
+ import { isAbsolute as isAbsolute6 } from "node:path";
8202
+ function metadataString(metadata, key) {
8203
+ const value = metadata[key];
8204
+ return typeof value === "string" && value.trim().length > 0 ? value : undefined;
8205
+ }
8206
+ function metadataNumber(metadata, key) {
8207
+ const value = metadata[key];
8208
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
8209
+ }
8210
+ function isRegularFile2(path) {
8211
+ try {
8212
+ return statSync(path).isFile();
8213
+ } catch {
8214
+ return false;
8215
+ }
8216
+ }
8217
+ function readFileExcerpt(path) {
8218
+ let descriptor;
8219
+ try {
8220
+ descriptor = openSync2(path, "r");
8221
+ const buffer = Buffer.alloc(MAX_CONTENT_CHARS * 4);
8222
+ const bytes = readSync(descriptor, buffer, 0, buffer.length, 0);
8223
+ const head = buffer.subarray(0, bytes);
8224
+ return head.includes(0) ? path : head.toString("utf8").slice(0, MAX_CONTENT_CHARS);
8225
+ } catch {
8226
+ return path;
8227
+ } finally {
8228
+ if (descriptor !== undefined)
8229
+ closeSync2(descriptor);
8230
+ }
8231
+ }
8232
+ function toEvidenceRef(entry) {
8233
+ return {
8234
+ method: entry.method,
8235
+ source: entry.source,
8236
+ content: entry.content,
8237
+ retrieverMix: [...entry.retrieverMix],
8238
+ ...entry.retrievalResultId !== undefined ? { retrievalResultId: entry.retrievalResultId } : {},
8239
+ ...entry.chunkIndex !== undefined ? { chunkIndex: entry.chunkIndex } : {},
8240
+ ...entry.lineNumber !== undefined ? { lineNumber: entry.lineNumber } : {},
8241
+ ...entry.parserType !== undefined ? { parserType: entry.parserType } : {},
8242
+ ...entry.documentType !== undefined ? { documentType: entry.documentType } : {},
8243
+ ...entry.documentArea !== undefined ? { documentArea: entry.documentArea } : {},
8244
+ ...entry.evidenceType !== undefined ? { evidenceType: entry.evidenceType } : {},
8245
+ ...entry.evidenceLocation !== undefined ? { evidenceLocation: entry.evidenceLocation } : {}
8246
+ };
8247
+ }
8248
+
8249
+ class EvidenceLedger {
8250
+ entries = [];
8251
+ byKey = new Map;
8252
+ byId = new Map;
8253
+ lastOrdinal = 0;
8254
+ register(input) {
8255
+ const content = input.content.slice(0, MAX_CONTENT_CHARS);
8256
+ const digest = createHash10("sha256").update(input.content.replace(/\s+/gu, " ").trim()).digest("hex");
8257
+ const key = `${input.source}\x00${digest}`;
8258
+ const existing = this.byKey.get(key);
8259
+ if (existing !== undefined) {
8260
+ if (!existing.retrieverMix.includes(input.method))
8261
+ existing.retrieverMix.push(input.method);
8262
+ return existing.id;
8263
+ }
8264
+ const entry = {
8265
+ ...input,
8266
+ content,
8267
+ id: `e${++this.lastOrdinal}`,
8268
+ retrieverMix: [input.method]
8269
+ };
8270
+ this.entries.push(entry);
8271
+ this.byKey.set(key, entry);
8272
+ this.byId.set(entry.id, entry);
8273
+ return entry.id;
8274
+ }
8275
+ registerResult(tool, result) {
8276
+ const metadata = result.metadata;
8277
+ const chunkIndex = metadataNumber(metadata, "chunkIndex");
8278
+ const lineNumber = metadataNumber(metadata, "lineNumber");
8279
+ const parserType = metadataString(metadata, "parserType");
8280
+ const documentType = metadataString(metadata, "documentType");
8281
+ const documentArea = metadataString(metadata, "documentArea");
8282
+ const evidenceType = metadataString(metadata, "evidenceType");
8283
+ const evidenceLocation = metadataString(metadata, "evidenceLocation");
8284
+ return this.register({
8285
+ method: metadataString(metadata, "method") ?? tool,
8286
+ source: result.source,
8287
+ content: result.content,
8288
+ retrievalResultId: result.id,
8289
+ ...chunkIndex !== undefined ? { chunkIndex } : {},
8290
+ ...lineNumber !== undefined ? { lineNumber } : {},
8291
+ ...parserType !== undefined ? { parserType } : {},
8292
+ ...documentType !== undefined ? { documentType } : {},
8293
+ ...documentArea !== undefined ? { documentArea } : {},
8294
+ ...evidenceType !== undefined ? { evidenceType } : {},
8295
+ ...evidenceLocation !== undefined ? { evidenceLocation } : {}
8296
+ });
8297
+ }
8298
+ lookup(raw, options) {
8299
+ const trimmed = raw.trim();
8300
+ const ref = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1).trim() : trimmed;
8301
+ const byId = this.byId.get(ref);
8302
+ if (byId !== undefined)
8303
+ return [toEvidenceRef(byId)];
8304
+ const bySource = this.entries.filter((entry) => entry.source === ref);
8305
+ if (bySource.length > 0)
8306
+ return bySource.map(toEvidenceRef);
8307
+ if (options.allowLocalFiles && isAbsolute6(ref) && isRegularFile2(ref)) {
8308
+ return [{ method: "bash", source: ref, content: readFileExcerpt(ref) }];
8309
+ }
8310
+ return [];
8311
+ }
8312
+ clear() {
8313
+ this.entries = [];
8314
+ this.byKey.clear();
8315
+ this.byId.clear();
8316
+ }
8317
+ }
8318
+ var MAX_CONTENT_CHARS = 2000;
8319
+ var init_evidence_ledger = () => {};
8320
+
8321
+ // src/agent/answer-citations.ts
8322
+ import { isAbsolute as isAbsolute7 } from "node:path";
8323
+ function claimBefore(text, end) {
8324
+ const window = text.slice(Math.max(0, end - CLAIM_LOOKBACK_CHARS), end);
8325
+ const lineStart = window.lastIndexOf(`
8326
+ `) + 1;
8327
+ const line = window.slice(lineStart).replace(EVIDENCE_MARKER_TEXT, "");
8328
+ let start = 0;
8329
+ for (let index = line.length - 2;index >= 0; index--) {
8330
+ if (SENTENCE_END.test(line.charAt(index)) && /\s/u.test(line.charAt(index + 1))) {
8331
+ start = index + 2;
8332
+ break;
8333
+ }
8334
+ }
8335
+ const claim = line.slice(start).replace(LEADING_LIST_MARK, "").replace(/\s+/gu, " ").trim();
8336
+ return claim.length > MAX_CLAIM_CHARS ? `${claim.slice(0, MAX_CLAIM_CHARS)}…` : claim;
8337
+ }
8338
+ function deriveResultsFromAnswer(text, ledger, options) {
8339
+ const isFile = (path) => options.allowLocalFiles && isAbsolute7(path) && isRegularFile2(path);
8340
+ const sources = new Map;
8341
+ const dropped = new Set;
8342
+ let answer = "";
8343
+ let cursor = 0;
8344
+ let lastEmitted;
8345
+ const cite = (reference, claimEnd) => {
8346
+ const refs = ledger.lookup(reference, options);
8347
+ const [primary] = refs;
8348
+ if (primary === undefined) {
8349
+ dropped.add(reference);
8350
+ return;
8351
+ }
8352
+ let cited = sources.get(primary.source);
8353
+ if (cited === undefined) {
8354
+ cited = { number: sources.size + 1, refs: [], chunkKeys: new Set, claim: claimBefore(text, claimEnd) };
8355
+ sources.set(primary.source, cited);
8356
+ }
8357
+ for (const ref of refs) {
8358
+ const chunkKey = `${ref.source}\x00${ref.content ?? ""}`;
8359
+ if (cited.chunkKeys.has(chunkKey))
8360
+ continue;
8361
+ cited.chunkKeys.add(chunkKey);
8362
+ cited.refs.push(ref);
8363
+ }
8364
+ return cited.number;
8365
+ };
8366
+ for (const marker of answerMarkers(text, isFile)) {
8367
+ answer += text.slice(cursor, marker.start);
8368
+ cursor = marker.end;
8369
+ if (marker.kind === "number") {
8370
+ dropped.add(`[${marker.number}]`);
8371
+ continue;
8372
+ }
8373
+ const references = marker.kind === "evidence" ? marker.ids : [`file:${marker.path}`];
8374
+ const numbers = [];
8375
+ for (const reference of references) {
8376
+ const number = cite(marker.kind === "file" ? marker.path : reference, marker.start);
8377
+ if (number !== undefined && !numbers.includes(number))
8378
+ numbers.push(number);
8379
+ }
8380
+ if (numbers.length === 0)
8381
+ continue;
8382
+ const adjacent = lastEmitted !== undefined && text.slice(lastEmitted.end, marker.start).trim() === "";
8383
+ const fresh = numbers.filter((number) => !(adjacent && lastEmitted?.number === number));
8384
+ lastEmitted = { number: numbers[numbers.length - 1], end: marker.end };
8385
+ if (fresh.length === 0)
8386
+ continue;
8387
+ const leading = LEADING_BLANK.exec(text.slice(marker.start, marker.end))?.[0] ?? "";
8388
+ answer += leading + fresh.map((number) => `[${number}]`).join("");
8389
+ }
8390
+ answer += text.slice(cursor);
8391
+ const results = [];
8392
+ const mapping = [];
8393
+ for (const [source, cited] of sources) {
8394
+ const [primary] = cited.refs;
8395
+ if (primary === undefined)
8396
+ continue;
8397
+ results.push({
8398
+ number: cited.number,
8399
+ title: source.split(/[\\/]/u).filter((part) => part.length > 0).pop() ?? source,
8400
+ summary: cited.claim,
8401
+ evidence: cited.refs.map((ref) => {
8402
+ const excerpt = (ref.content ?? "").replace(/\s+/gu, " ").trim().slice(0, MAX_EXCERPT_CHARS);
8403
+ return ref.lineNumber !== undefined ? { excerpt, lineNumber: ref.lineNumber } : { excerpt };
8404
+ })
8405
+ });
8406
+ mapping.push({
8407
+ number: cited.number,
8408
+ source,
8409
+ method: primary.method,
8410
+ content: primary.content ?? "",
8411
+ evidenceRefs: cited.refs
8412
+ });
8413
+ }
8414
+ const diagnostics = dropped.size === 0 ? [] : [
8415
+ {
8416
+ code: "citation-without-result",
8417
+ severity: "warning",
8418
+ message: `Dropped ${dropped.size} answer citation(s) that matched no evidence retrieved in this run: ${[...dropped].join(", ")}. Every remaining citation resolves to a recorded source.`,
8419
+ source: "agent"
8420
+ }
8421
+ ];
8422
+ return { details: { answer: answer.trim(), results, mapping, warnings: [] }, diagnostics };
8423
+ }
8424
+ var MAX_EXCERPT_CHARS = 400, MAX_CLAIM_CHARS = 280, CLAIM_LOOKBACK_CHARS = 600, SENTENCE_END, LEADING_LIST_MARK, EVIDENCE_MARKER_TEXT, LEADING_BLANK;
8425
+ var init_answer_citations = __esm(() => {
8426
+ init_evidence_ledger();
8427
+ SENTENCE_END = /[.!?。!?]/u;
8428
+ LEADING_LIST_MARK = /^[\s\-*•>]+/u;
8429
+ EVIDENCE_MARKER_TEXT = /\[(?:e\d+(?:,\s*)?)+\]|\[file:[^\]]*\]/gu;
8430
+ LEADING_BLANK = /^[ \t]*/u;
8431
+ });
8432
+
8043
8433
  // src/agent/answer-guidelines.ts
8044
- 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 `answer` as a markdown image on its own line: `![short description](<absolute source path>)`, followed by its citation, e.g. `![Q3 revenue chart](</data/reports/q3 chart.png>) [2]`. Use only the real source path of a result in `results`/`mapping`. 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 = "**Citation numbers**: every bracketed citation [n] in `answer` must be the `number` of a result you emit in `results` of the same call. Numbers shown in retrieval candidates or in an earlier first answer are NOT citation numbers — renumber against your own `results`, and add a result for any evidence you cite.";
8434
+ 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: `![short description](<absolute source path>)`, followed by its citation, e.g. `![Q3 revenue chart](</data/reports/q3 chart.png>) [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.";
8045
8435
 
8046
8436
  // src/agent/datasource-skill.ts
8047
8437
  import { Type as Type2 } from "typebox";
@@ -8242,230 +8632,8 @@ var init_dupey_tool = __esm(() => {
8242
8632
  scanDuplicateDocumentsSchema = Type3.Object({});
8243
8633
  });
8244
8634
 
8245
- // src/agent/citations.ts
8246
- function isAsciiDigit(code) {
8247
- return code >= 48 && code <= 57;
8248
- }
8249
- function citationMarkers(answer) {
8250
- const markers = [];
8251
- let floor = 0;
8252
- let closeIndex = -1;
8253
- let index = 0;
8254
- while (index < answer.length) {
8255
- const char = answer[index];
8256
- if (char === "(" && answer[index + 1] === "<") {
8257
- if (closeIndex < index + 2) {
8258
- const found = answer.indexOf(">", index + 2);
8259
- closeIndex = found === -1 ? answer.length : found;
8260
- }
8261
- if (answer[closeIndex + 1] === ")") {
8262
- index = closeIndex + 2;
8263
- floor = index;
8264
- continue;
8265
- }
8266
- index += 1;
8267
- continue;
8268
- }
8269
- if (char === "[") {
8270
- let digitsEnd = index + 1;
8271
- while (digitsEnd < answer.length && isAsciiDigit(answer.charCodeAt(digitsEnd)))
8272
- digitsEnd += 1;
8273
- if (digitsEnd > index + 1 && answer[digitsEnd] === "]" && answer[digitsEnd + 1] !== "(") {
8274
- let start = index;
8275
- while (start > floor && (answer[start - 1] === " " || answer[start - 1] === "\t"))
8276
- start -= 1;
8277
- markers.push({ start, end: digitsEnd + 1, number: Number(answer.slice(index + 1, digitsEnd)) });
8278
- index = digitsEnd + 1;
8279
- floor = index;
8280
- continue;
8281
- }
8282
- }
8283
- index += 1;
8284
- }
8285
- return markers;
8286
- }
8287
- function unresolvedCitations(answer, results) {
8288
- const known = new Set(results.map((result) => result.number));
8289
- const unresolved = new Set;
8290
- for (const marker of citationMarkers(answer)) {
8291
- if (!known.has(marker.number))
8292
- unresolved.add(marker.number);
8293
- }
8294
- return [...unresolved].sort((a, b) => a - b);
8295
- }
8296
- function stripUnresolvedCitations(answer, results) {
8297
- const known = new Set(results.map((result) => result.number));
8298
- const unresolved = new Set;
8299
- let stripped = "";
8300
- let cursor = 0;
8301
- for (const marker of citationMarkers(answer)) {
8302
- if (known.has(marker.number))
8303
- continue;
8304
- unresolved.add(marker.number);
8305
- stripped += answer.slice(cursor, marker.start);
8306
- cursor = marker.end;
8307
- }
8308
- if (unresolved.size === 0)
8309
- return { answer, unresolved: [] };
8310
- return { answer: stripped + answer.slice(cursor), unresolved: [...unresolved].sort((a, b) => a - b) };
8311
- }
8312
- function formatCitationList(numbers) {
8313
- return numbers.length === 0 ? "none" : numbers.map((number) => `[${number}]`).join(", ");
8314
- }
8315
- function duplicateNumbers(sortedNumbers) {
8316
- const duplicates = [];
8317
- for (let index = 1;index < sortedNumbers.length; index++) {
8318
- const number = sortedNumbers[index];
8319
- if (number === sortedNumbers[index - 1] && duplicates[duplicates.length - 1] !== number)
8320
- duplicates.push(number);
8321
- }
8322
- return duplicates;
8323
- }
8324
- function assertUniqueResultNumbers(label, results) {
8325
- const duplicates = duplicateNumbers(results.map((result) => result.number).sort((a, b) => a - b));
8326
- if (duplicates.length === 0)
8327
- return;
8328
- throw new Error(`${label}: result numbers must be unique, but results repeat ${formatCitationList(duplicates)}. Give every result its own number and re-emit.`);
8329
- }
8330
- function assertResultsMappingOneToOne(label, results, mapping) {
8331
- const resultNumbers = results.map((result) => result.number).sort((a, b) => a - b);
8332
- const mappingNumbers = mapping.map((entry) => entry.number).sort((a, b) => a - b);
8333
- const resultDuplicates = duplicateNumbers(resultNumbers);
8334
- const mappingDuplicates = duplicateNumbers(mappingNumbers);
8335
- const sameNumbers = resultNumbers.length === mappingNumbers.length && resultNumbers.every((number, index) => number === mappingNumbers[index]);
8336
- if (sameNumbers && resultDuplicates.length === 0 && mappingDuplicates.length === 0)
8337
- return;
8338
- const duplicateNotes = [
8339
- resultDuplicates.length > 0 ? `results repeat ${formatCitationList(resultDuplicates)}` : undefined,
8340
- mappingDuplicates.length > 0 ? `mapping repeats ${formatCitationList(mappingDuplicates)}` : undefined
8341
- ].filter((note) => note !== undefined);
8342
- 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. Re-emit with consistent numbering.");
8343
- }
8344
- function assertCitationsResolve(label, answer, results) {
8345
- const unresolved = unresolvedCitations(answer, results);
8346
- if (unresolved.length === 0)
8347
- return;
8348
- const emitted = [...new Set(results.map((result) => result.number))].sort((a, b) => a - b);
8349
- throw new Error(`${label}: answer cites ${formatCitationList(unresolved)} but results only contain ${formatCitationList(emitted)}. ` + "Every bracketed citation in answer must be the number of a result in results of this same call; " + "candidate numbers from retrieval context or an earlier answer are not citation numbers. Re-emit with consistent numbering.");
8350
- }
8351
-
8352
- // src/agent/emit-results-tool.ts
8353
- import { Type as Type4 } from "typebox";
8354
- function createEmitResultsTool(capture, options) {
8355
- return {
8356
- name: EMIT_AUTORAG_RESULTS_TOOL_NAME,
8357
- label: "Emit AutoRAG Results",
8358
- description: "Return the final structured AutoRAG answer. Call this exactly once as your last action after searching, reading, and curating. Cite each result's supporting evidence in its refs, using the evidence ids shown next to retrieved results (e.g. e3); the source paths are attached for you. A call whose answer cites a number missing from results, whose result numbers repeat, or whose refs name evidence no tool returned is rejected; fix it and call again.",
8359
- parameters: emitResultsSchema,
8360
- async execute(_toolCallId, params) {
8361
- assertCitationsResolve(EMIT_AUTORAG_RESULTS_TOOL_NAME, params.answer, params.results);
8362
- assertUniqueResultNumbers(EMIT_AUTORAG_RESULTS_TOOL_NAME, params.results);
8363
- const mapping = params.results.map((result) => {
8364
- const evidenceRefs = options.ledger.resolve(result.refs, {
8365
- label: EMIT_AUTORAG_RESULTS_TOOL_NAME,
8366
- number: result.number,
8367
- fallbackContent: result.evidence.map((evidence) => evidence.excerpt).join(`
8368
- `) || result.summary,
8369
- allowLocalFiles: options.allowLocalFiles
8370
- });
8371
- const primary = evidenceRefs[0];
8372
- if (primary === undefined) {
8373
- throw new Error(`${EMIT_AUTORAG_RESULTS_TOOL_NAME}: result ${result.number} resolved no evidence. Give it at least one valid ref.`);
8374
- }
8375
- return {
8376
- number: result.number,
8377
- source: primary.source,
8378
- method: primary.method,
8379
- content: primary.content ?? "",
8380
- evidenceRefs
8381
- };
8382
- });
8383
- const details = {
8384
- answer: params.answer,
8385
- results: params.results.map((result) => ({
8386
- number: result.number,
8387
- title: result.title,
8388
- summary: result.summary,
8389
- evidence: result.evidence.map((evidence) => evidence.lineNumber !== undefined ? { excerpt: evidence.excerpt, lineNumber: evidence.lineNumber } : { excerpt: evidence.excerpt }),
8390
- confidence: result.confidence
8391
- })),
8392
- mapping,
8393
- warnings: params.warnings ?? []
8394
- };
8395
- capture(details);
8396
- return {
8397
- content: [{ type: "text", text: "AutoRAG results emitted." }],
8398
- details,
8399
- terminate: true
8400
- };
8401
- }
8402
- };
8403
- }
8404
- var EMIT_AUTORAG_RESULTS_TOOL_NAME = "emit_autorag_results", evidenceRefSchema, answerSchema, warningsSchema, curatedResultFields, emitResultsSchema, reportSchema;
8405
- var init_emit_results_tool = __esm(() => {
8406
- evidenceRefSchema = Type4.Object({
8407
- method: Type4.String({ description: "Retrieval method namespace for this evidence chunk" }),
8408
- source: Type4.String({ description: "Internal opaque source identifier for this evidence chunk" }),
8409
- excerpt: Type4.Optional(Type4.String({ description: "Evidence excerpt used for stable ID normalization" })),
8410
- content: Type4.Optional(Type4.String({ description: "Evidence content used for stable ID normalization" })),
8411
- retrievalResultId: Type4.Optional(Type4.String({ description: "Backend retrieval ID when path-opaque and stable" })),
8412
- chunkIndex: Type4.Optional(Type4.Integer({ description: "Chunk index, if available" })),
8413
- lineNumber: Type4.Optional(Type4.Integer({ description: "Line number, if available" })),
8414
- stableEvidenceId: Type4.Optional(Type4.String({ description: "Stable evidence ID, if already normalized" })),
8415
- retrieverMix: Type4.Optional(Type4.Array(Type4.String({ maxLength: 64 }), {
8416
- description: "Retrievers that contributed to this result",
8417
- maxItems: 8
8418
- })),
8419
- parserType: Type4.Optional(Type4.String({ description: "Parser that produced this evidence", maxLength: 120 })),
8420
- documentType: Type4.Optional(Type4.String({ description: "Document type, if known", maxLength: 120 })),
8421
- documentArea: Type4.Optional(Type4.String({ description: "Document collection area, if known", maxLength: 120 })),
8422
- evidenceType: Type4.Optional(Type4.String({ description: "Evidence type, if known", maxLength: 120 })),
8423
- evidenceLocation: Type4.Optional(Type4.String({ description: "Human-readable evidence location", maxLength: 120 })),
8424
- confidence: Type4.Optional(Type4.Number({ description: "Evidence confidence, 0..1", minimum: 0, maximum: 1 }))
8425
- });
8426
- answerSchema = Type4.String({
8427
- description: `Answer for the caller. When a first answer was already delivered to the caller during this run, this MUST contain only the corrections and newly verified findings relative to it — never restate the first answer; otherwise it is the complete answer. At most 5 bullet points (plus optional explanation); reference results by bracketed number (e.g. [1], [2]) without file paths or raw chunk text, except the image-embed exception below. ${ANSWER_CITATION_RULE} ${ANSWER_IMAGE_EMBED_RULE} ${ANSWER_IMAGE_DELTA_RULE}`
8428
- });
8429
- warningsSchema = Type4.Optional(Type4.Array(Type4.String(), { description: "Optional warnings about this result set" }));
8430
- curatedResultFields = {
8431
- number: Type4.Integer({ description: "1-based result number" }),
8432
- title: Type4.String({ description: "Short name of the curated knowledge unit" }),
8433
- summary: Type4.String({ description: "Key insight: purpose, details, and line range" }),
8434
- evidence: Type4.Array(Type4.Object({
8435
- excerpt: Type4.String({ description: "Supporting excerpt" }),
8436
- lineNumber: Type4.Optional(Type4.Integer({ description: "Line number of the excerpt, if known" }))
8437
- })),
8438
- confidence: Type4.Number({ description: "Confidence in this result, 0..1", minimum: 0, maximum: 1 })
8439
- };
8440
- emitResultsSchema = Type4.Object({
8441
- answer: answerSchema,
8442
- results: Type4.Array(Type4.Object({
8443
- ...curatedResultFields,
8444
- refs: Type4.Array(Type4.String(), {
8445
- minItems: 1,
8446
- description: "Evidence ids supporting this result, copied from the ids shown next to retrieved results (e.g. e3). For a local file you opened yourself with bash, give its absolute path instead."
8447
- })
8448
- }), { description: "Numbered curated knowledge units." }),
8449
- warnings: warningsSchema
8450
- });
8451
- reportSchema = Type4.Object({
8452
- answer: answerSchema,
8453
- results: Type4.Array(Type4.Object(curatedResultFields), { description: "Numbered curated knowledge units." }),
8454
- mapping: Type4.Array(Type4.Object({
8455
- number: Type4.Integer({ description: "Matches the result number this entry maps" }),
8456
- source: Type4.String({ description: "Source identifier — a real file path or a datasource id" }),
8457
- method: Type4.String({ description: "Retrieval method or tool that produced the source" }),
8458
- content: Type4.String({ description: "Raw content snippet of the evidence behind this result" }),
8459
- evidenceRefs: Type4.Optional(Type4.Array(evidenceRefSchema, {
8460
- description: "Hidden evidence chunk references supporting this curated result"
8461
- }))
8462
- }), { description: "Internal number -> source/method mapping. One entry per result number." }),
8463
- warnings: warningsSchema
8464
- });
8465
- });
8466
-
8467
8635
  // src/agent/everything-search-tool.ts
8468
- import { Type as Type5 } from "typebox";
8636
+ import { Type as Type4 } from "typebox";
8469
8637
  function createEverythingSearchTool(provider) {
8470
8638
  return {
8471
8639
  name: EVERYTHING_SEARCH_TOOL_NAME,
@@ -8517,28 +8685,28 @@ ${lines.join(`
8517
8685
  }
8518
8686
  var EVERYTHING_SEARCH_TOOL_NAME = "everything_search", MAX_RESULTS_CAP2 = 1000, DEFAULT_MAX_RESULTS3 = 100, everythingSearchSchema;
8519
8687
  var init_everything_search_tool = __esm(() => {
8520
- everythingSearchSchema = Type5.Object({
8521
- query: Type5.String({
8688
+ everythingSearchSchema = Type4.Object({
8689
+ query: Type4.String({
8522
8690
  description: 'Everything search syntax over file and folder names: space = AND, | = OR, ! = NOT, "quoted phrase", wildcards (*.pdf), ext:pdf;docx, dm:thisweek, size:>10mb, parent:<folder>, path fragments like \\reports\\ when matchPath is true.'
8523
8691
  }),
8524
- regex: Type5.Optional(Type5.Boolean({ description: "Treat query as a regular expression." })),
8525
- matchCase: Type5.Optional(Type5.Boolean()),
8526
- matchPath: Type5.Optional(Type5.Boolean({ description: "Match against the full path instead of the name." })),
8527
- wholeWord: Type5.Optional(Type5.Boolean()),
8528
- kind: Type5.Optional(Type5.Union([Type5.Literal("files"), Type5.Literal("folders")])),
8529
- path: Type5.Optional(Type5.String({ description: "Absolute folder to search within." })),
8530
- sort: Type5.Optional(Type5.Union([
8531
- Type5.Literal("name-ascending"),
8532
- Type5.Literal("name-descending"),
8533
- Type5.Literal("path-ascending"),
8534
- Type5.Literal("path-descending"),
8535
- Type5.Literal("size-ascending"),
8536
- Type5.Literal("size-descending"),
8537
- Type5.Literal("date-modified-ascending"),
8538
- Type5.Literal("date-modified-descending")
8692
+ regex: Type4.Optional(Type4.Boolean({ description: "Treat query as a regular expression." })),
8693
+ matchCase: Type4.Optional(Type4.Boolean()),
8694
+ matchPath: Type4.Optional(Type4.Boolean({ description: "Match against the full path instead of the name." })),
8695
+ wholeWord: Type4.Optional(Type4.Boolean()),
8696
+ kind: Type4.Optional(Type4.Union([Type4.Literal("files"), Type4.Literal("folders")])),
8697
+ path: Type4.Optional(Type4.String({ description: "Absolute folder to search within." })),
8698
+ sort: Type4.Optional(Type4.Union([
8699
+ Type4.Literal("name-ascending"),
8700
+ Type4.Literal("name-descending"),
8701
+ Type4.Literal("path-ascending"),
8702
+ Type4.Literal("path-descending"),
8703
+ Type4.Literal("size-ascending"),
8704
+ Type4.Literal("size-descending"),
8705
+ Type4.Literal("date-modified-ascending"),
8706
+ Type4.Literal("date-modified-descending")
8539
8707
  ])),
8540
- offset: Type5.Optional(Type5.Integer({ minimum: 0, description: "Skip this many results (pagination)." })),
8541
- maxResults: Type5.Optional(Type5.Integer({
8708
+ offset: Type4.Optional(Type4.Integer({ minimum: 0, description: "Skip this many results (pagination)." })),
8709
+ maxResults: Type4.Optional(Type4.Integer({
8542
8710
  minimum: 1,
8543
8711
  description: `Maximum results (default ${DEFAULT_MAX_RESULTS3}, cap ${MAX_RESULTS_CAP2}).`
8544
8712
  }))
@@ -8670,146 +8838,6 @@ var init_evidence_judgment = __esm(() => {
8670
8838
  };
8671
8839
  });
8672
8840
 
8673
- // src/agent/evidence-ledger.ts
8674
- import { createHash as createHash10 } from "node:crypto";
8675
- import { statSync } from "node:fs";
8676
- import { isAbsolute as isAbsolute6 } from "node:path";
8677
- function metadataString(metadata, key) {
8678
- const value = metadata[key];
8679
- return typeof value === "string" && value.trim().length > 0 ? value : undefined;
8680
- }
8681
- function metadataNumber(metadata, key) {
8682
- const value = metadata[key];
8683
- return typeof value === "number" && Number.isFinite(value) ? value : undefined;
8684
- }
8685
- function isRegularFile2(path) {
8686
- try {
8687
- return statSync(path).isFile();
8688
- } catch {
8689
- return false;
8690
- }
8691
- }
8692
- function toEvidenceRef(entry) {
8693
- return {
8694
- method: entry.method,
8695
- source: entry.source,
8696
- content: entry.content,
8697
- retrieverMix: [...entry.retrieverMix],
8698
- ...entry.retrievalResultId !== undefined ? { retrievalResultId: entry.retrievalResultId } : {},
8699
- ...entry.chunkIndex !== undefined ? { chunkIndex: entry.chunkIndex } : {},
8700
- ...entry.lineNumber !== undefined ? { lineNumber: entry.lineNumber } : {},
8701
- ...entry.parserType !== undefined ? { parserType: entry.parserType } : {},
8702
- ...entry.documentType !== undefined ? { documentType: entry.documentType } : {},
8703
- ...entry.documentArea !== undefined ? { documentArea: entry.documentArea } : {},
8704
- ...entry.evidenceType !== undefined ? { evidenceType: entry.evidenceType } : {},
8705
- ...entry.evidenceLocation !== undefined ? { evidenceLocation: entry.evidenceLocation } : {}
8706
- };
8707
- }
8708
-
8709
- class EvidenceLedger {
8710
- entries = [];
8711
- byKey = new Map;
8712
- byId = new Map;
8713
- lastOrdinal = 0;
8714
- register(input) {
8715
- const content = input.content.slice(0, MAX_CONTENT_CHARS);
8716
- const digest = createHash10("sha256").update(input.content.replace(/\s+/gu, " ").trim()).digest("hex");
8717
- const key = `${input.source}\x00${digest}`;
8718
- const existing = this.byKey.get(key);
8719
- if (existing !== undefined) {
8720
- if (!existing.retrieverMix.includes(input.method))
8721
- existing.retrieverMix.push(input.method);
8722
- return existing.id;
8723
- }
8724
- const entry = {
8725
- ...input,
8726
- content,
8727
- id: `e${++this.lastOrdinal}`,
8728
- retrieverMix: [input.method]
8729
- };
8730
- this.entries.push(entry);
8731
- this.byKey.set(key, entry);
8732
- this.byId.set(entry.id, entry);
8733
- return entry.id;
8734
- }
8735
- registerResult(tool, result) {
8736
- const metadata = result.metadata;
8737
- const chunkIndex = metadataNumber(metadata, "chunkIndex");
8738
- const lineNumber = metadataNumber(metadata, "lineNumber");
8739
- const parserType = metadataString(metadata, "parserType");
8740
- const documentType = metadataString(metadata, "documentType");
8741
- const documentArea = metadataString(metadata, "documentArea");
8742
- const evidenceType = metadataString(metadata, "evidenceType");
8743
- const evidenceLocation = metadataString(metadata, "evidenceLocation");
8744
- return this.register({
8745
- method: metadataString(metadata, "method") ?? tool,
8746
- source: result.source,
8747
- content: result.content,
8748
- retrievalResultId: result.id,
8749
- ...chunkIndex !== undefined ? { chunkIndex } : {},
8750
- ...lineNumber !== undefined ? { lineNumber } : {},
8751
- ...parserType !== undefined ? { parserType } : {},
8752
- ...documentType !== undefined ? { documentType } : {},
8753
- ...documentArea !== undefined ? { documentArea } : {},
8754
- ...evidenceType !== undefined ? { evidenceType } : {},
8755
- ...evidenceLocation !== undefined ? { evidenceLocation } : {}
8756
- });
8757
- }
8758
- resolve(refs, options) {
8759
- const resolved = [];
8760
- const seen = new Set;
8761
- const push = (entry) => {
8762
- if (seen.has(entry.id))
8763
- return;
8764
- seen.add(entry.id);
8765
- resolved.push(toEvidenceRef(entry));
8766
- };
8767
- for (const raw of refs) {
8768
- const trimmed = raw.trim();
8769
- const ref = trimmed.startsWith("[") && trimmed.endsWith("]") ? trimmed.slice(1, -1).trim() : trimmed;
8770
- const byId = this.byId.get(ref);
8771
- if (byId !== undefined) {
8772
- push(byId);
8773
- continue;
8774
- }
8775
- const bySource = this.entries.filter((entry) => entry.source === ref);
8776
- if (bySource.length > 0) {
8777
- for (const entry of bySource)
8778
- push(entry);
8779
- continue;
8780
- }
8781
- if (options.allowLocalFiles && isAbsolute6(ref) && isRegularFile2(ref)) {
8782
- const key = `file\x00${ref}`;
8783
- if (seen.has(key))
8784
- continue;
8785
- seen.add(key);
8786
- resolved.push({
8787
- method: "bash",
8788
- source: ref,
8789
- content: (options.fallbackContent.trim() || ref).slice(0, MAX_CONTENT_CHARS)
8790
- });
8791
- continue;
8792
- }
8793
- throw new Error(this.unresolvedMessage(ref, EVIDENCE_ID_PATTERN.test(ref), options));
8794
- }
8795
- return resolved;
8796
- }
8797
- clear() {
8798
- this.entries = [];
8799
- this.byKey.clear();
8800
- this.byId.clear();
8801
- }
8802
- unresolvedMessage(ref, looksLikeId, options) {
8803
- const known = this.entries.length === 0 ? "No search or fetch has returned evidence in this run yet." : `Evidence ids issued in this run: ${this.entries[0]?.id}${this.entries.length > 1 ? `..${this.entries[this.entries.length - 1]?.id}` : ""}.`;
8804
- const local = options.allowLocalFiles ? " For a file you opened yourself with bash, pass its absolute path." : "";
8805
- return `${options.label}: result ${options.number} cites evidence "${ref}" that no tool returned in this run` + `${looksLikeId ? " (unknown evidence id)" : ""}. ${known} ` + `Cite only the evidence ids shown next to retrieved results (e.g. e3).${local} Re-emit with valid refs.`;
8806
- }
8807
- }
8808
- var MAX_CONTENT_CHARS = 2000, EVIDENCE_ID_PATTERN;
8809
- var init_evidence_ledger = __esm(() => {
8810
- EVIDENCE_ID_PATTERN = /^e\d+$/u;
8811
- });
8812
-
8813
8841
  // src/agent/evidence-origins.ts
8814
8842
  function normalize3(text) {
8815
8843
  return text.normalize("NFC").toLowerCase().replace(/\s+/g, " ").trim();
@@ -8908,170 +8936,690 @@ class EvidenceOriginIndex {
8908
8936
  }
8909
8937
  }
8910
8938
  }
8911
- return;
8939
+ return;
8940
+ }
8941
+ clear() {
8942
+ this.candidates.length = 0;
8943
+ this.seen.clear();
8944
+ }
8945
+ }
8946
+ var MAX_CANDIDATES = 2000, MAX_CONTENT_LENGTH = 4000, MIN_MATCH_LENGTH = 12;
8947
+
8948
+ // src/agent/fsearch-search-tool.ts
8949
+ import { Type as Type5 } from "typebox";
8950
+ function createFSearchSearchTool(provider) {
8951
+ return {
8952
+ name: FSEARCH_SEARCH_TOOL_NAME,
8953
+ label: "FSearch Search",
8954
+ description: "Instantly find files and folders by name, extension, path, size, or modified date across the configured search folders using fsearch-cli/FSearch (macOS/Linux), falling back to a slow filesystem walk when fsearch-cli is not installed. Returns absolute paths; read file contents with bash.",
8955
+ parameters: fsearchSearchSchema,
8956
+ async execute(_toolCallId, params) {
8957
+ const query = params.query.trim();
8958
+ if (query.length === 0) {
8959
+ return {
8960
+ content: [{ type: "text", text: "FSearch query was empty; nothing searched." }],
8961
+ details: { method: FSEARCH_SEARCH_TOOL_NAME, resultCount: 0, sources: [], results: [] }
8962
+ };
8963
+ }
8964
+ const request = {
8965
+ ...params,
8966
+ query,
8967
+ maxResults: Math.min(params.maxResults ?? DEFAULT_MAX_RESULTS4, MAX_RESULTS_CAP3)
8968
+ };
8969
+ const result = await provider.searchFsearch(request);
8970
+ if (!result.ok) {
8971
+ return {
8972
+ content: [{ type: "text", text: `FSearch ${result.reason}: ${result.message}` }],
8973
+ details: { method: FSEARCH_SEARCH_TOOL_NAME, resultCount: 0, sources: [], results: [] }
8974
+ };
8975
+ }
8976
+ const lines = result.results.map((entry, index) => {
8977
+ const parts = [`[${index + 1}] ${entry.type} ${entry.path}`];
8978
+ if (entry.size !== undefined)
8979
+ parts.push(`size=${entry.size}`);
8980
+ if (entry.dateModified !== undefined)
8981
+ parts.push(`modified=${entry.dateModified}`);
8982
+ return parts.join(" ");
8983
+ });
8984
+ const backendLabel = result.backend === "walk" ? "FSearch slow filesystem walk (fsearch-cli unavailable)" : "FSearch";
8985
+ const limitNote = result.backend === "fsearch-cli" && (result.total ?? 0) > lines.length && lines.length === request.maxResults ? ` of ${result.total} total (limit reached; narrow the query or page with offset)` : "";
8986
+ const noteSuffix = result.note !== undefined ? `
8987
+ ${result.note}` : "";
8988
+ const text = lines.length === 0 ? `${backendLabel} found no files or folders matching: ${query}${noteSuffix}` : `${backendLabel} found ${lines.length} item(s)${limitNote}:
8989
+ ${lines.join(`
8990
+ `)}${noteSuffix}`;
8991
+ return {
8992
+ content: [{ type: "text", text }],
8993
+ details: {
8994
+ method: FSEARCH_SEARCH_TOOL_NAME,
8995
+ resultCount: result.results.length,
8996
+ sources: result.results.map((entry) => entry.path),
8997
+ results: result.results.map((entry, index) => ({ source: entry.path, excerpt: lines[index] }))
8998
+ }
8999
+ };
9000
+ }
9001
+ };
9002
+ }
9003
+ var FSEARCH_SEARCH_TOOL_NAME = "fsearch_search", MAX_RESULTS_CAP3 = 1000, DEFAULT_MAX_RESULTS4 = 100, fsearchSearchSchema;
9004
+ var init_fsearch_search_tool = __esm(() => {
9005
+ fsearchSearchSchema = Type5.Object({
9006
+ query: Type5.String({
9007
+ description: 'FSearch query syntax over file and folder names: space = AND, OR keyword = OR (never the pipe |), ! = NOT, "quoted phrase", wildcards (*.pdf), ext:pdf;docx, size:>10mb, path:<fragment>, case:, regex:. When the slow-walk fallback answers (fsearch-cli not installed), only substring/regex name matching applies.'
9008
+ }),
9009
+ regex: Type5.Optional(Type5.Boolean({ description: "Treat query as a regular expression." })),
9010
+ matchCase: Type5.Optional(Type5.Boolean()),
9011
+ matchPath: Type5.Optional(Type5.Boolean({ description: "Match against the full path instead of the name." })),
9012
+ kind: Type5.Optional(Type5.Union([Type5.Literal("files"), Type5.Literal("folders")])),
9013
+ path: Type5.Optional(Type5.String({ description: "Absolute folder to search within." })),
9014
+ sort: Type5.Optional(Type5.Union([
9015
+ Type5.Literal("name-ascending"),
9016
+ Type5.Literal("name-descending"),
9017
+ Type5.Literal("path-ascending"),
9018
+ Type5.Literal("path-descending"),
9019
+ Type5.Literal("size-ascending"),
9020
+ Type5.Literal("size-descending"),
9021
+ Type5.Literal("date-modified-ascending"),
9022
+ Type5.Literal("date-modified-descending")
9023
+ ])),
9024
+ offset: Type5.Optional(Type5.Integer({ minimum: 0, description: "Skip this many results (pagination)." })),
9025
+ maxResults: Type5.Optional(Type5.Integer({
9026
+ minimum: 1,
9027
+ description: `Maximum results (default ${DEFAULT_MAX_RESULTS4}, cap ${MAX_RESULTS_CAP3}).`
9028
+ }))
9029
+ });
9030
+ });
9031
+
9032
+ // src/cloud/config.ts
9033
+ function normalizeBaseUrl(value) {
9034
+ const trimmed = value.trim();
9035
+ let end = trimmed.length;
9036
+ while (end > 0 && trimmed.charCodeAt(end - 1) === 47)
9037
+ end--;
9038
+ return trimmed.slice(0, end);
9039
+ }
9040
+ function resolveBaseUrl(env) {
9041
+ const configured = env[AUTORAG_BASE_URL_ENV];
9042
+ const value = typeof configured === "string" && configured.trim() !== "" ? configured : DEFAULT_BASE_URL;
9043
+ return normalizeBaseUrl(value);
9044
+ }
9045
+ var PROVIDER_ID = "autorag", PROVIDER_NAME = "AutoRAG", DEFAULT_BASE_URL = "https://api.dazziapp.com", AUTORAG_BASE_URL_ENV = "AUTORAG_BASE_URL", AUTORAG_API_KEY_ENV = "AUTORAG_API_KEY", API_PATH = "/v1", MODELS_PATH = "/v1/models", AUTHORIZE_PATH = "/desktop/authorize", TOKEN_PATH = "/api/desktop/token", OAUTH_CLIENT_ID = "agent", API_KEY_CONFIG_VALUE;
9046
+ var init_config = __esm(() => {
9047
+ API_KEY_CONFIG_VALUE = `$${AUTORAG_API_KEY_ENV}`;
9048
+ });
9049
+
9050
+ // src/cloud/errors.ts
9051
+ function extractErrorPayload(body) {
9052
+ if (typeof body !== "object" || body === null)
9053
+ return {};
9054
+ const record = body;
9055
+ const error = record.error;
9056
+ if (typeof error === "string")
9057
+ return error.length > 0 ? { message: error } : {};
9058
+ if (typeof error === "object" && error !== null) {
9059
+ const inner = error;
9060
+ const payload = {};
9061
+ if (typeof inner.message === "string")
9062
+ payload.message = inner.message;
9063
+ if (typeof inner.code === "string")
9064
+ payload.code = inner.code;
9065
+ if (typeof inner.type === "string")
9066
+ payload.type = inner.type;
9067
+ return payload;
9068
+ }
9069
+ return typeof record.message === "string" ? { message: record.message } : {};
9070
+ }
9071
+ var AutoragError, AutoragOAuthError, AutoragHttpError;
9072
+ var init_errors3 = __esm(() => {
9073
+ AutoragError = class AutoragError extends Error {
9074
+ constructor(message, options) {
9075
+ super(message, options);
9076
+ this.name = "AutoragError";
9077
+ }
9078
+ };
9079
+ AutoragOAuthError = class AutoragOAuthError extends AutoragError {
9080
+ constructor(message, options) {
9081
+ super(message, options);
9082
+ this.name = "AutoragOAuthError";
9083
+ }
9084
+ };
9085
+ AutoragHttpError = class AutoragHttpError extends AutoragError {
9086
+ status;
9087
+ code;
9088
+ type;
9089
+ constructor(message, options) {
9090
+ super(message, { cause: options.cause });
9091
+ this.name = "AutoragHttpError";
9092
+ this.status = options.status;
9093
+ if (options.code !== undefined)
9094
+ this.code = options.code;
9095
+ if (options.type !== undefined)
9096
+ this.type = options.type;
9097
+ }
9098
+ };
9099
+ });
9100
+
9101
+ // src/cloud/models.ts
9102
+ function resolveCredentialKey(credential) {
9103
+ if (credential === undefined)
9104
+ return;
9105
+ const value = credential.type === "oauth" ? credential.access : credential.key;
9106
+ return typeof value === "string" && value !== "" ? value : undefined;
9107
+ }
9108
+ async function fetchModels(options) {
9109
+ const doFetch = options.fetchImpl ?? fetch;
9110
+ let response;
9111
+ try {
9112
+ response = await doFetch(`${normalizeBaseUrl(options.baseUrl)}${MODELS_PATH}`, {
9113
+ headers: { authorization: `Bearer ${options.apiKey}`, accept: "application/json" },
9114
+ signal: options.signal
9115
+ });
9116
+ } catch (cause) {
9117
+ if (options.signal?.aborted) {
9118
+ throw new AutoragOAuthError("AutoRAG model refresh was cancelled.", { cause });
9119
+ }
9120
+ throw new AutoragOAuthError(`AutoRAG model listing could not reach ${normalizeBaseUrl(options.baseUrl)}.`, {
9121
+ cause
9122
+ });
9123
+ }
9124
+ let body;
9125
+ try {
9126
+ body = await response.json();
9127
+ } catch {
9128
+ body = undefined;
9129
+ }
9130
+ if (!response.ok)
9131
+ throw mapModelsError(response.status, body);
9132
+ return mapServerModels(body);
9133
+ }
9134
+ function mapModelsError(status, body) {
9135
+ const payload = extractErrorPayload(body);
9136
+ if (status === 401) {
9137
+ return new AutoragHttpError("AutoRAG rejected the API key (HTTP 401). Run /login again or set AUTORAG_API_KEY.", {
9138
+ status,
9139
+ code: payload.code ?? "unauthorized",
9140
+ type: payload.type
9141
+ });
9142
+ }
9143
+ const reason = payload.message ?? "the server returned no error message";
9144
+ return new AutoragHttpError(`AutoRAG model listing failed (HTTP ${status}): ${reason}`, {
9145
+ status,
9146
+ code: payload.code,
9147
+ type: payload.type
9148
+ });
9149
+ }
9150
+ function mapServerModels(payload) {
9151
+ if (typeof payload !== "object" || payload === null)
9152
+ return [];
9153
+ const data = payload.data;
9154
+ if (!Array.isArray(data))
9155
+ return [];
9156
+ const models = [];
9157
+ for (const entry of data) {
9158
+ if (typeof entry !== "object" || entry === null)
9159
+ continue;
9160
+ const model = mapServerModel(entry);
9161
+ if (model !== undefined)
9162
+ models.push(model);
9163
+ }
9164
+ return models;
9165
+ }
9166
+ function mapServerModel(entry) {
9167
+ const id = readString2(entry.id);
9168
+ if (id === undefined)
9169
+ return;
9170
+ return {
9171
+ type: "chat",
9172
+ id,
9173
+ name: readString2(entry.name) ?? id,
9174
+ input: readInputModalities(entry.input_modalities),
9175
+ cost: readPricing(entry.pricing),
9176
+ reasoning: entry.reasoning === true,
9177
+ contextWindow: readPositiveNumber(entry.context_window) ?? DEFAULT_CONTEXT_WINDOW,
9178
+ maxTokens: readPositiveNumber(entry.max_output_tokens) ?? DEFAULT_MAX_TOKENS
9179
+ };
9180
+ }
9181
+ function buildStoreEntry(models, providerId, baseUrl) {
9182
+ const stored = [];
9183
+ for (const model of models) {
9184
+ if (model.type === "image" || model.type === "classifier")
9185
+ continue;
9186
+ stored.push({
9187
+ type: "chat",
9188
+ id: model.id,
9189
+ name: model.name,
9190
+ api: model.api ?? "openai-responses",
9191
+ provider: providerId,
9192
+ baseUrl: model.baseUrl ?? baseUrl,
9193
+ input: model.input,
9194
+ cost: model.cost,
9195
+ reasoning: model.reasoning,
9196
+ contextWindow: model.contextWindow,
9197
+ maxTokens: model.maxTokens
9198
+ });
9199
+ }
9200
+ return { models: stored, checkedAt: Date.now() };
9201
+ }
9202
+ function modelsFromStore(stored) {
9203
+ if (stored === undefined)
9204
+ return [];
9205
+ const models = [];
9206
+ for (const entry of stored.models) {
9207
+ if (entry.type === "image" || entry.type === "classifier")
9208
+ continue;
9209
+ models.push({
9210
+ type: "chat",
9211
+ id: entry.id,
9212
+ name: entry.name,
9213
+ input: entry.input,
9214
+ cost: entry.cost,
9215
+ reasoning: entry.reasoning,
9216
+ contextWindow: entry.contextWindow,
9217
+ maxTokens: entry.maxTokens
9218
+ });
9219
+ }
9220
+ return models;
9221
+ }
9222
+ function readString2(value) {
9223
+ return typeof value === "string" && value.trim() !== "" ? value.trim() : undefined;
9224
+ }
9225
+ function readPositiveNumber(value) {
9226
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
9227
+ }
9228
+ function readInputModalities(value) {
9229
+ if (!Array.isArray(value))
9230
+ return ["text"];
9231
+ const modalities = value.filter((item) => item === "text" || item === "image");
9232
+ const unique = Array.from(new Set(modalities));
9233
+ return unique.length > 0 ? unique : ["text"];
9234
+ }
9235
+ function readPricing(value) {
9236
+ const pricing = typeof value === "object" && value !== null ? value : {};
9237
+ return {
9238
+ input: readPositiveNumber(pricing.input) ?? 0,
9239
+ output: readPositiveNumber(pricing.output) ?? 0,
9240
+ cacheRead: readPositiveNumber(pricing.cache_read) ?? 0,
9241
+ cacheWrite: readPositiveNumber(pricing.cache_write) ?? 0
9242
+ };
9243
+ }
9244
+ var DEFAULT_CONTEXT_WINDOW = 128000, DEFAULT_MAX_TOKENS = 16384;
9245
+ var init_models = __esm(() => {
9246
+ init_config();
9247
+ init_errors3();
9248
+ });
9249
+
9250
+ // src/cloud/authorize.ts
9251
+ function buildAuthorizeUrl(params) {
9252
+ const url = new URL(`${normalizeBaseUrl(params.baseUrl)}${AUTHORIZE_PATH}`);
9253
+ url.searchParams.set("client", OAUTH_CLIENT_ID);
9254
+ url.searchParams.set("redirect_uri", params.redirectUri);
9255
+ url.searchParams.set("state", params.state);
9256
+ url.searchParams.set("code_challenge", params.codeChallenge);
9257
+ url.searchParams.set("code_challenge_method", "S256");
9258
+ return url.toString();
9259
+ }
9260
+ var init_authorize = __esm(() => {
9261
+ init_config();
9262
+ });
9263
+
9264
+ // src/cloud/callback.ts
9265
+ function parseCallbackQuery(params) {
9266
+ const result = {};
9267
+ const code = params.get("code");
9268
+ const error = params.get("error");
9269
+ const state = params.get("state");
9270
+ if (code !== null && code !== "")
9271
+ result.code = code;
9272
+ if (error !== null && error !== "")
9273
+ result.error = error;
9274
+ if (state !== null && state !== "")
9275
+ result.state = state;
9276
+ return result;
9277
+ }
9278
+ function parseCallbackInput(input) {
9279
+ const trimmed = input.trim();
9280
+ if (trimmed === "") {
9281
+ throw new AutoragOAuthError("AutoRAG sign-in failed: nothing was pasted.");
9282
+ }
9283
+ if (/^https?:\/\//i.test(trimmed)) {
9284
+ try {
9285
+ return parseCallbackQuery(new URL(trimmed).searchParams);
9286
+ } catch {
9287
+ throw new AutoragOAuthError("AutoRAG sign-in failed: the pasted URL is not valid.");
9288
+ }
9289
+ }
9290
+ const query = trimmed.startsWith("?") ? trimmed.slice(1) : trimmed;
9291
+ if (!query.includes(" ")) {
9292
+ const params = new URLSearchParams(query);
9293
+ if (params.has("code") || params.has("error") || params.has("state")) {
9294
+ return parseCallbackQuery(params);
9295
+ }
9296
+ }
9297
+ return { code: trimmed };
9298
+ }
9299
+ function requireAuthorizationCode(result, expectedState) {
9300
+ if (result.state !== undefined && result.state !== expectedState) {
9301
+ throw new AutoragOAuthError("AutoRAG sign-in failed: state mismatch (the callback did not originate from this login attempt).");
9302
+ }
9303
+ if (result.error !== undefined) {
9304
+ throw new AutoragOAuthError(result.error === "access_denied" ? "AutoRAG sign-in was cancelled in the browser." : `AutoRAG sign-in failed: ${result.error}.`);
9305
+ }
9306
+ if (result.code === undefined) {
9307
+ throw new AutoragOAuthError("AutoRAG sign-in failed: the callback carried no authorization code.");
9308
+ }
9309
+ return { code: result.code };
9310
+ }
9311
+ var init_callback = __esm(() => {
9312
+ init_errors3();
9313
+ });
9314
+
9315
+ // src/cloud/callback-server.ts
9316
+ import { createServer as createServer3 } from "node:http";
9317
+ function createDeferred() {
9318
+ let resolve;
9319
+ let reject;
9320
+ const promise = new Promise((res, rej) => {
9321
+ resolve = res;
9322
+ reject = rej;
9323
+ });
9324
+ return { promise, resolve, reject };
9325
+ }
9326
+ function renderCallbackPage(failed) {
9327
+ const message = failed ? "AutoRAG sign-in did not complete. You can close this tab and try again." : "Signed in to AutoRAG. You can close this tab.";
9328
+ return [
9329
+ "<!doctype html>",
9330
+ '<html lang="en">',
9331
+ "<head>",
9332
+ '<meta charset="utf-8">',
9333
+ '<meta name="viewport" content="width=device-width, initial-scale=1">',
9334
+ "<title>AutoRAG</title>",
9335
+ "</head>",
9336
+ '<body style="font-family: system-ui, sans-serif; margin: 4rem auto; max-width: 32rem; text-align: center;">',
9337
+ `<p>${message}</p>`,
9338
+ "</body>",
9339
+ "</html>"
9340
+ ].join(`
9341
+ `);
9342
+ }
9343
+ function startCallbackServer(options) {
9344
+ const host = options.host ?? "127.0.0.1";
9345
+ const timeoutMs = options.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS;
9346
+ const server = createServer3();
9347
+ const started = createDeferred();
9348
+ const callback = createDeferred();
9349
+ callback.promise.catch(() => {});
9350
+ let finished = false;
9351
+ let startSettled = false;
9352
+ let timer;
9353
+ function cleanup() {
9354
+ clearTimeout(timer);
9355
+ options.signal?.removeEventListener("abort", onAbort);
9356
+ if (server.listening) {
9357
+ server.closeAllConnections();
9358
+ server.close();
9359
+ }
9360
+ }
9361
+ function fail(error) {
9362
+ if (finished)
9363
+ return;
9364
+ finished = true;
9365
+ cleanup();
9366
+ callback.reject(error);
9367
+ }
9368
+ function onAbort() {
9369
+ fail(new AutoragOAuthError("AutoRAG sign-in was cancelled."));
9370
+ }
9371
+ server.on("request", (req, res) => {
9372
+ const url = new URL(req.url ?? "/", `http://${host}`);
9373
+ if (req.method !== "GET" || url.pathname !== CALLBACK_PATH) {
9374
+ res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
9375
+ res.end("Not found");
9376
+ return;
9377
+ }
9378
+ if (finished) {
9379
+ res.writeHead(410, { "content-type": "text/plain; charset=utf-8" });
9380
+ res.end("Callback already handled");
9381
+ return;
9382
+ }
9383
+ const params = url.searchParams;
9384
+ const code = params.get("code");
9385
+ const oauthError = params.get("error");
9386
+ const hasResponse = code !== null && code !== "" || oauthError !== null && oauthError !== "";
9387
+ if (params.get("state") !== options.state || !hasResponse) {
9388
+ res.writeHead(400, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" });
9389
+ res.end(renderCallbackPage(true));
9390
+ return;
9391
+ }
9392
+ finished = true;
9393
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" });
9394
+ res.end(renderCallbackPage(params.has("error")));
9395
+ cleanup();
9396
+ callback.resolve(params);
9397
+ });
9398
+ server.on("error", (error) => {
9399
+ const wrapped = error instanceof Error ? error : new Error(String(error));
9400
+ if (!startSettled) {
9401
+ startSettled = true;
9402
+ cleanup();
9403
+ started.reject(wrapped);
9404
+ return;
9405
+ }
9406
+ fail(wrapped);
9407
+ });
9408
+ server.listen(0, host, () => {
9409
+ const address = server.address();
9410
+ const port = typeof address === "object" && address !== null ? address.port : 0;
9411
+ const redirectUri = `http://${host}:${port}${CALLBACK_PATH}`;
9412
+ timer = setTimeout(() => fail(new AutoragOAuthError("AutoRAG sign-in timed out waiting for the browser callback.")), timeoutMs);
9413
+ if (options.signal?.aborted)
9414
+ onAbort();
9415
+ else
9416
+ options.signal?.addEventListener("abort", onAbort, { once: true });
9417
+ startSettled = true;
9418
+ options.onListening?.(redirectUri);
9419
+ started.resolve({
9420
+ redirectUri,
9421
+ waitForCallback: () => callback.promise,
9422
+ close: () => {
9423
+ if (!finished)
9424
+ fail(new AutoragOAuthError("AutoRAG sign-in was cancelled."));
9425
+ }
9426
+ });
9427
+ });
9428
+ return started.promise;
9429
+ }
9430
+ var CALLBACK_PATH = "/callback", DEFAULT_LOGIN_TIMEOUT_MS;
9431
+ var init_callback_server = __esm(() => {
9432
+ init_errors3();
9433
+ DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
9434
+ });
9435
+
9436
+ // src/cloud/pkce.ts
9437
+ import { createHash as createHash11, randomBytes } from "node:crypto";
9438
+ function base64UrlEncode(bytes) {
9439
+ return Buffer.from(bytes).toString("base64url");
9440
+ }
9441
+ function createCodeChallenge(verifier) {
9442
+ return base64UrlEncode(createHash11("sha256").update(verifier, "ascii").digest());
9443
+ }
9444
+ function createCodeVerifier(byteLength = 32) {
9445
+ return base64UrlEncode(randomBytes(byteLength));
9446
+ }
9447
+ function createState(byteLength = 24) {
9448
+ return base64UrlEncode(randomBytes(byteLength));
9449
+ }
9450
+ function createPkcePair(byteLength = 32) {
9451
+ const verifier = createCodeVerifier(byteLength);
9452
+ return { verifier, challenge: createCodeChallenge(verifier) };
9453
+ }
9454
+ var init_pkce = () => {};
9455
+
9456
+ // src/cloud/token.ts
9457
+ function mapTokenExchangeError(status, body) {
9458
+ const payload = extractErrorPayload(body);
9459
+ const reason = payload.message ?? "the server returned no error message";
9460
+ return new AutoragHttpError(`AutoRAG token exchange failed (HTTP ${status}): ${reason}`, {
9461
+ status,
9462
+ code: payload.code,
9463
+ type: payload.type
9464
+ });
9465
+ }
9466
+ async function exchangeCodeForToken(options) {
9467
+ const doFetch = options.fetchImpl ?? fetch;
9468
+ let response;
9469
+ try {
9470
+ response = await doFetch(`${normalizeBaseUrl(options.baseUrl)}${TOKEN_PATH}`, {
9471
+ method: "POST",
9472
+ headers: { "content-type": "application/json", accept: "application/json" },
9473
+ body: JSON.stringify({
9474
+ code: options.code,
9475
+ code_verifier: options.codeVerifier,
9476
+ redirect_uri: options.redirectUri
9477
+ }),
9478
+ signal: options.signal
9479
+ });
9480
+ } catch (cause) {
9481
+ if (options.signal?.aborted) {
9482
+ throw new AutoragOAuthError("AutoRAG sign-in was cancelled.", { cause });
9483
+ }
9484
+ throw new AutoragOAuthError(`AutoRAG token exchange could not reach ${normalizeBaseUrl(options.baseUrl)}.`, {
9485
+ cause
9486
+ });
8912
9487
  }
8913
- clear() {
8914
- this.candidates.length = 0;
8915
- this.seen.clear();
9488
+ let body;
9489
+ try {
9490
+ body = await response.json();
9491
+ } catch {
9492
+ body = undefined;
9493
+ }
9494
+ if (!response.ok)
9495
+ throw mapTokenExchangeError(response.status, body);
9496
+ const accessToken = readAccessToken(body);
9497
+ if (accessToken === undefined) {
9498
+ throw new AutoragOAuthError("AutoRAG token exchange succeeded but returned no access token.");
8916
9499
  }
9500
+ const email = readEmail(body);
9501
+ return email === undefined ? { accessToken } : { accessToken, email };
8917
9502
  }
8918
- var MAX_CANDIDATES = 2000, MAX_CONTENT_LENGTH = 4000, MIN_MATCH_LENGTH = 12;
8919
-
8920
- // src/agent/fast-answer-tool.ts
8921
- import { Type as Type6 } from "typebox";
8922
- function createEmitFastAnswerTool(capture, options) {
8923
- return {
8924
- name: EMIT_FAST_ANSWER_TOOL_NAME,
8925
- label: "Emit Fast Answer",
8926
- description: "Deliver the immediate first answer to the user. Call this exactly once during the fast phase with a complete, self-contained answer built only from the baseline retrieval evidence. Do not call any other tool before this one. The run continues afterwards for verification. A call whose answer cites a number missing from results, or whose refs name evidence that was not provided, is rejected; fix it and call again.",
8927
- parameters: fastAnswerSchema,
8928
- async execute(_toolCallId, params) {
8929
- assertCitationsResolve(EMIT_FAST_ANSWER_TOOL_NAME, params.answer, params.results);
8930
- const evidenceRefs = [];
8931
- for (const result of params.results) {
8932
- if (result.refs === undefined || result.refs.length === 0)
8933
- continue;
8934
- const refs = options.ledger.resolve(result.refs, {
8935
- label: EMIT_FAST_ANSWER_TOOL_NAME,
8936
- number: result.number,
8937
- fallbackContent: result.summary,
8938
- allowLocalFiles: false
8939
- });
8940
- if (refs.length > 0)
8941
- evidenceRefs.push({ number: result.number, refs });
8942
- }
8943
- const details = {
8944
- answer: params.answer,
8945
- results: params.results.map((result) => ({
8946
- number: result.number,
8947
- title: result.title,
8948
- summary: result.summary,
8949
- evidence: (result.evidence ?? []).map((evidence) => evidence.lineNumber !== undefined ? { excerpt: evidence.excerpt, lineNumber: evidence.lineNumber } : { excerpt: evidence.excerpt }),
8950
- ...result.confidence !== undefined ? { confidence: result.confidence } : {}
8951
- })),
8952
- sources: evidenceRefs.flatMap((entry) => {
8953
- const [primary] = entry.refs;
8954
- return primary === undefined ? [] : [{ number: entry.number, source: primary.source }];
8955
- }),
8956
- evidenceRefs
8957
- };
8958
- capture(details);
8959
- return {
8960
- content: [
8961
- {
8962
- type: "text",
8963
- text: "Fast answer delivered to the user. Stop now; a deeper verification pass follows separately."
8964
- }
8965
- ],
8966
- details
8967
- };
8968
- }
8969
- };
9503
+ function readAccessToken(body) {
9504
+ if (typeof body !== "object" || body === null)
9505
+ return;
9506
+ const value = body.access_token;
9507
+ return typeof value === "string" && value !== "" ? value : undefined;
8970
9508
  }
8971
- var EMIT_FAST_ANSWER_TOOL_NAME = "emit_fast_answer", fastAnswerSchema;
8972
- var init_fast_answer_tool = __esm(() => {
8973
- fastAnswerSchema = Type6.Object({
8974
- answer: Type6.String({
8975
- description: `Complete, self-contained first answer for the caller in at most 5 bullet points (plus optional explanation), produced immediately from baseline retrieval evidence. Reference results by bracketed number (e.g. [1], [2]) without file paths or raw chunk text, except the image-embed exception below. ${ANSWER_CITATION_RULE} ${ANSWER_IMAGE_EMBED_RULE}`
8976
- }),
8977
- results: Type6.Array(Type6.Object({
8978
- number: Type6.Integer({ description: "1-based result number" }),
8979
- title: Type6.String({ description: "Short name of the knowledge unit" }),
8980
- summary: Type6.String({ description: "Key insight backing the first answer" }),
8981
- evidence: Type6.Optional(Type6.Array(Type6.Object({
8982
- excerpt: Type6.String({ description: "Supporting excerpt" }),
8983
- lineNumber: Type6.Optional(Type6.Integer({ description: "Line number of the excerpt, if known" }))
8984
- }))),
8985
- confidence: Type6.Optional(Type6.Number({ description: "Confidence in this result, 0..1", minimum: 0, maximum: 1 })),
8986
- refs: Type6.Optional(Type6.Array(Type6.String(), {
8987
- description: "Evidence ids from the baseline evidence that support this result (e.g. e3). The source path is attached for you. Omit only when no baseline evidence backs the result."
8988
- }))
8989
- }), { description: "Numbered knowledge units backing the first answer." })
9509
+ function readEmail(body) {
9510
+ if (typeof body !== "object" || body === null)
9511
+ return;
9512
+ const value = body.email;
9513
+ return typeof value === "string" && value !== "" ? value : undefined;
9514
+ }
9515
+ var init_token = __esm(() => {
9516
+ init_config();
9517
+ init_errors3();
9518
+ });
9519
+
9520
+ // src/cloud/oauth-login.ts
9521
+ function refreshAutoragCredentials(credentials) {
9522
+ return credentials;
9523
+ }
9524
+ async function loginAutorag(callbacks, options) {
9525
+ const { verifier, challenge } = createPkcePair();
9526
+ const state = createState();
9527
+ const server = await startCallbackServer({
9528
+ state,
9529
+ timeoutMs: options.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS,
9530
+ signal: callbacks.signal
8990
9531
  });
9532
+ try {
9533
+ callbacks.onAuth({
9534
+ url: buildAuthorizeUrl({
9535
+ baseUrl: options.baseUrl,
9536
+ redirectUri: server.redirectUri,
9537
+ state,
9538
+ codeChallenge: challenge
9539
+ }),
9540
+ instructions: `Waiting for the AutoRAG sign-in callback on ${server.redirectUri}. If the browser cannot reach this machine, paste the final redirect URL or the code instead.`
9541
+ });
9542
+ callbacks.onProgress?.(`Listening for the AutoRAG sign-in callback on ${server.redirectUri}`);
9543
+ const outcome = await Promise.race([
9544
+ server.waitForCallback().then((params) => ({ params })),
9545
+ callbacks.onPrompt({
9546
+ message: "Complete the AutoRAG sign-in in your browser, or paste the final redirect URL / authorization code here:",
9547
+ placeholder: server.redirectUri,
9548
+ allowEmpty: true
9549
+ }).then((value) => ({ value }))
9550
+ ]);
9551
+ const parsed = "params" in outcome ? parseCallbackQuery(outcome.params) : parseCallbackInput(outcome.value);
9552
+ const { code } = requireAuthorizationCode(parsed, state);
9553
+ const token = await exchangeCodeForToken({
9554
+ baseUrl: options.baseUrl,
9555
+ code,
9556
+ codeVerifier: verifier,
9557
+ redirectUri: server.redirectUri,
9558
+ signal: callbacks.signal,
9559
+ fetchImpl: options.fetchImpl
9560
+ });
9561
+ return {
9562
+ access: token.accessToken,
9563
+ refresh: "",
9564
+ expires: Number.MAX_SAFE_INTEGER,
9565
+ ...token.email !== undefined ? { email: token.email } : {}
9566
+ };
9567
+ } finally {
9568
+ server.close();
9569
+ }
9570
+ }
9571
+ var init_oauth_login = __esm(() => {
9572
+ init_authorize();
9573
+ init_callback();
9574
+ init_callback_server();
9575
+ init_pkce();
9576
+ init_token();
8991
9577
  });
8992
9578
 
8993
- // src/agent/fsearch-search-tool.ts
8994
- import { Type as Type7 } from "typebox";
8995
- function createFSearchSearchTool(provider) {
9579
+ // src/cloud/provider.ts
9580
+ async function refreshAutoragModels(context, endpoints) {
9581
+ const stored = modelsFromStore(context.stored);
9582
+ const apiKey = resolveCredentialKey(context.credential);
9583
+ if (!context.allowNetwork || apiKey === undefined)
9584
+ return stored;
9585
+ let models;
9586
+ try {
9587
+ models = await fetchModels({ baseUrl: endpoints.baseUrl, apiKey, signal: context.signal });
9588
+ } catch {
9589
+ return stored;
9590
+ }
9591
+ if (models.length === 0)
9592
+ return stored;
9593
+ try {
9594
+ await context.publish({ persist: buildStoreEntry(models, PROVIDER_ID, endpoints.apiBaseUrl) });
9595
+ } catch {}
9596
+ return models;
9597
+ }
9598
+ function createAutoRAGProviderConfig(env = process.env) {
9599
+ const baseUrl = resolveBaseUrl(env);
9600
+ const endpoints = { baseUrl, apiBaseUrl: `${baseUrl}${API_PATH}` };
8996
9601
  return {
8997
- name: FSEARCH_SEARCH_TOOL_NAME,
8998
- label: "FSearch Search",
8999
- description: "Instantly find files and folders by name, extension, path, size, or modified date across the configured search folders using fsearch-cli/FSearch (macOS/Linux), falling back to a slow filesystem walk when fsearch-cli is not installed. Returns absolute paths; read file contents with bash.",
9000
- parameters: fsearchSearchSchema,
9001
- async execute(_toolCallId, params) {
9002
- const query = params.query.trim();
9003
- if (query.length === 0) {
9004
- return {
9005
- content: [{ type: "text", text: "FSearch query was empty; nothing searched." }],
9006
- details: { method: FSEARCH_SEARCH_TOOL_NAME, resultCount: 0, sources: [], results: [] }
9007
- };
9008
- }
9009
- const request = {
9010
- ...params,
9011
- query,
9012
- maxResults: Math.min(params.maxResults ?? DEFAULT_MAX_RESULTS4, MAX_RESULTS_CAP3)
9013
- };
9014
- const result = await provider.searchFsearch(request);
9015
- if (!result.ok) {
9016
- return {
9017
- content: [{ type: "text", text: `FSearch ${result.reason}: ${result.message}` }],
9018
- details: { method: FSEARCH_SEARCH_TOOL_NAME, resultCount: 0, sources: [], results: [] }
9019
- };
9020
- }
9021
- const lines = result.results.map((entry, index) => {
9022
- const parts = [`[${index + 1}] ${entry.type} ${entry.path}`];
9023
- if (entry.size !== undefined)
9024
- parts.push(`size=${entry.size}`);
9025
- if (entry.dateModified !== undefined)
9026
- parts.push(`modified=${entry.dateModified}`);
9027
- return parts.join(" ");
9028
- });
9029
- const backendLabel = result.backend === "walk" ? "FSearch slow filesystem walk (fsearch-cli unavailable)" : "FSearch";
9030
- const limitNote = result.backend === "fsearch-cli" && (result.total ?? 0) > lines.length && lines.length === request.maxResults ? ` of ${result.total} total (limit reached; narrow the query or page with offset)` : "";
9031
- const noteSuffix = result.note !== undefined ? `
9032
- ${result.note}` : "";
9033
- const text = lines.length === 0 ? `${backendLabel} found no files or folders matching: ${query}${noteSuffix}` : `${backendLabel} found ${lines.length} item(s)${limitNote}:
9034
- ${lines.join(`
9035
- `)}${noteSuffix}`;
9036
- return {
9037
- content: [{ type: "text", text }],
9038
- details: {
9039
- method: FSEARCH_SEARCH_TOOL_NAME,
9040
- resultCount: result.results.length,
9041
- sources: result.results.map((entry) => entry.path),
9042
- results: result.results.map((entry, index) => ({ source: entry.path, excerpt: lines[index] }))
9043
- }
9044
- };
9045
- }
9602
+ name: PROVIDER_NAME,
9603
+ baseUrl: endpoints.apiBaseUrl,
9604
+ api: "openai-responses",
9605
+ apiKey: API_KEY_CONFIG_VALUE,
9606
+ oauth: {
9607
+ name: PROVIDER_NAME,
9608
+ login: (callbacks) => loginAutorag(callbacks, { baseUrl }),
9609
+ refreshToken: async (credentials) => refreshAutoragCredentials(credentials),
9610
+ getApiKey: (credentials) => credentials.access
9611
+ },
9612
+ refreshModels: (context) => refreshAutoragModels(context, endpoints)
9046
9613
  };
9047
9614
  }
9048
- var FSEARCH_SEARCH_TOOL_NAME = "fsearch_search", MAX_RESULTS_CAP3 = 1000, DEFAULT_MAX_RESULTS4 = 100, fsearchSearchSchema;
9049
- var init_fsearch_search_tool = __esm(() => {
9050
- fsearchSearchSchema = Type7.Object({
9051
- query: Type7.String({
9052
- description: 'FSearch query syntax over file and folder names: space = AND, OR keyword = OR (never the pipe |), ! = NOT, "quoted phrase", wildcards (*.pdf), ext:pdf;docx, size:>10mb, path:<fragment>, case:, regex:. When the slow-walk fallback answers (fsearch-cli not installed), only substring/regex name matching applies.'
9053
- }),
9054
- regex: Type7.Optional(Type7.Boolean({ description: "Treat query as a regular expression." })),
9055
- matchCase: Type7.Optional(Type7.Boolean()),
9056
- matchPath: Type7.Optional(Type7.Boolean({ description: "Match against the full path instead of the name." })),
9057
- kind: Type7.Optional(Type7.Union([Type7.Literal("files"), Type7.Literal("folders")])),
9058
- path: Type7.Optional(Type7.String({ description: "Absolute folder to search within." })),
9059
- sort: Type7.Optional(Type7.Union([
9060
- Type7.Literal("name-ascending"),
9061
- Type7.Literal("name-descending"),
9062
- Type7.Literal("path-ascending"),
9063
- Type7.Literal("path-descending"),
9064
- Type7.Literal("size-ascending"),
9065
- Type7.Literal("size-descending"),
9066
- Type7.Literal("date-modified-ascending"),
9067
- Type7.Literal("date-modified-descending")
9068
- ])),
9069
- offset: Type7.Optional(Type7.Integer({ minimum: 0, description: "Skip this many results (pagination)." })),
9070
- maxResults: Type7.Optional(Type7.Integer({
9071
- minimum: 1,
9072
- description: `Maximum results (default ${DEFAULT_MAX_RESULTS4}, cap ${MAX_RESULTS_CAP3}).`
9073
- }))
9074
- });
9615
+ async function registerAutoRAGProvider(runtime, env = process.env) {
9616
+ runtime.registerProvider(PROVIDER_ID, createAutoRAGProviderConfig(env));
9617
+ await runtime.refresh({ allowNetwork: false, providers: [PROVIDER_ID] });
9618
+ }
9619
+ var init_provider = __esm(() => {
9620
+ init_config();
9621
+ init_models();
9622
+ init_oauth_login();
9075
9623
  });
9076
9624
 
9077
9625
  // src/agent/pi-session.ts
@@ -9200,6 +9748,7 @@ async function createAutoRAGPiSession(options) {
9200
9748
  authPath: join21(agentDir, "auth.json"),
9201
9749
  modelsPath: join21(agentDir, "models.json")
9202
9750
  });
9751
+ await registerAutoRAGProvider(modelRuntime);
9203
9752
  await configureModelRuntime(modelRuntime, options.model, options.apiKey, options.providerApiKeys);
9204
9753
  const customTools = [...options.customTools];
9205
9754
  const extensionToolNames = [...options.extensionToolNames ?? []];
@@ -9249,6 +9798,7 @@ async function createAutoRAGPiInteractiveRuntime(options) {
9249
9798
  authPath: join21(agentDir, "auth.json"),
9250
9799
  modelsPath: join21(agentDir, "models.json")
9251
9800
  });
9801
+ await registerAutoRAGProvider(modelRuntime);
9252
9802
  await configureModelRuntime(modelRuntime, options.model, options.apiKey, options.providerApiKeys);
9253
9803
  const sessionManager = options.sessionPath ? SessionManager.open(options.sessionPath, options.sessionDir, options.cwd) : SessionManager.create(options.cwd, options.sessionDir);
9254
9804
  const createRuntime = async ({
@@ -9298,12 +9848,13 @@ async function createAutoRAGPiInteractiveRuntime(options) {
9298
9848
  }
9299
9849
  var PI_BUILTIN_TOOL_NAMES, EMBEDDED_AUTH_MARKER = "<autorag-embedded-model>";
9300
9850
  var init_pi_session = __esm(() => {
9851
+ init_provider();
9301
9852
  PI_BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"];
9302
9853
  });
9303
9854
 
9304
9855
  // src/agent/jev-extension.ts
9305
9856
  import { Jev } from "jev-use";
9306
- import { Type as Type8 } from "typebox";
9857
+ import { Type as Type6 } from "typebox";
9307
9858
  function createJevJudge(options = {}) {
9308
9859
  let client;
9309
9860
  return async (state, questions, callOptions = {}) => {
@@ -9356,32 +9907,32 @@ var JEV_TOOL_NAME = "jev", OPENROUTER_DEFAULT_MODEL = "typesafe/jev-1.13", BACKE
9356
9907
  var init_jev_extension = __esm(() => {
9357
9908
  init_pi_session();
9358
9909
  BACKEND_DEFAULT_MODEL = { openrouter: OPENROUTER_DEFAULT_MODEL };
9359
- jevQuestionSchema = Type8.Object({
9360
- id: Type8.Optional(Type8.String({ description: "Caller-assigned id, echoed back in the verdict" })),
9361
- type: Type8.Union([Type8.Literal("noul"), Type8.Literal("choice"), Type8.Literal("score")], {
9910
+ jevQuestionSchema = Type6.Object({
9911
+ id: Type6.Optional(Type6.String({ description: "Caller-assigned id, echoed back in the verdict" })),
9912
+ type: Type6.Union([Type6.Literal("noul"), Type6.Literal("choice"), Type6.Literal("score")], {
9362
9913
  description: "noul = P(yes), choice = pick one option, score = ordered level index"
9363
9914
  }),
9364
- question: Type8.String({
9915
+ question: Type6.String({
9365
9916
  minLength: 1,
9366
9917
  description: "One narrow question about the state. Jev answers exactly this."
9367
9918
  }),
9368
- options: Type8.Optional(Type8.Union([Type8.Array(Type8.String()), Type8.Record(Type8.String(), Type8.String())], {
9919
+ options: Type6.Optional(Type6.Union([Type6.Array(Type6.String()), Type6.Record(Type6.String(), Type6.String())], {
9369
9920
  description: "choice only: at least two option labels (or label -> meaning)"
9370
9921
  })),
9371
- levels: Type8.Optional(Type8.Array(Type8.String(), {
9922
+ levels: Type6.Optional(Type6.Array(Type6.String(), {
9372
9923
  description: "score only: at least two ordered level descriptions; the answer indexes into them"
9373
9924
  })),
9374
- criteria: Type8.Optional(Type8.Object({ true: Type8.String(), false: Type8.String() }, { description: "noul only: what yes and no mean, to sharpen calibration" }))
9925
+ criteria: Type6.Optional(Type6.Object({ true: Type6.String(), false: Type6.String() }, { description: "noul only: what yes and no mean, to sharpen calibration" }))
9375
9926
  });
9376
- jevParameters = Type8.Object({
9377
- state: Type8.Unknown({
9927
+ jevParameters = Type6.Object({
9928
+ state: Type6.Unknown({
9378
9929
  description: "Text, JSON object, or JSON array Jev should judge. Keep it small and self-contained."
9379
9930
  }),
9380
- questions: Type8.Array(jevQuestionSchema, {
9931
+ questions: Type6.Array(jevQuestionSchema, {
9381
9932
  minItems: 1,
9382
9933
  description: "Named questions, all evaluated against the same state in one batched request."
9383
9934
  }),
9384
- confidence_threshold: Type8.Optional(Type8.Number({
9935
+ confidence_threshold: Type6.Optional(Type6.Number({
9385
9936
  minimum: 0,
9386
9937
  maximum: 1,
9387
9938
  description: "Escalate any verdict below this confidence; omitted uses jev-use's per-source defaults"
@@ -9390,7 +9941,7 @@ var init_jev_extension = __esm(() => {
9390
9941
  });
9391
9942
 
9392
9943
  // src/agent/jikji-find-tool.ts
9393
- import { Type as Type9 } from "typebox";
9944
+ import { Type as Type7 } from "typebox";
9394
9945
  function createJikjiFindTool(provider, ledger = new EvidenceLedger) {
9395
9946
  return {
9396
9947
  name: JIKJI_FIND_TOOL_NAME,
@@ -9521,10 +10072,10 @@ function formatDirective(policy) {
9521
10072
  var JIKJI_FIND_TOOL_NAME = "jikji_find", jikjiFindSchema;
9522
10073
  var init_jikji_find_tool = __esm(() => {
9523
10074
  init_evidence_ledger();
9524
- jikjiFindSchema = Type9.Object({
9525
- query: Type9.String({ description: "Query for Jikji local file discovery." }),
9526
- topK: Type9.Optional(Type9.Integer({ description: "Maximum number of answer paths / candidates to return." })),
9527
- first: Type9.Optional(Type9.Boolean({ description: "Return only the first/best answer." }))
10075
+ jikjiFindSchema = Type7.Object({
10076
+ query: Type7.String({ description: "Query for Jikji local file discovery." }),
10077
+ topK: Type7.Optional(Type7.Integer({ description: "Maximum number of answer paths / candidates to return." })),
10078
+ first: Type7.Optional(Type7.Boolean({ description: "Return only the first/best answer." }))
9528
10079
  });
9529
10080
  });
9530
10081
 
@@ -10493,57 +11044,57 @@ var init_policy = __esm(() => {
10493
11044
  });
10494
11045
 
10495
11046
  // src/p2p/wire.ts
10496
- import { Type as Type10 } from "typebox";
11047
+ import { Type as Type8 } from "typebox";
10497
11048
  var PeerRefusalCodeSchema, PeerDiagnosticSchema, PeerFileEntrySchema, PeerResultEntrySchema, PeerQueryRequestSchema, PeerQueryResponseSchema;
10498
11049
  var init_wire = __esm(() => {
10499
- PeerRefusalCodeSchema = Type10.Union([
10500
- Type10.Literal("auth-error"),
10501
- Type10.Literal("replay-rejected"),
10502
- Type10.Literal("rate-limited"),
10503
- Type10.Literal("injection-detected"),
10504
- Type10.Literal("policy-denied"),
10505
- Type10.Literal("queue-full"),
10506
- Type10.Literal("internal-error")
11050
+ PeerRefusalCodeSchema = Type8.Union([
11051
+ Type8.Literal("auth-error"),
11052
+ Type8.Literal("replay-rejected"),
11053
+ Type8.Literal("rate-limited"),
11054
+ Type8.Literal("injection-detected"),
11055
+ Type8.Literal("policy-denied"),
11056
+ Type8.Literal("queue-full"),
11057
+ Type8.Literal("internal-error")
10507
11058
  ]);
10508
- PeerDiagnosticSchema = Type10.Object({
10509
- code: Type10.String({ minLength: 1, description: "Diagnostic or refusal code" }),
10510
- message: Type10.String({ description: "Human-readable diagnostic message" })
11059
+ PeerDiagnosticSchema = Type8.Object({
11060
+ code: Type8.String({ minLength: 1, description: "Diagnostic or refusal code" }),
11061
+ message: Type8.String({ description: "Human-readable diagnostic message" })
10511
11062
  });
10512
- PeerFileEntrySchema = Type10.Object({
10513
- source: Type10.String({
11063
+ PeerFileEntrySchema = Type8.Object({
11064
+ source: Type8.String({
10514
11065
  description: "Canonical retrieval source identifier"
10515
11066
  }),
10516
- contentBase64: Type10.String({ description: "Base64-encoded file content" }),
10517
- redacted: Type10.Boolean({ description: "Whether the content was PII-redacted" })
11067
+ contentBase64: Type8.String({ description: "Base64-encoded file content" }),
11068
+ redacted: Type8.Boolean({ description: "Whether the content was PII-redacted" })
10518
11069
  });
10519
- PeerResultEntrySchema = Type10.Object({
10520
- number: Type10.Integer({ minimum: 1, description: "1-based result number" }),
10521
- title: Type10.String({ minLength: 1, description: "Short title of the result" }),
10522
- summary: Type10.String({ description: "Key insight summary" }),
10523
- source: Type10.String({
11070
+ PeerResultEntrySchema = Type8.Object({
11071
+ number: Type8.Integer({ minimum: 1, description: "1-based result number" }),
11072
+ title: Type8.String({ minLength: 1, description: "Short title of the result" }),
11073
+ summary: Type8.String({ description: "Key insight summary" }),
11074
+ source: Type8.String({
10524
11075
  description: "Canonical retrieval source identifier"
10525
11076
  }),
10526
- excerpt: Type10.String({ description: "Supporting excerpt" })
11077
+ excerpt: Type8.String({ description: "Supporting excerpt" })
10527
11078
  });
10528
- PeerQueryRequestSchema = Type10.Object({
10529
- v: Type10.Literal(1, { description: "Protocol version" }),
10530
- query: Type10.String({ minLength: 1, maxLength: 4096, description: "Search query (max 4096 chars)" }),
10531
- topK: Type10.Optional(Type10.Integer({ minimum: 1, maximum: 20, description: "Max results to return (max 20)" })),
10532
- scope: Type10.Optional(Type10.String({ description: "Optional search scope hint" }))
11079
+ PeerQueryRequestSchema = Type8.Object({
11080
+ v: Type8.Literal(1, { description: "Protocol version" }),
11081
+ query: Type8.String({ minLength: 1, maxLength: 4096, description: "Search query (max 4096 chars)" }),
11082
+ topK: Type8.Optional(Type8.Integer({ minimum: 1, maximum: 20, description: "Max results to return (max 20)" })),
11083
+ scope: Type8.Optional(Type8.String({ description: "Optional search scope hint" }))
10533
11084
  });
10534
- PeerQueryResponseSchema = Type10.Object({
10535
- v: Type10.Literal(1, { description: "Protocol version" }),
10536
- status: Type10.Union([Type10.Literal("ok"), Type10.Literal("rejected")], {
11085
+ PeerQueryResponseSchema = Type8.Object({
11086
+ v: Type8.Literal(1, { description: "Protocol version" }),
11087
+ status: Type8.Union([Type8.Literal("ok"), Type8.Literal("rejected")], {
10537
11088
  description: "Response status"
10538
11089
  }),
10539
- answer: Type10.String({ description: "Curated answer text" }),
10540
- results: Type10.Array(PeerResultEntrySchema, {
11090
+ answer: Type8.String({ description: "Curated answer text" }),
11091
+ results: Type8.Array(PeerResultEntrySchema, {
10541
11092
  description: "Curated result entries with opaque source ids"
10542
11093
  }),
10543
- files: Type10.Array(PeerFileEntrySchema, {
11094
+ files: Type8.Array(PeerFileEntrySchema, {
10544
11095
  description: "Attached original files with opaque source ids"
10545
11096
  }),
10546
- diagnostics: Type10.Array(PeerDiagnosticSchema, {
11097
+ diagnostics: Type8.Array(PeerDiagnosticSchema, {
10547
11098
  description: "Diagnostic messages (degraded-mode, rejection reasons, etc.)"
10548
11099
  })
10549
11100
  });
@@ -10790,7 +11341,7 @@ var init_simplex_query_store = __esm(() => {
10790
11341
  // src/p2p/simplex-server.ts
10791
11342
  import { existsSync as existsSync19, mkdirSync as mkdirSync15, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "node:fs";
10792
11343
  import { join as join26 } from "node:path";
10793
- import { Type as Type11 } from "typebox";
11344
+ import { Type as Type9 } from "typebox";
10794
11345
  import { Value as Value2 } from "typebox/value";
10795
11346
  function targetTokens(value) {
10796
11347
  return new Set((value.toLocaleLowerCase().match(TARGET_TOKEN_PATTERN) ?? []).filter((token) => token.length > 1));
@@ -11104,11 +11655,11 @@ var init_simplex_server = __esm(() => {
11104
11655
  shareBytes: false,
11105
11656
  redact: true
11106
11657
  };
11107
- SimplexWireEnvelopeSchema = Type11.Object({
11108
- v: Type11.Literal(1, { description: "Envelope version" }),
11109
- kind: Type11.Union([Type11.Literal("query"), Type11.Literal("response")], { description: "Payload kind" }),
11110
- id: Type11.String({ minLength: 1, maxLength: 128, description: "Correlation id" }),
11111
- payload: Type11.Unknown({ description: "PeerQueryRequest or PeerQueryResponse" })
11658
+ SimplexWireEnvelopeSchema = Type9.Object({
11659
+ v: Type9.Literal(1, { description: "Envelope version" }),
11660
+ kind: Type9.Union([Type9.Literal("query"), Type9.Literal("response")], { description: "Payload kind" }),
11661
+ id: Type9.String({ minLength: 1, maxLength: 128, description: "Correlation id" }),
11662
+ payload: Type9.Unknown({ description: "PeerQueryRequest or PeerQueryResponse" })
11112
11663
  });
11113
11664
  TARGET_TOKEN_PATTERN = /[\p{L}\p{N}]+/gu;
11114
11665
  PEERS_DIR = join26(".autorag", "p2p");
@@ -11121,7 +11672,7 @@ var init_simplex_server = __esm(() => {
11121
11672
  });
11122
11673
 
11123
11674
  // src/agent/peer-target-tool.ts
11124
- import { Type as Type12 } from "typebox";
11675
+ import { Type as Type10 } from "typebox";
11125
11676
  function createRecommendPeerTargetsTool(workspacePath) {
11126
11677
  return {
11127
11678
  name: RECOMMEND_PEER_TARGETS_TOOL_NAME,
@@ -11154,8 +11705,8 @@ function createRecommendPeerTargetsTool(workspacePath) {
11154
11705
  var RECOMMEND_PEER_TARGETS_TOOL_NAME = "recommend_peer_targets", recommendPeerTargetsSchema;
11155
11706
  var init_peer_target_tool = __esm(() => {
11156
11707
  init_simplex_server();
11157
- recommendPeerTargetsSchema = Type12.Object({
11158
- query: Type12.String({ description: "Question or topic to match against local peer personas." })
11708
+ recommendPeerTargetsSchema = Type10.Object({
11709
+ query: Type10.String({ description: "Question or topic to match against local peer personas." })
11159
11710
  });
11160
11711
  });
11161
11712
 
@@ -11231,7 +11782,7 @@ var MAX_DECOMPOSED_QUERIES = 5;
11231
11782
  var init_query_decomposition = () => {};
11232
11783
 
11233
11784
  // src/agent/query-peer-tool.ts
11234
- import { Type as Type13 } from "typebox";
11785
+ import { Type as Type11 } from "typebox";
11235
11786
  function failure(alias, message) {
11236
11787
  const details = { method: "query_peer_agent", ok: false, alias, message };
11237
11788
  return { content: [{ type: "text", text: message }], details };
@@ -11346,11 +11897,11 @@ var init_query_peer_tool = __esm(() => {
11346
11897
  init_simplex_query_store();
11347
11898
  init_simplex_server();
11348
11899
  init_evidence_ledger();
11349
- queryPeerAgentSchema = Type13.Object({
11350
- alias: Type13.String({ minLength: 1 }),
11351
- query: Type13.String({ minLength: 1, maxLength: 4096 }),
11352
- topK: Type13.Optional(Type13.Integer({ minimum: 1, maximum: 20 })),
11353
- scope: Type13.Optional(Type13.String())
11900
+ queryPeerAgentSchema = Type11.Object({
11901
+ alias: Type11.String({ minLength: 1 }),
11902
+ query: Type11.String({ minLength: 1, maxLength: 4096 }),
11903
+ topK: Type11.Optional(Type11.Integer({ minimum: 1, maximum: 20 })),
11904
+ scope: Type11.Optional(Type11.String())
11354
11905
  });
11355
11906
  });
11356
11907
 
@@ -11617,29 +12168,29 @@ function reconcileCitations(answer, results) {
11617
12168
  ]
11618
12169
  };
11619
12170
  }
11620
- function createPreliminarySearchDocumentsResponse(sessionId, query, details, diagnostics = []) {
11621
- const sourceByNumber = new Map(details.sources.map((entry) => [entry.number, entry.source]));
11622
- const results = details.results.map((result) => ({
12171
+ function toSearchResults(details, sourceByNumber) {
12172
+ return details.results.map((result) => ({
11623
12173
  number: result.number,
11624
12174
  title: result.title,
11625
12175
  summary: result.summary,
11626
12176
  evidence: result.evidence.map((evidence) => evidence.lineNumber !== undefined ? { excerpt: evidence.excerpt, lineNumber: evidence.lineNumber } : { excerpt: evidence.excerpt }),
11627
- confidence: confidenceFrom(result.confidence ?? 0.5),
12177
+ ...result.confidence !== undefined ? { confidence: confidenceFrom(result.confidence) } : {},
11628
12178
  source: sourceByNumber.get(result.number)
11629
12179
  }));
11630
- const citations = reconcileCitations(details.answer, results);
12180
+ }
12181
+ function createPreliminarySearchDocumentsResponse(sessionId, query, details, diagnostics = []) {
12182
+ const sourceByNumber = new Map(details.mapping.map((entry) => [entry.number, entry.source]));
11631
12183
  return {
11632
12184
  sessionId,
11633
12185
  query,
11634
- results,
11635
- answer: citations.answer,
12186
+ results: toSearchResults(details, sourceByNumber),
12187
+ answer: details.answer,
11636
12188
  searched: details.results.length,
11637
12189
  warnings: [],
11638
- diagnostics: [...citations.diagnostics, ...diagnostics]
12190
+ diagnostics: [...diagnostics]
11639
12191
  };
11640
12192
  }
11641
- function createEmptySearchDocumentsResponse(sessionId, query, sessions, diagnostics = []) {
11642
- sessions.set(sessionId, { query, registry: new Map });
12193
+ function createEmptySearchDocumentsResponse(sessionId, query, diagnostics = []) {
11643
12194
  return {
11644
12195
  sessionId,
11645
12196
  query,
@@ -11655,7 +12206,7 @@ function normalizeEntryEvidenceRefs(entry) {
11655
12206
  const derivedRetrieverMix = Array.from(new Set(rawRefs.map((ref) => ref.method)));
11656
12207
  return rawRefs.map((ref) => {
11657
12208
  if (ref.excerpt === undefined && ref.content === undefined) {
11658
- throw new Error("emit_autorag_results: every evidenceRef must include excerpt or content");
12209
+ throw new Error("results: every evidenceRef must include excerpt or content");
11659
12210
  }
11660
12211
  return normalizeSessionEvidenceRef({
11661
12212
  method: ref.method,
@@ -11676,19 +12227,11 @@ function normalizeEntryEvidenceRefs(entry) {
11676
12227
  });
11677
12228
  });
11678
12229
  }
11679
- function recordStructuredResultsSession(sessionId, query, details, sessions, memory, componentDiagnostics = [], options = {}) {
11680
- assertResultsMappingOneToOne("emit_autorag_results", details.results, details.mapping);
11681
- const registry = new Map;
12230
+ function recordStructuredResultsSession(sessionId, query, details, memory, componentDiagnostics = [], options = {}) {
12231
+ assertResultsMappingOneToOne("results", details.results, details.mapping);
11682
12232
  const memoryResults = [];
11683
12233
  for (const entry of details.mapping) {
11684
12234
  const evidenceRefs = normalizeEntryEvidenceRefs(entry);
11685
- registry.set(entry.number, {
11686
- index: entry.number,
11687
- content: entry.content,
11688
- source: entry.source,
11689
- method: entry.method,
11690
- evidenceRefs
11691
- });
11692
12235
  const emittedResult = details.results.find((result) => result.number === entry.number);
11693
12236
  memoryResults.push({
11694
12237
  number: entry.number,
@@ -11697,23 +12240,15 @@ function recordStructuredResultsSession(sessionId, query, details, sessions, mem
11697
12240
  content: entry.content,
11698
12241
  method: entry.method,
11699
12242
  source: entry.source,
11700
- ...emittedResult !== undefined ? { confidence: confidenceFrom(emittedResult.confidence) } : {},
12243
+ ...emittedResult?.confidence !== undefined ? { confidence: confidenceFrom(emittedResult.confidence) } : {},
11701
12244
  evidenceRefs
11702
12245
  });
11703
12246
  }
11704
- sessions.set(sessionId, { query, registry });
11705
12247
  if (!options.isolateMemory) {
11706
12248
  memory.recordCuratedResultsSession({ sessionId, query, results: memoryResults });
11707
12249
  memory.save();
11708
12250
  }
11709
- const results = details.results.map((result) => ({
11710
- number: result.number,
11711
- title: result.title,
11712
- summary: result.summary,
11713
- evidence: result.evidence.map((evidence) => evidence.lineNumber !== undefined ? { excerpt: evidence.excerpt, lineNumber: evidence.lineNumber } : { excerpt: evidence.excerpt }),
11714
- confidence: confidenceFrom(result.confidence),
11715
- source: registry.get(result.number)?.source || undefined
11716
- }));
12251
+ const results = toSearchResults(details, new Map(details.mapping.map((entry) => [entry.number, entry.source || undefined])));
11717
12252
  const citations = reconcileCitations(details.answer, results);
11718
12253
  const answer = citations.answer;
11719
12254
  const diagnostics = [...citations.diagnostics];
@@ -11743,7 +12278,7 @@ var init_search_documents = __esm(() => {
11743
12278
  });
11744
12279
 
11745
12280
  // src/agent/search-all-tool.ts
11746
- import { Type as Type14 } from "typebox";
12281
+ import { Type as Type12 } from "typebox";
11747
12282
  function createSearchAllDocumentsTool(provider, ledger = new EvidenceLedger) {
11748
12283
  return {
11749
12284
  name: SEARCH_ALL_DOCUMENTS_TOOL_NAME,
@@ -11798,17 +12333,17 @@ var SEARCH_ALL_DOCUMENTS_TOOL_NAME = "search_all_documents", searchAllSchema;
11798
12333
  var init_search_all_tool = __esm(() => {
11799
12334
  init_evidence_ledger();
11800
12335
  init_search_documents();
11801
- searchAllSchema = Type14.Object({
11802
- query: Type14.String({ description: "Query to search across all configured retrieval methods." }),
11803
- topK: Type14.Optional(Type14.Integer({
12336
+ searchAllSchema = Type12.Object({
12337
+ query: Type12.String({ description: "Query to search across all configured retrieval methods." }),
12338
+ topK: Type12.Optional(Type12.Integer({
11804
12339
  description: "Upper bound on returned evidence. Omit it to receive every distinct chunk the methods found; set it only when you deliberately want a shorter list."
11805
12340
  })),
11806
- scope: Type14.Optional(Type14.String({ description: "Optional opaque virtual-path scope, e.g. /docs or /docs/**." }))
12341
+ scope: Type12.Optional(Type12.String({ description: "Optional opaque virtual-path scope, e.g. /docs or /docs/**." }))
11807
12342
  });
11808
12343
  });
11809
12344
 
11810
12345
  // src/agent/search-minsync-tool.ts
11811
- import { Type as Type15 } from "typebox";
12346
+ import { Type as Type13 } from "typebox";
11812
12347
  function createSearchMinSyncDocumentsTool(getMethod, resolveScope = (scope) => scope, ledger = new EvidenceLedger) {
11813
12348
  return {
11814
12349
  name: SEARCH_MINSYNC_DOCUMENTS_TOOL_NAME,
@@ -11858,12 +12393,12 @@ var SEARCH_MINSYNC_DOCUMENTS_TOOL_NAME = "semantic_search_local_docs", searchMin
11858
12393
  var init_search_minsync_tool = __esm(() => {
11859
12394
  init_evidence_ledger();
11860
12395
  init_search_documents();
11861
- searchMinSyncSchema = Type15.Object({
11862
- query: Type15.String({
12396
+ searchMinSyncSchema = Type13.Object({
12397
+ query: Type13.String({
11863
12398
  description: "Semantic query to search parsed document mirrors with MinSync vector retrieval."
11864
12399
  }),
11865
- topK: Type15.Optional(Type15.Integer({ description: "Maximum number of MinSync semantic chunks to return. Defaults to 50." })),
11866
- scope: Type15.Optional(Type15.String({ description: "Optional opaque virtual-path scope, e.g. /docs or /docs/**." }))
12400
+ topK: Type13.Optional(Type13.Integer({ description: "Maximum number of MinSync semantic chunks to return. Defaults to 50." })),
12401
+ scope: Type13.Optional(Type13.String({ description: "Optional opaque virtual-path scope, e.g. /docs or /docs/**." }))
11867
12402
  });
11868
12403
  });
11869
12404
 
@@ -11881,7 +12416,7 @@ function datasourceSearchToolName(datasourceId) {
11881
12416
  }
11882
12417
 
11883
12418
  // src/agent/search-single-datasource-tool.ts
11884
- import { Type as Type16 } from "typebox";
12419
+ import { Type as Type14 } from "typebox";
11885
12420
  function createSingleDatasourceSearchTools(provider, specs, ledger = new EvidenceLedger) {
11886
12421
  return specs.map((spec) => createTool(provider, spec, ledger));
11887
12422
  }
@@ -11947,10 +12482,10 @@ var init_search_single_datasource_tool = __esm(() => {
11947
12482
  init_evidence_ledger();
11948
12483
  init_search_documents();
11949
12484
  singleDatasourceToolName = datasourceSearchToolName;
11950
- searchSingleDatasourceSchema = Type16.Object({
11951
- query: Type16.String({ description: "Query to search within this datasource connection." }),
11952
- topK: Type16.Optional(Type16.Integer({ description: "Maximum number of chunks to return. Defaults to 20." })),
11953
- scope: Type16.Optional(Type16.String({ description: "Optional scope narrowing inside this datasource, e.g. one instance root." }))
12485
+ searchSingleDatasourceSchema = Type14.Object({
12486
+ query: Type14.String({ description: "Query to search within this datasource connection." }),
12487
+ topK: Type14.Optional(Type14.Integer({ description: "Maximum number of chunks to return. Defaults to 20." })),
12488
+ scope: Type14.Optional(Type14.String({ description: "Optional scope narrowing inside this datasource, e.g. one instance root." }))
11954
12489
  });
11955
12490
  });
11956
12491
 
@@ -12035,7 +12570,7 @@ ${input.skill}
12035
12570
 
12036
12571
  ${input.query}
12037
12572
 
12038
- ` + `When done, call emit_autorag_results exactly once with: \`answer\` = a short report of what you changed (field: old → new), what you verified and how, ` + `and anything that still needs the user (for example an unset API-key environment variable, by name only); \`results\` = []. ` + `If you changed nothing, say why.`;
12573
+ ` + `When done, reply with a short report as your final message, with no tool call: what you changed (field: old → new), what you verified and how, ` + `and anything that still needs the user (for example an unset API-key environment variable, by name only). ` + `If you changed nothing, say why.`;
12039
12574
  }
12040
12575
  var SETUP_SKILL_RELATIVE_PATH;
12041
12576
  var init_self_config = __esm(() => {
@@ -12066,7 +12601,6 @@ function buildSystemPrompt(config) {
12066
12601
  toolLine(config, "check_memory", "inspect evidence earlier searches found for similar questions"),
12067
12602
  toolLine(config, "recommend_peer_targets", "recommend local peer personas to ask about a topic without contacting them"),
12068
12603
  toolLine(config, "query_peer_agent", "ask one trusted peer agent over SimpleX"),
12069
- toolLine(config, "emit_autorag_results", "return the final structured answer with evidence ids for each result"),
12070
12604
  ...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`),
12071
12605
  ...config.toolNames.filter((name) => ![
12072
12606
  "bash",
@@ -12082,8 +12616,7 @@ function buildSystemPrompt(config) {
12082
12616
  "jev",
12083
12617
  "check_memory",
12084
12618
  "recommend_peer_targets",
12085
- "query_peer_agent",
12086
- "emit_autorag_results"
12619
+ "query_peer_agent"
12087
12620
  ].includes(name) && !name.startsWith("search_datasource_")).map((name) => `- **${name}**: caller-provided tool`)
12088
12621
  ].filter((line) => line !== undefined);
12089
12622
  const manifests = config.manifests.length === 0 ? "" : `
@@ -12147,12 +12680,12 @@ Your job is to retrieve candidates, read the relevant source material directly,
12147
12680
  ## Workflow
12148
12681
 
12149
12682
  Searches follow a progressive, two-phase loop:
12150
- 1. **PLAN & FAST ANSWER** — Decide whether the query is answerable from general knowledge or memory. When baseline retrieval evidence is provided, produce and emit a complete, self-contained immediate first answer via \`emit_fast_answer\` right away from that evidence without calling tools or waiting.
12683
+ 1. **PLAN & FAST ANSWER** — Decide whether the query is answerable from general knowledge or memory. When baseline retrieval evidence is provided, write a complete, self-contained immediate first answer from that evidence as your reply, right away, without calling tools or waiting.
12151
12684
  2. **EXPLORE & RETRIEVE** — Immediately following the fast answer, begin deeper exploration: ${discovery ? `use ${discovery} actively to locate relevant files and folders, and ` : ""}fan out across MinSync lexical/vector/hybrid retrieval, combined retrieval, and datasource search to expand candidates and fill evidence gaps.
12152
12685
  3. **READ & VERIFY** — Use \`bash\` to open and verify relevant local files directly when needed, and in local sessions to organize files when the user asks (see File Organization); rely on Jikji and retrieval rather than blind directory browsing.
12153
12686
  4. **JUDGE & RESOLVE** — Evaluate relevance, sufficiency, conflicts, and temporal context. When search results or evidence contain conflicting information, treat the freshest (most recent) information as authoritative and correct.
12154
12687
  5. **CURATE** — Produce concise numbered knowledge units grounded in source evidence.
12155
- 6. **FINALIZE** — Call \`emit_autorag_results\` exactly once as the final action.
12688
+ 6. **FINALIZE** — End with the final answer as a plain reply, with no tool call. Earlier replies that are followed by a tool call are progress notes; only the last reply is the answer.
12156
12689
 
12157
12690
  ## Available Tools
12158
12691
 
@@ -12197,17 +12730,17 @@ ${fsearch}
12197
12730
  ${manifests}
12198
12731
  ## Output Format
12199
12732
 
12200
- Call \`emit_autorag_results\` exactly once with:
12201
- - \`answer\`: the curated answer for the caller following the Answer Guidelines below. When a first answer was already delivered to the caller during this run, include only corrections and newly verified findings — never restate the first answer; otherwise give the complete answer. Reference results by bracketed numbers such as [1] and [2].
12202
- - \`results\`: curated units with number, title, summary, evidence, confidence, and \`refs\`.
12203
- - \`refs\`: for every result, the evidence ids (such as \`e3\`) shown next to the retrieved results that support it; for a local file you opened yourself with \`bash\`, its absolute path. The source, method, and chunk are attached from these ids; never type a path or copy a chunk to describe a source.
12733
+ Your answer is your final reply, written as plain text, never as a tool call. Cite evidence inline with the ids the retrieval tools print next to each result:
12734
+ - Put the id in square brackets at the end of the sentence it supports, e.g. \`The cap is 28% [e3].\` Use \`[e3][e7]\` or \`[e3, e7]\` for several. Copy ids exactly as printed; never invent one.
12735
+ - For a local file you opened yourself with \`bash\`, cite \`[file:<absolute path>]\`.
12736
+ - The harness resolves every id to its recorded source, number the citations \`[1]\`, \`[2]\`, … in order of first appearance, and attaches the sources for you. Never type a source path, a chunk, or a citation number yourself.
12737
+ - A citation whose id no tool returned in this run is dropped, so cite only ids you saw.
12204
12738
 
12205
12739
  ## Answer Guidelines
12206
12740
 
12207
12741
  - **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.
12208
12742
  - **Bullet-point core answer**: 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.
12209
- - **Direct answer only**: The caller only needs the answer to their question. Never include specific file paths, datasource descriptions, or retrieval mechanics/principles in \`answer\` (source paths are attached to each result from the evidence ids you cite in \`refs\`).
12210
- - **Citation style**: Cite supporting evidence chunks using bracketed numbers only (e.g. [1], [2]). Do not quote raw chunk text or mention source paths directly in \`answer\`.
12743
+ - **Direct answer only**: The caller only needs the 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.
12211
12744
  - ${ANSWER_CITATION_RULE}
12212
12745
  - ${ANSWER_IMAGE_EMBED_RULE} ${ANSWER_IMAGE_DELTA_RULE}
12213
12746
  - **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.
@@ -12220,7 +12753,7 @@ Call \`emit_autorag_results\` exactly once with:
12220
12753
  - **Curate, don't dump**: return useful knowledge units, not raw search output.
12221
12754
  - **Address intent**: answer the caller's actual need.
12222
12755
  - **Preserve traceability**: cite evidence by the ids retrieval tools show, so source paths and excerpts are attached for you.
12223
- - **Finalize once**: the structured result tool is the final action.
12756
+ - **Finalize once**: the last reply, with no tool call, is the final answer.
12224
12757
  `;
12225
12758
  }
12226
12759
  var init_system_prompt = __esm(() => {
@@ -14207,7 +14740,7 @@ var init_render = __esm(() => {
14207
14740
  });
14208
14741
 
14209
14742
  // src/agent/web-fetch-tool.ts
14210
- import { Type as Type17 } from "typebox";
14743
+ import { Type as Type15 } from "typebox";
14211
14744
  function truncateHead(output, maxBytes, maxLines) {
14212
14745
  const lines = output.split(`
14213
14746
  `);
@@ -14311,9 +14844,9 @@ var init_web_fetch_tool = __esm(() => {
14311
14844
  init_render();
14312
14845
  init_evidence_ledger();
14313
14846
  FETCH_MAX_OUTPUT_BYTES = 50 * 1024;
14314
- webFetchSchema = Type17.Object({
14315
- url: Type17.String({ description: "The http(s) URL to fetch and read." }),
14316
- raw: Type17.Optional(Type17.Boolean({ description: "Return the raw response body without markdown rendering or formatting." }))
14847
+ webFetchSchema = Type15.Object({
14848
+ url: Type15.String({ description: "The http(s) URL to fetch and read." }),
14849
+ raw: Type15.Optional(Type15.Boolean({ description: "Return the raw response body without markdown rendering or formatting." }))
14317
14850
  });
14318
14851
  });
14319
14852
 
@@ -15272,7 +15805,7 @@ function parseResponse(data) {
15272
15805
  } : {}
15273
15806
  };
15274
15807
  }
15275
- var DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com", API_VERSION = "v1beta", DEFAULT_MODEL = "gemini-3.8-flash", MAX_RESPONSE_BYTES, MAX_ERROR_BYTES, GeminiProvider;
15808
+ var DEFAULT_BASE_URL2 = "https://generativelanguage.googleapis.com", API_VERSION = "v1beta", DEFAULT_MODEL = "gemini-3.8-flash", MAX_RESPONSE_BYTES, MAX_ERROR_BYTES, GeminiProvider;
15276
15809
  var init_gemini = __esm(() => {
15277
15810
  init_model_auth();
15278
15811
  init_query();
@@ -15294,7 +15827,7 @@ var init_gemini = __esm(() => {
15294
15827
  const model = envCredential("GEMINI_SEARCH_MODEL") ?? (credential.source === "injected" ? credential.modelId : undefined) ?? DEFAULT_MODEL;
15295
15828
  const parsed = params.parsedQuery ?? parseSearchQuery(params.query);
15296
15829
  const query = parsed.hasDirectives ? formatQuery(parsed, GOOGLE_QUERY_SYNTAX) : params.query;
15297
- const baseUrl = (credential.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
15830
+ const baseUrl = (credential.baseUrl ?? DEFAULT_BASE_URL2).replace(/\/+$/, "");
15298
15831
  const fetchImpl = params.fetch ?? fetch;
15299
15832
  const response = await fetchImpl(`${baseUrl}/${API_VERSION}/models/${model}:generateContent`, {
15300
15833
  method: "POST",
@@ -15439,7 +15972,7 @@ function parseResponse2(response) {
15439
15972
  ...response.id !== undefined ? { requestId: response.id } : {}
15440
15973
  };
15441
15974
  }
15442
- var DEFAULT_BASE_URL2 = "https://api.anthropic.com", DEFAULT_MODEL2 = "claude-haiku-5-5", DEFAULT_MAX_TOKENS = 4096, MAX_RESPONSE_BYTES2, MAX_ERROR_BYTES2, WEB_SEARCH_TOOL_TYPE = "web_search_20250305", ANTHROPIC_QUERY_SYNTAX, AnthropicProvider;
15975
+ var DEFAULT_BASE_URL3 = "https://api.anthropic.com", DEFAULT_MODEL2 = "claude-haiku-5-5", DEFAULT_MAX_TOKENS2 = 4096, MAX_RESPONSE_BYTES2, MAX_ERROR_BYTES2, WEB_SEARCH_TOOL_TYPE = "web_search_20250305", ANTHROPIC_QUERY_SYNTAX, AnthropicProvider;
15443
15976
  var init_anthropic = __esm(() => {
15444
15977
  init_model_auth();
15445
15978
  init_query();
@@ -15470,7 +16003,7 @@ var init_anthropic = __esm(() => {
15470
16003
  const model = envCredential("ANTHROPIC_SEARCH_MODEL") ?? (credential.source === "injected" ? credential.modelId : undefined) ?? DEFAULT_MODEL2;
15471
16004
  const parsed = params.parsedQuery ?? parseSearchQuery(params.query);
15472
16005
  const plan = planQuery(params.query, parsed);
15473
- const baseUrl = (credential.baseUrl ?? DEFAULT_BASE_URL2).replace(/\/+$/, "");
16006
+ const baseUrl = (credential.baseUrl ?? DEFAULT_BASE_URL3).replace(/\/+$/, "");
15474
16007
  const fetchImpl = params.fetch ?? fetch;
15475
16008
  const response = await fetchImpl(`${baseUrl}/v1/messages?beta=true`, {
15476
16009
  method: "POST",
@@ -15482,7 +16015,7 @@ var init_anthropic = __esm(() => {
15482
16015
  },
15483
16016
  body: JSON.stringify({
15484
16017
  model,
15485
- max_tokens: params.maxOutputTokens ?? DEFAULT_MAX_TOKENS,
16018
+ max_tokens: params.maxOutputTokens ?? DEFAULT_MAX_TOKENS2,
15486
16019
  ...params.systemPrompt ? { system: params.systemPrompt } : {},
15487
16020
  messages: [{ role: "user", content: plan.query }],
15488
16021
  tools: [
@@ -17565,7 +18098,7 @@ async function searchPublicWeb(params, deadlines = {}) {
17565
18098
  }
17566
18099
  var PUBLIC_ENGINE_IDS, DEFAULT_NUM_RESULTS8 = 15, MAX_NUM_RESULTS8 = 30, SOFT_DEADLINE_MS = 5000, HARD_DEADLINE_MS = 30000, PublicWebProvider;
17567
18100
  var init_public = __esm(() => {
17568
- init_provider();
18101
+ init_provider2();
17569
18102
  init_types3();
17570
18103
  init_utils();
17571
18104
  PUBLIC_ENGINE_IDS = [
@@ -17639,7 +18172,7 @@ function resolveProviderCandidates(routing = {}) {
17639
18172
  return candidates;
17640
18173
  }
17641
18174
  var PROVIDER_META, instanceCache;
17642
- var init_provider = __esm(() => {
18175
+ var init_provider2 = __esm(() => {
17643
18176
  init_types3();
17644
18177
  init_types3();
17645
18178
  PROVIDER_META = {
@@ -17825,15 +18358,15 @@ async function executeWebSearch(params, options = {}) {
17825
18358
  }
17826
18359
  var TOTAL_TIMEOUT_MULTIPLIER = 2;
17827
18360
  var init_search = __esm(() => {
17828
- init_provider();
18361
+ init_provider2();
17829
18362
  init_query();
17830
18363
  init_types3();
17831
- init_provider();
18364
+ init_provider2();
17832
18365
  init_types3();
17833
18366
  });
17834
18367
 
17835
18368
  // src/agent/web-search-tool.ts
17836
- import { Type as Type18 } from "typebox";
18369
+ import { Type as Type16 } from "typebox";
17837
18370
  function createWebSearchTool(options = {}, ledger = new EvidenceLedger) {
17838
18371
  return {
17839
18372
  name: WEB_SEARCH_TOOL_NAME,
@@ -17909,15 +18442,15 @@ var WEB_SEARCH_TOOL_NAME = "web_search", webSearchSchema;
17909
18442
  var init_web_search_tool = __esm(() => {
17910
18443
  init_search();
17911
18444
  init_evidence_ledger();
17912
- webSearchSchema = Type18.Object({
17913
- query: Type18.String({
18445
+ webSearchSchema = Type16.Object({
18446
+ query: Type16.String({
17914
18447
  description: 'Search query. Supports Google-style directives: site:/-site:, after:/before: (YYYY-MM-DD), inurl:, intitle:, filetype:, "exact phrase", -term, OR.'
17915
18448
  }),
17916
- recency: Type18.Optional(Type18.Union([Type18.Literal("day"), Type18.Literal("week"), Type18.Literal("month"), Type18.Literal("year")], {
18449
+ recency: Type16.Optional(Type16.Union([Type16.Literal("day"), Type16.Literal("week"), Type16.Literal("month"), Type16.Literal("year")], {
17917
18450
  description: "Optional recency window for results (pure time filter)."
17918
18451
  })),
17919
- limit: Type18.Optional(Type18.Integer({ description: "Maximum number of results to return." })),
17920
- num_search_results: Type18.Optional(Type18.Integer({ description: "Alias for limit (result count hint)." }))
18452
+ limit: Type16.Optional(Type16.Integer({ description: "Maximum number of results to return." })),
18453
+ num_search_results: Type16.Optional(Type16.Integer({ description: "Alias for limit (result count hint)." }))
17921
18454
  });
17922
18455
  });
17923
18456
 
@@ -17964,10 +18497,7 @@ class AutoRAGAgent {
17964
18497
  runLogger;
17965
18498
  lastQuery;
17966
18499
  lastSessionId;
17967
- sessions = new Map;
17968
18500
  activeRun = false;
17969
- resultCapture;
17970
- interactiveFastAnswerCallback;
17971
18501
  modelNativeSearchAuth;
17972
18502
  retrievalTrace = [];
17973
18503
  evidenceLedger = new EvidenceLedger;
@@ -18129,10 +18659,6 @@ class AutoRAGAgent {
18129
18659
  const searchMinSyncTool = createSearchMinSyncDocumentsTool(() => this.remoteFilteredRetrievalMethod(this.minSyncMethod), (scope) => this.resolveRetrievalScope(scope), this.evidenceLedger);
18130
18660
  const searchAllTool = createSearchAllDocumentsTool(this, this.evidenceLedger);
18131
18661
  const loadDatasourceSkillTool = createLoadDatasourceSkillTool(this);
18132
- const emitResultsTool = createEmitResultsTool((details) => this.resultCapture?.(details), {
18133
- ledger: this.evidenceLedger,
18134
- allowLocalFiles: !this.remoteSession
18135
- });
18136
18662
  const scanDuplicateDocumentsTool = this.dupeyOptions === false ? undefined : createScanDuplicateDocumentsTool(this);
18137
18663
  this.jevJudge = options.jev === undefined || options.jev === false || this.remoteSession ? undefined : createJevJudge(options.jev);
18138
18664
  this.jevExtension = this.jevJudge === undefined ? undefined : createJevExtension(this.jevJudge);
@@ -18167,8 +18693,6 @@ class AutoRAGAgent {
18167
18693
  "check_memory",
18168
18694
  ...singleDatasourceTools.map((tool) => tool.name),
18169
18695
  LOAD_DATASOURCE_SKILL_TOOL_NAME,
18170
- EMIT_AUTORAG_RESULTS_TOOL_NAME,
18171
- EMIT_FAST_ANSWER_TOOL_NAME,
18172
18696
  SEARCH_MINSYNC_DOCUMENTS_TOOL_NAME,
18173
18697
  SEARCH_ALL_DOCUMENTS_TOOL_NAME,
18174
18698
  JIKJI_FIND_TOOL_NAME,
@@ -18199,7 +18723,6 @@ class AutoRAGAgent {
18199
18723
  loadDatasourceSkillTool,
18200
18724
  ...webSearchTool !== undefined ? [webSearchTool] : [],
18201
18725
  ...webFetchTool !== undefined ? [webFetchTool] : [],
18202
- emitResultsTool,
18203
18726
  ...scanDuplicateDocumentsTool !== undefined ? [scanDuplicateDocumentsTool] : [],
18204
18727
  ...jikjiFindTool !== undefined ? [jikjiFindTool] : [],
18205
18728
  ...everythingSearchTool !== undefined ? [everythingSearchTool] : [],
@@ -18346,12 +18869,11 @@ ${context.text}
18346
18869
  source: "memory"
18347
18870
  };
18348
18871
  }
18349
- const registry = this.sessions.get(sessionId)?.registry;
18350
18872
  const resultByNumber = new Map(details.results.map((result) => [result.number, result]));
18351
18873
  const units = [];
18352
18874
  for (const entry of details.mapping) {
18353
18875
  const result = resultByNumber.get(entry.number);
18354
- const refs = registry?.get(entry.number)?.evidenceRefs ?? [];
18876
+ const refs = normalizeEntryEvidenceRefs(entry);
18355
18877
  if (result === undefined || refs.length === 0)
18356
18878
  continue;
18357
18879
  for (const ref of refs) {
@@ -18432,7 +18954,7 @@ ${context.text}
18432
18954
  providerApiKeys: { [local.provider]: local.apiKey }
18433
18955
  };
18434
18956
  }
18435
- async createSearchSession(resolved, systemPrompt, extraTools = []) {
18957
+ async createSearchSession(resolved, systemPrompt) {
18436
18958
  if (this.boundPiRuntime !== undefined) {
18437
18959
  const session = this.boundPiRuntime.session;
18438
18960
  return {
@@ -18452,10 +18974,7 @@ ${context.text}
18452
18974
  apiKey: resolved.apiKey,
18453
18975
  providerApiKeys: resolved.providerApiKeys,
18454
18976
  getSystemPrompt: () => systemPrompt,
18455
- customTools: [
18456
- ...this.tools.filter((tool) => !PI_BUILTIN_TOOL_NAMES.includes(tool.name)),
18457
- ...extraTools
18458
- ],
18977
+ customTools: this.tools.filter((tool) => !PI_BUILTIN_TOOL_NAMES.includes(tool.name)),
18459
18978
  remoteSession: this.remoteSession,
18460
18979
  contextTransform: (messages) => this.withMemoryContext(messages),
18461
18980
  ...this.jevExtension !== undefined ? { extensionFactories: [this.jevExtension], extensionToolNames: [JEV_TOOL_NAME] } : {}
@@ -18536,19 +19055,12 @@ ${context.text}
18536
19055
  modelId: this.boundPiRuntime?.session.model?.id ?? resolved?.model.id
18537
19056
  })),
18538
19057
  contextTransform: (messages) => this.withMemoryContext(messages),
18539
- customTools: [
18540
- ...this.tools.filter((tool) => !PI_BUILTIN_TOOL_NAMES.includes(tool.name)),
18541
- createEmitFastAnswerTool((details) => this.interactiveFastAnswerCallback?.(details), {
18542
- ledger: this.evidenceLedger
18543
- })
18544
- ],
19058
+ customTools: this.tools.filter((tool) => !PI_BUILTIN_TOOL_NAMES.includes(tool.name)),
18545
19059
  onQuery: (query, pi) => this.runInteractivePiQuery(query, pi),
18546
- inactiveToolNames: [EMIT_FAST_ANSWER_TOOL_NAME],
18547
19060
  ...this.jevExtension !== undefined ? { extensionFactories: [this.jevExtension], extensionToolNames: [JEV_TOOL_NAME] } : {},
18548
19061
  ...this.updateNotice === undefined ? {} : { updateNotice: this.updateNotice }
18549
19062
  });
18550
19063
  this.boundPiRuntime = runtime.runtime;
18551
- runtime.runtime.session.setActiveToolsByName(runtime.runtime.session.getActiveToolNames().filter((name) => name !== EMIT_FAST_ANSWER_TOOL_NAME));
18552
19064
  const dispose = runtime.dispose;
18553
19065
  return {
18554
19066
  ...runtime,
@@ -18559,17 +19071,14 @@ ${context.text}
18559
19071
  }
18560
19072
  };
18561
19073
  }
19074
+ deriveAnswer(text) {
19075
+ const derived = deriveResultsFromAnswer(text, this.evidenceLedger, { allowLocalFiles: !this.remoteSession });
19076
+ this.routingDiagnostics.push(...derived.diagnostics);
19077
+ return derived.details;
19078
+ }
18562
19079
  async runInteractivePiQuery(query, pi) {
18563
19080
  pi.setSessionName(query.slice(0, 80));
18564
- for await (const event of this.searchDocumentsStream(query)) {
18565
- const text = event.type === "progress" ? event.text : event.response.answer;
18566
- pi.sendMessage({
18567
- customType: `autorag.${event.type}`,
18568
- content: [{ type: "text", text }],
18569
- display: true,
18570
- details: event
18571
- }, { triggerTurn: false });
18572
- }
19081
+ for await (const _event of this.searchDocumentsStream(query)) {}
18573
19082
  }
18574
19083
  abort() {
18575
19084
  this.activeSession?.abort();
@@ -18600,11 +19109,6 @@ ${context.text}
18600
19109
  this.refreshing = false;
18601
19110
  }
18602
19111
  }
18603
- getResultRegistry(sessionId) {
18604
- const sid = sessionId ?? this.lastSessionId;
18605
- const session = sid ? this.sessions.get(sid) : undefined;
18606
- return session?.registry ?? new Map;
18607
- }
18608
19112
  async searchDocuments(query, options = {}) {
18609
19113
  if (this.activeRun) {
18610
19114
  throw new Error("AutoRAG agent is busy; await the in-flight searchDocuments() call before starting another");
@@ -18616,7 +19120,7 @@ ${context.text}
18616
19120
  if (trimmedQuery.length === 0) {
18617
19121
  this.lastQuery = trimmedQuery;
18618
19122
  this.lastSessionId = sessionId;
18619
- return createEmptySearchDocumentsResponse(sessionId, trimmedQuery, this.sessions, this.startupDiagnostics);
19123
+ return createEmptySearchDocumentsResponse(sessionId, trimmedQuery, this.startupDiagnostics);
18620
19124
  }
18621
19125
  options = this.normalizeRetrievalOptions(options);
18622
19126
  if (this.remoteSession && options.observedSources !== undefined)
@@ -18652,11 +19156,7 @@ ${context.text}
18652
19156
  publishPreliminary(details);
18653
19157
  };
18654
19158
  let session;
18655
- this.interactiveFastAnswerCallback = emitPreliminary;
18656
19159
  let unsubscribers = [];
18657
- this.resultCapture = (details) => {
18658
- captured = details;
18659
- };
18660
19160
  let searchStarted = false;
18661
19161
  try {
18662
19162
  const resolved = await this.resolveSessionModel();
@@ -18674,7 +19174,7 @@ ${context.text}
18674
19174
  model: resolved.model.id
18675
19175
  });
18676
19176
  searchStarted = true;
18677
- session = await this.createSearchSession(resolved, buildSystemPrompt(this.currentSystemPromptConfig({ modelId: resolved.model.id })), [createEmitFastAnswerTool(emitPreliminary, { ledger: this.evidenceLedger })]);
19177
+ session = await this.createSearchSession(resolved, buildSystemPrompt(this.currentSystemPromptConfig({ modelId: resolved.model.id })));
18678
19178
  this.activeSession = session;
18679
19179
  unsubscribers = this.configureSearchSession(session);
18680
19180
  let timeout;
@@ -18683,45 +19183,36 @@ ${context.text}
18683
19183
  await Promise.race([
18684
19184
  (async () => {
18685
19185
  const plan = await this.planQuery(trimmedQuery, resolved, planAbort.signal);
18686
- const sessionAgent = session.piSession;
18687
19186
  const activeSession = session;
18688
- const activateFastPhase = () => {
18689
- if (sessionAgent !== undefined) {
18690
- sessionAgent.setThinkingLevel(clampThinkingLevel(resolved.model, this.fastThinkingLevel));
18691
- sessionAgent.setActiveToolsByName([
18692
- ...sessionAgent.getActiveToolNames().filter((name) => name !== EMIT_FAST_ANSWER_TOOL_NAME),
18693
- EMIT_FAST_ANSWER_TOOL_NAME
18694
- ]);
18695
- } else {
18696
- activeSession.agent.state.thinkingLevel = clampThinkingLevel(resolved.model, this.fastThinkingLevel);
18697
- activeSession.agent.state.tools = [
18698
- ...this.tools,
18699
- { name: EMIT_FAST_ANSWER_TOOL_NAME }
18700
- ];
18701
- }
19187
+ const sessionAgent = activeSession.piSession;
19188
+ const transcript = () => sessionAgent?.messages ?? activeSession.agent.state.messages;
19189
+ const setThinking = (level) => {
19190
+ const clamped = clampThinkingLevel(resolved.model, level);
19191
+ if (sessionAgent !== undefined)
19192
+ sessionAgent.setThinkingLevel(clamped);
19193
+ else
19194
+ activeSession.agent.state.thinkingLevel = clamped;
18702
19195
  };
18703
19196
  if (plan.route === "config" && plan.selfConfigSkill !== undefined && this.selfConfig !== undefined) {
18704
19197
  selfConfigRun = true;
18705
19198
  mkdirSync17(this.workspaceProjectRoot, { recursive: true });
18706
19199
  const previousTools = sessionAgent?.getActiveToolNames();
18707
- if (sessionAgent !== undefined) {
18708
- sessionAgent.setThinkingLevel(clampThinkingLevel(resolved.model, this.finalThinkingLevel));
18709
- sessionAgent.setActiveToolsByName([...PI_BUILTIN_TOOL_NAMES, EMIT_AUTORAG_RESULTS_TOOL_NAME]);
18710
- } else {
18711
- activeSession.agent.state.thinkingLevel = clampThinkingLevel(resolved.model, this.finalThinkingLevel);
18712
- activeSession.agent.state.tools = this.tools.filter((tool) => tool.name === EMIT_AUTORAG_RESULTS_TOOL_NAME);
18713
- }
19200
+ setThinking(this.finalThinkingLevel);
19201
+ if (sessionAgent !== undefined)
19202
+ sessionAgent.setActiveToolsByName([...PI_BUILTIN_TOOL_NAMES]);
19203
+ else
19204
+ activeSession.agent.state.tools = [];
18714
19205
  const configBefore = snapshotConfigFile(this.selfConfig.configPath);
18715
19206
  try {
18716
- await session.prompt(buildSelfConfigPrompt({
19207
+ await activeSession.prompt(buildSelfConfigPrompt({
18717
19208
  query: trimmedQuery,
18718
19209
  configPath: this.selfConfig.configPath,
18719
19210
  agentDir: this.piAgentDir ?? getAgentDir2(),
18720
19211
  skill: plan.selfConfigSkill
18721
19212
  }));
18722
- if (captured === undefined && !planAbort.signal.aborted && lastModelRequestError(session.piSession?.messages ?? session.agent.state.messages) === undefined) {
18723
- await session.prompt(buildFinalEmitReminder());
18724
- }
19213
+ const report = finalAnswerText(transcript());
19214
+ if (report !== undefined)
19215
+ captured = this.deriveAnswer(report);
18725
19216
  const rolledBack = await rollbackIfBroken(this.selfConfig, configBefore);
18726
19217
  if (rolledBack !== undefined) {
18727
19218
  this.routingDiagnostics.push({
@@ -18730,11 +19221,10 @@ ${context.text}
18730
19221
  message: `The edited config did not validate, so the previous config was restored. ${rolledBack}`,
18731
19222
  source: "self-config"
18732
19223
  });
18733
- const emitted = captured;
18734
- if (emitted !== undefined) {
19224
+ if (captured !== undefined) {
18735
19225
  captured = {
18736
- ...emitted,
18737
- answer: `${emitted.answer}
19226
+ ...captured,
19227
+ answer: `${captured.answer}
18738
19228
 
18739
19229
  Warning: the edited config did not validate, so the previous config was restored (nothing was changed). Problem: ${rolledBack}`
18740
19230
  };
@@ -18748,49 +19238,35 @@ Warning: the edited config did not validate, so the previous config was restored
18748
19238
  return;
18749
19239
  }
18750
19240
  if (plan.route === "direct") {
18751
- activateFastPhase();
18752
- await session.prompt(this.buildDirectAnswerPrompt(trimmedQuery));
18753
- const answer = fastCaptured?.answer ?? lastAssistantText(session.piSession?.messages ?? session.agent.state.messages);
19241
+ setThinking(this.fastThinkingLevel);
19242
+ await activeSession.prompt(this.buildDirectAnswerPrompt(trimmedQuery));
19243
+ const answer = finalAnswerText(transcript());
18754
19244
  if (answer !== undefined)
18755
- captured = { answer, results: [], mapping: [], warnings: [] };
19245
+ captured = this.deriveAnswer(answer);
18756
19246
  return;
18757
19247
  }
18758
19248
  const baseline = await this.prefetchInitialRetrievalContext(trimmedQuery, plan.queries, options, plan.datasources);
18759
- activateFastPhase();
18760
- await session.prompt(this.buildFastAnswerPrompt(trimmedQuery, options, baseline));
18761
- let preliminary = fastCaptured;
18762
- if (preliminary === undefined) {
18763
- const text = lastAssistantText(session.piSession?.messages ?? session.agent.state.messages);
18764
- if (text !== undefined)
18765
- preliminary = { answer: text, results: [], sources: [], evidenceRefs: [] };
18766
- }
18767
- if (preliminary !== undefined)
18768
- emitPreliminary(preliminary);
18769
- if (captured !== undefined)
18770
- return;
19249
+ setThinking(this.fastThinkingLevel);
19250
+ await activeSession.prompt(this.buildFastAnswerPrompt(trimmedQuery, options, baseline));
19251
+ const fastText = finalAnswerText(transcript());
19252
+ const preliminary = fastText === undefined ? undefined : this.deriveAnswer(fastText);
18771
19253
  if (preliminary !== undefined) {
19254
+ emitPreliminary(preliminary);
18772
19255
  const followUp = await this.shouldFollowUp(trimmedQuery, preliminary);
18773
19256
  if (planAbort.signal.aborted)
18774
19257
  return;
18775
19258
  if (!followUp) {
18776
- captured = fastAnswerAsFinal(preliminary);
19259
+ captured = preliminary;
18777
19260
  return;
18778
19261
  }
18779
- }
18780
- if (preliminary !== undefined)
18781
19262
  publishPreliminary(preliminary);
18782
- if (sessionAgent !== undefined) {
18783
- sessionAgent.setThinkingLevel(clampThinkingLevel(resolved.model, this.finalThinkingLevel));
18784
- sessionAgent.setActiveToolsByName(sessionAgent.getActiveToolNames().filter((name) => name !== EMIT_FAST_ANSWER_TOOL_NAME));
18785
- } else {
18786
- session.agent.state.thinkingLevel = clampThinkingLevel(resolved.model, this.finalThinkingLevel);
18787
- session.agent.state.tools = [...this.tools];
18788
19263
  }
19264
+ setThinking(this.finalThinkingLevel);
18789
19265
  const fastAnswerDelivered = preliminary !== undefined && this.preliminaryCallback !== undefined;
18790
- await session.prompt(this.buildRefinementPrompt(trimmedQuery, options, preliminary, fastAnswerDelivered));
18791
- if (captured === undefined && !planAbort.signal.aborted && this.searchToolCallCount < this.maxSearchToolCalls && lastModelRequestError(session.piSession?.messages ?? session.agent.state.messages) === undefined) {
18792
- await session.prompt(buildFinalEmitReminder());
18793
- }
19266
+ await activeSession.prompt(this.buildRefinementPrompt(trimmedQuery, options, fastText, fastAnswerDelivered));
19267
+ const finalText = finalAnswerText(transcript());
19268
+ if (finalText !== undefined)
19269
+ captured = this.deriveAnswer(finalText);
18794
19270
  })(),
18795
19271
  new Promise((resolve, reject) => {
18796
19272
  timeout = setTimeout(() => {
@@ -18810,7 +19286,7 @@ Warning: the edited config did not validate, so the previous config was restored
18810
19286
  clearTimeout(timeout);
18811
19287
  }
18812
19288
  if (captured === undefined && timedOutAfterFastAnswer && fastCaptured !== undefined) {
18813
- const response = recordStructuredResultsSession(sessionId, trimmedQuery, fastAnswerAsFinal(fastCaptured), this.sessions, this.memory, [
19289
+ const response = recordStructuredResultsSession(sessionId, trimmedQuery, fastCaptured, this.memory, [
18814
19290
  ...this.collectComponentDiagnostics(),
18815
19291
  {
18816
19292
  code: "search-timeout",
@@ -18844,15 +19320,15 @@ Warning: the edited config did not validate, so the previous config was restored
18844
19320
  sessionId,
18845
19321
  query: trimmedQuery,
18846
19322
  results: [],
18847
- answer: buildMissingFinalEmitAnswer(trimmedQuery, reason, this.retrievalTrace, modelError),
19323
+ answer: buildMissingFinalAnswer(trimmedQuery, reason, this.retrievalTrace, modelError),
18848
19324
  searched: this.retrievalTrace.reduce((total, entry) => total + entry.resultCount, 0),
18849
19325
  warnings: [],
18850
19326
  diagnostics: [
18851
19327
  ...this.collectComponentDiagnostics(),
18852
19328
  {
18853
- code: "missing-final-emit",
19329
+ code: "no-final-answer",
18854
19330
  severity: "warning",
18855
- message: "The agent ended its run without calling emit_autorag_results; returning a degraded response that carries the run's retrieval trace."
19331
+ message: "The agent ended its run without a final answer message; returning a degraded response that carries the run's retrieval trace."
18856
19332
  },
18857
19333
  ...modelError === undefined ? [] : [
18858
19334
  {
@@ -18898,7 +19374,7 @@ Warning: the edited config did not validate, so the previous config was restored
18898
19374
  source: "agent"
18899
19375
  });
18900
19376
  }
18901
- const recorded = recordStructuredResultsSession(sessionId, trimmedQuery, captured, this.sessions, this.memory, componentDiagnostics, { isolateMemory: this.remoteSession || selfConfigRun });
19377
+ const recorded = recordStructuredResultsSession(sessionId, trimmedQuery, captured, this.memory, componentDiagnostics, { isolateMemory: this.remoteSession || selfConfigRun });
18902
19378
  const memoryDiagnostic = selfConfigRun ? undefined : await this.recordJudgedEvidence(sessionId, trimmedQuery, captured);
18903
19379
  const response = memoryDiagnostic === undefined ? recorded : { ...recorded, diagnostics: [...recorded.diagnostics ?? [], memoryDiagnostic] };
18904
19380
  this.runLogger.write({
@@ -18939,36 +19415,25 @@ Warning: the edited config did not validate, so the previous config was restored
18939
19415
  });
18940
19416
  }
18941
19417
  this.activeSession = undefined;
18942
- this.resultCapture = undefined;
18943
19418
  this.activeRetrievalOptions = undefined;
18944
19419
  this.preliminaryCallback = undefined;
18945
- if (this.interactiveFastAnswerCallback === emitPreliminary)
18946
- this.interactiveFastAnswerCallback = undefined;
18947
19420
  this.activeRun = false;
18948
19421
  this.memoryContextCache = undefined;
18949
19422
  }
18950
19423
  }
18951
19424
  async* searchDocumentsStream(query, options = {}) {
18952
19425
  const queue = [];
18953
- let progressBuffer = "";
18954
19426
  let wake;
18955
19427
  let settled = false;
18956
19428
  const unsubscribe = this.subscribe((event) => {
18957
- if (event.type !== "message_update" || event.assistantMessageEvent.type !== "text_delta")
19429
+ if (event.type !== "message_end" || event.message.role !== "assistant")
18958
19430
  return;
18959
- const text = event.assistantMessageEvent.delta;
18960
- if (text.trim().length === 0)
19431
+ if (event.message.stopReason !== "toolUse")
18961
19432
  return;
18962
- progressBuffer += text;
18963
- if (!/[.!?。!?]\s*$/u.test(progressBuffer))
19433
+ const text = event.message.content.filter((block) => block.type === "text").map((block) => block.text).join("").trim();
19434
+ if (text === "")
18964
19435
  return;
18965
- queue.push({
18966
- type: "progress",
18967
- sessionId: this.lastSessionId ?? "",
18968
- query: query.trim(),
18969
- text: progressBuffer
18970
- });
18971
- progressBuffer = "";
19436
+ queue.push({ type: "progress", sessionId: this.lastSessionId ?? "", query: query.trim(), text });
18972
19437
  wake?.();
18973
19438
  wake = undefined;
18974
19439
  });
@@ -18984,15 +19449,6 @@ Warning: the edited config did not validate, so the previous config was restored
18984
19449
  wake = undefined;
18985
19450
  };
18986
19451
  const run = this.searchDocuments(query, options).then((response) => {
18987
- if (progressBuffer.trim() !== "") {
18988
- queue.push({
18989
- type: "progress",
18990
- sessionId: this.lastSessionId ?? "",
18991
- query: query.trim(),
18992
- text: progressBuffer
18993
- });
18994
- progressBuffer = "";
18995
- }
18996
19452
  queue.push({ type: "complete", response });
18997
19453
  }).catch((error) => {
18998
19454
  settled = true;
@@ -19383,7 +19839,7 @@ ${result.content.replace(/\s+/gu, " ")}`).join(`
19383
19839
  }
19384
19840
  }
19385
19841
  buildFastAnswerPrompt(query, options, baseline) {
19386
- const limit = typeof options.topK === "number" ? ` Return at most ${options.topK} knowledge units.` : "";
19842
+ const limit = typeof options.topK === "number" ? ` Cite at most ${options.topK} sources.` : "";
19387
19843
  const scope = options.scope ? ` Restrict search to virtual path scope ${options.scope}.` : "";
19388
19844
  return `Answer this original query immediately: ${query}${limit}${scope}
19389
19845
 
@@ -19395,21 +19851,18 @@ ${baseline}
19395
19851
  ` + `Formatting and content rules for the answer:
19396
19852
  ` + `- 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.
19397
19853
  ` + `- Answer the question directly. Do not include specific file paths, datasource descriptions, or retrieval mechanics in the answer text.
19398
- ` + `- Cite evidence with bracketed numbers only (e.g. [1], [2]); do not quote raw chunks or mention source paths directly in the answer.
19399
19854
  ` + `- ${ANSWER_CITATION_RULE}
19400
19855
  ` + `- ${ANSWER_IMAGE_EMBED_RULE}
19401
19856
  ` + `- Do not report per-source negative findings (e.g. "no information found in Slack" or "checked Drive but found nothing").
19402
19857
  ` + `- When evidence conflicts, treat the freshest (most recent) information as the correct source of truth.
19403
19858
  ` + `- 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.
19404
19859
 
19405
- ` + `Call emit_fast_answer exactly once with the answer, its numbered knowledge units, and the evidence ids (e.g. e3) from the baseline evidence that back each unit in refs, then stop.`;
19860
+ ` + `Write the answer as your reply, then stop.`;
19406
19861
  }
19407
19862
  buildDirectAnswerPrompt(query) {
19408
19863
  return `Answer this query directly from your own general knowledge: ${query}
19409
19864
 
19410
- ` + `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.
19411
-
19412
- ` + `Call emit_fast_answer exactly once with the answer and an empty results list, then stop.`;
19865
+ ` + `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.`;
19413
19866
  }
19414
19867
  discoveryHint(sentence) {
19415
19868
  const names = this.tools.map((tool) => tool.name).filter((name) => name === JIKJI_FIND_TOOL_NAME || name === EVERYTHING_SEARCH_TOOL_NAME);
@@ -19423,22 +19876,20 @@ ${baseline}
19423
19876
  return `When local evidence is missing, thin, or possibly outdated — or the question concerns current events, recent releases, prices, or other public facts — use ${WEB_SEARCH_TOOL_NAME}${fetch2} to confirm or correct claims and fill gaps; use URLs as result sources for web-derived evidence. `;
19424
19877
  }
19425
19878
  buildRefinementPrompt(query, options, fastAnswer, fastAnswerDelivered) {
19426
- const limit = typeof options.topK === "number" ? ` Return at most ${options.topK} curated results.` : "";
19879
+ const limit = typeof options.topK === "number" ? ` Cite at most ${options.topK} sources.` : "";
19427
19880
  const scope = options.scope ? ` Restrict search to virtual path scope ${options.scope}.` : "";
19428
19881
  const firstAnswer = formatFirstAnswerContext(fastAnswer, fastAnswerDelivered);
19429
- const answerRules = fastAnswerDelivered ? `Formatting and content rules for the final \`answer\` (DELTA ONLY):
19430
- ` + `- The user already has the first answer above. \`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.
19882
+ const answerRules = fastAnswerDelivered ? `Formatting and content rules for your final answer (DELTA ONLY):
19883
+ ` + `- 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.
19431
19884
  ` + `- NEVER restate or re-list first-answer facts that remain correct, and never repeat its bullet list.
19432
19885
  ` + `- Mark each item clearly as a correction or as a new finding.
19433
19886
  ` + `- 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.
19434
- ` + `- Cite evidence with bracketed numbers only (e.g. [1], [2]); do not quote raw chunks or mention source paths directly in the answer.
19435
- ` + `- ${ANSWER_CITATION_RULE} A correction or new finding that relies on a first-answer unit must re-emit that evidence as a result of this call and cite its new number.
19887
+ ` + `- ${ANSWER_CITATION_RULE} A correction or new finding that relies on evidence the first answer cited must cite that evidence's id again.
19436
19888
  ` + `- ${ANSWER_IMAGE_EMBED_RULE} ${ANSWER_IMAGE_DELTA_RULE}
19437
19889
  ` + `- Do not report per-source negative findings (e.g. "no information found in Slack").
19438
19890
  ` + `- 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):
19439
19891
  ` + `- 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.
19440
19892
  ` + `- Answer the question directly. Do not include specific file paths, datasource descriptions, or retrieval mechanics in the answer text.
19441
- ` + `- Cite evidence with bracketed numbers only (e.g. [1], [2]); do not quote raw chunks or mention source paths directly in the answer.
19442
19893
  ` + `- ${ANSWER_CITATION_RULE}
19443
19894
  ` + `- ${ANSWER_IMAGE_EMBED_RULE}
19444
19895
  ` + `- Do not report per-source negative findings (e.g. "no information found in Slack").
@@ -19447,35 +19898,34 @@ ${baseline}
19447
19898
 
19448
19899
  ` + `${firstAnswer}
19449
19900
 
19450
- ` + `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()}` + `Cite the evidence ids (e.g. e3) shown next to retrieved results in each result's refs; for a local file you opened yourself with bash, give its absolute path.
19901
+ ` + `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()}
19451
19902
 
19452
19903
  ` + `${answerRules}
19453
19904
 
19454
- ` + `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, call ${EMIT_AUTORAG_RESULTS_TOOL_NAME} exactly once as your final action with the curated ` + `results, each citing its supporting evidence ids in refs.`;
19905
+ ` + `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.`;
19455
19906
  }
19456
19907
  buildSearchPrompt(query, options, initialRetrievalContext) {
19457
- const limit = typeof options.topK === "number" ? ` Return at most ${options.topK} curated results.` : "";
19908
+ const limit = typeof options.topK === "number" ? ` Cite at most ${options.topK} sources.` : "";
19458
19909
  const scope = options.scope ? ` Restrict search to virtual path scope ${options.scope}.` : "";
19459
19910
  return `Find and curate information for this original query: ${query}${limit}${scope}
19460
19911
 
19461
- ` + `Start by deciding whether this is answerable from general knowledge or memory. If it is a generic, stable question, answer it immediately without retrieval and emit the structured result. ` + `Otherwise, baseline MinSync and Jikji retrieval is already running in parallel; do not emit final results until its next message arrives.
19912
+ ` + `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.
19462
19913
 
19463
19914
  ` + `Baseline retrieval context:
19464
19915
  ${initialRetrievalContext ?? "Pending; continue only with a brief progress statement."}
19465
19916
 
19466
- ` + `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. Cite the evidence ids (e.g. e3) shown next to retrieved results in each result's refs.
19917
+ ` + `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.
19467
19918
 
19468
19919
  ` + `Formatting and content rules for the answer:
19469
19920
  ` + `- 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.
19470
19921
  ` + `- Answer the question directly. Do not include specific file paths, datasource descriptions, or retrieval mechanics in the answer text.
19471
- ` + `- Cite evidence with bracketed numbers only (e.g. [1], [2]); do not quote raw chunks or mention source paths directly in the answer.
19472
19922
  ` + `- ${ANSWER_CITATION_RULE}
19473
19923
  ` + `- ${ANSWER_IMAGE_EMBED_RULE}
19474
19924
  ` + `- Do not report per-source negative findings (e.g. "no information found in Slack").
19475
19925
  ` + `- When evidence conflicts, treat the freshest (most recent) information as the correct source of truth.
19476
19926
  ` + `- If information is incomplete or uncertain, acknowledge it briefly without lengthy explanations. If there are partial clues or leads, mention them concisely.
19477
19927
 
19478
- ` + `${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, call ${EMIT_AUTORAG_RESULTS_TOOL_NAME} exactly once as your final action with the curated ` + `results, each citing its supporting evidence ids in refs.`;
19928
+ ` + `${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.`;
19479
19929
  }
19480
19930
  async refresh(force = false, opts) {
19481
19931
  const methods = opts?.methods;
@@ -20107,57 +20557,21 @@ ${initialRetrievalContext ?? "Pending; continue only with a brief progress state
20107
20557
  function formatRerankedBaseline(results, evidenceIds) {
20108
20558
  const lines = results.map((result, index) => `[${evidenceIds[index]}] ${result.source}
20109
20559
  ${result.content.replace(/\s+/gu, " ")}`);
20110
- return `Reranked initial candidates (ordered by relevance to the query; cite evidence by its [eN] id in refs):
20560
+ return `Reranked initial candidates (ordered by relevance to the query; cite evidence by its [eN] id):
20111
20561
  ${lines.join(`
20112
20562
  `)}`;
20113
20563
  }
20114
- function fastAnswerAsFinal(fastAnswer) {
20115
- const refsByNumber = new Map(fastAnswer.evidenceRefs.map((entry) => [entry.number, entry.refs]));
20116
- return {
20117
- answer: fastAnswer.answer,
20118
- results: fastAnswer.results.map((result) => ({
20119
- number: result.number,
20120
- title: result.title,
20121
- summary: result.summary,
20122
- evidence: result.evidence,
20123
- confidence: result.confidence ?? 0.5
20124
- })),
20125
- mapping: fastAnswer.results.map((result) => {
20126
- const refs = refsByNumber.get(result.number) ?? [];
20127
- const primary = refs[0];
20128
- return {
20129
- number: result.number,
20130
- source: primary?.source ?? "",
20131
- method: primary?.method ?? EMIT_FAST_ANSWER_TOOL_NAME,
20132
- content: primary?.content ?? (result.evidence.map((evidence) => evidence.excerpt).join(`
20133
- `) || result.summary),
20134
- evidenceRefs: refs
20135
- };
20136
- }),
20137
- warnings: []
20138
- };
20139
- }
20140
20564
  function formatFirstAnswerContext(fastAnswer, delivered) {
20141
20565
  if (fastAnswer === undefined) {
20142
20566
  return "(the fast phase produced no answer — write the complete answer)";
20143
20567
  }
20144
- const header = delivered ? "The user has ALREADY received this immediate first answer:" : "An internal first-pass draft was produced but was NOT shown to the caller; write the complete answer:";
20145
- const units = fastAnswer.results.length === 0 ? "" : `
20146
-
20147
- Numbered units of that first answer (its own numbering — NOT citation numbers for your final answer; cite only the results you emit):
20148
- ${fastAnswer.results.map((result) => `[${result.number}] ${result.title} — ${result.summary}`).join(`
20149
- `)}`;
20150
- const sources = fastAnswer.sources.length === 0 ? "" : `
20151
-
20152
- Sources of that first answer:
20153
- ${fastAnswer.sources.map((entry) => `[${entry.number}] -> ${entry.source}`).join(`
20154
- `)}`;
20568
+ const header = delivered ? "The user has ALREADY received this immediate first answer (its citations were renumbered for the user; the [eN] ids below are still valid for you):" : "An internal first-pass draft was produced but was NOT shown to the caller; write the complete answer:";
20155
20569
  return `${header}
20156
- ${fastAnswer.answer}${units}${sources}`;
20570
+ ${fastAnswer}`;
20157
20571
  }
20158
- function buildMissingFinalEmitAnswer(query, reason, trace, modelError) {
20572
+ function buildMissingFinalAnswer(query, reason, trace, modelError) {
20159
20573
  const lines = [
20160
- `The search run for "${query}" ended without finalized results: the agent ended its run without calling emit_autorag_results, so no curated answer is available within the configured search range.`,
20574
+ `The search run for "${query}" ended without a final answer: the agent stopped before writing one, so no curated answer is available within the configured search range.`,
20161
20575
  ...modelError === undefined ? [] : [`The model request failed: ${modelError}`],
20162
20576
  ...reason === undefined ? modelError === undefined ? ["The agent did not record why it stopped."] : [] : [`The agent's last note before stopping: "${reason.length > 500 ? `${reason.slice(0, 500)}…` : reason}"`],
20163
20577
  modelError === undefined ? "Next steps: broaden the configured searchPaths, connect additional datasources via the datasources configuration, or retry with a narrower or different query." : "Next steps: fix the model provider error above (rate limit, quota, credentials, or model id) and retry the same query.",
@@ -20178,8 +20592,17 @@ function lastModelRequestError(messages) {
20178
20592
  }
20179
20593
  return;
20180
20594
  }
20181
- function buildFinalEmitReminder() {
20182
- return `You ended without calling ${EMIT_AUTORAG_RESULTS_TOOL_NAME}, so the user has not received your verified answer. ` + `Do not search again. Call ${EMIT_AUTORAG_RESULTS_TOOL_NAME} now, exactly once, with the answer you just wrote, ` + `its numbered results, each with the evidence ids (refs) of the sources you used (a local file you opened yourself: its absolute path). ` + `If verification found nothing usable, call it with an answer that says so and an empty results list.`;
20595
+ function finalAnswerText(messages) {
20596
+ for (let index = messages.length - 1;index >= 0; index--) {
20597
+ const message = messages[index];
20598
+ if (message.role !== "assistant")
20599
+ continue;
20600
+ if (message.stopReason !== "stop")
20601
+ return;
20602
+ const text = message.content.filter((block) => block.type === "text").map((block) => block.text).join("").trim();
20603
+ return text === "" ? undefined : text;
20604
+ }
20605
+ return;
20183
20606
  }
20184
20607
  function lastAssistantText(messages) {
20185
20608
  for (let index = messages.length - 1;index >= 0; index--) {
@@ -20341,13 +20764,12 @@ var init_agent = __esm(() => {
20341
20764
  init_rerank();
20342
20765
  init_scope();
20343
20766
  init_model_auth();
20767
+ init_answer_citations();
20344
20768
  init_datasource_skill();
20345
20769
  init_dupey_tool();
20346
- init_emit_results_tool();
20347
20770
  init_everything_search_tool();
20348
20771
  init_evidence_judgment();
20349
20772
  init_evidence_ledger();
20350
- init_fast_answer_tool();
20351
20773
  init_fsearch_search_tool();
20352
20774
  init_jev_extension();
20353
20775
  init_jikji_find_tool();
@@ -20789,7 +21211,7 @@ function datasourceCliError(datasourceId, command, failure) {
20789
21211
  return new DatasourceCliError(datasourceId, command, failure);
20790
21212
  }
20791
21213
  var DatasourceCliError;
20792
- var init_errors3 = __esm(() => {
21214
+ var init_errors4 = __esm(() => {
20793
21215
  DatasourceCliError = class DatasourceCliError extends Error {
20794
21216
  datasourceId;
20795
21217
  command;
@@ -20881,7 +21303,7 @@ function toResult(hit, source, mode, instanceId) {
20881
21303
  };
20882
21304
  }
20883
21305
  var init_methods = __esm(() => {
20884
- init_errors3();
21306
+ init_errors4();
20885
21307
  init_scope2();
20886
21308
  });
20887
21309
 
@@ -21475,7 +21897,7 @@ var init_rclone_connector = __esm(() => {
21475
21897
  });
21476
21898
 
21477
21899
  // src/datasource/connector.ts
21478
- import { createHash as createHash11 } from "node:crypto";
21900
+ import { createHash as createHash12 } from "node:crypto";
21479
21901
  function connectorFailureToDiagnosticCode(reason) {
21480
21902
  switch (reason) {
21481
21903
  case "auth":
@@ -21509,7 +21931,7 @@ function sanitizeIdSegment(value) {
21509
21931
  const cleaned = normalized.slice(start, end);
21510
21932
  if (cleaned === value && cleaned.length > 0 && cleaned.length <= 80)
21511
21933
  return cleaned;
21512
- const hash = createHash11("sha256").update(value).digest("hex").slice(0, 8);
21934
+ const hash = createHash12("sha256").update(value).digest("hex").slice(0, 8);
21513
21935
  const stem = cleaned.slice(0, 60);
21514
21936
  return stem.length > 0 ? `${stem}-${hash}` : hash;
21515
21937
  }
@@ -22708,7 +23130,7 @@ function toRetrievalResult(hit, source, methodName, instanceId, mode, diagnostic
22708
23130
  }
22709
23131
  var DISCORD_DATASOURCE_ID = "discord", DEFAULT_DISCORD_TAGS, DEFAULT_TOP_K2 = 20;
22710
23132
  var init_methods2 = __esm(() => {
22711
- init_errors3();
23133
+ init_errors4();
22712
23134
  init_scope2();
22713
23135
  init_paths3();
22714
23136
  DEFAULT_DISCORD_TAGS = ["discord", "chat", "pii"];
@@ -23006,7 +23428,7 @@ class GitHubConnector {
23006
23428
  if (repos.length === 0)
23007
23429
  return { ok: false, reason: "not-configured", message: "no repositories configured" };
23008
23430
  const token = resolveToken(this.options.token, this.options.tokenEnv ?? DEFAULT_TOKEN_ENV);
23009
- const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL3;
23431
+ const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL4;
23010
23432
  const request = {
23011
23433
  headers: {
23012
23434
  Accept: "application/vnd.github+json",
@@ -23079,7 +23501,7 @@ ${body}` : title,
23079
23501
  return { ok: true, documents, ...warnings.length > 0 ? { warnings } : {} };
23080
23502
  }
23081
23503
  }
23082
- var DEFAULT_BASE_URL3 = "https://api.github.com", DEFAULT_TOKEN_ENV = "GITHUB_TOKEN", DEFAULT_MAX_PAGES = 10, DEFAULT_MAX_DOCUMENTS2 = 500;
23504
+ var DEFAULT_BASE_URL4 = "https://api.github.com", DEFAULT_TOKEN_ENV = "GITHUB_TOKEN", DEFAULT_MAX_PAGES = 10, DEFAULT_MAX_DOCUMENTS2 = 500;
23083
23505
  var init_connector2 = () => {};
23084
23506
 
23085
23507
  // src/datasource/skills/github/skill.ts
@@ -23146,7 +23568,7 @@ class GitHubGistConnector {
23146
23568
  message: "no GitHub token: set GITHUB_TOKEN (or connector.tokenEnv) or authenticate the gh CLI"
23147
23569
  };
23148
23570
  }
23149
- const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL4;
23571
+ const baseUrl = this.options.baseUrl ?? DEFAULT_BASE_URL5;
23150
23572
  const request = {
23151
23573
  headers: {
23152
23574
  Accept: "application/vnd.github+json",
@@ -23295,15 +23717,15 @@ ${content}`);
23295
23717
  }
23296
23718
  };
23297
23719
  }
23298
- var DEFAULT_BASE_URL4 = "https://api.github.com", DEFAULT_TOKEN_ENV2 = "GITHUB_TOKEN", DEFAULT_MAX_PAGES2 = 10, DEFAULT_MAX_GISTS = 500, GH_TOKEN_TIMEOUT_MS = 5000, STATE_VERSION = 1;
23720
+ var DEFAULT_BASE_URL5 = "https://api.github.com", DEFAULT_TOKEN_ENV2 = "GITHUB_TOKEN", DEFAULT_MAX_PAGES2 = 10, DEFAULT_MAX_GISTS = 500, GH_TOKEN_TIMEOUT_MS = 5000, STATE_VERSION = 1;
23299
23721
  var init_connector3 = () => {};
23300
23722
 
23301
23723
  // src/datasource/skills/github-gist/semantic.ts
23302
- import { createHash as createHash12 } from "node:crypto";
23724
+ import { createHash as createHash13 } from "node:crypto";
23303
23725
  import { existsSync as existsSync26, mkdirSync as mkdirSync22, readFileSync as readFileSync23, writeFileSync as writeFileSync21 } from "node:fs";
23304
23726
  import { dirname as dirname21 } from "node:path";
23305
23727
  function contentHash(chunk) {
23306
- return createHash12("sha256").update(`${chunk.title ?? ""}
23728
+ return createHash13("sha256").update(`${chunk.title ?? ""}
23307
23729
  ${chunk.content}`).digest("hex");
23308
23730
  }
23309
23731
  function sameIdentity(a, b) {
@@ -24233,7 +24655,7 @@ function isRemoteEmbedder(value) {
24233
24655
  }
24234
24656
  var CHAT_TAG = "lark:chat", DOCS_TAG = "lark:docs", DEFAULT_TAGS2, REMOTE_NOTE = "remote-search datasource: no local mirror";
24235
24657
  var init_skill6 = __esm(() => {
24236
- init_errors3();
24658
+ init_errors4();
24237
24659
  init_scope2();
24238
24660
  init_client7();
24239
24661
  DEFAULT_TAGS2 = [CHAT_TAG, DOCS_TAG];
@@ -24761,7 +25183,7 @@ function toRetrievalResult2(hit, source, methodName, instanceId, mode) {
24761
25183
  }
24762
25184
  var KAKAO_DATASOURCE_ID = "kakao", DEFAULT_KAKAO_TAGS, DEFAULT_TOP_K4 = 20;
24763
25185
  var init_methods3 = __esm(() => {
24764
- init_errors3();
25186
+ init_errors4();
24765
25187
  init_paths4();
24766
25188
  DEFAULT_KAKAO_TAGS = ["kakaotalk", "personal", "pii"];
24767
25189
  });
@@ -25115,7 +25537,7 @@ function ensurePrivateMailcrawlDataDir(path) {
25115
25537
  if (process.platform !== "win32")
25116
25538
  chmodSync(path, 448);
25117
25539
  }
25118
- var init_config = () => {};
25540
+ var init_config2 = () => {};
25119
25541
 
25120
25542
  // src/datasource/skills/mailcrawl/client.ts
25121
25543
  import { spawn as spawn15 } from "node:child_process";
@@ -25441,7 +25863,7 @@ function truncateUtf8(value, maxBytes) {
25441
25863
  var DEFAULT_BINARY4 = "mailcrawl", DEFAULT_SEARCH_TIMEOUT_MS3 = 60000, DEFAULT_INDEX_TIMEOUT_MS3 = 1800000, DEFAULT_MAX_BUFFER_BYTES4 = 1048576, LOOPBACK_HOSTS2, SAFE_ENV_KEYS2, SAFE_MAILCRAWL_ENV_KEYS, REMOTE_EMBEDDING_ENV_KEYS;
25442
25864
  var init_client9 = __esm(() => {
25443
25865
  init_portable_spawn();
25444
- init_config();
25866
+ init_config2();
25445
25867
  LOOPBACK_HOSTS2 = new Set(["127.0.0.1", "localhost", "[::1]", "::1"]);
25446
25868
  SAFE_ENV_KEYS2 = new Set(["HOME", "LANG", "LC_ALL", "PATH", "TMPDIR", "TMP", "TEMP", "NO_COLOR"]);
25447
25869
  SAFE_MAILCRAWL_ENV_KEYS = new Set([
@@ -25540,7 +25962,7 @@ class MailcrawlMethod {
25540
25962
  }
25541
25963
  var modes, MAILCRAWL_MODES;
25542
25964
  var init_methods4 = __esm(() => {
25543
- init_errors3();
25965
+ init_errors4();
25544
25966
  init_scope2();
25545
25967
  modes = ["bm25", "semantic", "hybrid"];
25546
25968
  MAILCRAWL_MODES = modes;
@@ -25688,7 +26110,7 @@ var TAGS;
25688
26110
  var init_skill9 = __esm(() => {
25689
26111
  init_scope2();
25690
26112
  init_client9();
25691
- init_config();
26113
+ init_config2();
25692
26114
  init_methods4();
25693
26115
  TAGS = ["mailcrawl", "email", "pii"];
25694
26116
  });
@@ -25908,7 +26330,7 @@ function failureCode2(reason) {
25908
26330
  }
25909
26331
  var DEFAULT_INSTANCE_ID5 = "default", DEFAULT_POLLING_INTERVAL_MS4;
25910
26332
  var init_crawler_skill = __esm(() => {
25911
- init_errors3();
26333
+ init_errors4();
25912
26334
  init_scope2();
25913
26335
  DEFAULT_POLLING_INTERVAL_MS4 = 15 * 60 * 1000;
25914
26336
  });
@@ -26094,7 +26516,7 @@ var DEFAULT_QMD_BINARY = "qmd", DEFAULT_QMD_TIMEOUT_MS = 60000, DEFAULT_QMD_MAX_
26094
26516
  // src/datasource/skills/obsidian/client.ts
26095
26517
  import { spawn as spawn16 } from "node:child_process";
26096
26518
  import { mkdirSync as mkdirSync24, writeFileSync as writeFileSync22 } from "node:fs";
26097
- import { isAbsolute as isAbsolute7, resolve as resolve14 } from "node:path";
26519
+ import { isAbsolute as isAbsolute8, resolve as resolve14 } from "node:path";
26098
26520
 
26099
26521
  class QmdClient {
26100
26522
  options;
@@ -26190,7 +26612,7 @@ function resolveVaultPath(vaultPath) {
26190
26612
  if (vaultPath === undefined || vaultPath.trim().length === 0)
26191
26613
  return;
26192
26614
  const trimmed = vaultPath.trim();
26193
- return isAbsolute7(trimmed) ? trimmed : resolve14(trimmed);
26615
+ return isAbsolute8(trimmed) ? trimmed : resolve14(trimmed);
26194
26616
  }
26195
26617
  function controlledEnv5(options) {
26196
26618
  const instanceId = options.instanceId ?? "default";
@@ -26546,7 +26968,7 @@ function toRetrievalResult3(hit, source, methodName, instanceId, mode) {
26546
26968
  }
26547
26969
  var OBSIDIAN_DATASOURCE_ID = "obsidian", DEFAULT_OBSIDIAN_TAGS, DEFAULT_TOP_K5 = 20;
26548
26970
  var init_methods5 = __esm(() => {
26549
- init_errors3();
26971
+ init_errors4();
26550
26972
  init_scope2();
26551
26973
  DEFAULT_OBSIDIAN_TAGS = ["obsidian", "notes"];
26552
26974
  });
@@ -27084,7 +27506,7 @@ var init_slack = __esm(() => {
27084
27506
 
27085
27507
  // src/datasource/skills/spotlight/connector.ts
27086
27508
  import { spawn as spawn17 } from "node:child_process";
27087
- import { createHash as createHash13 } from "node:crypto";
27509
+ import { createHash as createHash14 } from "node:crypto";
27088
27510
  import { readFile as fsReadFile, stat as stat7 } from "node:fs/promises";
27089
27511
  import { basename as basename13, extname as extname8 } from "node:path";
27090
27512
 
@@ -27169,7 +27591,7 @@ class SpotlightConnector {
27169
27591
  if (content.length === 0)
27170
27592
  continue;
27171
27593
  documents.push({
27172
- docId: createHash13("sha256").update(path).digest("hex").slice(0, 16),
27594
+ docId: createHash14("sha256").update(path).digest("hex").slice(0, 16),
27173
27595
  hierarchy: ["files"],
27174
27596
  title: basename13(path),
27175
27597
  content,
@@ -27764,7 +28186,7 @@ var init_factory = __esm(() => {
27764
28186
  // src/cli/config.ts
27765
28187
  import { randomUUID as randomUUID9 } from "node:crypto";
27766
28188
  import { existsSync as existsSync28, mkdirSync as mkdirSync25, readFileSync as readFileSync24, renameSync as renameSync11, unlinkSync as unlinkSync5, writeFileSync as writeFileSync23 } from "node:fs";
27767
- import { basename as basename14, dirname as dirname23, isAbsolute as isAbsolute8, join as join36, resolve as resolve15 } from "node:path";
28189
+ import { basename as basename14, dirname as dirname23, isAbsolute as isAbsolute9, join as join36, resolve as resolve15 } from "node:path";
27768
28190
  import { findEnvKeys, getEnvApiKey } from "@earendil-works/pi-ai/compat";
27769
28191
  import { getAgentDir as getAgentDir3, ModelRuntime as ModelRuntime2, SettingsManager as SettingsManager2 } from "@earendil-works/pi-coding-agent";
27770
28192
  function resolveConfigPath(input) {
@@ -27892,7 +28314,7 @@ function resolveSearchPaths(searchPaths, origin) {
27892
28314
  return searchPaths.map((searchPath) => resolvePersistedPath(searchPath, origin));
27893
28315
  }
27894
28316
  function resolvePersistedPath(path, origin) {
27895
- return isAbsolute8(path) ? path : resolve15(origin, path);
28317
+ return isAbsolute9(path) ? path : resolve15(origin, path);
27896
28318
  }
27897
28319
  function normalizeLegacyConfigPaths(partial, origin) {
27898
28320
  const workspacePath = resolvePersistedPath(partial.workspacePath ?? ".", origin);
@@ -28732,11 +29154,15 @@ function getModelRuntime(agentDir) {
28732
29154
  const cached = modelRuntimeCache.get(agentDir);
28733
29155
  if (cached !== undefined)
28734
29156
  return cached;
28735
- const created = ModelRuntime2.create({
28736
- authPath: join36(agentDir, "auth.json"),
28737
- modelsPath: join36(agentDir, "models.json"),
28738
- allowModelNetwork: false
28739
- });
29157
+ const created = (async () => {
29158
+ const runtime = await ModelRuntime2.create({
29159
+ authPath: join36(agentDir, "auth.json"),
29160
+ modelsPath: join36(agentDir, "models.json"),
29161
+ allowModelNetwork: false
29162
+ });
29163
+ await registerAutoRAGProvider(runtime);
29164
+ return runtime;
29165
+ })();
28740
29166
  modelRuntimeCache.set(agentDir, created);
28741
29167
  created.catch(() => {
28742
29168
  modelRuntimeCache.delete(agentDir);
@@ -29065,8 +29491,9 @@ function writeDefaultConfig(path, partial, opts = {}) {
29065
29491
  }
29066
29492
  }
29067
29493
  var DEFAULT_CONFIG_FILENAME = "config.json", LEGACY_CONFIG_FILENAME = "autorag.config.json", CONFIG_LOCK_RETRY_MS = 10, CONFIG_LOCK_TIMEOUT_MS = 1e4, CONFIG_LOCK_STALE_MS = 30000, ConfigError, MINSYNC_REQUIRED_MESSAGE = "MinSync is required and cannot be disabled; remove `minSync: false` / `minSync.enabled: false` from the config.", AGENT_MODEL_APIS, AGENT_MODEL_INPUTS, API_KEY_ENV_PATTERN2, POSITIVE_INT_FIELDS, EMBEDDER_ALLOWLIST, MINSYNC_ALLOWLIST, MINSYNC_LEGACY_IGNORED, P2P_ALLOWLIST, P2P_QUOTA_ALLOWLIST, LIMITS_ALLOWLIST, LIMITS_PREFETCH_ALLOWLIST, JEV_CONFIG_FIELDS, JEV_BACKEND_NAMES, DEFAULT_JEV_BACKEND = "openrouter", DEFAULT_QUERY_DECOMPOSITION_MODEL, RERANK_ALLOWLIST, UNKNOWN_MODEL_HINT = "Add baseUrl (and optional api/apiKeyEnv) for an OpenAI-compatible endpoint outside the pi-ai catalog, or use a pi-ai catalog model id.", modelRuntimeCache;
29068
- var init_config2 = __esm(() => {
29494
+ var init_config3 = __esm(() => {
29069
29495
  init_local_model();
29496
+ init_provider();
29070
29497
  init_home();
29071
29498
  init_factory();
29072
29499
  init_file_lock();
@@ -29161,7 +29588,6 @@ class AutoRAGLite {
29161
29588
  agent;
29162
29589
  retrievalEngine;
29163
29590
  memory;
29164
- sessions = new Map;
29165
29591
  constructor(config) {
29166
29592
  this.config = config;
29167
29593
  const agentOptions = buildAgentOptions(config);
@@ -29216,14 +29642,11 @@ class AutoRAGLite {
29216
29642
  return this.retrievalEngine.retrieveByMethod(query, options);
29217
29643
  }
29218
29644
  recordStructuredResultsSession(sessionId, query, details) {
29219
- return recordStructuredResultsSession(sessionId, query, details, this.sessions, this.memory);
29645
+ return recordStructuredResultsSession(sessionId, query, details, this.memory);
29220
29646
  }
29221
29647
  recordReport(query, details) {
29222
29648
  return this.recordStructuredResultsSession(randomUUID10(), query, details);
29223
29649
  }
29224
- getResultRegistry(sessionId) {
29225
- return this.sessions.get(sessionId)?.registry ?? new Map;
29226
- }
29227
29650
  getMemorySchema() {
29228
29651
  return structuredClone(this.memory.getSchema());
29229
29652
  }
@@ -29241,9 +29664,9 @@ function createAutoRAGLite(options = {}) {
29241
29664
  var init_core = __esm(() => {
29242
29665
  init_agent();
29243
29666
  init_search_documents();
29244
- init_config2();
29667
+ init_config3();
29245
29668
  init_memory();
29246
- init_config2();
29669
+ init_config3();
29247
29670
  });
29248
29671
  init_core();
29249
29672