@stackfactor/agent-utils 1.11.0 → 1.13.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.
package/README.md CHANGED
@@ -392,6 +392,7 @@ const result = await langChain.runPromptWithModel(
392
392
  | `excludeDomains` | — | Exclude these domains from `web_search` |
393
393
  | `crawlLimit` | `10` | Maximum pages one `web_crawl` or `web_map` call may return |
394
394
  | `format` | `"markdown"` | `"markdown"` preserves table and heading structure at fewer tokens than the prose equivalent |
395
+ | `allowedUrls` | — | The only URLs `web_extract` may read; any other gets an error line in the tool result and is never sent to Tavily |
395
396
 
396
397
  ### Context management
397
398
 
@@ -416,6 +417,7 @@ A model that cannot tell it received half a regulation will reason over the half
416
417
  - Works with every supported provider.
417
418
  - Credits come from each response's own `usage` block rather than an estimate. They are reported to the platform as `credits` of the model `tavily` (see [Usage](#usage)) and counted on `usageTracker.tokens.tavilyCredits`.
418
419
  - URLs are collected on `usageTracker.webSearchSources`, deduplicated across the whole run — Tavily's terms require citing the original sources when its content is shown to end users.
420
+ - Every URL `web_extract` asked Tavily for is on `usageTracker.attemptedUrls`, each once, whether Tavily could read it or not; a URL `allowedUrls` refused was never asked for and is not there. `webSearchSources` holds what Tavily answered, under the address it answered with; `attemptedUrls` is what the run tried to read, so a caller can check a model's claim that it read a page.
419
421
  - Per-URL failures are surfaced in the tool result instead of being dropped, so a fetch failure is not mistaken for an absence of content.
420
422
  - Tool calls honour the caller's abort signal, so a cancelled run stops paying for in-flight crawls.
421
423
 
@@ -436,6 +438,28 @@ const kept = references.filter((r) => !r.url || live.has(r.url));
436
438
 
437
439
  ---
438
440
 
441
+ ## Reading references (`referenceContext`)
442
+
443
+ An agent reading a skill's (or a microskill's, a question's) references gathers what they point at once, with `referenceContext.gatherReferenceContext({ references, queries, search, token, topK?, maxPassages?, maxPages? })`:
444
+
445
+ - **pages**: WEB and PUBLICATION references with an http(s) url, each `{ id: "r<N>", title, url }`, opened by the model through Tavily `extract`. All of them by default; `maxPages` keeps the first N in reference order.
446
+ - **passages**: the passages of the knowledge base documents STACKFACTOR references cite, searched under the run's token (client-api `knowledgeBaseAgent.search`). A passage names the references citing its chunk (`knowledgeBase.chunkId`); when none does, every reference citing its document. The best-ranked 40 are kept (`maxPassages`).
447
+
448
+ **A cited knowledge base that cannot be read stops the run.** With no token, or when the person may not open a cited knowledge base (401, 403, 404) or its search fails, it throws `KnowledgeAccessError` (exported from the package and from `referenceContext`). A search that fails for any reason other than access is tried once more, 200 ms later, before it counts as failed; a refusal is not asked again.
449
+
450
+ | Field | Value |
451
+ | ----------- | -------------------------------------------------------------------------------------------- |
452
+ | `code` | `403` when every skipped knowledge base refused the person (or there was no token), else `502` |
453
+ | `documents` | The titles of the STACKFACTOR references citing those knowledge bases, each once |
454
+ | `skipped` | The knowledge bases that could not be searched, and why |
455
+ | `message` | `The knowledge base documents this content cites could not be read with your access: "A", "B".` (502: `…could not be read: the knowledge base search failed for "A".`) |
456
+
457
+ The agent answers with `code` and `message`, so the person sees which documents could not be read. An agent that does not catch it answers the same: `serve` and `runOnce` turn an uncaught `KnowledgeAccessError` (known by its `name`, so a second copy of this package counts too) into an error with its own `code` and `message`, not a 500. A search that works and finds nothing is not an error: the run gets no passages and the `none` guidance. Grounding from skill coverage (`stackfactorKnowledge.retrievePassages`) still skips a knowledge base it cannot read.
458
+
459
+ `referenceContext.toolsFor(context)` gives the run its tools: `{ tavily: { tools: ["extract"], allowedUrls } }` when there are pages (their urls), else `{}`. Spread it after `tavily: undefined`, so `web_extract` reads only the referenced pages.
460
+
461
+ ---
462
+
439
463
  ## Supported Models
440
464
 
441
465
  ### Text / Chat
@@ -7,7 +7,7 @@ import { callAgent, checkAgent } from "./client.js";
7
7
  import * as runtimeContext from "./runtimeContext.js";
8
8
  import { recordUsage, USAGE_UNITS } from "./usage.js";
9
9
  import stackfactorKnowledge from "./stackfactorKnowledge.js";
10
- import referenceContext from "./referenceContext.js";
10
+ import referenceContext, { KnowledgeAccessError } from "./referenceContext.js";
11
11
  import { checkUrlsReachable } from "./tavily.js";
12
12
  export { constants };
13
13
  export { errorHandling, AppError };
@@ -24,7 +24,7 @@ export type { AgentExecuteRequest, CallAgentOptions, CheckAgentOptions, AgentHea
24
24
  export { runtimeContext };
25
25
  export { recordUsage, USAGE_UNITS };
26
26
  export type { UsageEntry, UsageUnit } from "./usage.js";
27
- export { stackfactorKnowledge, referenceContext };
27
+ export { stackfactorKnowledge, referenceContext, KnowledgeAccessError };
28
28
  export type { GroundingPassage, KnowledgeChunk, KnowledgeSearch, KnowledgeSource, SkippedSource, StackFactorReference, } from "./stackfactorKnowledge.js";
29
29
  export type { ReferenceContext, ReferencePage, ReferencePassage, SourceGuidance, StoredReference, } from "./referenceContext.js";
30
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,aAAa,EAAE,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,oBAAoB,MAAM,2BAA2B,CAAC;AAC7D,OAAO,gBAAgB,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AAEnC,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,MAAM,EAAE,CAAC;AAElB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,CAAC;AAE7D,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,GACZ,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AAEpC,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,CAAC;AAElD,YAAY,EACV,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,eAAe,EACf,aAAa,EACb,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,eAAe,GAChB,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,aAAa,EAAE,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,oBAAoB,MAAM,2BAA2B,CAAC;AAC7D,OAAO,gBAAgB,EAAE,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AAEnC,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EAAE,MAAM,EAAE,CAAC;AAElB,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,CAAC;AAE7D,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,GACZ,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AAEpC,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,CAAC;AAExE,YAAY,EACV,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,eAAe,EACf,aAAa,EACb,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,eAAe,GAChB,MAAM,uBAAuB,CAAC"}
package/dist/cjs/index.js CHANGED
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.referenceContext = exports.stackfactorKnowledge = exports.USAGE_UNITS = exports.recordUsage = exports.runtimeContext = exports.checkAgent = exports.callAgent = exports.EXECUTION_ENV = exports.isRunOnceExecution = exports.runOnce = exports.serve = exports.logger = exports.checkUrlsReachable = exports.langChain = exports.AppError = exports.errorHandling = exports.constants = void 0;
39
+ exports.KnowledgeAccessError = exports.referenceContext = exports.stackfactorKnowledge = exports.USAGE_UNITS = exports.recordUsage = exports.runtimeContext = exports.checkAgent = exports.callAgent = exports.EXECUTION_ENV = exports.isRunOnceExecution = exports.runOnce = exports.serve = exports.logger = exports.checkUrlsReachable = exports.langChain = exports.AppError = exports.errorHandling = exports.constants = void 0;
40
40
  const const_js_1 = __importDefault(require("./const.js"));
41
41
  exports.constants = const_js_1.default;
42
42
  const errorHandling_js_1 = __importStar(require("./errorHandling.js"));
@@ -61,7 +61,8 @@ Object.defineProperty(exports, "recordUsage", { enumerable: true, get: function
61
61
  Object.defineProperty(exports, "USAGE_UNITS", { enumerable: true, get: function () { return usage_js_1.USAGE_UNITS; } });
62
62
  const stackfactorKnowledge_js_1 = __importDefault(require("./stackfactorKnowledge.js"));
63
63
  exports.stackfactorKnowledge = stackfactorKnowledge_js_1.default;
64
- const referenceContext_js_1 = __importDefault(require("./referenceContext.js"));
64
+ const referenceContext_js_1 = __importStar(require("./referenceContext.js"));
65
65
  exports.referenceContext = referenceContext_js_1.default;
66
+ Object.defineProperty(exports, "KnowledgeAccessError", { enumerable: true, get: function () { return referenceContext_js_1.KnowledgeAccessError; } });
66
67
  const tavily_js_1 = require("./tavily.js");
67
68
  Object.defineProperty(exports, "checkUrlsReachable", { enumerable: true, get: function () { return tavily_js_1.checkUrlsReachable; } });
@@ -31,6 +31,14 @@ export type UsageTracker = {
31
31
  url: string;
32
32
  title?: string;
33
33
  }[];
34
+ /**
35
+ * Every URL `web_extract` asked Tavily for, each once, in the order first
36
+ * asked, whether Tavily could read it or not (a failed URL, a failed call).
37
+ * A URL refused by `allowedUrls` was never asked for and is not here. What
38
+ * Tavily answered is `webSearchSources`, under the address it answered with
39
+ * (after a redirect); this is what the run actually tried to read.
40
+ */
41
+ attemptedUrls?: string[];
34
42
  };
35
43
  /**
36
44
  * Instantiates and returns the appropriate LangChain chat model based on the model
@@ -1 +1 @@
1
- {"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AAoGA,KAAK,UAAU,GAAG;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IAKtB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACvC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtD,CAAC;AA0iBF;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,eAAO,MAAM,WAAW,GACtB,WAAW,MAAM,EACjB,QAAQ,GAAG,EACX,SAAQ,GAAU,KACjB,GAwHF,CAAC;;8CAkb+C,OAAO,UAAU,GAAG,KAAG,OAAO;oCAWxC,GAAG,KAAG,OAAO;0CAgBtC,MAAM,cACN,GAAG,SACR,GAAG,KACT,MAAM;2CA0CqC,GAAG,UAAU,GAAG,KAAG,MAAM;sCApxC9B,MAAM,KAAG,OAAO;0CA+FZ,GAAG,KAAG,IAAI;wBA8sB/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,iBACG,YAAY,GAAG,IAAI,KAChC,GAAG;sBAsCG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,iBACb,YAAY,GAAG,IAAI,aACvB,MAAM,KACf,OAAO,CAAC,GAAG,CAAC;oCAghBF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDA2sBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDAmTF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CAx6C8B,GAAG,KAAG,MAAM;uCAxyB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,YACP,GAAG,WACJ,GAAG,YACF,QAAQ,GAAG,QAAQ,KAC5B,IAAI;kCA5CI,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,SACV,UAAU,GAAG,IAAI,YACd,GAAG,KACZ,IAAI;+CA0FI,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,YACZ,GAAG,KACZ,IAAI;uCAoBI,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,WACR,MAAM,YACL,GAAG,KACZ,IAAI;mCAyuBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AA28CT,wBAkBE"}
1
+ {"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AAoGA,KAAK,UAAU,GAAG;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IAKtB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACvC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACrD;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC;AA0iBF;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,eAAO,MAAM,WAAW,GACtB,WAAW,MAAM,EACjB,QAAQ,GAAG,EACX,SAAQ,GAAU,KACjB,GAwHF,CAAC;;8CAkb+C,OAAO,UAAU,GAAG,KAAG,OAAO;oCAWxC,GAAG,KAAG,OAAO;0CAgBtC,MAAM,cACN,GAAG,SACR,GAAG,KACT,MAAM;2CA0CqC,GAAG,UAAU,GAAG,KAAG,MAAM;sCA5xC9B,MAAM,KAAG,OAAO;0CAuGZ,GAAG,KAAG,IAAI;wBA8sB/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,iBACG,YAAY,GAAG,IAAI,KAChC,GAAG;sBAsCG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,iBACb,YAAY,GAAG,IAAI,aACvB,MAAM,KACf,OAAO,CAAC,GAAG,CAAC;oCAihBF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDA2sBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDAmTF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CAz6C8B,GAAG,KAAG,MAAM;uCAxyB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,YACP,GAAG,WACJ,GAAG,YACF,QAAQ,GAAG,QAAQ,KAC5B,IAAI;kCA5CI,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,SACV,UAAU,GAAG,IAAI,YACd,GAAG,KACZ,IAAI;+CA0FI,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,YACZ,GAAG,KACZ,IAAI;uCAoBI,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,WACR,MAAM,YACL,GAAG,KACZ,IAAI;mCAyuBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AA48CT,wBAkBE"}
@@ -1210,8 +1210,9 @@ const parseAgenticJsonResponse = (rawContent, schema) => {
1210
1210
  *
1211
1211
  * Setting `config.tavily` adds the Tavily web tools (`web_search`, `web_extract`, and
1212
1212
  * optionally `web_map` / `web_crawl`). They execute in this process, so they require
1213
- * `config.agentic` to be `true`; the credits spent and the URLs returned are recorded
1214
- * on `usageTracker`.
1213
+ * `config.agentic` to be `true`; the credits spent, the URLs returned and the URLs
1214
+ * `web_extract` asked for are recorded on `usageTracker`. `tavily.allowedUrls` keeps
1215
+ * `web_extract` to those URLs.
1215
1216
  * @param modelName - The model identifier, e.g. `"gpt-4o"`, `"claude-3-5-sonnet"`,
1216
1217
  * `"gemini-1.5-pro"`
1217
1218
  * @param config - Configuration object with API keys, `temperature`, optional `agentic`
@@ -10,7 +10,10 @@ export interface ReferencePage {
10
10
  }
11
11
  /** A passage of a document that references cite. */
12
12
  export interface ReferencePassage {
13
- /** The short ids of every STACKFACTOR reference that cites this passage's document. */
13
+ /**
14
+ * The short ids of the STACKFACTOR references that cite this passage's chunk
15
+ * (`knowledgeBase.chunkId`); when none does, of every one citing its document.
16
+ */
14
17
  references: string[];
15
18
  document: string | null;
16
19
  section: string | null;
@@ -23,21 +26,56 @@ export interface ReferencePassage {
23
26
  export interface ReferenceContext {
24
27
  pages: ReferencePage[];
25
28
  passages: ReferencePassage[];
29
+ /**
30
+ * Always empty from `gatherReferenceContext`: a cited knowledge base that
31
+ * cannot be read throws KnowledgeAccessError, which carries the skips.
32
+ */
26
33
  skipped: SkippedSource[];
27
- /** True when the references cite at least one knowledge base document, whether or not it could be read. */
34
+ /** True when the references cite at least one knowledge base document (all of them readable, or it would have thrown). */
28
35
  citesKnowledge: boolean;
29
36
  }
37
+ /**
38
+ * The knowledge base documents a run's references cite could not be read: the
39
+ * run has no token, the person may not open a cited knowledge base (401, 403,
40
+ * 404), or its search failed. The agent answers with it — `code` and `message`
41
+ * are what the person sees — and generates nothing.
42
+ */
43
+ export declare class KnowledgeAccessError extends Error {
44
+ /** 403 when every skipped knowledge base refused the person (or there was no token), else 502. */
45
+ readonly code: 403 | 502;
46
+ /** The knowledge bases that could not be searched, and why; empty when there was no token. */
47
+ readonly skipped: SkippedSource[];
48
+ /** The titles of the STACKFACTOR references citing those knowledge bases, each once. */
49
+ readonly documents: string[];
50
+ /**
51
+ * @param details `skipped` (from the search) and `documents` (titles); the
52
+ * code and the message follow from them
53
+ */
54
+ constructor(details?: {
55
+ skipped?: SkippedSource[];
56
+ documents?: unknown[];
57
+ });
58
+ }
30
59
  /**
31
60
  * What a list of references points at: its pages, and the passages of the
32
61
  * documents its STACKFACTOR references cite. One search per knowledge base per
33
- * query (per batch of 500 documents), however many references there are. With
34
- * no token no search is made and the pages are still listed. A knowledge base
35
- * the person cannot open is in `skipped`; the other passages stand.
62
+ * query (per batch of 500 documents), however many references there are.
63
+ *
64
+ * References that cite a knowledge base the run cannot read stop it: with no
65
+ * token, or when any cited knowledge base is skipped (refused or failed), it
66
+ * throws a KnowledgeAccessError naming the documents. A search that works and
67
+ * finds nothing is not an error. With no knowledge cited, no token is needed.
68
+ *
69
+ * A passage is attributed to the references citing its chunk, else to every
70
+ * reference citing its document. The best-ranked `maxPassages` are kept, and
71
+ * the first `maxPages` pages in reference order.
36
72
  * @param options `references` (a skill's, a microskill's…), `queries` to
37
73
  * search the cited documents with, `search` (client-api
38
74
  * `knowledgeBaseAgent.search`), `token` (the run's AGENT token), `topK` per
39
- * search (default 5)
40
- * @returns `{ pages, passages, skipped, citesKnowledge }`
75
+ * search (default 5), `maxPassages` (default 40), `maxPages` (default: all)
76
+ * @returns `{ pages, passages, skipped, citesKnowledge }`; `skipped` is always
77
+ * empty, since a skip throws
78
+ * @throws KnowledgeAccessError When cited knowledge bases cannot be read
41
79
  */
42
80
  export declare const gatherReferenceContext: (options: {
43
81
  references: unknown;
@@ -45,6 +83,8 @@ export declare const gatherReferenceContext: (options: {
45
83
  search: KnowledgeSearch;
46
84
  token: string | null | undefined;
47
85
  topK?: number;
86
+ maxPassages?: number;
87
+ maxPages?: number;
48
88
  }) => Promise<ReferenceContext>;
49
89
  /**
50
90
  * The passages as a prompt shows them: who cites each, where it came from, and
@@ -86,29 +126,33 @@ export interface StoredReference {
86
126
  knowledgeBase?: {
87
127
  knowledgeBaseId: string;
88
128
  documentId: string;
129
+ documentName?: string;
89
130
  chunkId?: string;
90
131
  sectionTitle?: string | null;
91
132
  };
92
133
  }
93
134
  /**
94
135
  * The tools a run is given. Never `search`, `map` or `crawl`: a run reads what
95
- * its references point at and discovers nothing. `extract` only, and only when
96
- * there are pages to open.
136
+ * its references point at and discovers nothing. `extract` only, only when
137
+ * there are pages to open, and kept to those pages: any other url the model
138
+ * asks for is refused (`allowedUrls`).
97
139
  * @param context From `gatherReferenceContext`
98
- * @returns `{ tavily: { tools: ["extract"] } }` with pages, else `{}`. With no
99
- * pages it returns `{}`, so a caller spreading it over a config that already
100
- * has a `tavily` key keeps that key (and `tavily: true` means search plus
101
- * extract): callers must not carry a base `tavily` key.
140
+ * @returns `{ tavily: { tools: ["extract"], allowedUrls } }` with pages, else
141
+ * `{}`. With no pages it returns `{}`, so a caller spreading it over a config
142
+ * that already has a `tavily` key keeps that key (and `tavily: true` means
143
+ * search plus extract): callers must not carry a base `tavily` key.
102
144
  */
103
145
  export declare const toolsFor: (context: ReferenceContext) => {
104
146
  tavily?: {
105
147
  tools: string[];
148
+ allowedUrls: string[];
106
149
  };
107
150
  };
108
151
  /**
109
152
  * The stage prompt followed by the guidance for each kind of source the run
110
- * has: pages, then passages; when it has neither, the `none` guidance. An empty
111
- * guidance adds nothing.
153
+ * has: pages, then passages; when it has neither (knowledge cited and readable
154
+ * but no passage found included), the `none` guidance. An empty guidance adds
155
+ * nothing.
112
156
  * @param stagePrompt The stage's own prompt
113
157
  * @param context From `gatherReferenceContext`
114
158
  * @param guidance From `sourceGuidanceFrom`
@@ -159,6 +203,7 @@ export declare const citedReferences: (ids: unknown, supplied: unknown, origin?:
159
203
  */
160
204
  export declare const sourceGuidanceFrom: (config: Record<string, unknown>) => SourceGuidance;
161
205
  declare const _default: {
206
+ KnowledgeAccessError: typeof KnowledgeAccessError;
162
207
  REFERENCE_PASSAGES_KEY: string;
163
208
  citedReferences: (ids: unknown, supplied: unknown, origin?: string) => StoredReference[];
164
209
  composeSourceInstructions: (stagePrompt: string, context: ReferenceContext, guidance: SourceGuidance) => string;
@@ -168,6 +213,8 @@ declare const _default: {
168
213
  search: KnowledgeSearch;
169
214
  token: string | null | undefined;
170
215
  topK?: number;
216
+ maxPassages?: number;
217
+ maxPages?: number;
171
218
  }) => Promise<ReferenceContext>;
172
219
  referenceForModel: <T extends {
173
220
  kind?: string;
@@ -184,6 +231,7 @@ declare const _default: {
184
231
  toolsFor: (context: ReferenceContext) => {
185
232
  tavily?: {
186
233
  tools: string[];
234
+ allowedUrls: string[];
187
235
  };
188
236
  };
189
237
  };
@@ -1 +1 @@
1
- {"version":3,"file":"referenceContext.d.ts","sourceRoot":"","sources":["../../src/referenceContext.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"referenceContext.d.ts","sourceRoot":"","sources":["../../src/referenceContext.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.sourceGuidanceFrom = exports.citedReferences = exports.toStoredReference = exports.referencesForModel = exports.referenceForModel = exports.composeSourceInstructions = exports.toolsFor = exports.referencePassagesForPrompt = exports.gatherReferenceContext = exports.REFERENCE_PASSAGES_KEY = void 0;
6
+ exports.sourceGuidanceFrom = exports.citedReferences = exports.toStoredReference = exports.referencesForModel = exports.referenceForModel = exports.composeSourceInstructions = exports.toolsFor = exports.referencePassagesForPrompt = exports.gatherReferenceContext = exports.KnowledgeAccessError = exports.REFERENCE_PASSAGES_KEY = void 0;
7
7
  //
8
8
  // Reference context — gathering what a list of references points at
9
9
  //
@@ -17,6 +17,12 @@ exports.sourceGuidanceFrom = exports.citedReferences = exports.toStoredReference
17
17
  // references cite, searched under the run's own token, so a run reads only
18
18
  // what the person it acts for may open.
19
19
  //
20
+ // A cited knowledge base that cannot be read stops the run (Candin,
21
+ // 2026-10-13): with no token, a refusal or a failed search, the gathering
22
+ // throws a KnowledgeAccessError naming the documents, and the agent answers
23
+ // with it rather than writing as if the content cited nothing. A search that
24
+ // works and finds nothing is not an error.
25
+ //
20
26
  // A reference is cited by its short id `r<N>`, N being its index in the
21
27
  // supplied list, whatever its kind.
22
28
  //
@@ -24,9 +30,44 @@ const logger_js_1 = __importDefault(require("./logger.js"));
24
30
  const stackfactorKnowledge_js_1 = require("./stackfactorKnowledge.js");
25
31
  /** How many passages one search asks for when the caller does not say. */
26
32
  const DEFAULT_REFERENCE_TOP_K = 5;
33
+ /** How many passages a gathering keeps when the caller does not say: the best-ranked. */
34
+ const DEFAULT_MAX_PASSAGES = 40;
27
35
  /** The prompt key the passages are handed to the model under. */
28
36
  exports.REFERENCE_PASSAGES_KEY = "Knowledge Base Passages";
29
37
  const text = (value) => (typeof value === "string" ? value.trim() : "");
38
+ /**
39
+ * The knowledge base documents a run's references cite could not be read: the
40
+ * run has no token, the person may not open a cited knowledge base (401, 403,
41
+ * 404), or its search failed. The agent answers with it — `code` and `message`
42
+ * are what the person sees — and generates nothing.
43
+ */
44
+ class KnowledgeAccessError extends Error {
45
+ /** 403 when every skipped knowledge base refused the person (or there was no token), else 502. */
46
+ code;
47
+ /** The knowledge bases that could not be searched, and why; empty when there was no token. */
48
+ skipped;
49
+ /** The titles of the STACKFACTOR references citing those knowledge bases, each once. */
50
+ documents;
51
+ /**
52
+ * @param details `skipped` (from the search) and `documents` (titles); the
53
+ * code and the message follow from them
54
+ */
55
+ constructor(details = {}) {
56
+ const skipped = Array.isArray(details.skipped) ? details.skipped : [];
57
+ const documents = [...new Set((Array.isArray(details.documents) ? details.documents : []).map(text).filter(Boolean))];
58
+ const code = skipped.every((source) => source?.noAccess) ? 403 : 502;
59
+ const titles = documents.map((title) => `"${title}"`).join(", ");
60
+ super(code === 403
61
+ ? `The knowledge base documents this content cites could not be read with your access${titles ? `: ${titles}` : ""}.`
62
+ : `The knowledge base documents this content cites could not be read: the knowledge base search failed${titles ? ` for ${titles}` : ""}.`);
63
+ this.name = "KnowledgeAccessError";
64
+ this.code = code;
65
+ this.skipped = skipped;
66
+ this.documents = documents;
67
+ Object.setPrototypeOf(this, KnowledgeAccessError.prototype);
68
+ }
69
+ }
70
+ exports.KnowledgeAccessError = KnowledgeAccessError;
30
71
  const id = (value) => typeof value === "string" ? value.trim() : value != null && typeof value === "object" ? String(value) : "";
31
72
  const shortId = (index) => `r${index}`;
32
73
  /** The document a STACKFACTOR reference cites, or "" when it cites none. */
@@ -35,6 +76,14 @@ const documentOf = (reference) => {
35
76
  return (id(reference.knowledgeBase?.documentId) ||
36
77
  (target.elementType === stackfactorKnowledge_js_1.STACKFACTOR_REFERENCE.TARGET_ELEMENT_TYPE ? id(target.elementId) : ""));
37
78
  };
79
+ /** The knowledge base a STACKFACTOR reference's document is in, or "" when it cites no document. */
80
+ const knowledgeBaseOf = (reference) => {
81
+ const target = reference.target ?? {};
82
+ return (id(reference.knowledgeBase?.knowledgeBaseId) ||
83
+ (target.elementType === stackfactorKnowledge_js_1.STACKFACTOR_REFERENCE.TARGET_ELEMENT_TYPE ? id(target.parentId) : ""));
84
+ };
85
+ /** A cap the caller passed (a whole number, 0 or more), else `fallback`. */
86
+ const capOf = (value, fallback) => typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : fallback;
38
87
  /** The kinds the platform retired; it reads them as PUBLICATION. */
39
88
  const RETIRED_KINDS = new Set(["DOCUMENT", "KNOWLEDGE_BASE", "NOTE"]);
40
89
  /** A reference's kind as the platform reads it: missing or retired is PUBLICATION. */
@@ -60,39 +109,71 @@ const pagesOf = (references) => {
60
109
  });
61
110
  return pages;
62
111
  };
63
- /** The short ids of the STACKFACTOR references citing each document. */
64
- const citersByDocument = (references) => {
65
- const citers = new Map();
112
+ /**
113
+ * The short ids of the STACKFACTOR references citing each document, and of
114
+ * those citing each chunk (`knowledgeBase.chunkId`) of a document.
115
+ */
116
+ const citersOf = (references) => {
117
+ const byDocument = new Map();
118
+ const byChunk = new Map();
66
119
  references.forEach((reference, index) => {
67
120
  if (reference?.kind !== stackfactorKnowledge_js_1.STACKFACTOR_REFERENCE.KIND)
68
121
  return;
69
122
  const documentId = documentOf(reference);
70
- if (documentId)
71
- citers.set(documentId, [...(citers.get(documentId) ?? []), shortId(index)]);
123
+ if (!documentId)
124
+ return;
125
+ byDocument.set(documentId, [...(byDocument.get(documentId) ?? []), shortId(index)]);
126
+ const chunkId = id(reference.knowledgeBase?.chunkId);
127
+ if (chunkId)
128
+ byChunk.set(chunkId, [...(byChunk.get(chunkId) ?? []), shortId(index)]);
72
129
  });
73
- return citers;
130
+ return { byChunk, byDocument };
74
131
  };
132
+ /**
133
+ * The error for cited knowledge bases that could not be read: it names the
134
+ * documents of the STACKFACTOR references citing them.
135
+ */
136
+ const accessError = (references, unread, skipped) => new KnowledgeAccessError({
137
+ documents: references
138
+ .filter((reference) => reference?.kind === stackfactorKnowledge_js_1.STACKFACTOR_REFERENCE.KIND && documentOf(reference) && unread.has(knowledgeBaseOf(reference)))
139
+ .map((reference) => reference.title),
140
+ skipped,
141
+ });
75
142
  /**
76
143
  * What a list of references points at: its pages, and the passages of the
77
144
  * documents its STACKFACTOR references cite. One search per knowledge base per
78
- * query (per batch of 500 documents), however many references there are. With
79
- * no token no search is made and the pages are still listed. A knowledge base
80
- * the person cannot open is in `skipped`; the other passages stand.
145
+ * query (per batch of 500 documents), however many references there are.
146
+ *
147
+ * References that cite a knowledge base the run cannot read stop it: with no
148
+ * token, or when any cited knowledge base is skipped (refused or failed), it
149
+ * throws a KnowledgeAccessError naming the documents. A search that works and
150
+ * finds nothing is not an error. With no knowledge cited, no token is needed.
151
+ *
152
+ * A passage is attributed to the references citing its chunk, else to every
153
+ * reference citing its document. The best-ranked `maxPassages` are kept, and
154
+ * the first `maxPages` pages in reference order.
81
155
  * @param options `references` (a skill's, a microskill's…), `queries` to
82
156
  * search the cited documents with, `search` (client-api
83
157
  * `knowledgeBaseAgent.search`), `token` (the run's AGENT token), `topK` per
84
- * search (default 5)
85
- * @returns `{ pages, passages, skipped, citesKnowledge }`
158
+ * search (default 5), `maxPassages` (default 40), `maxPages` (default: all)
159
+ * @returns `{ pages, passages, skipped, citesKnowledge }`; `skipped` is always
160
+ * empty, since a skip throws
161
+ * @throws KnowledgeAccessError When cited knowledge bases cannot be read
86
162
  */
87
163
  const gatherReferenceContext = async (options) => {
88
164
  const references = Array.isArray(options.references) ? options.references : [];
89
165
  const sources = (0, stackfactorKnowledge_js_1.sourcesFromReferences)(references);
90
166
  const citesKnowledge = sources.length > 0;
91
- const pages = pagesOf(references);
92
- if (!options.token || !citesKnowledge) {
93
- logContext(pages.length, 0, [], citesKnowledge && !options.token);
167
+ const pages = pagesOf(references).slice(0, capOf(options.maxPages, Infinity));
168
+ if (!citesKnowledge) {
169
+ logContext(pages.length, 0, []);
94
170
  return { citesKnowledge, pages, passages: [], skipped: [] };
95
171
  }
172
+ if (!options.token) {
173
+ const error = accessError(references, new Set(sources.map((source) => source.knowledgeBaseId)), []);
174
+ logContext(pages.length, 0, [], { error, noToken: true });
175
+ throw error;
176
+ }
96
177
  const found = await (0, stackfactorKnowledge_js_1.retrievePassages)({
97
178
  queries: options.queries,
98
179
  search: options.search,
@@ -100,31 +181,45 @@ const gatherReferenceContext = async (options) => {
100
181
  token: options.token,
101
182
  topK: options.topK ?? DEFAULT_REFERENCE_TOP_K,
102
183
  });
103
- const citers = citersByDocument(references);
184
+ if (found.skipped.length > 0) {
185
+ const unread = new Set(found.skipped.map((source) => source.knowledgeBaseId));
186
+ const error = accessError(references, unread, found.skipped);
187
+ logContext(pages.length, 0, found.skipped, { error });
188
+ throw error;
189
+ }
190
+ const citers = citersOf(references);
104
191
  const passages = found.passages
105
- .filter((passage) => citers.has(passage.documentId))
192
+ .filter((passage) => citers.byDocument.has(passage.documentId))
193
+ .slice(0, capOf(options.maxPassages, DEFAULT_MAX_PASSAGES))
106
194
  .map((passage) => ({
107
195
  chunkId: passage.chunkId,
108
196
  document: passage.documentName,
109
197
  documentId: passage.documentId,
110
198
  knowledgeBaseId: passage.knowledgeBaseId,
111
- references: citers.get(passage.documentId),
199
+ references: citers.byChunk.get(passage.chunkId) ?? citers.byDocument.get(passage.documentId),
112
200
  section: passage.sectionTitle,
113
201
  text: passage.text,
114
202
  }));
115
- logContext(pages.length, passages.length, found.skipped, false);
116
- return { citesKnowledge, pages, passages, skipped: found.skipped };
203
+ logContext(pages.length, passages.length, []);
204
+ return { citesKnowledge, pages, passages, skipped: [] };
117
205
  };
118
206
  exports.gatherReferenceContext = gatherReferenceContext;
119
- /** The one summary line of a gathering, for the logs. */
120
- const logContext = (pages, passages, skipped, noToken) => {
207
+ /**
208
+ * The one summary line of a gathering, for the logs: a warning when it was
209
+ * refused. Document titles stay out of the logs.
210
+ */
211
+ const logContext = (pages, passages, skipped, refusal) => {
121
212
  const parts = [`${pages} page${pages === 1 ? "" : "s"}`, `${passages} passage${passages === 1 ? "" : "s"}`];
122
213
  if (skipped.length > 0) {
123
214
  parts.push(`skipped knowledge bases: ${skipped.map((s) => `${s.knowledgeBaseId} (noAccess: ${s.noAccess})`).join(", ")}`);
124
215
  }
125
- if (noToken)
126
- parts.push("no token, so no passages");
127
- logger_js_1.default.log(null, logger_js_1.default.levels.info, `Reference context: ${parts.join("; ")}`);
216
+ if (refusal?.noToken)
217
+ parts.push("no token");
218
+ if (refusal) {
219
+ const count = refusal.error.documents.length;
220
+ parts.push(`refused (${refusal.error.code}): ${count} document${count === 1 ? "" : "s"} unreadable`);
221
+ }
222
+ logger_js_1.default.log(null, refusal ? logger_js_1.default.levels.warn : logger_js_1.default.levels.info, `Reference context: ${parts.join("; ")}`);
128
223
  };
129
224
  /**
130
225
  * The passages as a prompt shows them: who cites each, where it came from, and
@@ -141,20 +236,24 @@ const referencePassagesForPrompt = (context) => context.passages.map((passage) =
141
236
  exports.referencePassagesForPrompt = referencePassagesForPrompt;
142
237
  /**
143
238
  * The tools a run is given. Never `search`, `map` or `crawl`: a run reads what
144
- * its references point at and discovers nothing. `extract` only, and only when
145
- * there are pages to open.
239
+ * its references point at and discovers nothing. `extract` only, only when
240
+ * there are pages to open, and kept to those pages: any other url the model
241
+ * asks for is refused (`allowedUrls`).
146
242
  * @param context From `gatherReferenceContext`
147
- * @returns `{ tavily: { tools: ["extract"] } }` with pages, else `{}`. With no
148
- * pages it returns `{}`, so a caller spreading it over a config that already
149
- * has a `tavily` key keeps that key (and `tavily: true` means search plus
150
- * extract): callers must not carry a base `tavily` key.
243
+ * @returns `{ tavily: { tools: ["extract"], allowedUrls } }` with pages, else
244
+ * `{}`. With no pages it returns `{}`, so a caller spreading it over a config
245
+ * that already has a `tavily` key keeps that key (and `tavily: true` means
246
+ * search plus extract): callers must not carry a base `tavily` key.
151
247
  */
152
- const toolsFor = (context) => context.pages.length > 0 ? { tavily: { tools: ["extract"] } } : {};
248
+ const toolsFor = (context) => context.pages.length > 0
249
+ ? { tavily: { tools: ["extract"], allowedUrls: context.pages.map((page) => page.url) } }
250
+ : {};
153
251
  exports.toolsFor = toolsFor;
154
252
  /**
155
253
  * The stage prompt followed by the guidance for each kind of source the run
156
- * has: pages, then passages; when it has neither, the `none` guidance. An empty
157
- * guidance adds nothing.
254
+ * has: pages, then passages; when it has neither (knowledge cited and readable
255
+ * but no passage found included), the `none` guidance. An empty guidance adds
256
+ * nothing.
158
257
  * @param stagePrompt The stage's own prompt
159
258
  * @param context From `gatherReferenceContext`
160
259
  * @param guidance From `sourceGuidanceFrom`
@@ -286,6 +385,7 @@ const sourceGuidanceFrom = (config) => ({
286
385
  });
287
386
  exports.sourceGuidanceFrom = sourceGuidanceFrom;
288
387
  exports.default = {
388
+ KnowledgeAccessError,
289
389
  REFERENCE_PASSAGES_KEY: exports.REFERENCE_PASSAGES_KEY,
290
390
  citedReferences: exports.citedReferences,
291
391
  composeSourceInstructions: exports.composeSourceInstructions,
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AAWtC,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAwPpB;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;AAE/D;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,GAAG,KACT,OAAO,CAAC;IAAE,IAAI,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC;AAEzC,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,uEAAuE;IACvE,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IACpE,sEAAsE;IACtE,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,iEAAiE;IACjE,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,mFAAmF;IACnF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,mFAAmF;IACnF,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACxD,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,EAAE,CACN,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,KACV,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACpC;AAKD;;;;;;GAMG;AACH,eAAO,MAAM,aAAa;;;;;CAKhB,CAAC;AAEX,mFAAmF;AACnF,eAAO,MAAM,kBAAkB,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,OACJ,CAAC;AAKvE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,GAAG,EAAE,SAAS,GAAG,KAAG,OAEhB,CAAC;AAKlD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,GAAG,EAAE,SAAS,GAAG,KAAG,OAGN,CAAC;AA4JrD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,OAAO,GAClB,MAAM,SAAS,EACf,UAAS,YAAiB,KACzB,OAAO,CAAC,IAAI,CA8Id,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,GAChB,MAAM,SAAS,EACf,UAAS,YAAiB,KACzB,IAAI,CAAC,MAyOP,CAAC"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AAWtC,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAwPpB;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;AAE/D;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAC1B,OAAO,EAAE,GAAG,KACT,OAAO,CAAC;IAAE,IAAI,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC;AAEzC,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,uEAAuE;IACvE,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;IACpE,sEAAsE;IACtE,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,iEAAiE;IACjE,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,mFAAmF;IACnF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,mFAAmF;IACnF,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACxD,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,EAAE,CACN,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,KACV,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACpC;AAKD;;;;;;GAMG;AACH,eAAO,MAAM,aAAa;;;;;CAKhB,CAAC;AAEX,mFAAmF;AACnF,eAAO,MAAM,kBAAkB,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,OACJ,CAAC;AAKvE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,GAAG,EAAE,SAAS,GAAG,KAAG,OAEhB,CAAC;AAKlD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,GAAG,EAAE,SAAS,GAAG,KAAG,OAGN,CAAC;AAmKrD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,OAAO,GAClB,MAAM,SAAS,EACf,UAAS,YAAiB,KACzB,OAAO,CAAC,IAAI,CA8Id,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,GAChB,MAAM,SAAS,EACf,UAAS,YAAiB,KACzB,IAAI,CAAC,MAyOP,CAAC"}
package/dist/cjs/serve.js CHANGED
@@ -381,8 +381,15 @@ const safeParse = (value, fallback) => {
381
381
  /**
382
382
  * Agents that fail throw `new Error(JSON.stringify({ code, message }))`. Unwrap
383
383
  * that back into a structured gRPC error frame; fall back to a 500 otherwise.
384
+ * A KnowledgeAccessError an agent did not catch (referenceContext) answers
385
+ * with its own code (403, 502) and message, which names the documents that
386
+ * could not be read. It is known by its name, so one thrown by another copy of
387
+ * this package in the agent's dependencies is known too.
384
388
  */
385
389
  const toErrorFrame = (error) => {
390
+ if (error?.name === "KnowledgeAccessError" && typeof error.code === "number") {
391
+ return { code: error.code, message: String(error.message ?? "") };
392
+ }
386
393
  try {
387
394
  const parsed = JSON.parse(error?.message);
388
395
  if (parsed && typeof parsed.code === "number") {