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