@effect-uai/core 0.8.0 → 0.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.
- package/dist/{AiError-CAX_48RU.d.mts → AiError-Bf1pq4BB.d.mts} +9 -4
- package/dist/{AiError-CAX_48RU.d.mts.map → AiError-Bf1pq4BB.d.mts.map} +1 -1
- package/dist/{Audio-DPlTsNdz.d.mts → Audio-CR7CSljJ.d.mts} +1 -1
- package/dist/{Audio-DPlTsNdz.d.mts.map → Audio-CR7CSljJ.d.mts.map} +1 -1
- package/dist/{Image-HNmMpMTh.d.mts → Image-BZmKfIdq.d.mts} +1 -1
- package/dist/{Image-HNmMpMTh.d.mts.map → Image-BZmKfIdq.d.mts.map} +1 -1
- package/dist/{Items-7ljaVxH_.d.mts → Items-BnIduEV2.d.mts} +7 -7
- package/dist/{Items-7ljaVxH_.d.mts.map → Items-BnIduEV2.d.mts.map} +1 -1
- package/dist/Loop-D8nWpowc.d.mts +173 -0
- package/dist/Loop-D8nWpowc.d.mts.map +1 -0
- package/dist/{SandboxError-BvKypkJm.d.mts → SandboxError-CIrVDOMV.d.mts} +2 -2
- package/dist/{SandboxError-BvKypkJm.d.mts.map → SandboxError-CIrVDOMV.d.mts.map} +1 -1
- package/dist/{StructuredFormat-BbN4dosH.d.mts → StructuredFormat-DHaUJBdd.d.mts} +2 -2
- package/dist/{StructuredFormat-BbN4dosH.d.mts.map → StructuredFormat-DHaUJBdd.d.mts.map} +1 -1
- package/dist/Tool-2lC_XMth.d.mts +284 -0
- package/dist/Tool-2lC_XMth.d.mts.map +1 -0
- package/dist/ToolEvent-cWZ2BnbS.d.mts +151 -0
- package/dist/ToolEvent-cWZ2BnbS.d.mts.map +1 -0
- package/dist/ToolResult-CL4hK_9z.d.mts +157 -0
- package/dist/ToolResult-CL4hK_9z.d.mts.map +1 -0
- package/dist/Toolkit-Dr9l_hNy.d.mts +196 -0
- package/dist/Toolkit-Dr9l_hNy.d.mts.map +1 -0
- package/dist/browser/Browser.d.mts +190 -0
- package/dist/browser/Browser.d.mts.map +1 -0
- package/dist/browser/Browser.mjs +34 -0
- package/dist/browser/Browser.mjs.map +1 -0
- package/dist/browser/BrowserError.d.mts +141 -0
- package/dist/browser/BrowserError.d.mts.map +1 -0
- package/dist/browser/BrowserError.mjs +89 -0
- package/dist/browser/BrowserError.mjs.map +1 -0
- package/dist/browser/BrowserTool.d.mts +58 -0
- package/dist/browser/BrowserTool.d.mts.map +1 -0
- package/dist/browser/BrowserTool.mjs +86 -0
- package/dist/browser/BrowserTool.mjs.map +1 -0
- package/dist/{dist-DV5ISja1.mjs → dist-B04dlFHg.mjs} +3 -3
- package/dist/{dist-DV5ISja1.mjs.map → dist-B04dlFHg.mjs.map} +1 -1
- package/dist/dist-DWgRbDN3.mjs +140 -0
- package/dist/dist-DWgRbDN3.mjs.map +1 -0
- package/dist/domain/AiError.d.mts +2 -2
- package/dist/domain/AiError.mjs +22 -3
- package/dist/domain/AiError.mjs.map +1 -1
- package/dist/domain/Audio.d.mts +1 -1
- package/dist/domain/Image.d.mts +1 -1
- package/dist/domain/Items.d.mts +1 -1
- package/dist/domain/Music.d.mts +1 -1
- package/dist/domain/Turn.d.mts +4 -4
- package/dist/domain/Turn.test.mjs +1 -1
- package/dist/embedding-model/Embedding.d.mts +1 -1
- package/dist/embedding-model/EmbeddingModel.d.mts +1 -1
- package/dist/embedding-model/EmbeddingModel.test.mjs +1 -1
- package/dist/embedding-model/EmbeddingModel.test.mjs.map +1 -1
- package/dist/index.d.mts +19 -13
- package/dist/index.mjs +15 -9
- package/dist/language-model/LanguageModel.d.mts +10 -5
- package/dist/language-model/LanguageModel.d.mts.map +1 -1
- package/dist/language-model/LanguageModel.mjs.map +1 -1
- package/dist/language-model/LanguageModel.test.mjs +1 -1
- package/dist/loop/Loop.d.mts +2 -141
- package/dist/loop/Loop.mjs +16 -20
- package/dist/loop/Loop.mjs.map +1 -1
- package/dist/loop/Loop.test.mjs +18 -1
- package/dist/loop/Loop.test.mjs.map +1 -1
- package/dist/{magic-string.es-BgIV5Mu3.mjs → magic-string.es-Dk-MMA0F.mjs} +1 -1
- package/dist/{magic-string.es-BgIV5Mu3.mjs.map → magic-string.es-Dk-MMA0F.mjs.map} +1 -1
- package/dist/music-generator/MusicGenerator.d.mts +2 -2
- package/dist/music-generator/MusicGenerator.test.mjs +1 -1
- package/dist/observability/Metrics.d.mts +116 -28
- package/dist/observability/Metrics.d.mts.map +1 -1
- package/dist/observability/Metrics.mjs +250 -38
- package/dist/observability/Metrics.mjs.map +1 -1
- package/dist/observability/Metrics.test.d.mts +1 -0
- package/dist/observability/Metrics.test.mjs +187 -0
- package/dist/observability/Metrics.test.mjs.map +1 -0
- package/dist/observability/Telemetry.d.mts +37 -0
- package/dist/observability/Telemetry.d.mts.map +1 -0
- package/dist/observability/Telemetry.mjs +57 -0
- package/dist/observability/Telemetry.mjs.map +1 -0
- package/dist/observability/Telemetry.test.d.mts +1 -0
- package/dist/observability/Telemetry.test.mjs +92 -0
- package/dist/observability/Telemetry.test.mjs.map +1 -0
- package/dist/retry/Retry.d.mts +2 -2
- package/dist/retry/Retry.test.mjs +1 -1
- package/dist/sandbox/Sandbox.d.mts +1 -1
- package/dist/sandbox/SandboxError.d.mts +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.d.mts +2 -2
- package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +1 -1
- package/dist/streaming/JSONL.d.mts +1 -1
- package/dist/streaming/JSONL.test.mjs +1 -1
- package/dist/streaming/SSE.test.mjs +1 -1
- package/dist/structured-format/StructuredFormat.d.mts +1 -1
- package/dist/structured-format/StructuredFormat.mjs.map +1 -1
- package/dist/structured-format/StructuredFormat.test.mjs +1 -1
- package/dist/testing/MockMusicGenerator.d.mts +1 -1
- package/dist/testing/MockProvider.d.mts +1 -1
- package/dist/testing/MockSpeechSynthesizer.d.mts +1 -1
- package/dist/tool/Approval.d.mts +19 -7
- package/dist/tool/Approval.d.mts.map +1 -1
- package/dist/tool/Approval.mjs +18 -11
- package/dist/tool/Approval.mjs.map +1 -1
- package/dist/tool/Approval.test.mjs +80 -40
- package/dist/tool/Approval.test.mjs.map +1 -1
- package/dist/tool/HistoryCheck.d.mts +2 -2
- package/dist/tool/Tool.d.mts +2 -2
- package/dist/tool/Tool.mjs +190 -36
- package/dist/tool/Tool.mjs.map +1 -1
- package/dist/tool/Tool.test.mjs +41 -3
- package/dist/tool/Tool.test.mjs.map +1 -1
- package/dist/tool/ToolEvent.d.mts +2 -151
- package/dist/tool/ToolEvent.mjs +5 -0
- package/dist/tool/ToolEvent.mjs.map +1 -1
- package/dist/tool/ToolResult.d.mts +2 -138
- package/dist/tool/ToolResult.mjs +32 -8
- package/dist/tool/ToolResult.mjs.map +1 -1
- package/dist/tool/Toolkit.d.mts +2 -73
- package/dist/tool/Toolkit.mjs +153 -35
- package/dist/tool/Toolkit.mjs.map +1 -1
- package/dist/tool/Toolkit.test.mjs +271 -11
- package/dist/tool/Toolkit.test.mjs.map +1 -1
- package/dist/transcriber/Transcriber.d.mts +2 -2
- package/dist/transcriber/Transcriber.test.mjs +1 -1
- package/dist/web-read/WebRead.d.mts +44 -0
- package/dist/web-read/WebRead.d.mts.map +1 -0
- package/dist/web-read/WebRead.mjs +18 -0
- package/dist/web-read/WebRead.mjs.map +1 -0
- package/dist/web-read/WebReadTool.d.mts +57 -0
- package/dist/web-read/WebReadTool.d.mts.map +1 -0
- package/dist/web-read/WebReadTool.mjs +59 -0
- package/dist/web-read/WebReadTool.mjs.map +1 -0
- package/dist/web-search/WebSearch.d.mts +1 -1
- package/dist/web-search/WebSearchTool.d.mts +12 -6
- package/dist/web-search/WebSearchTool.d.mts.map +1 -1
- package/dist/web-search/WebSearchTool.mjs +10 -5
- package/dist/web-search/WebSearchTool.mjs.map +1 -1
- package/package.json +21 -1
- package/src/browser/Browser.ts +268 -0
- package/src/browser/BrowserError.ts +145 -0
- package/src/browser/BrowserTool.ts +161 -0
- package/src/domain/AiError.ts +27 -1
- package/src/embedding-model/EmbeddingModel.test.ts +4 -0
- package/src/index.ts +6 -0
- package/src/language-model/LanguageModel.ts +7 -2
- package/src/loop/Loop.test.ts +22 -0
- package/src/loop/Loop.ts +27 -4
- package/src/observability/Metrics.test.ts +266 -0
- package/src/observability/Metrics.ts +505 -65
- package/src/observability/Telemetry.test.ts +123 -0
- package/src/observability/Telemetry.ts +96 -0
- package/src/structured-format/StructuredFormat.ts +4 -0
- package/src/tool/Approval.test.ts +77 -26
- package/src/tool/Approval.ts +72 -27
- package/src/tool/Tool.test.ts +55 -2
- package/src/tool/Tool.ts +335 -70
- package/src/tool/ToolEvent.ts +5 -0
- package/src/tool/ToolResult.ts +36 -9
- package/src/tool/Toolkit.test.ts +339 -9
- package/src/tool/Toolkit.ts +470 -120
- package/src/web-read/WebRead.ts +70 -0
- package/src/web-read/WebReadTool.ts +96 -0
- package/src/web-search/WebSearchTool.ts +11 -5
- package/dist/Tool-BKiieFXk.d.mts +0 -102
- package/dist/Tool-BKiieFXk.d.mts.map +0 -1
- package/dist/loop/Loop.d.mts.map +0 -1
- package/dist/tool/ToolEvent.d.mts.map +0 -1
- package/dist/tool/ToolResult.d.mts.map +0 -1
- package/dist/tool/Toolkit.d.mts.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { l as Tool } from "../Tool-2lC_XMth.mjs";
|
|
2
|
+
import { t as AiError } from "../AiError-Bf1pq4BB.mjs";
|
|
2
3
|
import { SearchResult, WebSearch } from "./WebSearch.mjs";
|
|
3
4
|
import { Schema } from "effect";
|
|
4
5
|
|
|
@@ -47,12 +48,17 @@ type WebSearchToolOptions = {
|
|
|
47
48
|
* `LanguageModel` tool-calling loop.
|
|
48
49
|
*
|
|
49
50
|
* The model controls only `query` and `recency`; domain scoping and the
|
|
50
|
-
* result cap are app policy, fixed here on the constructor. `Output` is
|
|
51
|
-
* rendered text
|
|
52
|
-
*
|
|
53
|
-
*
|
|
51
|
+
* result cap are app policy, fixed here on the constructor. `Output` is the
|
|
52
|
+
* rendered text (the model reads a numbered list better than raw
|
|
53
|
+
* `SearchResult[]`; pass `render` to change the format); failures stay typed
|
|
54
|
+
* on the `AiError` channel. To let the model recover from a failed search,
|
|
55
|
+
* wrap the toolkit in `Toolkit.describeFailures(AiError.describe)`.
|
|
56
|
+
*
|
|
57
|
+
* This is a simple default implementation for quick use. For more elaborate
|
|
58
|
+
* cases (a different output contract, more model-facing knobs), build your
|
|
59
|
+
* own tool with `Tool.make` over the same `search` helper.
|
|
54
60
|
*/
|
|
55
|
-
declare const webSearchTool: (options?: WebSearchToolOptions) => Tool<string, WebSearchToolArgs, string, WebSearch>;
|
|
61
|
+
declare const webSearchTool: (options?: WebSearchToolOptions) => Tool<string, WebSearchToolArgs, never, string, AiError, WebSearch>;
|
|
56
62
|
//#endregion
|
|
57
63
|
export { WebSearchToolArgs, WebSearchToolOptions, WebSearchTool_d_exports as t, webSearchTool };
|
|
58
64
|
//# sourceMappingURL=WebSearchTool.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSearchTool.d.mts","names":[],"sources":["../../src/web-search/WebSearchTool.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"WebSearchTool.d.mts","names":[],"sources":["../../src/web-search/WebSearchTool.ts"],"mappings":";;;;;;;;;;;;;;;;;cAaM,iBAAA,EAAiB,MAAA,CAAA,MAAA;EAAA;;;KAKX,iBAAA,UAA2B,iBAAA,CAAkB,IAAA;AAAA,KAa7C,oBAAA;EAlBW,iEAoBZ,IAAA;EApBY;;;;EAAA,SAyBZ,UAAA;;;;;WAKA,cAAA,GAAiB,aAAA;WAEjB,cAAA,GAAiB,aAAA;;AA3B5B;;;WAgCW,MAAA,IAAU,OAAA,EAAS,aAAA,CAAc,YAAA;AAAA;AAnB5C;;;;;;;;;;;;;;;;;;;AAAA,cAyCa,aAAA,GACX,OAAA,GAAU,oBAAA,KACT,IAAA,SAAkB,iBAAA,iBAAkC,OAAA,EAAiB,SAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __exportAll } from "../chunk-uyGKjUfl.mjs";
|
|
2
|
-
import { SearchRecency, search } from "./WebSearch.mjs";
|
|
3
2
|
import { fromEffectSchema, make } from "../tool/Tool.mjs";
|
|
3
|
+
import { SearchRecency, search } from "./WebSearch.mjs";
|
|
4
4
|
import { Effect, Schema } from "effect";
|
|
5
5
|
//#region src/web-search/WebSearchTool.ts
|
|
6
6
|
var WebSearchTool_exports = /* @__PURE__ */ __exportAll({ webSearchTool: () => webSearchTool });
|
|
@@ -30,10 +30,15 @@ const defaultRender = (results) => results.length === 0 ? "No results found." :
|
|
|
30
30
|
* `LanguageModel` tool-calling loop.
|
|
31
31
|
*
|
|
32
32
|
* The model controls only `query` and `recency`; domain scoping and the
|
|
33
|
-
* result cap are app policy, fixed here on the constructor. `Output` is
|
|
34
|
-
* rendered text
|
|
35
|
-
*
|
|
36
|
-
*
|
|
33
|
+
* result cap are app policy, fixed here on the constructor. `Output` is the
|
|
34
|
+
* rendered text (the model reads a numbered list better than raw
|
|
35
|
+
* `SearchResult[]`; pass `render` to change the format); failures stay typed
|
|
36
|
+
* on the `AiError` channel. To let the model recover from a failed search,
|
|
37
|
+
* wrap the toolkit in `Toolkit.describeFailures(AiError.describe)`.
|
|
38
|
+
*
|
|
39
|
+
* This is a simple default implementation for quick use. For more elaborate
|
|
40
|
+
* cases (a different output contract, more model-facing knobs), build your
|
|
41
|
+
* own tool with `Tool.make` over the same `search` helper.
|
|
37
42
|
*/
|
|
38
43
|
const webSearchTool = (options) => {
|
|
39
44
|
const name = options?.name ?? "web_search";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebSearchTool.mjs","names":["Tool.make","Tool.fromEffectSchema"],"sources":["../../src/web-search/WebSearchTool.ts"],"sourcesContent":["import { Effect, Schema } from \"effect\"\nimport * as Tool from \"../tool/Tool.js\"\nimport { search, SearchRecency, type SearchResult, WebSearch } from \"./WebSearch.js\"\n\n/**\n * Model-facing argument schema. Deliberately narrow: `query` plus the one\n * filter that is genuinely the model's call - `recency` (it knows whether a\n * question is about current events or evergreen). App-policy scoping\n * (`includeDomains` / `excludeDomains`) and the `maxResults` cost ceiling\n * are pinned on the constructor instead, so they are guaranteed rather than\n * left to the model to remember each call.\n */\nconst WebSearchToolArgs = Schema.Struct({\n query: Schema.String,\n recency: Schema.optional(SearchRecency),\n})\n\nexport type WebSearchToolArgs = typeof WebSearchToolArgs.Type\n\n/** Numbered `title / url / snippet` list - what a model reads best. */\nconst defaultRender = (results: ReadonlyArray<SearchResult>): string =>\n results.length === 0\n ? \"No results found.\"\n : results\n .map((r, i) => {\n const head = `${i + 1}. ${r.title ?? r.url}\\n ${r.url}`\n return r.snippet === undefined ? head : `${head}\\n ${r.snippet}`\n })\n .join(\"\\n\\n\")\n\nexport type WebSearchToolOptions = {\n /** Tool name the model sees. Default `\"web_search\"`. */\n readonly name?: string\n /**\n * App-fixed ceiling on results per call - a cost guard, not exposed to\n * the model. Default `5`.\n */\n readonly maxResults?: number\n /**\n * App-fixed allowlist applied to every search. A policy scope (\"only\n * search our docs\"), not a model knob - so it is enforced, not requested.\n */\n readonly includeDomains?: ReadonlyArray<string>\n /** App-fixed denylist applied to every search (\"never return X\"). */\n readonly excludeDomains?: ReadonlyArray<string>\n /**\n * Override how results are rendered into the model-facing string.\n * Default: a numbered `title / url / snippet` list.\n */\n readonly render?: (results: ReadonlyArray<SearchResult>) => string\n}\n\n/**\n * The canonical web-search tool. Its `R` is just `WebSearch`, so providing\n * any search-provider Layer satisfies it and the model-facing contract\n * (name, description, schema) is identical no matter which backend answers\n * - swap `PerplexitySearch.layer` for `ExaSearch.layer` and the tool the\n * model sees does not change. Drops straight into a `Toolkit` and the\n * `LanguageModel` tool-calling loop.\n *\n * The model controls only `query` and `recency`; domain scoping and the\n * result cap are app policy, fixed here on the constructor. `Output` is\n * rendered text
|
|
1
|
+
{"version":3,"file":"WebSearchTool.mjs","names":["Tool.make","Tool.fromEffectSchema"],"sources":["../../src/web-search/WebSearchTool.ts"],"sourcesContent":["import { Effect, Schema } from \"effect\"\nimport type * as AiError from \"../domain/AiError.js\"\nimport * as Tool from \"../tool/Tool.js\"\nimport { search, SearchRecency, type SearchResult, WebSearch } from \"./WebSearch.js\"\n\n/**\n * Model-facing argument schema. Deliberately narrow: `query` plus the one\n * filter that is genuinely the model's call - `recency` (it knows whether a\n * question is about current events or evergreen). App-policy scoping\n * (`includeDomains` / `excludeDomains`) and the `maxResults` cost ceiling\n * are pinned on the constructor instead, so they are guaranteed rather than\n * left to the model to remember each call.\n */\nconst WebSearchToolArgs = Schema.Struct({\n query: Schema.String,\n recency: Schema.optional(SearchRecency),\n})\n\nexport type WebSearchToolArgs = typeof WebSearchToolArgs.Type\n\n/** Numbered `title / url / snippet` list - what a model reads best. */\nconst defaultRender = (results: ReadonlyArray<SearchResult>): string =>\n results.length === 0\n ? \"No results found.\"\n : results\n .map((r, i) => {\n const head = `${i + 1}. ${r.title ?? r.url}\\n ${r.url}`\n return r.snippet === undefined ? head : `${head}\\n ${r.snippet}`\n })\n .join(\"\\n\\n\")\n\nexport type WebSearchToolOptions = {\n /** Tool name the model sees. Default `\"web_search\"`. */\n readonly name?: string\n /**\n * App-fixed ceiling on results per call - a cost guard, not exposed to\n * the model. Default `5`.\n */\n readonly maxResults?: number\n /**\n * App-fixed allowlist applied to every search. A policy scope (\"only\n * search our docs\"), not a model knob - so it is enforced, not requested.\n */\n readonly includeDomains?: ReadonlyArray<string>\n /** App-fixed denylist applied to every search (\"never return X\"). */\n readonly excludeDomains?: ReadonlyArray<string>\n /**\n * Override how results are rendered into the model-facing string.\n * Default: a numbered `title / url / snippet` list.\n */\n readonly render?: (results: ReadonlyArray<SearchResult>) => string\n}\n\n/**\n * The canonical web-search tool. Its `R` is just `WebSearch`, so providing\n * any search-provider Layer satisfies it and the model-facing contract\n * (name, description, schema) is identical no matter which backend answers\n * - swap `PerplexitySearch.layer` for `ExaSearch.layer` and the tool the\n * model sees does not change. Drops straight into a `Toolkit` and the\n * `LanguageModel` tool-calling loop.\n *\n * The model controls only `query` and `recency`; domain scoping and the\n * result cap are app policy, fixed here on the constructor. `Output` is the\n * rendered text (the model reads a numbered list better than raw\n * `SearchResult[]`; pass `render` to change the format); failures stay typed\n * on the `AiError` channel. To let the model recover from a failed search,\n * wrap the toolkit in `Toolkit.describeFailures(AiError.describe)`.\n *\n * This is a simple default implementation for quick use. For more elaborate\n * cases (a different output contract, more model-facing knobs), build your\n * own tool with `Tool.make` over the same `search` helper.\n */\nexport const webSearchTool = (\n options?: WebSearchToolOptions,\n): Tool.Tool<string, WebSearchToolArgs, never, string, AiError.AiError, WebSearch> => {\n const name = options?.name ?? \"web_search\"\n const render = options?.render ?? defaultRender\n const maxResults = options?.maxResults ?? 5\n const includeDomains = options?.includeDomains\n const excludeDomains = options?.excludeDomains\n return Tool.make({\n name,\n description:\n \"Search the web for current information. Returns ranked results with titles, URLs, and snippets.\",\n inputSchema: Tool.fromEffectSchema(WebSearchToolArgs),\n // The call is wrapped in a tracing span so agent traces show the tool\n // invocation (query, filters, result count) nested under the model turn.\n // `withSpan` is a no-op until a Tracer is installed, so this is free by\n // default and adds nothing to the requirements.\n run: (args) =>\n search({\n query: args.query,\n maxResults,\n ...(args.recency !== undefined && { recency: args.recency }),\n ...(includeDomains !== undefined && { includeDomains }),\n ...(excludeDomains !== undefined && { excludeDomains }),\n }).pipe(\n Effect.tap((r) => Effect.annotateCurrentSpan(\"web_search.result_count\", r.results.length)),\n Effect.map((r) => render(r.results)),\n Effect.withSpan(name, {\n kind: \"client\",\n attributes: {\n \"web_search.query\": args.query,\n \"web_search.max_results\": maxResults,\n ...(args.recency !== undefined && { \"web_search.recency\": args.recency }),\n ...(includeDomains !== undefined && { \"web_search.include_domains\": includeDomains }),\n ...(excludeDomains !== undefined && { \"web_search.exclude_domains\": excludeDomains }),\n },\n }),\n ),\n })\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,MAAM,oBAAoB,OAAO,OAAO;CACtC,OAAO,OAAO;CACd,SAAS,OAAO,SAAS,cAAc;CACxC,CAAC;;AAKF,MAAM,iBAAiB,YACrB,QAAQ,WAAW,IACf,sBACA,QACG,KAAK,GAAG,MAAM;CACb,MAAM,OAAO,GAAG,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,OAAO,EAAE;AACpD,QAAO,EAAE,YAAY,KAAA,IAAY,OAAO,GAAG,KAAK,OAAO,EAAE;EACzD,CACD,KAAK,OAAO;;;;;;;;;;;;;;;;;;;;AA2CrB,MAAa,iBACX,YACoF;CACpF,MAAM,OAAO,SAAS,QAAQ;CAC9B,MAAM,SAAS,SAAS,UAAU;CAClC,MAAM,aAAa,SAAS,cAAc;CAC1C,MAAM,iBAAiB,SAAS;CAChC,MAAM,iBAAiB,SAAS;AAChC,QAAOA,KAAU;EACf;EACA,aACE;EACF,aAAaC,iBAAsB,kBAAkB;EAKrD,MAAM,SACJ,OAAO;GACL,OAAO,KAAK;GACZ;GACA,GAAI,KAAK,YAAY,KAAA,KAAa,EAAE,SAAS,KAAK,SAAS;GAC3D,GAAI,mBAAmB,KAAA,KAAa,EAAE,gBAAgB;GACtD,GAAI,mBAAmB,KAAA,KAAa,EAAE,gBAAgB;GACvD,CAAC,CAAC,KACD,OAAO,KAAK,MAAM,OAAO,oBAAoB,2BAA2B,EAAE,QAAQ,OAAO,CAAC,EAC1F,OAAO,KAAK,MAAM,OAAO,EAAE,QAAQ,CAAC,EACpC,OAAO,SAAS,MAAM;GACpB,MAAM;GACN,YAAY;IACV,oBAAoB,KAAK;IACzB,0BAA0B;IAC1B,GAAI,KAAK,YAAY,KAAA,KAAa,EAAE,sBAAsB,KAAK,SAAS;IACxE,GAAI,mBAAmB,KAAA,KAAa,EAAE,8BAA8B,gBAAgB;IACpF,GAAI,mBAAmB,KAAA,KAAa,EAAE,8BAA8B,gBAAgB;IACrF;GACF,CAAC,CACH;EACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-uai/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Low-level primitives (loop, conversation, items, tools, streaming codecs) for building AI agents with Effect.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -42,6 +42,18 @@
|
|
|
42
42
|
"types": "./dist/domain/Audio.d.mts",
|
|
43
43
|
"import": "./dist/domain/Audio.mjs"
|
|
44
44
|
},
|
|
45
|
+
"./Browser": {
|
|
46
|
+
"types": "./dist/browser/Browser.d.mts",
|
|
47
|
+
"import": "./dist/browser/Browser.mjs"
|
|
48
|
+
},
|
|
49
|
+
"./BrowserError": {
|
|
50
|
+
"types": "./dist/browser/BrowserError.d.mts",
|
|
51
|
+
"import": "./dist/browser/BrowserError.mjs"
|
|
52
|
+
},
|
|
53
|
+
"./BrowserTool": {
|
|
54
|
+
"types": "./dist/browser/BrowserTool.d.mts",
|
|
55
|
+
"import": "./dist/browser/BrowserTool.mjs"
|
|
56
|
+
},
|
|
45
57
|
"./Image": {
|
|
46
58
|
"types": "./dist/domain/Image.d.mts",
|
|
47
59
|
"import": "./dist/domain/Image.mjs"
|
|
@@ -110,6 +122,14 @@
|
|
|
110
122
|
"types": "./dist/transcriber/Transcriber.d.mts",
|
|
111
123
|
"import": "./dist/transcriber/Transcriber.mjs"
|
|
112
124
|
},
|
|
125
|
+
"./WebRead": {
|
|
126
|
+
"types": "./dist/web-read/WebRead.d.mts",
|
|
127
|
+
"import": "./dist/web-read/WebRead.mjs"
|
|
128
|
+
},
|
|
129
|
+
"./WebReadTool": {
|
|
130
|
+
"types": "./dist/web-read/WebReadTool.d.mts",
|
|
131
|
+
"import": "./dist/web-read/WebReadTool.mjs"
|
|
132
|
+
},
|
|
113
133
|
"./WebSearch": {
|
|
114
134
|
"types": "./dist/web-search/WebSearch.d.mts",
|
|
115
135
|
"import": "./dist/web-search/WebSearch.mjs"
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { Brand, Context, type Duration, Effect, Scope } from "effect"
|
|
2
|
+
import type * as BrowserError from "./BrowserError.js"
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Identity
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Session identifier. Branded `string` so providers don't accidentally
|
|
10
|
+
* accept a raw user-supplied id, and so a session id can't be passed where
|
|
11
|
+
* another service's id is expected. Each provider mints these per its own
|
|
12
|
+
* scheme; core code treats them opaquely. Mint via `BrowserSessionId(...)`.
|
|
13
|
+
*/
|
|
14
|
+
export type BrowserSessionId = Brand.Branded<string, "BrowserSessionId">
|
|
15
|
+
export const BrowserSessionId = Brand.nominal<BrowserSessionId>()
|
|
16
|
+
|
|
17
|
+
/** Reference returned by `list`; enough to `attach`. */
|
|
18
|
+
export type BrowserSessionRef = {
|
|
19
|
+
readonly id: BrowserSessionId
|
|
20
|
+
/** Current top-level URL, when the provider reports it. */
|
|
21
|
+
readonly url?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Output representation for {@link BrowserSession.content}. `markdown` is
|
|
26
|
+
* the default.
|
|
27
|
+
*/
|
|
28
|
+
export type PageFormat = "markdown" | "html"
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Create request
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Cross-provider session-create request. Provider-specific knobs (proxy,
|
|
36
|
+
* stealth, persistent profile, ...) live on each provider's narrowed
|
|
37
|
+
* request type which extends this.
|
|
38
|
+
*/
|
|
39
|
+
export type CommonSessionRequest = {
|
|
40
|
+
/**
|
|
41
|
+
* Hard max lifetime of the session. Enforced locally (the handle is
|
|
42
|
+
* disposed and further calls fail with `BrowserSessionExpired`) and,
|
|
43
|
+
* where the provider supports it, passed through as a server-side cap so
|
|
44
|
+
* a crashed client still gets the session reclaimed. The provider cap
|
|
45
|
+
* must be >= the local reaper. Accepts millis, a string like
|
|
46
|
+
* `"5 minutes"`, or a `Duration`.
|
|
47
|
+
*/
|
|
48
|
+
readonly timeout?: Duration.Input
|
|
49
|
+
/**
|
|
50
|
+
* Idle teardown: dispose the session after this much inactivity, the
|
|
51
|
+
* timer resetting on each operation. Opt-in; omit for no idle limit.
|
|
52
|
+
*/
|
|
53
|
+
readonly idleTimeout?: Duration.Input
|
|
54
|
+
/** Initial viewport size. Providers default differently. */
|
|
55
|
+
readonly viewport?: { readonly width: number; readonly height: number }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Observation shapes
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
export type BoundingBox = {
|
|
63
|
+
readonly x: number
|
|
64
|
+
readonly y: number
|
|
65
|
+
readonly width: number
|
|
66
|
+
readonly height: number
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* One element returned by {@link BrowserSession.query}. Serializable data,
|
|
71
|
+
* not a live handle (handles go stale across navigation and do not cross a
|
|
72
|
+
* provider boundary cleanly).
|
|
73
|
+
*/
|
|
74
|
+
export type ElementInfo = {
|
|
75
|
+
/**
|
|
76
|
+
* Reference usable as a selector for a follow-up action. Valid until the
|
|
77
|
+
* next navigation; do not cache across `goto` or a page-changing click.
|
|
78
|
+
*/
|
|
79
|
+
readonly ref: string
|
|
80
|
+
readonly tag: string
|
|
81
|
+
readonly text?: string
|
|
82
|
+
readonly attributes: Readonly<Record<string, string>>
|
|
83
|
+
readonly box?: BoundingBox
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Accessibility-tree node returned by {@link BrowserSession.snapshot}. */
|
|
87
|
+
export type AxNode = {
|
|
88
|
+
readonly role: string
|
|
89
|
+
readonly name?: string
|
|
90
|
+
readonly value?: string
|
|
91
|
+
/**
|
|
92
|
+
* Reference usable as a selector for a follow-up action. Valid until the
|
|
93
|
+
* next navigation; do not cache across `goto` or a page-changing click.
|
|
94
|
+
*/
|
|
95
|
+
readonly ref?: string
|
|
96
|
+
readonly children: ReadonlyArray<AxNode>
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// Interaction shapes
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
export type ScrollDirection = "up" | "down" | "left" | "right"
|
|
104
|
+
|
|
105
|
+
export type ScrollOptions = {
|
|
106
|
+
readonly direction: ScrollDirection
|
|
107
|
+
/** Distance in CSS pixels. Provider default applies when omitted. */
|
|
108
|
+
readonly pixels?: number
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export type ScreenshotOptions = {
|
|
112
|
+
/** Clip to the first element matching this selector. */
|
|
113
|
+
readonly selector?: string
|
|
114
|
+
/** Capture the full scrollable page rather than the viewport. */
|
|
115
|
+
readonly fullPage?: boolean
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export type Cookie = {
|
|
119
|
+
readonly name: string
|
|
120
|
+
readonly value: string
|
|
121
|
+
readonly domain?: string
|
|
122
|
+
readonly path?: string
|
|
123
|
+
readonly expires?: number
|
|
124
|
+
readonly httpOnly?: boolean
|
|
125
|
+
readonly secure?: boolean
|
|
126
|
+
readonly sameSite?: "Strict" | "Lax" | "None"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Read / write the session's cookie jar. Needed for authenticated flows. */
|
|
130
|
+
export type CookieApi = {
|
|
131
|
+
readonly get: Effect.Effect<ReadonlyArray<Cookie>, BrowserError.BrowserError>
|
|
132
|
+
readonly set: (cookies: ReadonlyArray<Cookie>) => Effect.Effect<void, BrowserError.BrowserError>
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// Live session handle
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Live session handle. Actions take a CSS selector string (so `#id` is
|
|
141
|
+
* just `#id`). Methods are plain `Effect` values; disposal is the scope
|
|
142
|
+
* finalizer, not a method here (see {@link BrowserService}). Anything not
|
|
143
|
+
* covered by a verb is reachable via `evaluate`. Per-operation time budgets
|
|
144
|
+
* are the provider's default; wrap any call in `Effect.timeout` for a
|
|
145
|
+
* tighter bound.
|
|
146
|
+
*/
|
|
147
|
+
export type BrowserSession = {
|
|
148
|
+
readonly id: BrowserSessionId
|
|
149
|
+
|
|
150
|
+
// Navigation and waiting
|
|
151
|
+
readonly goto: (url: string) => Effect.Effect<void, BrowserError.BrowserError>
|
|
152
|
+
readonly waitFor: (selector: string) => Effect.Effect<void, BrowserError.BrowserError>
|
|
153
|
+
|
|
154
|
+
// Interaction
|
|
155
|
+
readonly click: (selector: string) => Effect.Effect<void, BrowserError.BrowserError>
|
|
156
|
+
readonly dblclick: (selector: string) => Effect.Effect<void, BrowserError.BrowserError>
|
|
157
|
+
readonly fill: (selector: string, text: string) => Effect.Effect<void, BrowserError.BrowserError>
|
|
158
|
+
readonly type: (selector: string, text: string) => Effect.Effect<void, BrowserError.BrowserError>
|
|
159
|
+
readonly press: (key: string) => Effect.Effect<void, BrowserError.BrowserError>
|
|
160
|
+
readonly hover: (selector: string) => Effect.Effect<void, BrowserError.BrowserError>
|
|
161
|
+
readonly focus: (selector: string) => Effect.Effect<void, BrowserError.BrowserError>
|
|
162
|
+
readonly select: (
|
|
163
|
+
selector: string,
|
|
164
|
+
value: string,
|
|
165
|
+
) => Effect.Effect<void, BrowserError.BrowserError>
|
|
166
|
+
readonly check: (selector: string) => Effect.Effect<void, BrowserError.BrowserError>
|
|
167
|
+
readonly uncheck: (selector: string) => Effect.Effect<void, BrowserError.BrowserError>
|
|
168
|
+
readonly scroll: (options: ScrollOptions) => Effect.Effect<void, BrowserError.BrowserError>
|
|
169
|
+
readonly scrollIntoView: (selector: string) => Effect.Effect<void, BrowserError.BrowserError>
|
|
170
|
+
|
|
171
|
+
// Observation
|
|
172
|
+
readonly content: (format?: PageFormat) => Effect.Effect<string, BrowserError.BrowserError>
|
|
173
|
+
readonly screenshot: (
|
|
174
|
+
options?: ScreenshotOptions,
|
|
175
|
+
) => Effect.Effect<Uint8Array, BrowserError.BrowserError>
|
|
176
|
+
readonly snapshot: Effect.Effect<AxNode, BrowserError.BrowserError>
|
|
177
|
+
readonly query: (
|
|
178
|
+
selector: string,
|
|
179
|
+
) => Effect.Effect<ReadonlyArray<ElementInfo>, BrowserError.BrowserError>
|
|
180
|
+
|
|
181
|
+
// State and escape hatch
|
|
182
|
+
readonly cookies: CookieApi
|
|
183
|
+
readonly evaluate: (script: string) => Effect.Effect<unknown, BrowserError.BrowserError>
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
// Service definition
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Cross-provider browser service.
|
|
192
|
+
*
|
|
193
|
+
* Lifetime: `create` and `attach` return a handle in `Scope.Scope`. The
|
|
194
|
+
* scope finalizer disposes the session (close the transport, destroy or
|
|
195
|
+
* detach the remote session, kill the local engine). Bind with
|
|
196
|
+
* `Effect.scoped` for a single call, or hold a wider scope to keep a
|
|
197
|
+
* session warm across many calls. This is why there is no `dispose()` on
|
|
198
|
+
* {@link BrowserSession}; the scope is the lifecycle handle.
|
|
199
|
+
*/
|
|
200
|
+
export type BrowserService = {
|
|
201
|
+
/**
|
|
202
|
+
* Provision a new session, bound to the caller's scope. Disposed on
|
|
203
|
+
* scope close.
|
|
204
|
+
*/
|
|
205
|
+
readonly create: (
|
|
206
|
+
request: CommonSessionRequest,
|
|
207
|
+
) => Effect.Effect<BrowserSession, BrowserError.BrowserError, Scope.Scope>
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Re-acquire an existing session by id. Scope-bound, but the finalizer
|
|
211
|
+
* detaches rather than destroying, for sessions kept warm beyond a
|
|
212
|
+
* single run.
|
|
213
|
+
*/
|
|
214
|
+
readonly attach: (
|
|
215
|
+
id: BrowserSessionId,
|
|
216
|
+
) => Effect.Effect<BrowserSession, BrowserError.BrowserError, Scope.Scope>
|
|
217
|
+
|
|
218
|
+
/** Enumerate sessions visible to the configured account / project. */
|
|
219
|
+
readonly list: Effect.Effect<ReadonlyArray<BrowserSessionRef>, BrowserError.BrowserError>
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Destroy a session from outside its owning scope. Most callers should
|
|
223
|
+
* let the scope finalizer handle this.
|
|
224
|
+
*/
|
|
225
|
+
readonly destroy: (id: BrowserSessionId) => Effect.Effect<void, BrowserError.BrowserError>
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export class Browser extends Context.Service<Browser, BrowserService>()(
|
|
229
|
+
"@betalyra/effect-uai/Browser",
|
|
230
|
+
) {}
|
|
231
|
+
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
// Free helpers
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Provision a session bound to the caller's scope. Drop the `Scope.Scope`
|
|
238
|
+
* requirement with `Effect.scoped`.
|
|
239
|
+
*/
|
|
240
|
+
export const create = (
|
|
241
|
+
request: CommonSessionRequest,
|
|
242
|
+
): Effect.Effect<BrowserSession, BrowserError.BrowserError, Browser | Scope.Scope> =>
|
|
243
|
+
Effect.flatMap(Browser.asEffect(), (s) => s.create(request))
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Re-acquire an existing session by id. Same scope semantics as `create`,
|
|
247
|
+
* but the finalizer detaches rather than destroying.
|
|
248
|
+
*/
|
|
249
|
+
export const attach = (
|
|
250
|
+
id: BrowserSessionId,
|
|
251
|
+
): Effect.Effect<BrowserSession, BrowserError.BrowserError, Browser | Scope.Scope> =>
|
|
252
|
+
Effect.flatMap(Browser.asEffect(), (s) => s.attach(id))
|
|
253
|
+
|
|
254
|
+
/** Enumerate sessions for the configured account / project. */
|
|
255
|
+
export const list: Effect.Effect<
|
|
256
|
+
ReadonlyArray<BrowserSessionRef>,
|
|
257
|
+
BrowserError.BrowserError,
|
|
258
|
+
Browser
|
|
259
|
+
> = Effect.flatMap(Browser.asEffect(), (s) => s.list)
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Destroy a session from outside its owning scope. Most callers should let
|
|
263
|
+
* the scope finalizer handle this automatically.
|
|
264
|
+
*/
|
|
265
|
+
export const destroy = (
|
|
266
|
+
id: BrowserSessionId,
|
|
267
|
+
): Effect.Effect<void, BrowserError.BrowserError, Browser> =>
|
|
268
|
+
Effect.flatMap(Browser.asEffect(), (s) => s.destroy(id))
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Data, Duration, Match } from "effect"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Failed to provision a browser session: the transport could not connect,
|
|
5
|
+
* a launched engine did not come up, or the provider refused to mint a
|
|
6
|
+
* session. Provider's structured reason on `reason`; the raw transport /
|
|
7
|
+
* SDK error on `raw`.
|
|
8
|
+
*/
|
|
9
|
+
export class BrowserCreateFailed extends Data.TaggedError("BrowserCreateFailed")<{
|
|
10
|
+
provider: string
|
|
11
|
+
reason?: string
|
|
12
|
+
raw: unknown
|
|
13
|
+
}> {}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The requested session id does not exist. Raised by `attach` / `destroy`
|
|
17
|
+
* against a stale or never-known id. Distinct from
|
|
18
|
+
* {@link BrowserSessionExpired}, which is a session that existed and has
|
|
19
|
+
* since ended.
|
|
20
|
+
*/
|
|
21
|
+
export class BrowserSessionNotFound extends Data.TaggedError("BrowserSessionNotFound")<{
|
|
22
|
+
provider: string
|
|
23
|
+
id: string
|
|
24
|
+
}> {}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* An operation was issued against a session that has ended: it reached its
|
|
28
|
+
* max lifetime (the create-time `timeout`), was destroyed, or the provider
|
|
29
|
+
* reaped it. The handle is dead; `create` or `attach` a live session.
|
|
30
|
+
* Distinct from {@link BrowserTimeout} (a single live operation was too
|
|
31
|
+
* slow).
|
|
32
|
+
*/
|
|
33
|
+
export class BrowserSessionExpired extends Data.TaggedError("BrowserSessionExpired")<{
|
|
34
|
+
provider: string
|
|
35
|
+
id: string
|
|
36
|
+
}> {}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A single operation exceeded its own time budget while the session was
|
|
40
|
+
* still alive (a navigation that never settled, a `waitFor` selector that
|
|
41
|
+
* never appeared). Carries the breached budget for observability.
|
|
42
|
+
*/
|
|
43
|
+
export class BrowserTimeout extends Data.TaggedError("BrowserTimeout")<{
|
|
44
|
+
provider: string
|
|
45
|
+
operation: "create" | "attach" | "navigate" | "action" | "query"
|
|
46
|
+
budget?: Duration.Duration
|
|
47
|
+
raw?: unknown
|
|
48
|
+
}> {}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* An interaction or observation failed at the transport / protocol layer:
|
|
52
|
+
* a selector resolved to nothing, an element was not actionable, an
|
|
53
|
+
* `evaluate` script threw, a navigation errored. `selector` and `reason`
|
|
54
|
+
* carry what is known.
|
|
55
|
+
*/
|
|
56
|
+
export class BrowserActionFailed extends Data.TaggedError("BrowserActionFailed")<{
|
|
57
|
+
provider: string
|
|
58
|
+
operation: "navigate" | "interact" | "observe" | "evaluate" | "cookies"
|
|
59
|
+
selector?: string
|
|
60
|
+
reason?: string
|
|
61
|
+
raw: unknown
|
|
62
|
+
}> {}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Provider rejected the request because an account / project quota or
|
|
66
|
+
* concurrency limit was exceeded. Separate from {@link BrowserCreateFailed}
|
|
67
|
+
* so callers can back off and retry.
|
|
68
|
+
*/
|
|
69
|
+
export class BrowserQuotaExceeded extends Data.TaggedError("BrowserQuotaExceeded")<{
|
|
70
|
+
provider: string
|
|
71
|
+
reason?: string
|
|
72
|
+
raw: unknown
|
|
73
|
+
}> {}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Caller's request shape is malformed for the wired provider. Adapters
|
|
77
|
+
* should reject at the request-schema layer where possible; this is the
|
|
78
|
+
* runtime fallback.
|
|
79
|
+
*/
|
|
80
|
+
export class BrowserInvalidRequest extends Data.TaggedError("BrowserInvalidRequest")<{
|
|
81
|
+
provider: string
|
|
82
|
+
param?: string
|
|
83
|
+
reason?: string
|
|
84
|
+
raw?: unknown
|
|
85
|
+
}> {}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Transport could not authenticate against the provider. Subtype mirrors
|
|
89
|
+
* the model-side `AiError.AuthFailed` shape for consistency.
|
|
90
|
+
*/
|
|
91
|
+
export type BrowserAuthSubtype = "auth" | "permission" | "billing" | "quota"
|
|
92
|
+
|
|
93
|
+
export class BrowserAuthFailed extends Data.TaggedError("BrowserAuthFailed")<{
|
|
94
|
+
provider: string
|
|
95
|
+
subtype: BrowserAuthSubtype
|
|
96
|
+
raw: unknown
|
|
97
|
+
}> {}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The wired provider does not implement the requested operation for these
|
|
101
|
+
* inputs. Distinct from {@link BrowserInvalidRequest} (malformed request)
|
|
102
|
+
* and from compile-time capability markers gating blanket provider gaps.
|
|
103
|
+
*/
|
|
104
|
+
export class BrowserUnsupported extends Data.TaggedError("BrowserUnsupported")<{
|
|
105
|
+
provider: string
|
|
106
|
+
capability: string
|
|
107
|
+
reason?: string
|
|
108
|
+
}> {}
|
|
109
|
+
|
|
110
|
+
export type BrowserError =
|
|
111
|
+
| BrowserCreateFailed
|
|
112
|
+
| BrowserSessionNotFound
|
|
113
|
+
| BrowserSessionExpired
|
|
114
|
+
| BrowserTimeout
|
|
115
|
+
| BrowserActionFailed
|
|
116
|
+
| BrowserQuotaExceeded
|
|
117
|
+
| BrowserInvalidRequest
|
|
118
|
+
| BrowserAuthFailed
|
|
119
|
+
| BrowserUnsupported
|
|
120
|
+
|
|
121
|
+
const withReason = (base: string, reason: string | undefined): string =>
|
|
122
|
+
reason === undefined ? base : `${base}: ${reason}`
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Short human-readable description of an error, for logs and model-facing
|
|
126
|
+
* failure messages. Prose, not a contract; branch on `_tag` instead.
|
|
127
|
+
*/
|
|
128
|
+
export const describe: (e: BrowserError) => string = Match.type<BrowserError>().pipe(
|
|
129
|
+
Match.discriminatorsExhaustive("_tag")({
|
|
130
|
+
BrowserCreateFailed: (e) => withReason("the browser session could not be created", e.reason),
|
|
131
|
+
BrowserSessionNotFound: () => "the browser session does not exist",
|
|
132
|
+
BrowserSessionExpired: () => "the browser session has expired",
|
|
133
|
+
BrowserTimeout: (e) => `the ${e.operation} operation timed out`,
|
|
134
|
+
BrowserActionFailed: (e) =>
|
|
135
|
+
withReason(
|
|
136
|
+
`the ${e.operation} failed${e.selector === undefined ? "" : ` on ${e.selector}`}`,
|
|
137
|
+
e.reason,
|
|
138
|
+
),
|
|
139
|
+
BrowserQuotaExceeded: (e) => withReason("the browser quota is exceeded", e.reason),
|
|
140
|
+
BrowserInvalidRequest: (e) => withReason("the request was invalid", e.reason),
|
|
141
|
+
BrowserAuthFailed: (e) => `browser authentication failed (${e.subtype})`,
|
|
142
|
+
BrowserUnsupported: (e) =>
|
|
143
|
+
withReason(`this browser does not support ${e.capability}`, e.reason),
|
|
144
|
+
}),
|
|
145
|
+
)
|