@abot-ai/runtime 1.1.0 → 1.2.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 (268) hide show
  1. package/README.md +6 -3
  2. package/dist/src/capabilities/tool-types.d.ts +1 -1
  3. package/dist/src/model-gateway/providers/ollama/generation-options.d.ts +1 -3
  4. package/dist/src/model-gateway/providers/ollama/generation-options.js +1 -5
  5. package/dist/src/model-gateway/providers/ollama/payload.js +0 -8
  6. package/dist/src/model-gateway/server/request-body.js +1 -0
  7. package/dist/src/plugin-sdk/index.d.ts +2 -0
  8. package/dist/src/plugin-sdk/index.js +2 -0
  9. package/dist/src/plugin-sdk/tool-availability-brief.d.ts +9 -0
  10. package/dist/src/plugin-sdk/tool-availability-brief.js +48 -0
  11. package/dist/src/plugin-sdk/tool-availability-overview.d.ts +9 -0
  12. package/dist/src/plugin-sdk/tool-availability-overview.js +62 -0
  13. package/dist/src/runtime/adapters/registered-tool-payload-plan.d.ts +0 -9
  14. package/dist/src/runtime/adapters/registered-tool-payload-plan.js +0 -67
  15. package/dist/src/runtime/adapters/registered-tool-payload-related-artifacts.d.ts +13 -0
  16. package/dist/src/runtime/adapters/registered-tool-payload-related-artifacts.js +84 -0
  17. package/dist/src/runtime/adapters/registered-tool-worker-capabilities/catalog.js +4 -0
  18. package/dist/src/runtime/adapters/registered-tool-worker-capabilities/execution.js +3 -0
  19. package/dist/src/runtime/adapters/registered-tool-worker-capabilities/payload-lifecycle.d.ts +1 -0
  20. package/dist/src/runtime/adapters/registered-tool-worker-capabilities/payload-lifecycle.js +11 -1
  21. package/dist/src/runtime/adapters/registered-tool-worker-capabilities/payload-stages.d.ts +1 -0
  22. package/dist/src/runtime/adapters/registered-tool-worker-capabilities/payload-stages.js +9 -1
  23. package/dist/src/runtime/capabilities/request-bound-tool-registry.js +1 -24
  24. package/dist/src/runtime/capabilities/tool-availability.d.ts +6 -0
  25. package/dist/src/runtime/capabilities/tool-availability.js +28 -0
  26. package/dist/src/runtime/context/capability-brief.d.ts +25 -0
  27. package/dist/src/runtime/context/capability-brief.js +106 -0
  28. package/dist/src/runtime/context/request-tool-results-message-binding.d.ts +6 -0
  29. package/dist/src/runtime/context/request-tool-results-message-binding.js +30 -0
  30. package/dist/src/runtime/context/request-tool-results-scope.d.ts +3 -0
  31. package/dist/src/runtime/context/request-tool-results-scope.js +8 -0
  32. package/dist/src/runtime/context/request-tool-results.d.ts +8 -4
  33. package/dist/src/runtime/context/request-tool-results.js +49 -38
  34. package/dist/src/runtime/orchestration/role-calls/candidate-validation.js +8 -1
  35. package/dist/src/runtime/orchestration/role-calls/child-return.d.ts +4 -0
  36. package/dist/src/runtime/orchestration/role-calls/child-return.js +25 -5
  37. package/dist/src/runtime/orchestration/role-calls/command-decoder.js +8 -3
  38. package/dist/src/runtime/orchestration/role-calls/command-dispatch/child-transitions.d.ts +1 -1
  39. package/dist/src/runtime/orchestration/role-calls/command-dispatch/child-transitions.js +17 -2
  40. package/dist/src/runtime/orchestration/role-calls/command-dispatch.d.ts +1 -1
  41. package/dist/src/runtime/orchestration/role-calls/command-dispatch.js +2 -2
  42. package/dist/src/runtime/orchestration/role-calls/contracts.d.ts +5 -2
  43. package/dist/src/runtime/orchestration/role-calls/contracts.js +4 -4
  44. package/dist/src/runtime/orchestration/role-calls/dependency-results.d.ts +1 -1
  45. package/dist/src/runtime/orchestration/role-calls/dependency-results.js +1 -0
  46. package/dist/src/runtime/orchestration/role-calls/index.d.ts +5 -1
  47. package/dist/src/runtime/orchestration/role-calls/index.js +5 -1
  48. package/dist/src/runtime/orchestration/role-calls/ledger.d.ts +3 -1
  49. package/dist/src/runtime/orchestration/role-calls/ledger.js +9 -2
  50. package/dist/src/runtime/orchestration/role-calls/reducer.d.ts +1 -1
  51. package/dist/src/runtime/orchestration/role-calls/reducer.js +2 -2
  52. package/dist/src/runtime/orchestration/role-calls/result-receipt.d.ts +49 -0
  53. package/dist/src/runtime/orchestration/role-calls/result-receipt.js +188 -0
  54. package/dist/src/runtime/orchestration/role-calls/return-result-receipt.d.ts +19 -0
  55. package/dist/src/runtime/orchestration/role-calls/return-result-receipt.js +52 -0
  56. package/dist/src/runtime/orchestration/role-calls/transactions.js +1 -0
  57. package/dist/src/runtime/orchestration/role-calls/work-result-lineage.d.ts +34 -0
  58. package/dist/src/runtime/orchestration/role-calls/work-result-lineage.js +185 -0
  59. package/dist/src/runtime/orchestration/role-calls/work-result-receipt.d.ts +36 -0
  60. package/dist/src/runtime/orchestration/role-calls/work-result-receipt.js +187 -0
  61. package/dist/src/runtime/orchestration/role-executors/activation/result-normalization.js +6 -3
  62. package/dist/src/runtime/orchestration/role-executors/child-invocation/transaction.js +1 -0
  63. package/dist/src/runtime/orchestration/role-executors/contracts.d.ts +2 -1
  64. package/dist/src/runtime/orchestration/worker-capabilities/assignment-provenance.d.ts +36 -0
  65. package/dist/src/runtime/orchestration/worker-capabilities/assignment-provenance.js +171 -0
  66. package/dist/src/runtime/orchestration/worker-capabilities/contracts.d.ts +32 -1
  67. package/dist/src/runtime/orchestration/worker-capabilities/execution/invocation-preparation.d.ts +2 -1
  68. package/dist/src/runtime/orchestration/worker-capabilities/execution/invocation-preparation.js +3 -0
  69. package/dist/src/runtime/orchestration/worker-capabilities/execution.js +42 -19
  70. package/dist/src/runtime/orchestration/worker-capabilities/index.d.ts +2 -0
  71. package/dist/src/runtime/orchestration/worker-capabilities/index.js +2 -0
  72. package/dist/src/runtime/orchestration/worker-capabilities/payload-assignment-validation.d.ts +3 -0
  73. package/dist/src/runtime/orchestration/worker-capabilities/payload-assignment-validation.js +30 -0
  74. package/dist/src/runtime/orchestration/worker-capabilities/payload-author.js +8 -19
  75. package/dist/src/runtime/orchestration/worker-capabilities/payload-contracts.d.ts +5 -1
  76. package/dist/src/runtime/orchestration/worker-capabilities/payload-instructions.js +1 -1
  77. package/dist/src/runtime/orchestration/worker-capabilities/payload-source-provenance.d.ts +14 -0
  78. package/dist/src/runtime/orchestration/worker-capabilities/payload-source-provenance.js +132 -0
  79. package/dist/src/runtime/request/capability-brief.d.ts +5 -0
  80. package/dist/src/runtime/request/capability-brief.js +24 -0
  81. package/dist/src/runtime/request/execution-scope.d.ts +2 -1
  82. package/dist/src/runtime/request/role-executor-composition.js +1 -1
  83. package/dist/src/runtime/request/worker-capability-composition.d.ts +2 -1
  84. package/dist/src/runtime/request/worker-capability-composition.js +6 -1
  85. package/dist/src/runtime/request/worker-capability-payload.js +11 -8
  86. package/dist/src/runtime/steps/execution-agent/prompt.js +4 -3
  87. package/dist/src/runtime/steps/planner-decision/capability-catalog-context.d.ts +14 -0
  88. package/dist/src/runtime/steps/planner-decision/capability-catalog-context.js +87 -0
  89. package/dist/src/runtime/steps/planner-decision/contracts.d.ts +3 -5
  90. package/dist/src/runtime/steps/planner-decision/contracts.js +3 -3
  91. package/dist/src/runtime/steps/planner-decision/format.d.ts +3 -2
  92. package/dist/src/runtime/steps/planner-decision/format.js +73 -76
  93. package/dist/src/runtime/steps/planner-decision/input.d.ts +4 -3
  94. package/dist/src/runtime/steps/planner-decision/input.js +10 -7
  95. package/dist/src/runtime/steps/planner-decision/parser.d.ts +3 -2
  96. package/dist/src/runtime/steps/planner-decision/parser.js +10 -19
  97. package/dist/src/runtime/steps/planner-decision/plan-parser.js +18 -12
  98. package/dist/src/runtime/steps/planner-decision/prompt.js +33 -28
  99. package/dist/src/runtime/steps/planner-decision/resume.d.ts +1 -1
  100. package/dist/src/runtime/steps/planner-decision/resume.js +38 -1
  101. package/dist/src/runtime/steps/planner-decision/run.d.ts +3 -2
  102. package/dist/src/runtime/steps/reviewer-decision/audit-coverage.d.ts +10 -0
  103. package/dist/src/runtime/steps/reviewer-decision/audit-coverage.js +186 -0
  104. package/dist/src/runtime/steps/reviewer-decision/contracts.d.ts +3 -8
  105. package/dist/src/runtime/steps/reviewer-decision/contracts.js +12 -6
  106. package/dist/src/runtime/steps/reviewer-decision/delegated-context.d.ts +24 -0
  107. package/dist/src/runtime/steps/reviewer-decision/delegated-context.js +70 -0
  108. package/dist/src/runtime/steps/reviewer-decision/diagnostics.js +1 -1
  109. package/dist/src/runtime/steps/reviewer-decision/format.js +37 -24
  110. package/dist/src/runtime/steps/reviewer-decision/input.js +1 -1
  111. package/dist/src/runtime/steps/reviewer-decision/model-context.d.ts +4 -3
  112. package/dist/src/runtime/steps/reviewer-decision/model-context.js +92 -10
  113. package/dist/src/runtime/steps/reviewer-decision/parser.js +19 -1
  114. package/dist/src/runtime/steps/reviewer-decision/projection.js +42 -68
  115. package/dist/src/runtime/steps/reviewer-decision/prompt.js +10 -5
  116. package/dist/src/runtime/steps/reviewer-decision/run.js +12 -3
  117. package/dist/src/runtime/steps/reviewer-decision/verification-coverage.d.ts +14 -0
  118. package/dist/src/runtime/steps/reviewer-decision/verification-coverage.js +52 -0
  119. package/dist/src/runtime/steps/supervisor-decision/capability-brief.d.ts +14 -0
  120. package/dist/src/runtime/steps/supervisor-decision/capability-brief.js +38 -0
  121. package/dist/src/runtime/steps/supervisor-decision/contracts.d.ts +7 -3
  122. package/dist/src/runtime/steps/supervisor-decision/diagnostics.d.ts +2 -0
  123. package/dist/src/runtime/steps/supervisor-decision/diagnostics.js +8 -2
  124. package/dist/src/runtime/steps/supervisor-decision/format.js +32 -20
  125. package/dist/src/runtime/steps/supervisor-decision/input.d.ts +4 -1
  126. package/dist/src/runtime/steps/supervisor-decision/input.js +19 -1
  127. package/dist/src/runtime/steps/supervisor-decision/parser.js +62 -59
  128. package/dist/src/runtime/steps/supervisor-decision/prompt.js +6 -7
  129. package/dist/src/runtime/steps/supervisor-decision/resume.js +47 -1
  130. package/dist/src/runtime/steps/supervisor-decision/run.js +10 -0
  131. package/dist/src/runtime/steps/supervisor-decision/working-directory.js +2 -1
  132. package/dist/src/runtime/steps/worker-decision/contracts.d.ts +2 -1
  133. package/dist/src/runtime/steps/worker-decision/input/canonical-state.js +8 -1
  134. package/dist/src/runtime/steps/worker-decision/input/diagnostic-projection.js +2 -2
  135. package/dist/src/runtime/steps/worker-decision/input/prompt-context.d.ts +1 -0
  136. package/dist/src/runtime/steps/worker-decision/input/prompt-context.js +4 -1
  137. package/dist/src/runtime/steps/worker-decision/input/reference-context.js +34 -14
  138. package/dist/src/runtime/steps/worker-decision/input/types.d.ts +2 -1
  139. package/dist/src/runtime/steps/worker-decision/input.d.ts +1 -0
  140. package/dist/src/runtime/steps/worker-decision/input.js +1 -0
  141. package/dist/src/runtime/steps/worker-decision/payload-dependency-results.d.ts +8 -0
  142. package/dist/src/runtime/steps/worker-decision/payload-dependency-results.js +19 -0
  143. package/dist/src/runtime/steps/worker-decision/prompt.js +8 -8
  144. package/dist/src/runtime/steps/worker-decision/result-author.js +18 -11
  145. package/dist/src/runtime/steps/worker-decision/run.js +2 -2
  146. package/dist/src/web-ui/app/ARCHITECTURE.md +19 -6
  147. package/dist/src/web-ui/app/app.js +6 -7
  148. package/dist/src/web-ui/app/components/composer-context-window.d.ts +25 -0
  149. package/dist/src/web-ui/app/components/composer-context-window.js +155 -0
  150. package/dist/src/web-ui/app/components/composer-plan.d.ts +9 -0
  151. package/dist/src/web-ui/app/components/composer-plan.js +128 -0
  152. package/dist/src/web-ui/app/components/conversation-activity.d.ts +5 -6
  153. package/dist/src/web-ui/app/components/conversation-activity.js +64 -157
  154. package/dist/src/web-ui/app/components/conversation-role-cards.d.ts +23 -0
  155. package/dist/src/web-ui/app/components/conversation-role-cards.js +181 -0
  156. package/dist/src/web-ui/app/components/conversation-sidebar-layout.js +12 -0
  157. package/dist/src/web-ui/app/components/conversation-sources.d.ts +7 -0
  158. package/dist/src/web-ui/app/components/conversation-sources.js +182 -0
  159. package/dist/src/web-ui/app/components/conversation-view.js +34 -6
  160. package/dist/src/web-ui/app/components/model-selector.js +4 -1
  161. package/dist/src/web-ui/app/components/operations-section.js +46 -0
  162. package/dist/src/web-ui/app/components/workspace-shell.js +86 -78
  163. package/dist/src/web-ui/app/controllers/realtime-event-controller.d.ts +1 -0
  164. package/dist/src/web-ui/app/controllers/realtime-event-controller.js +55 -90
  165. package/dist/src/web-ui/app/controllers/runtime-selection-controller.js +2 -0
  166. package/dist/src/web-ui/app/controllers/session-controller.js +17 -12
  167. package/dist/src/web-ui/app/index.html +209 -210
  168. package/dist/src/web-ui/app/lib/composer-plan-model.d.ts +32 -0
  169. package/dist/src/web-ui/app/lib/composer-plan-model.js +108 -0
  170. package/dist/src/web-ui/app/lib/conversation-role-model.d.ts +19 -0
  171. package/dist/src/web-ui/app/lib/conversation-role-model.js +193 -0
  172. package/dist/src/web-ui/app/lib/event-presentation.js +30 -9
  173. package/dist/src/web-ui/app/lib/source-url-policy.d.ts +2 -0
  174. package/dist/src/web-ui/app/lib/source-url-policy.js +31 -0
  175. package/dist/src/web-ui/app/lib/task-progress.d.ts +53 -0
  176. package/dist/src/web-ui/app/lib/task-progress.js +233 -0
  177. package/dist/src/web-ui/app/lib/tool-invocation-count.d.ts +5 -0
  178. package/dist/src/web-ui/app/lib/tool-invocation-count.js +21 -0
  179. package/dist/src/web-ui/app/lib/web-source-event.d.ts +8 -0
  180. package/dist/src/web-ui/app/lib/web-source-event.js +21 -0
  181. package/dist/src/web-ui/app/lib/web-sources.d.ts +36 -0
  182. package/dist/src/web-ui/app/lib/web-sources.js +177 -0
  183. package/dist/src/web-ui/app/styles/00-tokens-base.css +15 -3
  184. package/dist/src/web-ui/app/styles/10-shell-sessions.css +24 -34
  185. package/dist/src/web-ui/app/styles/12-conversation-sidebar.css +93 -0
  186. package/dist/src/web-ui/app/styles/20-chat-composer.css +55 -472
  187. package/dist/src/web-ui/app/styles/21-composer-controls.css +368 -0
  188. package/dist/src/web-ui/app/styles/22-composer-context-window.css +90 -0
  189. package/dist/src/web-ui/app/styles/23-composer-plan.css +171 -0
  190. package/dist/src/web-ui/app/styles/24-conversation-role-cards.css +205 -0
  191. package/dist/src/web-ui/app/styles/25-conversation-sources.css +141 -0
  192. package/dist/src/web-ui/app/styles/30-inspector-config.css +21 -209
  193. package/dist/src/web-ui/app/styles/35-memory-onboarding.css +8 -8
  194. package/dist/src/web-ui/app/styles/36-memory-management.css +13 -13
  195. package/dist/src/web-ui/app/styles/37-config-operations.css +197 -0
  196. package/dist/src/web-ui/app/styles/40-config-workspace.css +36 -36
  197. package/dist/src/web-ui/app/styles/90-responsive.css +14 -77
  198. package/dist/src/web-ui/app/styles/foundation.css +2 -2
  199. package/dist/src/web-ui/app/styles.css +7 -0
  200. package/dist/src/web-ui/app/ui-behavior.d.ts +1 -1
  201. package/dist/src/web-ui/app/ui-behavior.js +1 -1
  202. package/docs/configuration.md +15 -8
  203. package/docs/installation.md +7 -4
  204. package/docs/known-limitations.md +21 -0
  205. package/docs/plugins.md +28 -13
  206. package/docs/runtime-invariant-coverage.md +18 -17
  207. package/docs/runtime-prompt-policy-contracts.md +27 -17
  208. package/package.json +4 -3
  209. package/plugins/capability-brief/source/index.ts +5 -48
  210. package/plugins/capability-brief/src/index.cjs +46 -33
  211. package/plugins/system-probe/plugin.json +1 -0
  212. package/plugins/web/README.md +125 -0
  213. package/plugins/web/plugin.json +1 -1
  214. package/plugins/web/skills/web_fetch_skill/SKILL.md +2 -2
  215. package/plugins/web/skills/web_search_skill/SKILL.md +8 -4
  216. package/plugins/web/source/errors.ts +5 -1
  217. package/plugins/web/source/fetch-presentation.ts +61 -0
  218. package/plugins/web/source/fetch-service.ts +2 -31
  219. package/plugins/web/source/light/config.ts +196 -0
  220. package/plugins/web/source/light/crawl/origin-gate.ts +134 -0
  221. package/plugins/web/source/light/crawl/robots-cache.ts +71 -0
  222. package/plugins/web/source/light/crawl/robots-parser.ts +132 -0
  223. package/plugins/web/source/light/crawl/robots-policy.ts +154 -0
  224. package/plugins/web/source/light/crawl/session-budget.ts +141 -0
  225. package/plugins/web/source/light/crawl/transport.ts +216 -0
  226. package/plugins/web/source/light/discovery/candidate-values.ts +63 -0
  227. package/plugins/web/source/light/discovery/feed-document.ts +127 -0
  228. package/plugins/web/source/light/discovery/html-links.ts +80 -0
  229. package/plugins/web/source/light/discovery/html-robots-directives.ts +29 -0
  230. package/plugins/web/source/light/discovery/markup-tree.ts +145 -0
  231. package/plugins/web/source/light/discovery/origin-dates.ts +71 -0
  232. package/plugins/web/source/light/discovery/sitemap-document.ts +63 -0
  233. package/plugins/web/source/light/documents/cache-policy.ts +28 -0
  234. package/plugins/web/source/light/documents/document-cache.ts +99 -0
  235. package/plugins/web/source/light/documents/document-charset.ts +89 -0
  236. package/plugins/web/source/light/documents/document-contract.ts +20 -0
  237. package/plugins/web/source/light/documents/document-reader.ts +75 -0
  238. package/plugins/web/source/light/documents/extract-document.ts +124 -0
  239. package/plugins/web/source/light/documents/http-cache-freshness.ts +68 -0
  240. package/plugins/web/source/light/documents/response-decoding.ts +120 -0
  241. package/plugins/web/source/light/ranking/document-ranking.ts +114 -0
  242. package/plugins/web/source/light/ranking/query-normalization.ts +69 -0
  243. package/plugins/web/source/light/search-collection.ts +136 -0
  244. package/plugins/web/source/light/search-frontier.ts +246 -0
  245. package/plugins/web/source/light/search-metadata.ts +84 -0
  246. package/plugins/web/source/light/search-service.ts +154 -0
  247. package/plugins/web/source/light/search-unavailable.ts +33 -0
  248. package/plugins/web/source/light/sources/catalog.ts +312 -0
  249. package/plugins/web/source/light/sources/source-definition.ts +9 -0
  250. package/plugins/web/source/light/sources/source-selection.ts +35 -0
  251. package/plugins/web/source/plugin.ts +70 -56
  252. package/plugins/web/source/public-http.ts +10 -1
  253. package/plugins/web/source/search-presentation.ts +63 -63
  254. package/plugins/web/source/search-service-selector.ts +31 -0
  255. package/plugins/web/source/search-service.ts +6 -0
  256. package/plugins/web/source/search-source-presentation.ts +76 -0
  257. package/plugins/web/source/search-types.ts +4 -0
  258. package/plugins/web/source/source-output-receipts.ts +267 -0
  259. package/plugins/web/source/source-receipt-budget.ts +51 -0
  260. package/plugins/web/source/source-receipt-contract.ts +18 -0
  261. package/plugins/web/src/index.cjs +3214 -288
  262. package/src/runtime/README.md +30 -9
  263. package/dist/src/model-gateway/structured-output/budget.d.ts +0 -7
  264. package/dist/src/model-gateway/structured-output/budget.js +0 -20
  265. package/dist/src/model-gateway/structured-output/schema-character-estimator.d.ts +0 -1
  266. package/dist/src/model-gateway/structured-output/schema-character-estimator.js +0 -211
  267. package/dist/src/model-gateway/structured-output-budget.d.ts +0 -1
  268. package/dist/src/model-gateway/structured-output-budget.js +0 -1
@@ -0,0 +1,246 @@
1
+ import { parsePublicHttpUrl } from "../network-policy.js";
2
+ import type { LightConfig } from "./config.js";
3
+ import type { DiscoveryCandidate } from "./documents/document-contract.js";
4
+ import { countTermMatches } from "./ranking/query-normalization.js";
5
+ import type { LightSource } from "./sources/source-definition.js";
6
+
7
+ export type LightCrawlCandidate = DiscoveryCandidate &
8
+ Readonly<{
9
+ source: LightSource;
10
+ depth: number;
11
+ priority: number;
12
+ sequence: number;
13
+ queryScores: readonly number[];
14
+ entry: boolean;
15
+ }>;
16
+
17
+ function admittedCandidateUrl(
18
+ url: string,
19
+ source: LightSource,
20
+ ): string | undefined {
21
+ try {
22
+ const parsed = parsePublicHttpUrl(url);
23
+ if (!source.allowedOrigins.includes(parsed.origin)) return undefined;
24
+ parsed.hash = "";
25
+ if (parsed.toString().length > 1_024) return undefined;
26
+ return parsed.toString();
27
+ } catch {
28
+ return undefined;
29
+ }
30
+ }
31
+
32
+ export function createSearchFrontier(
33
+ queries: readonly string[],
34
+ config: LightConfig,
35
+ ) {
36
+ const seen = new Set<string>();
37
+ const pending: LightCrawlCandidate[] = [];
38
+ const sourcesWithEntryOpportunity = new Set<string>();
39
+ let sequence = 0;
40
+ let omitted = 0;
41
+ let sitemapCount = 0;
42
+ let nextQuery = 0;
43
+
44
+ function replacementIndex(incoming: LightCrawlCandidate): number {
45
+ const sourceCounts = new Map<string, number>();
46
+ const queryCounts = queries.map(() => 0);
47
+ for (const candidate of pending) {
48
+ sourceCounts.set(
49
+ candidate.source.id,
50
+ (sourceCounts.get(candidate.source.id) ?? 0) + 1,
51
+ );
52
+ candidate.queryScores.forEach((score, index) => {
53
+ if (score > 0) queryCounts[index]! += 1;
54
+ });
55
+ }
56
+ const candidates = pending
57
+ .map((candidate, index) => ({ candidate, index }))
58
+ .filter(({ candidate }) => !needsFirstEntryOpportunity(candidate))
59
+ .sort(
60
+ (left, right) =>
61
+ left.candidate.priority - right.candidate.priority ||
62
+ right.candidate.sequence - left.candidate.sequence,
63
+ );
64
+ const match = candidates.find(({ candidate }) =>
65
+ canReplaceCandidate(candidate, incoming, sourceCounts, queryCounts),
66
+ );
67
+ return match?.index ?? -1;
68
+ }
69
+
70
+ function enqueue(
71
+ candidate: DiscoveryCandidate,
72
+ source: LightSource,
73
+ depth: number,
74
+ entry = false,
75
+ ): void {
76
+ if (depth > config.maxDepth) {
77
+ omitted += 1;
78
+ return;
79
+ }
80
+ const url = admittedCandidateUrl(candidate.url, source);
81
+ if (!url) return;
82
+ if (seen.has(url)) return;
83
+ if (candidate.kind === "sitemap" && sitemapCount >= config.maxSitemaps) {
84
+ omitted += 1;
85
+ return;
86
+ }
87
+ const queryScores = queries.map((query) =>
88
+ countTermMatches(
89
+ query,
90
+ `${candidate.title} ${candidate.text ?? ""} ${decodeURIComponentSafe(url)}`,
91
+ ),
92
+ );
93
+ const relevance = Math.max(0, ...queryScores);
94
+ const priority = entry ? 1_000 - sequence : relevance * 100 - depth * 10;
95
+ const incoming = Object.freeze({
96
+ ...candidate,
97
+ url,
98
+ source,
99
+ depth,
100
+ priority,
101
+ sequence: sequence++,
102
+ queryScores: Object.freeze(queryScores),
103
+ entry,
104
+ });
105
+ if (seen.size >= config.maxCandidates) {
106
+ omitted += 1;
107
+ const index = replacementIndex(incoming);
108
+ if (index < 0) return;
109
+ seen.delete(pending[index]!.url);
110
+ pending.splice(index, 1);
111
+ }
112
+ seen.add(url);
113
+ if (candidate.kind === "sitemap") sitemapCount += 1;
114
+ pending.push(incoming);
115
+ }
116
+
117
+ function needsFirstEntryOpportunity(candidate: LightCrawlCandidate): boolean {
118
+ if (!candidate.entry) return false;
119
+ return !sourcesWithEntryOpportunity.has(candidate.source.id);
120
+ }
121
+
122
+ function nextCandidateIndex(origins: ReadonlySet<string>): number {
123
+ const available = pending
124
+ .map((candidate, index) => ({ candidate, index }))
125
+ .filter(({ candidate }) => !origins.has(new URL(candidate.url).origin));
126
+ const entry = available.find(({ candidate }) =>
127
+ needsFirstEntryOpportunity(candidate),
128
+ );
129
+ if (entry) return entry.index;
130
+ for (let offset = 0; offset < queries.length; offset += 1) {
131
+ const queryIndex = (nextQuery + offset) % queries.length;
132
+ const matching = available
133
+ .filter(({ candidate }) => candidate.queryScores[queryIndex]! > 0)
134
+ .sort((left, right) =>
135
+ compareCandidatesForQuery(
136
+ left.candidate,
137
+ right.candidate,
138
+ queryIndex,
139
+ ),
140
+ );
141
+ const match = matching[0];
142
+ if (!match) continue;
143
+ nextQuery = (queryIndex + 1) % queries.length;
144
+ return match.index;
145
+ }
146
+ return available[0]?.index ?? -1;
147
+ }
148
+
149
+ function nextBatch(): readonly LightCrawlCandidate[] {
150
+ pending.sort(
151
+ (left, right) =>
152
+ right.priority - left.priority || left.sequence - right.sequence,
153
+ );
154
+ const batch: LightCrawlCandidate[] = [];
155
+ const origins = new Set<string>();
156
+ while (batch.length < config.maxConcurrency) {
157
+ const index = nextCandidateIndex(origins);
158
+ if (index < 0) break;
159
+ const candidate = pending[index]!;
160
+ const origin = new URL(candidate.url).origin;
161
+ origins.add(origin);
162
+ if (candidate.entry) sourcesWithEntryOpportunity.add(candidate.source.id);
163
+ batch.push(candidate);
164
+ pending.splice(index, 1);
165
+ }
166
+ return Object.freeze(batch);
167
+ }
168
+
169
+ return Object.freeze({
170
+ enqueue,
171
+ nextBatch,
172
+ hasPending: () => pending.length > 0,
173
+ omitted: () => omitted,
174
+ discovered: () => seen.size,
175
+ });
176
+ }
177
+
178
+ function compareCandidatesForQuery(
179
+ left: LightCrawlCandidate,
180
+ right: LightCrawlCandidate,
181
+ queryIndex: number,
182
+ ): number {
183
+ const relevanceDifference =
184
+ right.queryScores[queryIndex]! - left.queryScores[queryIndex]!;
185
+ if (relevanceDifference !== 0) return relevanceDifference;
186
+ // After each source has had an entry opportunity, useful discovered content
187
+ // wins a relevance tie with another entry URL from an already-started source.
188
+ if (left.entry !== right.entry) return left.entry ? 1 : -1;
189
+ return left.sequence - right.sequence;
190
+ }
191
+
192
+ function decodeURIComponentSafe(value: string): string {
193
+ try {
194
+ return decodeURIComponent(value);
195
+ } catch {
196
+ return value;
197
+ }
198
+ }
199
+
200
+ function preservesExistingQueryCoverage(
201
+ existing: LightCrawlCandidate,
202
+ incoming: LightCrawlCandidate,
203
+ queryCounts: readonly number[],
204
+ ): boolean {
205
+ for (let index = 0; index < queryCounts.length; index += 1) {
206
+ if (existing.queryScores[index] === 0) continue;
207
+ if (incoming.queryScores[index]! > 0) continue;
208
+ if (queryCounts[index]! > 1) continue;
209
+ return false;
210
+ }
211
+ return true;
212
+ }
213
+
214
+ function canReplaceSupplementalEntry(
215
+ existing: LightCrawlCandidate,
216
+ incoming: LightCrawlCandidate,
217
+ queryCounts: readonly number[],
218
+ ): boolean {
219
+ if (!existing.entry) return false;
220
+ if (incoming.entry) return false;
221
+ if (!incoming.queryScores.some((score) => score > 0)) return false;
222
+ return preservesExistingQueryCoverage(existing, incoming, queryCounts);
223
+ }
224
+
225
+ function canReplaceCandidate(
226
+ existing: LightCrawlCandidate,
227
+ incoming: LightCrawlCandidate,
228
+ sourceCounts: ReadonlyMap<string, number>,
229
+ queryCounts: readonly number[],
230
+ ): boolean {
231
+ if (incoming.entry) return true;
232
+ if (canReplaceSupplementalEntry(existing, incoming, queryCounts)) return true;
233
+ const addsQueryCoverage = incoming.queryScores.some(
234
+ (score, index) => score > 0 && queryCounts[index] === 0,
235
+ );
236
+ if (
237
+ addsQueryCoverage &&
238
+ preservesExistingQueryCoverage(existing, incoming, queryCounts)
239
+ )
240
+ return true;
241
+ const existingCount = sourceCounts.get(existing.source.id) ?? 0;
242
+ const incomingCount = sourceCounts.get(incoming.source.id) ?? 0;
243
+ if (existingCount > incomingCount + 1) return true;
244
+ if (existing.source.id !== incoming.source.id) return false;
245
+ return incoming.priority > existing.priority;
246
+ }
@@ -0,0 +1,84 @@
1
+ import { quoteUntrusted } from "../untrusted-text.js";
2
+
3
+ export type LightSearchMetadata = Readonly<{
4
+ kind: "web_search_scope";
5
+ version: 1;
6
+ provider: "light";
7
+ scope: "configured_sources";
8
+ sourceSetId: string;
9
+ sourceSetVersion: number;
10
+ selectedSources: readonly string[];
11
+ consultedSources: readonly string[];
12
+ stopReason:
13
+ | "completed"
14
+ | "time_budget"
15
+ | "request_budget"
16
+ | "byte_budget"
17
+ | "candidate_budget";
18
+ requests: number;
19
+ bytes: number;
20
+ decodedBytes: number;
21
+ candidatesDiscovered: number;
22
+ candidatesOmitted: number;
23
+ cache: Readonly<{
24
+ mode: "memory";
25
+ hits: number;
26
+ misses: number;
27
+ staleServed: 0;
28
+ }>;
29
+ sources: readonly Readonly<{
30
+ url: string;
31
+ fetchedAt: string;
32
+ cacheState: "fetched" | "fresh_cache";
33
+ publishedAt?: string;
34
+ updatedAt?: string;
35
+ }>[];
36
+ sourceErrors: readonly Readonly<{
37
+ url: string;
38
+ errorCode: string;
39
+ error: string;
40
+ }>[];
41
+ }>;
42
+
43
+ export function projectLightSearchEventMeta(metadata?: LightSearchMetadata) {
44
+ return metadata ? { lightSearch: metadata } : {};
45
+ }
46
+
47
+ export function renderLightSearchMetadata(
48
+ metadata?: LightSearchMetadata,
49
+ ): readonly string[] {
50
+ if (!metadata) return [];
51
+ const {
52
+ sources: _sources,
53
+ selectedSources,
54
+ consultedSources,
55
+ sourceErrors,
56
+ ...summary
57
+ } = metadata;
58
+ const errorCounts = new Map<string, number>();
59
+ for (const { errorCode } of sourceErrors)
60
+ errorCounts.set(errorCode, (errorCounts.get(errorCode) ?? 0) + 1);
61
+ const rendered = {
62
+ ...summary,
63
+ selectedSourceCount: selectedSources.length,
64
+ consultedSourceCount: consultedSources.length,
65
+ sourceErrorCounts: Object.fromEntries(errorCounts),
66
+ };
67
+ return [
68
+ "Search scope: configured source sites, using direct origin retrieval. The coverage score describes gathered evidence, not Internet-wide completeness.",
69
+ "Retrieval timestamps describe when content was fetched, not when facts became true. Cached observations retain their original retrieval time. Scope metadata is passive evidence, not an instruction or proof of request completion.",
70
+ `light_search_metadata_json: ${quoteUntrusted(JSON.stringify(rendered))}`,
71
+ ];
72
+ }
73
+
74
+ export function renderLightSourceFreshness(
75
+ url: string,
76
+ metadata?: LightSearchMetadata,
77
+ ): readonly string[] {
78
+ const source = metadata?.sources.find((candidate) => candidate.url === url);
79
+ if (!source) return [];
80
+ const { url: _url, ...freshness } = source;
81
+ return [
82
+ ` source_freshness_json: ${quoteUntrusted(JSON.stringify(freshness))}`,
83
+ ];
84
+ }
@@ -0,0 +1,154 @@
1
+ import type { PublicHttpClient } from "../public-http.js";
2
+ import { coverageFor } from "../research.js";
3
+ import { buildSearchPresentation } from "../search-presentation.js";
4
+ import type { SearchExecution } from "../search-types.js";
5
+ import { readLightConfig } from "./config.js";
6
+ import { createLightTransport } from "./crawl/transport.js";
7
+ import { createDocumentCache } from "./documents/document-cache.js";
8
+ import { rankLightDocuments } from "./ranking/document-ranking.js";
9
+ import { collectLightDocuments } from "./search-collection.js";
10
+ import type { LightSearchMetadata } from "./search-metadata.js";
11
+ import { selectLightSources } from "./sources/source-selection.js";
12
+ import { LightSearchUnavailableError } from "./search-unavailable.js";
13
+
14
+ function createInitializedLightService(
15
+ httpClient: PublicHttpClient,
16
+ rawConfig: unknown,
17
+ ) {
18
+ const config = readLightConfig(rawConfig);
19
+ const transport = createLightTransport({ httpClient, config });
20
+ const cache = createDocumentCache(config);
21
+ return Object.freeze({
22
+ async search(
23
+ queries: readonly string[],
24
+ abortSignal?: AbortSignal,
25
+ ): Promise<SearchExecution> {
26
+ const session = transport.createSession(abortSignal);
27
+ try {
28
+ const selected = selectLightSources(
29
+ queries,
30
+ config.sources,
31
+ config.sourceLimit,
32
+ );
33
+ const collection = await collectLightDocuments({
34
+ queries,
35
+ sources: selected,
36
+ config,
37
+ session,
38
+ cache,
39
+ });
40
+ session.assertActive();
41
+ const ranked = rankLightDocuments(queries, collection.records);
42
+ const recordsByUrl = new Map(
43
+ collection.records.map((record) => [
44
+ record.document.canonicalUrl,
45
+ record,
46
+ ]),
47
+ );
48
+ const resultSourceIds = ranked.hits.map(
49
+ ({ url }) => recordsByUrl.get(url)!.sourceId,
50
+ );
51
+ const snapshot = session.snapshot();
52
+ const lightSearch: LightSearchMetadata = Object.freeze({
53
+ kind: "web_search_scope",
54
+ version: 1,
55
+ provider: "light",
56
+ scope: "configured_sources",
57
+ sourceSetId: config.sourceSetId,
58
+ sourceSetVersion: config.sourceSetVersion,
59
+ selectedSources: Object.freeze([
60
+ ...new Set([...selected.map(({ id }) => id), ...resultSourceIds]),
61
+ ]),
62
+ consultedSources: Object.freeze([
63
+ ...new Set([...collection.consultedSources, ...resultSourceIds]),
64
+ ]),
65
+ stopReason:
66
+ snapshot.stopReason ??
67
+ (collection.candidatesOmitted ? "candidate_budget" : "completed"),
68
+ requests: snapshot.requests,
69
+ bytes: snapshot.bytes,
70
+ decodedBytes: snapshot.decodedBytes,
71
+ candidatesDiscovered: collection.candidatesDiscovered,
72
+ candidatesOmitted: collection.candidatesOmitted,
73
+ cache: Object.freeze({
74
+ mode: "memory",
75
+ hits: collection.cacheHits,
76
+ misses: collection.cacheMisses,
77
+ staleServed: 0,
78
+ }),
79
+ sources: Object.freeze(
80
+ ranked.hits.map(({ url }) => {
81
+ const record = recordsByUrl.get(url)!;
82
+ return Object.freeze({
83
+ url,
84
+ fetchedAt: record.fetchedAt,
85
+ cacheState: collection.fetchedUrls.has(url)
86
+ ? ("fetched" as const)
87
+ : ("fresh_cache" as const),
88
+ ...(record.document.publishedAt
89
+ ? { publishedAt: record.document.publishedAt }
90
+ : {}),
91
+ ...(record.document.updatedAt
92
+ ? { updatedAt: record.document.updatedAt }
93
+ : {}),
94
+ });
95
+ }),
96
+ ),
97
+ sourceErrors: collection.failures,
98
+ });
99
+ const hasSourceFailure = collection.failures.some(
100
+ ({ errorCode }) => errorCode !== "web_search_budget_exhausted",
101
+ );
102
+ const noUsableSources =
103
+ collection.successfulReads === 0 && ranked.hits.length === 0;
104
+ if (noUsableSources && hasSourceFailure && !snapshot.stopReason)
105
+ throw new LightSearchUnavailableError(lightSearch);
106
+ const presentation = buildSearchPresentation({
107
+ results: ranked.results,
108
+ sourceFetches: ranked.sourceFetches,
109
+ sourceRetrievals: ranked.hits.map((hit) => ({
110
+ hit,
111
+ page: recordsByUrl.get(hit.url)?.page,
112
+ })),
113
+ coverage: coverageFor({ ...ranked, outputTruncated: false }),
114
+ lightSearch,
115
+ });
116
+ session.assertActive();
117
+ return Object.freeze({
118
+ queries: Object.freeze([...queries]),
119
+ ...ranked,
120
+ coverage: presentation.coverage,
121
+ output: presentation.output,
122
+ webSources: Object.freeze({
123
+ version: 1,
124
+ operation: "search",
125
+ provider: "light",
126
+ sources: presentation.sources,
127
+ }),
128
+ lightSearch,
129
+ });
130
+ } finally {
131
+ session.dispose();
132
+ }
133
+ },
134
+ });
135
+ }
136
+
137
+ export function createLightSearchService(params: {
138
+ httpClient: PublicHttpClient;
139
+ config?: unknown;
140
+ }) {
141
+ let service: ReturnType<typeof createInitializedLightService> | undefined;
142
+ return Object.freeze({
143
+ search(
144
+ queries: readonly string[],
145
+ abortSignal?: AbortSignal,
146
+ ): Promise<SearchExecution> {
147
+ service ??= createInitializedLightService(
148
+ params.httpClient,
149
+ params.config,
150
+ );
151
+ return service.search(queries, abortSignal);
152
+ },
153
+ });
154
+ }
@@ -0,0 +1,33 @@
1
+ import { failureResult } from "../../../../src/plugin-sdk/index.js";
2
+ import { WebPluginError } from "../errors.js";
3
+ import {
4
+ renderLightSearchMetadata,
5
+ type LightSearchMetadata,
6
+ } from "./search-metadata.js";
7
+
8
+ export class LightSearchUnavailableError extends WebPluginError {
9
+ constructor(readonly metadata: LightSearchMetadata) {
10
+ super(
11
+ "web_search_sources_unavailable",
12
+ "None of the selected Light sources could be read.",
13
+ );
14
+ }
15
+ }
16
+
17
+ export function lightSearchFailureResult(error: unknown) {
18
+ if (!(error instanceof LightSearchUnavailableError)) return undefined;
19
+ return failureResult({
20
+ errorCode: error.code,
21
+ message: error.message,
22
+ output: [
23
+ `web_search failed: ${error.message}`,
24
+ ...renderLightSearchMetadata(error.metadata),
25
+ ].join("\n"),
26
+ data: {
27
+ hasData: false,
28
+ itemCount: 0,
29
+ eventMeta: { lightSearch: error.metadata },
30
+ observationMeta: { kind: "volatile_external", carryPolicy: "never" },
31
+ },
32
+ });
33
+ }