@effect-uai/core 0.11.0 → 0.12.1
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-Bf1pq4BB.d.mts → AiError-eLV9t1lU.d.mts} +12 -16
- package/dist/AiError-eLV9t1lU.d.mts.map +1 -0
- package/dist/{Audio-CR7CSljJ.d.mts → Audio-60lKKHxS.d.mts} +4 -5
- package/dist/Audio-60lKKHxS.d.mts.map +1 -0
- package/dist/{Image-C4vuBmu0.d.mts → Image-BK8-4H6s.d.mts} +12 -14
- package/dist/Image-BK8-4H6s.d.mts.map +1 -0
- package/dist/{Items-BWNu6VGp.d.mts → Items-SfUabNuB.d.mts} +92 -81
- package/dist/Items-SfUabNuB.d.mts.map +1 -0
- package/dist/{Loop-D8nWpowc.d.mts → Loop-B54AB1ME.d.mts} +19 -21
- package/dist/Loop-B54AB1ME.d.mts.map +1 -0
- package/dist/{Media-D_CpcM1Z.d.mts → Media-DwgpifPD.d.mts} +1 -1
- package/dist/Media-DwgpifPD.d.mts.map +1 -0
- package/dist/{SandboxError-CIrVDOMV.d.mts → SandboxError-BN8b28Bh.d.mts} +11 -15
- package/dist/SandboxError-BN8b28Bh.d.mts.map +1 -0
- package/dist/{StructuredFormat-DHaUJBdd.d.mts → StructuredFormat-BlZhyOsi.d.mts} +3 -7
- package/dist/StructuredFormat-BlZhyOsi.d.mts.map +1 -0
- package/dist/{Tool-CXFHCQrT.d.mts → Tool-DE-rnvkw.d.mts} +5 -8
- package/dist/Tool-DE-rnvkw.d.mts.map +1 -0
- package/dist/{ToolEvent-nVijCrvm.d.mts → ToolEvent-D99MGQYH.d.mts} +56 -63
- package/dist/ToolEvent-D99MGQYH.d.mts.map +1 -0
- package/dist/{ToolResult-CQqKkcDa.d.mts → ToolResult-Cj8ChodL.d.mts} +49 -54
- package/dist/ToolResult-Cj8ChodL.d.mts.map +1 -0
- package/dist/{Toolkit-BYPwwu-j.d.mts → Toolkit-BEZrNPTI.d.mts} +13 -16
- package/dist/Toolkit-BEZrNPTI.d.mts.map +1 -0
- package/dist/browser/Browser.d.mts +11 -7
- package/dist/browser/Browser.d.mts.map +1 -1
- package/dist/browser/Browser.mjs +1 -1
- package/dist/browser/Browser.mjs.map +1 -1
- package/dist/browser/BrowserError.d.mts +9 -13
- package/dist/browser/BrowserError.d.mts.map +1 -1
- package/dist/browser/BrowserError.mjs +1 -1
- package/dist/browser/BrowserError.mjs.map +1 -1
- package/dist/browser/BrowserTool.d.mts +33 -36
- package/dist/browser/BrowserTool.d.mts.map +1 -1
- package/dist/browser/BrowserTool.mjs +1 -1
- package/dist/browser/BrowserTool.mjs.map +1 -1
- package/dist/capabilities/Capabilities.d.mts +8 -6
- package/dist/capabilities/Capabilities.d.mts.map +1 -1
- package/dist/capabilities/Capabilities.mjs +1 -1
- package/dist/capabilities/Capabilities.mjs.map +1 -1
- package/dist/dist-BaenoU1A.mjs +145 -0
- package/dist/dist-BaenoU1A.mjs.map +1 -0
- package/dist/{dist-C0m0QnUP.mjs → dist-nohcnkD6.mjs} +16 -15
- package/dist/dist-nohcnkD6.mjs.map +1 -0
- package/dist/domain/AiError.d.mts +1 -1
- package/dist/domain/AiError.mjs +1 -1
- package/dist/domain/AiError.mjs.map +1 -1
- package/dist/domain/Audio.d.mts +1 -1
- package/dist/domain/Audio.mjs +1 -1
- package/dist/domain/Citation.d.mts +0 -2
- package/dist/domain/Citation.d.mts.map +1 -1
- package/dist/domain/Citation.mjs +1 -1
- package/dist/domain/Image.d.mts +1 -1
- package/dist/domain/Image.mjs +1 -1
- package/dist/domain/Image.mjs.map +1 -1
- package/dist/domain/Items.d.mts +1 -1
- package/dist/domain/Items.mjs +16 -2
- package/dist/domain/Items.mjs.map +1 -1
- package/dist/domain/Media.d.mts +1 -1
- package/dist/domain/Media.mjs +1 -1
- package/dist/domain/Music.d.mts +18 -11
- package/dist/domain/Music.d.mts.map +1 -1
- package/dist/domain/Music.mjs +1 -1
- package/dist/domain/Music.mjs.map +1 -1
- package/dist/domain/Research.d.mts +3 -4
- package/dist/domain/Research.d.mts.map +1 -1
- package/dist/domain/Research.mjs +1 -1
- package/dist/domain/Transcript.d.mts +2 -3
- package/dist/domain/Transcript.d.mts.map +1 -1
- package/dist/domain/Transcript.mjs +1 -1
- package/dist/domain/Turn.d.mts +144 -164
- package/dist/domain/Turn.d.mts.map +1 -1
- package/dist/domain/Turn.mjs +1 -1
- package/dist/domain/Turn.mjs.map +1 -1
- package/dist/domain/Turn.test.d.mts +1 -1
- package/dist/domain/Turn.test.mjs +28 -18
- package/dist/domain/Turn.test.mjs.map +1 -1
- package/dist/embedding-model/Embedding.d.mts +1 -3
- package/dist/embedding-model/Embedding.d.mts.map +1 -1
- package/dist/embedding-model/Embedding.mjs +1 -1
- package/dist/embedding-model/EmbeddingModel.d.mts +1 -3
- package/dist/embedding-model/EmbeddingModel.d.mts.map +1 -1
- package/dist/embedding-model/EmbeddingModel.mjs +1 -1
- package/dist/embedding-model/EmbeddingModel.mjs.map +1 -1
- package/dist/embedding-model/EmbeddingModel.test.d.mts +1 -1
- package/dist/embedding-model/EmbeddingModel.test.mjs +14 -9
- package/dist/embedding-model/EmbeddingModel.test.mjs.map +1 -1
- package/dist/index.d.mts +12 -12
- package/dist/job/Job.d.mts +4 -4
- package/dist/job/Job.d.mts.map +1 -1
- package/dist/job/Job.mjs +1 -1
- package/dist/job/Job.mjs.map +1 -1
- package/dist/language-model/LanguageModel.d.mts +4 -6
- package/dist/language-model/LanguageModel.d.mts.map +1 -1
- package/dist/language-model/LanguageModel.mjs +1 -1
- package/dist/language-model/LanguageModel.mjs.map +1 -1
- package/dist/language-model/LanguageModel.test.d.mts +1 -1
- package/dist/language-model/LanguageModel.test.mjs +6 -3
- package/dist/language-model/LanguageModel.test.mjs.map +1 -1
- package/dist/loop/Loop.d.mts +1 -1
- package/dist/loop/Loop.mjs +1 -1
- package/dist/loop/Loop.mjs.map +1 -1
- package/dist/loop/Loop.test.d.mts +1 -1
- package/dist/loop/Loop.test.mjs +60 -34
- package/dist/loop/Loop.test.mjs.map +1 -1
- package/dist/{magic-string.es-Dk-MMA0F.mjs → magic-string.es-CB2hOw9v.mjs} +14 -13
- package/dist/{magic-string.es-Dk-MMA0F.mjs.map → magic-string.es-CB2hOw9v.mjs.map} +1 -1
- package/dist/math/Vector.d.mts +0 -2
- package/dist/math/Vector.d.mts.map +1 -1
- package/dist/math/Vector.mjs +1 -1
- package/dist/math/Vector.mjs.map +1 -1
- package/dist/music-generator/MusicGenerator.d.mts +2 -4
- package/dist/music-generator/MusicGenerator.d.mts.map +1 -1
- package/dist/music-generator/MusicGenerator.mjs +1 -1
- package/dist/music-generator/MusicGenerator.mjs.map +1 -1
- package/dist/music-generator/MusicGenerator.test.d.mts +1 -1
- package/dist/music-generator/MusicGenerator.test.mjs +19 -10
- package/dist/music-generator/MusicGenerator.test.mjs.map +1 -1
- package/dist/observability/Metrics.d.mts +21 -10
- package/dist/observability/Metrics.d.mts.map +1 -1
- package/dist/observability/Metrics.mjs +31 -8
- package/dist/observability/Metrics.mjs.map +1 -1
- package/dist/observability/Metrics.test.d.mts +1 -1
- package/dist/observability/Metrics.test.mjs +87 -23
- package/dist/observability/Metrics.test.mjs.map +1 -1
- package/dist/observability/Telemetry.d.mts +2 -3
- package/dist/observability/Telemetry.d.mts.map +1 -1
- package/dist/observability/Telemetry.mjs +1 -1
- package/dist/observability/Telemetry.mjs.map +1 -1
- package/dist/observability/Telemetry.test.d.mts +1 -1
- package/dist/observability/Telemetry.test.mjs +15 -7
- package/dist/observability/Telemetry.test.mjs.map +1 -1
- package/dist/research/DeepResearch.d.mts +1 -3
- package/dist/research/DeepResearch.d.mts.map +1 -1
- package/dist/research/DeepResearch.mjs +1 -1
- package/dist/research/DeepResearch.mjs.map +1 -1
- package/dist/retry/Retry.d.mts +4 -8
- package/dist/retry/Retry.d.mts.map +1 -1
- package/dist/retry/Retry.mjs +31 -2
- package/dist/retry/Retry.mjs.map +1 -1
- package/dist/retry/Retry.test.d.mts +1 -1
- package/dist/retry/Retry.test.mjs +6 -3
- package/dist/retry/Retry.test.mjs.map +1 -1
- package/dist/sandbox/Memory.d.mts +0 -2
- package/dist/sandbox/Memory.d.mts.map +1 -1
- package/dist/sandbox/Memory.mjs +4 -4
- package/dist/sandbox/Memory.mjs.map +1 -1
- package/dist/sandbox/Sandbox.d.mts +144 -156
- package/dist/sandbox/Sandbox.d.mts.map +1 -1
- package/dist/sandbox/Sandbox.mjs +1 -1
- package/dist/sandbox/Sandbox.mjs.map +1 -1
- package/dist/sandbox/SandboxError.d.mts +1 -1
- package/dist/sandbox/SandboxError.mjs +1 -1
- package/dist/sandbox/SandboxError.mjs.map +1 -1
- package/dist/sandbox/SandboxImage.d.mts +0 -2
- package/dist/sandbox/SandboxImage.d.mts.map +1 -1
- package/dist/sandbox/SandboxImage.mjs +1 -1
- package/dist/sandbox/SandboxImage.mjs.map +1 -1
- package/dist/sandbox/SandboxNetwork.d.mts +0 -2
- package/dist/sandbox/SandboxNetwork.d.mts.map +1 -1
- package/dist/sandbox/SandboxNetwork.mjs +1 -1
- package/dist/sandbox/SandboxNetwork.mjs.map +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.d.mts +8 -7
- package/dist/speech-synthesizer/SpeechSynthesizer.d.mts.map +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.mjs +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.mjs.map +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.test.d.mts +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +31 -16
- package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs.map +1 -1
- package/dist/streaming/JSONL.d.mts +1 -5
- package/dist/streaming/JSONL.d.mts.map +1 -1
- package/dist/streaming/JSONL.mjs +1 -1
- package/dist/streaming/JSONL.mjs.map +1 -1
- package/dist/streaming/JSONL.test.d.mts +1 -1
- package/dist/streaming/JSONL.test.mjs +16 -8
- package/dist/streaming/JSONL.test.mjs.map +1 -1
- package/dist/streaming/Lines.d.mts +0 -2
- package/dist/streaming/Lines.d.mts.map +1 -1
- package/dist/streaming/Lines.mjs +1 -1
- package/dist/streaming/Lines.mjs.map +1 -1
- package/dist/streaming/SSE.d.mts +0 -2
- package/dist/streaming/SSE.d.mts.map +1 -1
- package/dist/streaming/SSE.mjs +1 -1
- package/dist/streaming/SSE.mjs.map +1 -1
- package/dist/streaming/SSE.test.d.mts +1 -1
- package/dist/streaming/SSE.test.mjs +34 -17
- package/dist/streaming/SSE.test.mjs.map +1 -1
- package/dist/structured-format/StructuredFormat.d.mts +1 -1
- package/dist/structured-format/StructuredFormat.mjs +1 -1
- package/dist/structured-format/StructuredFormat.mjs.map +1 -1
- package/dist/structured-format/StructuredFormat.test.d.mts +1 -1
- package/dist/structured-format/StructuredFormat.test.mjs +7 -4
- package/dist/structured-format/StructuredFormat.test.mjs.map +1 -1
- package/dist/{dist-CcRV3-A5.mjs → test.DNmyFkvJ-YPzDR0Co.mjs} +1644 -5172
- package/dist/test.DNmyFkvJ-YPzDR0Co.mjs.map +1 -0
- package/dist/testing/MockMusicGenerator.d.mts +4 -3
- package/dist/testing/MockMusicGenerator.d.mts.map +1 -1
- package/dist/testing/MockMusicGenerator.mjs.map +1 -1
- package/dist/testing/MockProvider.d.mts +1 -2
- package/dist/testing/MockProvider.d.mts.map +1 -1
- package/dist/testing/MockProvider.mjs.map +1 -1
- package/dist/testing/MockSandbox.d.mts +256 -290
- package/dist/testing/MockSandbox.d.mts.map +1 -1
- package/dist/testing/MockSandbox.mjs.map +1 -1
- package/dist/testing/MockSpeechSynthesizer.d.mts +10 -6
- package/dist/testing/MockSpeechSynthesizer.d.mts.map +1 -1
- package/dist/testing/MockSpeechSynthesizer.mjs.map +1 -1
- package/dist/testing/MockTranscriber.d.mts +3 -2
- package/dist/testing/MockTranscriber.d.mts.map +1 -1
- package/dist/testing/MockTranscriber.mjs.map +1 -1
- package/dist/tool/Approval.d.mts +31 -36
- package/dist/tool/Approval.d.mts.map +1 -1
- package/dist/tool/Approval.mjs +1 -1
- package/dist/tool/Approval.mjs.map +1 -1
- package/dist/tool/Approval.test.d.mts +1 -1
- package/dist/tool/Approval.test.mjs +37 -24
- package/dist/tool/Approval.test.mjs.map +1 -1
- package/dist/tool/HistoryCheck.d.mts +2 -4
- package/dist/tool/HistoryCheck.d.mts.map +1 -1
- package/dist/tool/HistoryCheck.mjs +1 -1
- package/dist/tool/HistoryCheck.mjs.map +1 -1
- package/dist/tool/Tool.d.mts +1 -1
- package/dist/tool/Tool.mjs +4 -4
- package/dist/tool/Tool.mjs.map +1 -1
- package/dist/tool/Tool.test.d.mts +1 -1
- package/dist/tool/Tool.test.mjs +23 -11
- package/dist/tool/Tool.test.mjs.map +1 -1
- package/dist/tool/ToolEvent.d.mts +1 -1
- package/dist/tool/ToolEvent.mjs +1 -1
- package/dist/tool/ToolEvent.mjs.map +1 -1
- package/dist/tool/ToolResult.d.mts +1 -1
- package/dist/tool/ToolResult.mjs +1 -1
- package/dist/tool/ToolResult.mjs.map +1 -1
- package/dist/tool/Toolkit.d.mts +1 -1
- package/dist/tool/Toolkit.mjs +4 -3
- package/dist/tool/Toolkit.mjs.map +1 -1
- package/dist/tool/Toolkit.test.d.mts +1 -1
- package/dist/tool/Toolkit.test.mjs +40 -21
- package/dist/tool/Toolkit.test.mjs.map +1 -1
- package/dist/transcriber/Transcriber.d.mts +6 -6
- package/dist/transcriber/Transcriber.d.mts.map +1 -1
- package/dist/transcriber/Transcriber.mjs +1 -1
- package/dist/transcriber/Transcriber.mjs.map +1 -1
- package/dist/transcriber/Transcriber.test.d.mts +1 -1
- package/dist/transcriber/Transcriber.test.mjs +20 -10
- package/dist/transcriber/Transcriber.test.mjs.map +1 -1
- package/dist/utils-CiH_DjFW.mjs +3918 -0
- package/dist/utils-CiH_DjFW.mjs.map +1 -0
- package/dist/web-read/WebRead.d.mts +15 -9
- package/dist/web-read/WebRead.d.mts.map +1 -1
- package/dist/web-read/WebRead.mjs +1 -1
- package/dist/web-read/WebRead.mjs.map +1 -1
- package/dist/web-read/WebReadTool.d.mts +4 -5
- package/dist/web-read/WebReadTool.d.mts.map +1 -1
- package/dist/web-read/WebReadTool.mjs +1 -1
- package/dist/web-read/WebReadTool.mjs.map +1 -1
- package/dist/web-search/WebSearch.d.mts +22 -13
- package/dist/web-search/WebSearch.d.mts.map +1 -1
- package/dist/web-search/WebSearch.mjs +1 -1
- package/dist/web-search/WebSearch.mjs.map +1 -1
- package/dist/web-search/WebSearchTool.d.mts +6 -6
- package/dist/web-search/WebSearchTool.d.mts.map +1 -1
- package/dist/web-search/WebSearchTool.mjs +1 -1
- package/dist/web-search/WebSearchTool.mjs.map +1 -1
- package/package.json +4 -4
- package/src/domain/Items.ts +18 -0
- package/src/loop/Loop.test.ts +13 -14
- package/src/loop/Loop.ts +74 -76
- package/src/observability/Metrics.test.ts +65 -0
- package/src/observability/Metrics.ts +53 -11
- package/src/retry/Retry.ts +44 -3
- package/src/testing/MockProvider.ts +8 -9
- package/src/tool/Tool.ts +13 -13
- package/dist/AiError-Bf1pq4BB.d.mts.map +0 -1
- package/dist/Audio-CR7CSljJ.d.mts.map +0 -1
- package/dist/Image-C4vuBmu0.d.mts.map +0 -1
- package/dist/Items-BWNu6VGp.d.mts.map +0 -1
- package/dist/Loop-D8nWpowc.d.mts.map +0 -1
- package/dist/Media-D_CpcM1Z.d.mts.map +0 -1
- package/dist/SandboxError-CIrVDOMV.d.mts.map +0 -1
- package/dist/StructuredFormat-DHaUJBdd.d.mts.map +0 -1
- package/dist/Tool-CXFHCQrT.d.mts.map +0 -1
- package/dist/ToolEvent-nVijCrvm.d.mts.map +0 -1
- package/dist/ToolResult-CQqKkcDa.d.mts.map +0 -1
- package/dist/Toolkit-BYPwwu-j.d.mts.map +0 -1
- package/dist/dist-C0m0QnUP.mjs.map +0 -1
- package/dist/dist-CcRV3-A5.mjs.map +0 -1
- /package/dist/{chunk-uyGKjUfl.mjs → rolldown-runtime-EAsCxrDo.mjs} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockSandbox.d.mts","names":[],"sources":["../../src/testing/MockSandbox.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"MockSandbox.d.mts","names":[],"sources":["../../src/testing/MockSandbox.ts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockSandbox.mjs","names":["SandboxError.SandboxInvalidRequest","SandboxError.SandboxExecFailed","Arr"],"sources":["../../src/testing/MockSandbox.ts"],"sourcesContent":["import { Array as Arr, Data, Effect, Layer, Ref, Stream } from \"effect\"\nimport {\n ExecEvent,\n Sandbox,\n SandboxCustomImage,\n SandboxHostnameAllowlist,\n SandboxKernelSession,\n SandboxPauseResume,\n SandboxPortExposure,\n SandboxPty,\n SandboxSecretInjection,\n SandboxSnapshots,\n SandboxVolumes,\n type CommonCreateRequest,\n type CommonExecRequest,\n type CommonSpawnRequest,\n type ExecResult,\n type FileEntry,\n type ProcessHandle,\n SandboxId,\n type SandboxInstance,\n type SandboxRef,\n type SandboxService,\n SnapshotId,\n VolumeId,\n} from \"../sandbox/Sandbox.js\"\nimport * as SandboxError from \"../sandbox/SandboxError.js\"\n\n// ---------------------------------------------------------------------------\n// Call log\n// ---------------------------------------------------------------------------\n\n/**\n * One entry per side-effecting method call. Tests filter via\n * `Call.$is(...)` / `Call.$match`.\n */\nexport type Call = Data.TaggedEnum<{\n Create: { readonly request: CommonCreateRequest }\n Attach: { readonly id: SandboxId }\n Destroy: { readonly id: SandboxId }\n Exec: { readonly id: SandboxId; readonly request: CommonExecRequest }\n ExecStream: { readonly id: SandboxId; readonly request: CommonExecRequest }\n Spawn: { readonly id: SandboxId; readonly request: CommonSpawnRequest }\n FileRead: { readonly id: SandboxId; readonly path: string }\n FileWrite: {\n readonly id: SandboxId\n readonly path: string\n readonly contents: Uint8Array | string\n }\n FileRemove: { readonly id: SandboxId; readonly path: string }\n FileMkdir: { readonly id: SandboxId; readonly path: string }\n FileList: { readonly id: SandboxId; readonly path: string }\n FileExists: { readonly id: SandboxId; readonly path: string }\n ExposePort: { readonly id: SandboxId; readonly port: number }\n SnapshotCreate: { readonly id: SandboxId; readonly name?: string }\n SnapshotDestroy: { readonly id: SnapshotId }\n VolumeCreate: { readonly name: string; readonly quotaBytes?: number }\n VolumeDestroy: { readonly id: VolumeId }\n}>\n\nexport const Call = Data.taggedEnum<Call>()\n\nexport type MockSandboxRecorder = {\n readonly calls: ReadonlyArray<Call>\n}\n\n// ---------------------------------------------------------------------------\n// Script\n// ---------------------------------------------------------------------------\n\n/**\n * What this mock should hand back per call. Each method has its own\n * scripted queue and a sensible default when the queue is empty.\n * The filesystem is an in-memory mutable map seeded by `files`.\n */\nexport type MockSandboxScript = {\n readonly id?: string\n readonly execs?: ReadonlyArray<ExecResult>\n readonly streams?: ReadonlyArray<ReadonlyArray<ExecEvent>>\n readonly spawns?: ReadonlyArray<ProcessHandle>\n readonly files?: Readonly<Record<string, Uint8Array | string>>\n readonly sandboxes?: ReadonlyArray<SandboxRef>\n readonly snapshots?: ReadonlyArray<{ readonly id: SnapshotId; readonly name?: string }>\n readonly volumes?: ReadonlyArray<{ readonly id: VolumeId; readonly name: string }>\n readonly portUrl?: (port: number) => string\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst defaultExec: ExecResult = { exitCode: 0, stdout: \"\", stderr: \"\", durationMs: 0 }\n\nconst toBytes = (contents: Uint8Array | string) =>\n typeof contents === \"string\" ? new TextEncoder().encode(contents) : contents\n\nconst seedFs = (files: MockSandboxScript[\"files\"]) =>\n new Map(Object.entries(files ?? {}).map(([k, v]) => [k, toBytes(v)]))\n\nconst exhausted = (method: string, scripted: number, attempt: number) =>\n new SandboxError.SandboxInvalidRequest({\n provider: \"mock\",\n reason: `MockSandbox exhausted: ${scripted} ${method}(s) scripted, but call ${attempt} was made`,\n })\n\nconst consume = <A>(\n cursor: Ref.Ref<number>,\n scripted: ReadonlyArray<A>,\n onMiss: (attempt: number) => Effect.Effect<A, SandboxError.SandboxError>,\n) =>\n Ref.getAndUpdate(cursor, (n) => n + 1).pipe(\n Effect.flatMap((i) => (i < scripted.length ? Effect.succeed(scripted[i]!) : onMiss(i + 1))),\n )\n\n// ---------------------------------------------------------------------------\n// Service builder\n// ---------------------------------------------------------------------------\n\nconst buildInstance = (\n id: SandboxId,\n script: MockSandboxScript,\n fs: Ref.Ref<Map<string, Uint8Array>>,\n execCursor: Ref.Ref<number>,\n streamCursor: Ref.Ref<number>,\n spawnCursor: Ref.Ref<number>,\n record: (call: Call) => Effect.Effect<void>,\n): SandboxInstance => ({\n id,\n\n exec: (request) =>\n record(Call.Exec({ id, request })).pipe(\n Effect.andThen(consume(execCursor, script.execs ?? [], () => Effect.succeed(defaultExec))),\n ),\n\n execStream: (request) =>\n Stream.unwrap(\n record(Call.ExecStream({ id, request })).pipe(\n Effect.andThen(\n consume(streamCursor, script.streams ?? [], () =>\n Effect.succeed<ReadonlyArray<ExecEvent>>([\n ExecEvent.Complete({ exitCode: 0, durationMs: 0 }),\n ]),\n ),\n ),\n Effect.map(\n (events) =>\n Stream.fromIterable(events) as Stream.Stream<ExecEvent, SandboxError.SandboxError>,\n ),\n ),\n ),\n\n spawn: (request) =>\n record(Call.Spawn({ id, request })).pipe(\n Effect.andThen(\n consume(spawnCursor, script.spawns ?? [], (n) =>\n Effect.fail(exhausted(\"spawn\", (script.spawns ?? []).length, n)),\n ),\n ),\n Effect.tap((handle) => Effect.addFinalizer(() => Effect.orDie(handle.kill))),\n ),\n\n files: {\n read: (path) =>\n record(Call.FileRead({ id, path })).pipe(\n Effect.andThen(Ref.get(fs)),\n Effect.flatMap((map) =>\n map.has(path)\n ? Effect.succeed(map.get(path)!)\n : Effect.fail(\n new SandboxError.SandboxExecFailed({\n provider: \"mock\",\n reason: `ENOENT: ${path}`,\n raw: undefined,\n }),\n ),\n ),\n ),\n\n write: (path, contents) =>\n record(Call.FileWrite({ id, path, contents })).pipe(\n Effect.andThen(Ref.update(fs, (m) => new Map(m).set(path, toBytes(contents)))),\n ),\n\n remove: (path) =>\n record(Call.FileRemove({ id, path })).pipe(\n Effect.andThen(\n Ref.update(fs, (m) => {\n const next = new Map(m)\n next.delete(path)\n return next\n }),\n ),\n ),\n\n mkdir: (path) => record(Call.FileMkdir({ id, path })),\n\n list: (path) =>\n record(Call.FileList({ id, path })).pipe(\n Effect.andThen(Ref.get(fs)),\n Effect.map((map) => {\n const prefix = path.endsWith(\"/\") ? path : `${path}/`\n const out: Array<FileEntry> = []\n for (const k of map.keys()) {\n if (k.startsWith(prefix)) out.push({ path: k, kind: \"file\" })\n }\n return out\n }),\n ),\n\n exists: (path) =>\n record(Call.FileExists({ id, path })).pipe(\n Effect.andThen(Ref.get(fs)),\n Effect.map((map) => map.has(path)),\n ),\n },\n})\n\nconst buildService = (\n script: MockSandboxScript,\n record: (call: Call) => Effect.Effect<void>,\n): SandboxService => {\n const id = SandboxId(script.id ?? \"mock-sandbox\")\n\n const acquireInstance = (sandboxId: SandboxId, onClose: Effect.Effect<void>) =>\n Effect.gen(function* () {\n const fs = yield* Ref.make(seedFs(script.files))\n const execCursor = yield* Ref.make(0)\n const streamCursor = yield* Ref.make(0)\n const spawnCursor = yield* Ref.make(0)\n yield* Effect.addFinalizer(() => onClose)\n return buildInstance(sandboxId, script, fs, execCursor, streamCursor, spawnCursor, record)\n })\n\n return {\n create: (request) =>\n record(Call.Create({ request })).pipe(\n Effect.andThen(acquireInstance(id, record(Call.Destroy({ id })))),\n ),\n\n attach: (sandboxId) =>\n record(Call.Attach({ id: sandboxId })).pipe(\n // Attach detaches on scope close (no destroy).\n Effect.andThen(acquireInstance(sandboxId, Effect.void)),\n ),\n\n list: Effect.succeed(script.sandboxes ?? []),\n\n destroy: (sandboxId) => record(Call.Destroy({ id: sandboxId })),\n\n snapshots: {\n create: (_from, name) =>\n record(\n name === undefined ? Call.SnapshotCreate({ id }) : Call.SnapshotCreate({ id, name }),\n ).pipe(\n Effect.as(SnapshotId(`mock-snapshot-${name ?? Math.random().toString(36).slice(2, 8)}`)),\n ),\n destroy: (sid) => record(Call.SnapshotDestroy({ id: sid })),\n list: Effect.succeed(script.snapshots ?? []),\n },\n\n volumes: {\n create: (name, options) =>\n record(\n options?.quotaBytes === undefined\n ? Call.VolumeCreate({ name })\n : Call.VolumeCreate({ name, quotaBytes: options.quotaBytes }),\n ).pipe(Effect.as(VolumeId(`mock-volume-${name}`))),\n destroy: (vid) => record(Call.VolumeDestroy({ id: vid })),\n list: Effect.succeed(script.volumes ?? []),\n },\n\n ports: {\n expose: (instance, port) =>\n record(Call.ExposePort({ id: instance.id, port })).pipe(\n Effect.as({ url: (script.portUrl ?? ((p) => `http://mock.local:${p}`))(port) }),\n ),\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Recorder\n// ---------------------------------------------------------------------------\n\nconst makeRecorderUnsafe = () => {\n const ref = Ref.makeUnsafe<ReadonlyArray<Call>>([])\n return {\n record: (call: Call) => Ref.update(ref, Arr.append(call)),\n recorder: Ref.get(ref).pipe(Effect.map((calls): MockSandboxRecorder => ({ calls }))),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nconst allCapabilities = Layer.mergeAll(\n Layer.succeed(SandboxHostnameAllowlist, undefined),\n Layer.succeed(SandboxSecretInjection, undefined),\n Layer.succeed(SandboxSnapshots, undefined),\n Layer.succeed(SandboxPauseResume, undefined),\n Layer.succeed(SandboxVolumes, undefined),\n Layer.succeed(SandboxKernelSession, undefined),\n Layer.succeed(SandboxPty, undefined),\n Layer.succeed(SandboxCustomImage, undefined),\n Layer.succeed(SandboxPortExposure, undefined),\n)\n\n/**\n * Layer that registers a `MockSandbox` against the `Sandbox` tag, with\n * every capability marker shipped — use when the code under test\n * exercises any of them.\n */\nexport const layer = (script: MockSandboxScript = {}) => {\n const { record, recorder } = makeRecorderUnsafe()\n return {\n layer: Layer.merge(Layer.succeed(Sandbox, buildService(script, record)), allCapabilities),\n recorder,\n }\n}\n\n/**\n * Layer with the `Sandbox` service but no capability markers. Use to\n * verify that consumers calling `Sandbox.snapshot` / `createVolume` /\n * etc. fail at `Effect.provide` with a type error.\n */\nexport const layerWithoutCapabilities = (script: MockSandboxScript = {}) => {\n const { record, recorder } = makeRecorderUnsafe()\n return {\n layer: Layer.succeed(Sandbox, buildService(script, record)),\n recorder,\n }\n}\n\n/** Bare service value + recorder. Use with `Effect.provideService`. */\nexport const make = (script: MockSandboxScript = {}) => {\n const { record, recorder } = makeRecorderUnsafe()\n return {\n service: buildService(script, record),\n recorder,\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MockSandbox.mjs","names":["SandboxError.SandboxInvalidRequest","SandboxError.SandboxExecFailed","Arr"],"sources":["../../src/testing/MockSandbox.ts"],"sourcesContent":["import { Array as Arr, Data, Effect, Layer, Ref, Stream } from \"effect\"\nimport {\n ExecEvent,\n Sandbox,\n SandboxCustomImage,\n SandboxHostnameAllowlist,\n SandboxKernelSession,\n SandboxPauseResume,\n SandboxPortExposure,\n SandboxPty,\n SandboxSecretInjection,\n SandboxSnapshots,\n SandboxVolumes,\n type CommonCreateRequest,\n type CommonExecRequest,\n type CommonSpawnRequest,\n type ExecResult,\n type FileEntry,\n type ProcessHandle,\n SandboxId,\n type SandboxInstance,\n type SandboxRef,\n type SandboxService,\n SnapshotId,\n VolumeId,\n} from \"../sandbox/Sandbox.js\"\nimport * as SandboxError from \"../sandbox/SandboxError.js\"\n\n// ---------------------------------------------------------------------------\n// Call log\n// ---------------------------------------------------------------------------\n\n/**\n * One entry per side-effecting method call. Tests filter via\n * `Call.$is(...)` / `Call.$match`.\n */\nexport type Call = Data.TaggedEnum<{\n Create: { readonly request: CommonCreateRequest }\n Attach: { readonly id: SandboxId }\n Destroy: { readonly id: SandboxId }\n Exec: { readonly id: SandboxId; readonly request: CommonExecRequest }\n ExecStream: { readonly id: SandboxId; readonly request: CommonExecRequest }\n Spawn: { readonly id: SandboxId; readonly request: CommonSpawnRequest }\n FileRead: { readonly id: SandboxId; readonly path: string }\n FileWrite: {\n readonly id: SandboxId\n readonly path: string\n readonly contents: Uint8Array | string\n }\n FileRemove: { readonly id: SandboxId; readonly path: string }\n FileMkdir: { readonly id: SandboxId; readonly path: string }\n FileList: { readonly id: SandboxId; readonly path: string }\n FileExists: { readonly id: SandboxId; readonly path: string }\n ExposePort: { readonly id: SandboxId; readonly port: number }\n SnapshotCreate: { readonly id: SandboxId; readonly name?: string }\n SnapshotDestroy: { readonly id: SnapshotId }\n VolumeCreate: { readonly name: string; readonly quotaBytes?: number }\n VolumeDestroy: { readonly id: VolumeId }\n}>\n\nexport const Call = Data.taggedEnum<Call>()\n\nexport type MockSandboxRecorder = {\n readonly calls: ReadonlyArray<Call>\n}\n\n// ---------------------------------------------------------------------------\n// Script\n// ---------------------------------------------------------------------------\n\n/**\n * What this mock should hand back per call. Each method has its own\n * scripted queue and a sensible default when the queue is empty.\n * The filesystem is an in-memory mutable map seeded by `files`.\n */\nexport type MockSandboxScript = {\n readonly id?: string\n readonly execs?: ReadonlyArray<ExecResult>\n readonly streams?: ReadonlyArray<ReadonlyArray<ExecEvent>>\n readonly spawns?: ReadonlyArray<ProcessHandle>\n readonly files?: Readonly<Record<string, Uint8Array | string>>\n readonly sandboxes?: ReadonlyArray<SandboxRef>\n readonly snapshots?: ReadonlyArray<{ readonly id: SnapshotId; readonly name?: string }>\n readonly volumes?: ReadonlyArray<{ readonly id: VolumeId; readonly name: string }>\n readonly portUrl?: (port: number) => string\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst defaultExec: ExecResult = { exitCode: 0, stdout: \"\", stderr: \"\", durationMs: 0 }\n\nconst toBytes = (contents: Uint8Array | string) =>\n typeof contents === \"string\" ? new TextEncoder().encode(contents) : contents\n\nconst seedFs = (files: MockSandboxScript[\"files\"]) =>\n new Map(Object.entries(files ?? {}).map(([k, v]) => [k, toBytes(v)]))\n\nconst exhausted = (method: string, scripted: number, attempt: number) =>\n new SandboxError.SandboxInvalidRequest({\n provider: \"mock\",\n reason: `MockSandbox exhausted: ${scripted} ${method}(s) scripted, but call ${attempt} was made`,\n })\n\nconst consume = <A>(\n cursor: Ref.Ref<number>,\n scripted: ReadonlyArray<A>,\n onMiss: (attempt: number) => Effect.Effect<A, SandboxError.SandboxError>,\n) =>\n Ref.getAndUpdate(cursor, (n) => n + 1).pipe(\n Effect.flatMap((i) => (i < scripted.length ? Effect.succeed(scripted[i]!) : onMiss(i + 1))),\n )\n\n// ---------------------------------------------------------------------------\n// Service builder\n// ---------------------------------------------------------------------------\n\nconst buildInstance = (\n id: SandboxId,\n script: MockSandboxScript,\n fs: Ref.Ref<Map<string, Uint8Array>>,\n execCursor: Ref.Ref<number>,\n streamCursor: Ref.Ref<number>,\n spawnCursor: Ref.Ref<number>,\n record: (call: Call) => Effect.Effect<void>,\n): SandboxInstance => ({\n id,\n\n exec: (request) =>\n record(Call.Exec({ id, request })).pipe(\n Effect.andThen(consume(execCursor, script.execs ?? [], () => Effect.succeed(defaultExec))),\n ),\n\n execStream: (request) =>\n Stream.unwrap(\n record(Call.ExecStream({ id, request })).pipe(\n Effect.andThen(\n consume(streamCursor, script.streams ?? [], () =>\n Effect.succeed<ReadonlyArray<ExecEvent>>([\n ExecEvent.Complete({ exitCode: 0, durationMs: 0 }),\n ]),\n ),\n ),\n Effect.map(\n (events) =>\n Stream.fromIterable(events) as Stream.Stream<ExecEvent, SandboxError.SandboxError>,\n ),\n ),\n ),\n\n spawn: (request) =>\n record(Call.Spawn({ id, request })).pipe(\n Effect.andThen(\n consume(spawnCursor, script.spawns ?? [], (n) =>\n Effect.fail(exhausted(\"spawn\", (script.spawns ?? []).length, n)),\n ),\n ),\n Effect.tap((handle) => Effect.addFinalizer(() => Effect.orDie(handle.kill))),\n ),\n\n files: {\n read: (path) =>\n record(Call.FileRead({ id, path })).pipe(\n Effect.andThen(Ref.get(fs)),\n Effect.flatMap((map) =>\n map.has(path)\n ? Effect.succeed(map.get(path)!)\n : Effect.fail(\n new SandboxError.SandboxExecFailed({\n provider: \"mock\",\n reason: `ENOENT: ${path}`,\n raw: undefined,\n }),\n ),\n ),\n ),\n\n write: (path, contents) =>\n record(Call.FileWrite({ id, path, contents })).pipe(\n Effect.andThen(Ref.update(fs, (m) => new Map(m).set(path, toBytes(contents)))),\n ),\n\n remove: (path) =>\n record(Call.FileRemove({ id, path })).pipe(\n Effect.andThen(\n Ref.update(fs, (m) => {\n const next = new Map(m)\n next.delete(path)\n return next\n }),\n ),\n ),\n\n mkdir: (path) => record(Call.FileMkdir({ id, path })),\n\n list: (path) =>\n record(Call.FileList({ id, path })).pipe(\n Effect.andThen(Ref.get(fs)),\n Effect.map((map) => {\n const prefix = path.endsWith(\"/\") ? path : `${path}/`\n const out: Array<FileEntry> = []\n for (const k of map.keys()) {\n if (k.startsWith(prefix)) out.push({ path: k, kind: \"file\" })\n }\n return out\n }),\n ),\n\n exists: (path) =>\n record(Call.FileExists({ id, path })).pipe(\n Effect.andThen(Ref.get(fs)),\n Effect.map((map) => map.has(path)),\n ),\n },\n})\n\nconst buildService = (\n script: MockSandboxScript,\n record: (call: Call) => Effect.Effect<void>,\n): SandboxService => {\n const id = SandboxId(script.id ?? \"mock-sandbox\")\n\n const acquireInstance = (sandboxId: SandboxId, onClose: Effect.Effect<void>) =>\n Effect.gen(function* () {\n const fs = yield* Ref.make(seedFs(script.files))\n const execCursor = yield* Ref.make(0)\n const streamCursor = yield* Ref.make(0)\n const spawnCursor = yield* Ref.make(0)\n yield* Effect.addFinalizer(() => onClose)\n return buildInstance(sandboxId, script, fs, execCursor, streamCursor, spawnCursor, record)\n })\n\n return {\n create: (request) =>\n record(Call.Create({ request })).pipe(\n Effect.andThen(acquireInstance(id, record(Call.Destroy({ id })))),\n ),\n\n attach: (sandboxId) =>\n record(Call.Attach({ id: sandboxId })).pipe(\n // Attach detaches on scope close (no destroy).\n Effect.andThen(acquireInstance(sandboxId, Effect.void)),\n ),\n\n list: Effect.succeed(script.sandboxes ?? []),\n\n destroy: (sandboxId) => record(Call.Destroy({ id: sandboxId })),\n\n snapshots: {\n create: (_from, name) =>\n record(\n name === undefined ? Call.SnapshotCreate({ id }) : Call.SnapshotCreate({ id, name }),\n ).pipe(\n Effect.as(SnapshotId(`mock-snapshot-${name ?? Math.random().toString(36).slice(2, 8)}`)),\n ),\n destroy: (sid) => record(Call.SnapshotDestroy({ id: sid })),\n list: Effect.succeed(script.snapshots ?? []),\n },\n\n volumes: {\n create: (name, options) =>\n record(\n options?.quotaBytes === undefined\n ? Call.VolumeCreate({ name })\n : Call.VolumeCreate({ name, quotaBytes: options.quotaBytes }),\n ).pipe(Effect.as(VolumeId(`mock-volume-${name}`))),\n destroy: (vid) => record(Call.VolumeDestroy({ id: vid })),\n list: Effect.succeed(script.volumes ?? []),\n },\n\n ports: {\n expose: (instance, port) =>\n record(Call.ExposePort({ id: instance.id, port })).pipe(\n Effect.as({ url: (script.portUrl ?? ((p) => `http://mock.local:${p}`))(port) }),\n ),\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Recorder\n// ---------------------------------------------------------------------------\n\nconst makeRecorderUnsafe = () => {\n const ref = Ref.makeUnsafe<ReadonlyArray<Call>>([])\n return {\n record: (call: Call) => Ref.update(ref, Arr.append(call)),\n recorder: Ref.get(ref).pipe(Effect.map((calls): MockSandboxRecorder => ({ calls }))),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nconst allCapabilities = Layer.mergeAll(\n Layer.succeed(SandboxHostnameAllowlist, undefined),\n Layer.succeed(SandboxSecretInjection, undefined),\n Layer.succeed(SandboxSnapshots, undefined),\n Layer.succeed(SandboxPauseResume, undefined),\n Layer.succeed(SandboxVolumes, undefined),\n Layer.succeed(SandboxKernelSession, undefined),\n Layer.succeed(SandboxPty, undefined),\n Layer.succeed(SandboxCustomImage, undefined),\n Layer.succeed(SandboxPortExposure, undefined),\n)\n\n/**\n * Layer that registers a `MockSandbox` against the `Sandbox` tag, with\n * every capability marker shipped — use when the code under test\n * exercises any of them.\n */\nexport const layer = (script: MockSandboxScript = {}) => {\n const { record, recorder } = makeRecorderUnsafe()\n return {\n layer: Layer.merge(Layer.succeed(Sandbox, buildService(script, record)), allCapabilities),\n recorder,\n }\n}\n\n/**\n * Layer with the `Sandbox` service but no capability markers. Use to\n * verify that consumers calling `Sandbox.snapshot` / `createVolume` /\n * etc. fail at `Effect.provide` with a type error.\n */\nexport const layerWithoutCapabilities = (script: MockSandboxScript = {}) => {\n const { record, recorder } = makeRecorderUnsafe()\n return {\n layer: Layer.succeed(Sandbox, buildService(script, record)),\n recorder,\n }\n}\n\n/** Bare service value + recorder. Use with `Effect.provideService`. */\nexport const make = (script: MockSandboxScript = {}) => {\n const { record, recorder } = makeRecorderUnsafe()\n return {\n service: buildService(script, record),\n recorder,\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { n as AudioChunk, t as AudioBlob } from "../Audio-
|
|
1
|
+
import { n as AudioChunk, t as AudioBlob } from "../Audio-60lKKHxS.mjs";
|
|
2
2
|
import { CommonStreamSynthesizeRequest, CommonSynthesizeDialogueRequest, CommonSynthesizeRequest, MultiSpeakerTts, SpeechSynthesizer, TtsIncrementalText } from "../speech-synthesizer/SpeechSynthesizer.mjs";
|
|
3
3
|
import { Effect, Layer } from "effect";
|
|
4
|
-
|
|
5
4
|
//#region src/testing/MockSpeechSynthesizer.d.ts
|
|
6
5
|
type MockSynthesizerRecorder = {
|
|
7
6
|
readonly synthesizeCalls: ReadonlyArray<CommonSynthesizeRequest>;
|
|
@@ -11,10 +10,15 @@ type MockSynthesizerRecorder = {
|
|
|
11
10
|
readonly streamSynthesizeDialogueCalls: ReadonlyArray<CommonSynthesizeDialogueRequest>;
|
|
12
11
|
};
|
|
13
12
|
type MockSynthesizerScript = {
|
|
14
|
-
/** One blob per `synthesize` call, consumed in order. */
|
|
15
|
-
readonly
|
|
16
|
-
|
|
17
|
-
readonly
|
|
13
|
+
/** One blob per `synthesize` call, consumed in order. */
|
|
14
|
+
readonly blobs?: ReadonlyArray<AudioBlob>;
|
|
15
|
+
/** One chunk-list per `streamSynthesis` call, consumed in order. */
|
|
16
|
+
readonly streamSynthesisChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>;
|
|
17
|
+
/** One chunk-list per `streamSynthesisFrom` call, consumed in order. */
|
|
18
|
+
readonly streamSynthesisFromChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>;
|
|
19
|
+
/** One blob per `synthesizeDialogue` call, consumed in order. */
|
|
20
|
+
readonly dialogueBlobs?: ReadonlyArray<AudioBlob>;
|
|
21
|
+
/** One chunk-list per `streamSynthesizeDialogue` call, consumed in order. */
|
|
18
22
|
readonly streamSynthesizeDialogueChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>;
|
|
19
23
|
};
|
|
20
24
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockSpeechSynthesizer.d.mts","names":[],"sources":["../../src/testing/MockSpeechSynthesizer.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"MockSpeechSynthesizer.d.mts","names":[],"sources":["../../src/testing/MockSpeechSynthesizer.ts"],"mappings":";;;;KAaY;WACD,iBAAiB,cAAc;WAC/B,sBAAsB,cAAc;WACpC,0BAA0B,cAAc;WACxC,yBAAyB,cAAc;WACvC,+BAA+B,cAAc;;KAG5C;;WAED,QAAQ,cAAc;;WAEtB,wBAAwB,cAAc,cAAc;;WAEpD,4BAA4B,cAAc,cAAc;;WAExD,gBAAgB,cAAc;;WAE9B,iCAAiC,cAAc,cAAc;;;;;;;cAwI3D,QAAK,QACR;WAEC,OAAO,MAAM,MAAM,oBAAoB,qBAAqB;WAC5D,UAAU,OAAO,OAAO;;;;;;;;cAkBtB,0BAAuB,QAC1B;WAEC,OAAO,MAAM,MAAM,oBAAoB;WACvC,UAAU,OAAO,OAAO;;;;;;;;;cAiBtB,2BAAwB,QAC3B;WAEC,OAAO,MAAM,MAAM,oBAAoB;WACvC,UAAU,OAAO,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockSpeechSynthesizer.mjs","names":["AiError.InvalidRequest"],"sources":["../../src/testing/MockSpeechSynthesizer.ts"],"sourcesContent":["import { Effect, Layer, Ref, Stream } from \"effect\"\nimport type { AudioBlob, AudioChunk } from \"../domain/Audio.js\"\nimport * as AiError from \"../domain/AiError.js\"\nimport {\n type CommonStreamSynthesizeRequest,\n type CommonSynthesizeDialogueRequest,\n type CommonSynthesizeRequest,\n MultiSpeakerTts,\n SpeechSynthesizer,\n type SpeechSynthesizerService,\n TtsIncrementalText,\n} from \"../speech-synthesizer/SpeechSynthesizer.js\"\n\nexport type MockSynthesizerRecorder = {\n readonly synthesizeCalls: ReadonlyArray<CommonSynthesizeRequest>\n readonly streamSynthesisCalls: ReadonlyArray<CommonSynthesizeRequest>\n readonly streamSynthesisFromCalls: ReadonlyArray<CommonStreamSynthesizeRequest>\n readonly synthesizeDialogueCalls: ReadonlyArray<CommonSynthesizeDialogueRequest>\n readonly streamSynthesizeDialogueCalls: ReadonlyArray<CommonSynthesizeDialogueRequest>\n}\n\nexport type MockSynthesizerScript = {\n /** One blob per `synthesize` call, consumed in order. */\n readonly blobs?: ReadonlyArray<AudioBlob>\n /** One chunk-list per `streamSynthesis` call, consumed in order. */\n readonly streamSynthesisChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>\n /** One chunk-list per `streamSynthesisFrom` call, consumed in order. */\n readonly streamSynthesisFromChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>\n /** One blob per `synthesizeDialogue` call, consumed in order. */\n readonly dialogueBlobs?: ReadonlyArray<AudioBlob>\n /** One chunk-list per `streamSynthesizeDialogue` call, consumed in order. */\n readonly streamSynthesizeDialogueChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>\n}\n\ntype Record = {\n readonly synthesize: (req: CommonSynthesizeRequest) => Effect.Effect<void>\n readonly streamSynthesis: (req: CommonSynthesizeRequest) => Effect.Effect<void>\n readonly streamSynthesisFrom: (req: CommonStreamSynthesizeRequest) => Effect.Effect<void>\n readonly synthesizeDialogue: (req: CommonSynthesizeDialogueRequest) => Effect.Effect<void>\n readonly streamSynthesizeDialogue: (req: CommonSynthesizeDialogueRequest) => Effect.Effect<void>\n}\n\nconst exhausted = (label: string, scripted: number, call: number): AiError.AiError =>\n new AiError.InvalidRequest({\n provider: \"mock\",\n raw: `MockSpeechSynthesizer exhausted: ${scripted} ${label} scripted, but call ${call} was made`,\n })\n\nconst makeService = (script: MockSynthesizerScript, record: Record) =>\n Effect.gen(function* () {\n const bCursor = yield* Ref.make(0)\n const ssCursor = yield* Ref.make(0)\n const ssfCursor = yield* Ref.make(0)\n const sdCursor = yield* Ref.make(0)\n const ssdCursor = yield* Ref.make(0)\n const service: SpeechSynthesizerService = {\n synthesize: (request) =>\n Effect.gen(function* () {\n yield* record.synthesize(request)\n const i = yield* Ref.getAndUpdate(bCursor, (n) => n + 1)\n const scripted = script.blobs ?? []\n if (i >= scripted.length) return yield* exhausted(\"blobs\", scripted.length, i + 1)\n return scripted[i]!\n }),\n streamSynthesis: (request) =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.streamSynthesis(request)\n const i = yield* Ref.getAndUpdate(ssCursor, (n) => n + 1)\n const scripted = script.streamSynthesisChunks ?? []\n if (i >= scripted.length) {\n return Stream.fail(exhausted(\"streamSynthesis lists\", scripted.length, i + 1))\n }\n return Stream.fromIterable(scripted[i]!)\n }),\n ),\n streamSynthesisFrom: <E, R>(\n textIn: Stream.Stream<string, E, R>,\n request: CommonStreamSynthesizeRequest,\n ): Stream.Stream<AudioChunk, AiError.AiError | E, R> =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.streamSynthesisFrom(request)\n const i = yield* Ref.getAndUpdate(ssfCursor, (n) => n + 1)\n const scripted = script.streamSynthesisFromChunks ?? []\n if (i >= scripted.length) {\n const fail: Stream.Stream<AudioChunk, AiError.AiError | E, R> = Stream.fail(\n exhausted(\"streamSynthesisFrom lists\", scripted.length, i + 1),\n )\n return fail\n }\n return Stream.drain(textIn).pipe(Stream.concat(Stream.fromIterable(scripted[i]!)))\n }),\n ),\n synthesizeDialogue: (request) =>\n Effect.gen(function* () {\n yield* record.synthesizeDialogue(request)\n const i = yield* Ref.getAndUpdate(sdCursor, (n) => n + 1)\n const scripted = script.dialogueBlobs ?? []\n if (i >= scripted.length) {\n return yield* exhausted(\"dialogueBlobs\", scripted.length, i + 1)\n }\n return scripted[i]!\n }),\n streamSynthesizeDialogue: (request) =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.streamSynthesizeDialogue(request)\n const i = yield* Ref.getAndUpdate(ssdCursor, (n) => n + 1)\n const scripted = script.streamSynthesizeDialogueChunks ?? []\n if (i >= scripted.length) {\n return Stream.fail(\n exhausted(\"streamSynthesizeDialogue lists\", scripted.length, i + 1),\n )\n }\n return Stream.fromIterable(scripted[i]!)\n }),\n ),\n }\n return service\n })\n\ntype CallBuffers = {\n readonly bCalls: Ref.Ref<ReadonlyArray<CommonSynthesizeRequest>>\n readonly ssCalls: Ref.Ref<ReadonlyArray<CommonSynthesizeRequest>>\n readonly ssfCalls: Ref.Ref<ReadonlyArray<CommonStreamSynthesizeRequest>>\n readonly sdCalls: Ref.Ref<ReadonlyArray<CommonSynthesizeDialogueRequest>>\n readonly ssdCalls: Ref.Ref<ReadonlyArray<CommonSynthesizeDialogueRequest>>\n}\n\nconst newBuffers = (): CallBuffers => ({\n bCalls: Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeRequest>>([]),\n ssCalls: Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeRequest>>([]),\n ssfCalls: Ref.makeUnsafe<ReadonlyArray<CommonStreamSynthesizeRequest>>([]),\n sdCalls: Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeDialogueRequest>>([]),\n ssdCalls: Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeDialogueRequest>>([]),\n})\n\nconst recordFor = (b: CallBuffers): Record => ({\n synthesize: (req) => Ref.update(b.bCalls, (xs) => [...xs, req]),\n streamSynthesis: (req) => Ref.update(b.ssCalls, (xs) => [...xs, req]),\n streamSynthesisFrom: (req) => Ref.update(b.ssfCalls, (xs) => [...xs, req]),\n synthesizeDialogue: (req) => Ref.update(b.sdCalls, (xs) => [...xs, req]),\n streamSynthesizeDialogue: (req) => Ref.update(b.ssdCalls, (xs) => [...xs, req]),\n})\n\nconst recorderEffect = (b: CallBuffers): Effect.Effect<MockSynthesizerRecorder> =>\n Effect.gen(function* () {\n const synthesizeCalls = yield* Ref.get(b.bCalls)\n const streamSynthesisCalls = yield* Ref.get(b.ssCalls)\n const streamSynthesisFromCalls = yield* Ref.get(b.ssfCalls)\n const synthesizeDialogueCalls = yield* Ref.get(b.sdCalls)\n const streamSynthesizeDialogueCalls = yield* Ref.get(b.ssdCalls)\n return {\n synthesizeCalls,\n streamSynthesisCalls,\n streamSynthesisFromCalls,\n synthesizeDialogueCalls,\n streamSynthesizeDialogueCalls,\n }\n })\n\n/**\n * Layer providing `SpeechSynthesizer` plus both capability markers\n * (`TtsIncrementalText` and `MultiSpeakerTts`). Use for the common case\n * where code under test exercises any of the optional methods.\n */\nexport const layer = (\n script: MockSynthesizerScript,\n): {\n readonly layer: Layer.Layer<SpeechSynthesizer | TtsIncrementalText | MultiSpeakerTts>\n readonly recorder: Effect.Effect<MockSynthesizerRecorder>\n} => {\n const buffers = newBuffers()\n const synthesizerLayer = Layer.effect(SpeechSynthesizer, makeService(script, recordFor(buffers)))\n const live = Layer.mergeAll(\n synthesizerLayer,\n Layer.succeed(TtsIncrementalText, undefined),\n Layer.succeed(MultiSpeakerTts, undefined),\n )\n return { layer: live, recorder: recorderEffect(buffers) }\n}\n\n/**\n * Variant that omits the `TtsIncrementalText` marker — simulates a\n * provider without incremental-text-in support (e.g. OpenAI). Calls to\n * `streamSynthesisFrom` in code under test should be a compile-time\n * error. `MultiSpeakerTts` is still provided.\n */\nexport const layerWithoutIncremental = (\n script: MockSynthesizerScript,\n): {\n readonly layer: Layer.Layer<SpeechSynthesizer | MultiSpeakerTts>\n readonly recorder: Effect.Effect<MockSynthesizerRecorder>\n} => {\n const buffers = newBuffers()\n const live = Layer.merge(\n Layer.effect(SpeechSynthesizer, makeService(script, recordFor(buffers))),\n Layer.succeed(MultiSpeakerTts, undefined),\n )\n return { layer: live, recorder: recorderEffect(buffers) }\n}\n\n/**\n * Variant that omits the `MultiSpeakerTts` marker — simulates a\n * provider without multi-speaker dialogue support (e.g. OpenAI,\n * Inworld). Calls to `synthesizeDialogue` /\n * `streamSynthesizeDialogue` in code under test should be a\n * compile-time error.\n */\nexport const layerWithoutMultiSpeaker = (\n script: MockSynthesizerScript,\n): {\n readonly layer: Layer.Layer<SpeechSynthesizer | TtsIncrementalText>\n readonly recorder: Effect.Effect<MockSynthesizerRecorder>\n} => {\n const buffers = newBuffers()\n const live = Layer.merge(\n Layer.effect(SpeechSynthesizer, makeService(script, recordFor(buffers))),\n Layer.succeed(TtsIncrementalText, undefined),\n )\n return { layer: live, recorder: recorderEffect(buffers) }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MockSpeechSynthesizer.mjs","names":["AiError.InvalidRequest"],"sources":["../../src/testing/MockSpeechSynthesizer.ts"],"sourcesContent":["import { Effect, Layer, Ref, Stream } from \"effect\"\nimport type { AudioBlob, AudioChunk } from \"../domain/Audio.js\"\nimport * as AiError from \"../domain/AiError.js\"\nimport {\n type CommonStreamSynthesizeRequest,\n type CommonSynthesizeDialogueRequest,\n type CommonSynthesizeRequest,\n MultiSpeakerTts,\n SpeechSynthesizer,\n type SpeechSynthesizerService,\n TtsIncrementalText,\n} from \"../speech-synthesizer/SpeechSynthesizer.js\"\n\nexport type MockSynthesizerRecorder = {\n readonly synthesizeCalls: ReadonlyArray<CommonSynthesizeRequest>\n readonly streamSynthesisCalls: ReadonlyArray<CommonSynthesizeRequest>\n readonly streamSynthesisFromCalls: ReadonlyArray<CommonStreamSynthesizeRequest>\n readonly synthesizeDialogueCalls: ReadonlyArray<CommonSynthesizeDialogueRequest>\n readonly streamSynthesizeDialogueCalls: ReadonlyArray<CommonSynthesizeDialogueRequest>\n}\n\nexport type MockSynthesizerScript = {\n /** One blob per `synthesize` call, consumed in order. */\n readonly blobs?: ReadonlyArray<AudioBlob>\n /** One chunk-list per `streamSynthesis` call, consumed in order. */\n readonly streamSynthesisChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>\n /** One chunk-list per `streamSynthesisFrom` call, consumed in order. */\n readonly streamSynthesisFromChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>\n /** One blob per `synthesizeDialogue` call, consumed in order. */\n readonly dialogueBlobs?: ReadonlyArray<AudioBlob>\n /** One chunk-list per `streamSynthesizeDialogue` call, consumed in order. */\n readonly streamSynthesizeDialogueChunks?: ReadonlyArray<ReadonlyArray<AudioChunk>>\n}\n\ntype Record = {\n readonly synthesize: (req: CommonSynthesizeRequest) => Effect.Effect<void>\n readonly streamSynthesis: (req: CommonSynthesizeRequest) => Effect.Effect<void>\n readonly streamSynthesisFrom: (req: CommonStreamSynthesizeRequest) => Effect.Effect<void>\n readonly synthesizeDialogue: (req: CommonSynthesizeDialogueRequest) => Effect.Effect<void>\n readonly streamSynthesizeDialogue: (req: CommonSynthesizeDialogueRequest) => Effect.Effect<void>\n}\n\nconst exhausted = (label: string, scripted: number, call: number): AiError.AiError =>\n new AiError.InvalidRequest({\n provider: \"mock\",\n raw: `MockSpeechSynthesizer exhausted: ${scripted} ${label} scripted, but call ${call} was made`,\n })\n\nconst makeService = (script: MockSynthesizerScript, record: Record) =>\n Effect.gen(function* () {\n const bCursor = yield* Ref.make(0)\n const ssCursor = yield* Ref.make(0)\n const ssfCursor = yield* Ref.make(0)\n const sdCursor = yield* Ref.make(0)\n const ssdCursor = yield* Ref.make(0)\n const service: SpeechSynthesizerService = {\n synthesize: (request) =>\n Effect.gen(function* () {\n yield* record.synthesize(request)\n const i = yield* Ref.getAndUpdate(bCursor, (n) => n + 1)\n const scripted = script.blobs ?? []\n if (i >= scripted.length) return yield* exhausted(\"blobs\", scripted.length, i + 1)\n return scripted[i]!\n }),\n streamSynthesis: (request) =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.streamSynthesis(request)\n const i = yield* Ref.getAndUpdate(ssCursor, (n) => n + 1)\n const scripted = script.streamSynthesisChunks ?? []\n if (i >= scripted.length) {\n return Stream.fail(exhausted(\"streamSynthesis lists\", scripted.length, i + 1))\n }\n return Stream.fromIterable(scripted[i]!)\n }),\n ),\n streamSynthesisFrom: <E, R>(\n textIn: Stream.Stream<string, E, R>,\n request: CommonStreamSynthesizeRequest,\n ): Stream.Stream<AudioChunk, AiError.AiError | E, R> =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.streamSynthesisFrom(request)\n const i = yield* Ref.getAndUpdate(ssfCursor, (n) => n + 1)\n const scripted = script.streamSynthesisFromChunks ?? []\n if (i >= scripted.length) {\n const fail: Stream.Stream<AudioChunk, AiError.AiError | E, R> = Stream.fail(\n exhausted(\"streamSynthesisFrom lists\", scripted.length, i + 1),\n )\n return fail\n }\n return Stream.drain(textIn).pipe(Stream.concat(Stream.fromIterable(scripted[i]!)))\n }),\n ),\n synthesizeDialogue: (request) =>\n Effect.gen(function* () {\n yield* record.synthesizeDialogue(request)\n const i = yield* Ref.getAndUpdate(sdCursor, (n) => n + 1)\n const scripted = script.dialogueBlobs ?? []\n if (i >= scripted.length) {\n return yield* exhausted(\"dialogueBlobs\", scripted.length, i + 1)\n }\n return scripted[i]!\n }),\n streamSynthesizeDialogue: (request) =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.streamSynthesizeDialogue(request)\n const i = yield* Ref.getAndUpdate(ssdCursor, (n) => n + 1)\n const scripted = script.streamSynthesizeDialogueChunks ?? []\n if (i >= scripted.length) {\n return Stream.fail(\n exhausted(\"streamSynthesizeDialogue lists\", scripted.length, i + 1),\n )\n }\n return Stream.fromIterable(scripted[i]!)\n }),\n ),\n }\n return service\n })\n\ntype CallBuffers = {\n readonly bCalls: Ref.Ref<ReadonlyArray<CommonSynthesizeRequest>>\n readonly ssCalls: Ref.Ref<ReadonlyArray<CommonSynthesizeRequest>>\n readonly ssfCalls: Ref.Ref<ReadonlyArray<CommonStreamSynthesizeRequest>>\n readonly sdCalls: Ref.Ref<ReadonlyArray<CommonSynthesizeDialogueRequest>>\n readonly ssdCalls: Ref.Ref<ReadonlyArray<CommonSynthesizeDialogueRequest>>\n}\n\nconst newBuffers = (): CallBuffers => ({\n bCalls: Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeRequest>>([]),\n ssCalls: Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeRequest>>([]),\n ssfCalls: Ref.makeUnsafe<ReadonlyArray<CommonStreamSynthesizeRequest>>([]),\n sdCalls: Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeDialogueRequest>>([]),\n ssdCalls: Ref.makeUnsafe<ReadonlyArray<CommonSynthesizeDialogueRequest>>([]),\n})\n\nconst recordFor = (b: CallBuffers): Record => ({\n synthesize: (req) => Ref.update(b.bCalls, (xs) => [...xs, req]),\n streamSynthesis: (req) => Ref.update(b.ssCalls, (xs) => [...xs, req]),\n streamSynthesisFrom: (req) => Ref.update(b.ssfCalls, (xs) => [...xs, req]),\n synthesizeDialogue: (req) => Ref.update(b.sdCalls, (xs) => [...xs, req]),\n streamSynthesizeDialogue: (req) => Ref.update(b.ssdCalls, (xs) => [...xs, req]),\n})\n\nconst recorderEffect = (b: CallBuffers): Effect.Effect<MockSynthesizerRecorder> =>\n Effect.gen(function* () {\n const synthesizeCalls = yield* Ref.get(b.bCalls)\n const streamSynthesisCalls = yield* Ref.get(b.ssCalls)\n const streamSynthesisFromCalls = yield* Ref.get(b.ssfCalls)\n const synthesizeDialogueCalls = yield* Ref.get(b.sdCalls)\n const streamSynthesizeDialogueCalls = yield* Ref.get(b.ssdCalls)\n return {\n synthesizeCalls,\n streamSynthesisCalls,\n streamSynthesisFromCalls,\n synthesizeDialogueCalls,\n streamSynthesizeDialogueCalls,\n }\n })\n\n/**\n * Layer providing `SpeechSynthesizer` plus both capability markers\n * (`TtsIncrementalText` and `MultiSpeakerTts`). Use for the common case\n * where code under test exercises any of the optional methods.\n */\nexport const layer = (\n script: MockSynthesizerScript,\n): {\n readonly layer: Layer.Layer<SpeechSynthesizer | TtsIncrementalText | MultiSpeakerTts>\n readonly recorder: Effect.Effect<MockSynthesizerRecorder>\n} => {\n const buffers = newBuffers()\n const synthesizerLayer = Layer.effect(SpeechSynthesizer, makeService(script, recordFor(buffers)))\n const live = Layer.mergeAll(\n synthesizerLayer,\n Layer.succeed(TtsIncrementalText, undefined),\n Layer.succeed(MultiSpeakerTts, undefined),\n )\n return { layer: live, recorder: recorderEffect(buffers) }\n}\n\n/**\n * Variant that omits the `TtsIncrementalText` marker — simulates a\n * provider without incremental-text-in support (e.g. OpenAI). Calls to\n * `streamSynthesisFrom` in code under test should be a compile-time\n * error. `MultiSpeakerTts` is still provided.\n */\nexport const layerWithoutIncremental = (\n script: MockSynthesizerScript,\n): {\n readonly layer: Layer.Layer<SpeechSynthesizer | MultiSpeakerTts>\n readonly recorder: Effect.Effect<MockSynthesizerRecorder>\n} => {\n const buffers = newBuffers()\n const live = Layer.merge(\n Layer.effect(SpeechSynthesizer, makeService(script, recordFor(buffers))),\n Layer.succeed(MultiSpeakerTts, undefined),\n )\n return { layer: live, recorder: recorderEffect(buffers) }\n}\n\n/**\n * Variant that omits the `MultiSpeakerTts` marker — simulates a\n * provider without multi-speaker dialogue support (e.g. OpenAI,\n * Inworld). Calls to `synthesizeDialogue` /\n * `streamSynthesizeDialogue` in code under test should be a\n * compile-time error.\n */\nexport const layerWithoutMultiSpeaker = (\n script: MockSynthesizerScript,\n): {\n readonly layer: Layer.Layer<SpeechSynthesizer | TtsIncrementalText>\n readonly recorder: Effect.Effect<MockSynthesizerRecorder>\n} => {\n const buffers = newBuffers()\n const live = Layer.merge(\n Layer.effect(SpeechSynthesizer, makeService(script, recordFor(buffers))),\n Layer.succeed(TtsIncrementalText, undefined),\n )\n return { layer: live, recorder: recorderEffect(buffers) }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { TranscriptEvent, TranscriptResult } from "../domain/Transcript.mjs";
|
|
2
2
|
import { CommonStreamTranscribeRequest, CommonTranscribeRequest, SttStreaming, Transcriber } from "../transcriber/Transcriber.mjs";
|
|
3
3
|
import { Effect, Layer } from "effect";
|
|
4
|
-
|
|
5
4
|
//#region src/testing/MockTranscriber.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* Recorder of every call made to the mock.
|
|
@@ -11,7 +10,9 @@ type MockTranscriberRecorder = {
|
|
|
11
10
|
readonly streamCalls: ReadonlyArray<CommonStreamTranscribeRequest>;
|
|
12
11
|
};
|
|
13
12
|
type MockTranscriberScript = {
|
|
14
|
-
/** One result per `transcribe` call, consumed in order. */
|
|
13
|
+
/** One result per `transcribe` call, consumed in order. */
|
|
14
|
+
readonly transcripts?: ReadonlyArray<TranscriptResult>;
|
|
15
|
+
/** One event-list per `streamTranscriptionFrom` call, consumed in order. */
|
|
15
16
|
readonly streams?: ReadonlyArray<ReadonlyArray<TranscriptEvent>>;
|
|
16
17
|
};
|
|
17
18
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockTranscriber.d.mts","names":[],"sources":["../../src/testing/MockTranscriber.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"MockTranscriber.d.mts","names":[],"sources":["../../src/testing/MockTranscriber.ts"],"mappings":";;;;;;;KAcY;WACD,iBAAiB,cAAc;WAC/B,aAAa,cAAc;;KAG1B;;WAED,cAAc,cAAc;;WAE5B,UAAU,cAAc,cAAc;;;;;;;cA2DpC,QAAK,QACR;WAEC,OAAO,MAAM,MAAM,cAAc;WACjC,UAAU,OAAO,OAAO;;;;;;;cA2BtB,gBAAa,QAChB;WAEC,OAAO,MAAM,MAAM;WACnB,UAAU,OAAO,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockTranscriber.mjs","names":["AiError.InvalidRequest"],"sources":["../../src/testing/MockTranscriber.ts"],"sourcesContent":["import { Effect, Layer, Ref, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { TranscriptEvent, TranscriptResult } from \"../domain/Transcript.js\"\nimport {\n SttStreaming,\n Transcriber,\n type CommonStreamTranscribeRequest,\n type CommonTranscribeRequest,\n type TranscriberService,\n} from \"../transcriber/Transcriber.js\"\n\n/**\n * Recorder of every call made to the mock.\n */\nexport type MockTranscriberRecorder = {\n readonly transcribeCalls: ReadonlyArray<CommonTranscribeRequest>\n readonly streamCalls: ReadonlyArray<CommonStreamTranscribeRequest>\n}\n\nexport type MockTranscriberScript = {\n /** One result per `transcribe` call, consumed in order. */\n readonly transcripts?: ReadonlyArray<TranscriptResult>\n /** One event-list per `streamTranscriptionFrom` call, consumed in order. */\n readonly streams?: ReadonlyArray<ReadonlyArray<TranscriptEvent>>\n}\n\nconst makeService = (\n script: MockTranscriberScript,\n record: {\n readonly transcribe: (req: CommonTranscribeRequest) => Effect.Effect<void>\n readonly stream: (req: CommonStreamTranscribeRequest) => Effect.Effect<void>\n },\n) =>\n Effect.gen(function* () {\n const tCursor = yield* Ref.make(0)\n const sCursor = yield* Ref.make(0)\n const service: TranscriberService = {\n transcribe: (request) =>\n Effect.gen(function* () {\n yield* record.transcribe(request)\n const i = yield* Ref.getAndUpdate(tCursor, (n) => n + 1)\n const scripted = script.transcripts ?? []\n if (i >= scripted.length) {\n return yield* new AiError.InvalidRequest({\n provider: \"mock\",\n raw: `MockTranscriber exhausted: ${scripted.length} transcripts scripted, but call ${i + 1} was made`,\n })\n }\n return scripted[i]!\n }),\n streamTranscriptionFrom: <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n request: CommonStreamTranscribeRequest,\n ): Stream.Stream<TranscriptEvent, AiError.AiError | E, R> =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.stream(request)\n const i = yield* Ref.getAndUpdate(sCursor, (n) => n + 1)\n const scripted = script.streams ?? []\n if (i >= scripted.length) {\n const exhausted: Stream.Stream<TranscriptEvent, AiError.AiError | E, R> = Stream.fail(\n new AiError.InvalidRequest({\n provider: \"mock\",\n raw: `MockTranscriber exhausted: ${scripted.length} streams scripted, but call ${i + 1} was made`,\n }),\n )\n return exhausted\n }\n // Drain the input audio fully before emitting the scripted events,\n // so consumers can assert on what bytes were pushed.\n return Stream.drain(audioIn).pipe(Stream.concat(Stream.fromIterable(scripted[i]!)))\n }),\n ),\n }\n return service\n })\n\n/**\n * Returns a Layer that provides both the `Transcriber` service and the\n * `SttStreaming` capability marker. Use when the code under test calls\n * `streamTranscriptionFrom`.\n */\nexport const layer = (\n script: MockTranscriberScript,\n): {\n readonly layer: Layer.Layer<Transcriber | SttStreaming>\n readonly recorder: Effect.Effect<MockTranscriberRecorder>\n} => {\n const tCalls = Ref.makeUnsafe<ReadonlyArray<CommonTranscribeRequest>>([])\n const sCalls = Ref.makeUnsafe<ReadonlyArray<CommonStreamTranscribeRequest>>([])\n const transcriberLayer = Layer.effect(\n Transcriber,\n makeService(script, {\n transcribe: (req) => Ref.update(tCalls, (xs) => [...xs, req]),\n stream: (req) => Ref.update(sCalls, (xs) => [...xs, req]),\n }),\n )\n const live = Layer.merge(transcriberLayer, Layer.succeed(SttStreaming, undefined))\n return {\n layer: live,\n recorder: Effect.gen(function* () {\n const transcribeCalls = yield* Ref.get(tCalls)\n const streamCalls = yield* Ref.get(sCalls)\n return { transcribeCalls, streamCalls }\n }),\n }\n}\n\n/**\n * Variant that omits the `SttStreaming` marker — use to test that\n * consumers calling `streamTranscriptionFrom` fail to compile against\n * a non-streaming provider.\n */\nexport const layerSyncOnly = (\n script: MockTranscriberScript,\n): {\n readonly layer: Layer.Layer<Transcriber>\n readonly recorder: Effect.Effect<MockTranscriberRecorder>\n} => {\n const tCalls = Ref.makeUnsafe<ReadonlyArray<CommonTranscribeRequest>>([])\n const sCalls = Ref.makeUnsafe<ReadonlyArray<CommonStreamTranscribeRequest>>([])\n const live = Layer.effect(\n Transcriber,\n makeService(script, {\n transcribe: (req) => Ref.update(tCalls, (xs) => [...xs, req]),\n stream: (req) => Ref.update(sCalls, (xs) => [...xs, req]),\n }),\n )\n return {\n layer: live,\n recorder: Effect.gen(function* () {\n const transcribeCalls = yield* Ref.get(tCalls)\n const streamCalls = yield* Ref.get(sCalls)\n return { transcribeCalls, streamCalls }\n }),\n }\n}\n"],"mappings":";;;;AA0BA,MAAM,eACJ,QACA,WAKA,OAAO,IAAI,aAAa;CACtB,MAAM,UAAU,OAAO,IAAI,KAAK,
|
|
1
|
+
{"version":3,"file":"MockTranscriber.mjs","names":["AiError.InvalidRequest"],"sources":["../../src/testing/MockTranscriber.ts"],"sourcesContent":["import { Effect, Layer, Ref, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { TranscriptEvent, TranscriptResult } from \"../domain/Transcript.js\"\nimport {\n SttStreaming,\n Transcriber,\n type CommonStreamTranscribeRequest,\n type CommonTranscribeRequest,\n type TranscriberService,\n} from \"../transcriber/Transcriber.js\"\n\n/**\n * Recorder of every call made to the mock.\n */\nexport type MockTranscriberRecorder = {\n readonly transcribeCalls: ReadonlyArray<CommonTranscribeRequest>\n readonly streamCalls: ReadonlyArray<CommonStreamTranscribeRequest>\n}\n\nexport type MockTranscriberScript = {\n /** One result per `transcribe` call, consumed in order. */\n readonly transcripts?: ReadonlyArray<TranscriptResult>\n /** One event-list per `streamTranscriptionFrom` call, consumed in order. */\n readonly streams?: ReadonlyArray<ReadonlyArray<TranscriptEvent>>\n}\n\nconst makeService = (\n script: MockTranscriberScript,\n record: {\n readonly transcribe: (req: CommonTranscribeRequest) => Effect.Effect<void>\n readonly stream: (req: CommonStreamTranscribeRequest) => Effect.Effect<void>\n },\n) =>\n Effect.gen(function* () {\n const tCursor = yield* Ref.make(0)\n const sCursor = yield* Ref.make(0)\n const service: TranscriberService = {\n transcribe: (request) =>\n Effect.gen(function* () {\n yield* record.transcribe(request)\n const i = yield* Ref.getAndUpdate(tCursor, (n) => n + 1)\n const scripted = script.transcripts ?? []\n if (i >= scripted.length) {\n return yield* new AiError.InvalidRequest({\n provider: \"mock\",\n raw: `MockTranscriber exhausted: ${scripted.length} transcripts scripted, but call ${i + 1} was made`,\n })\n }\n return scripted[i]!\n }),\n streamTranscriptionFrom: <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n request: CommonStreamTranscribeRequest,\n ): Stream.Stream<TranscriptEvent, AiError.AiError | E, R> =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* record.stream(request)\n const i = yield* Ref.getAndUpdate(sCursor, (n) => n + 1)\n const scripted = script.streams ?? []\n if (i >= scripted.length) {\n const exhausted: Stream.Stream<TranscriptEvent, AiError.AiError | E, R> = Stream.fail(\n new AiError.InvalidRequest({\n provider: \"mock\",\n raw: `MockTranscriber exhausted: ${scripted.length} streams scripted, but call ${i + 1} was made`,\n }),\n )\n return exhausted\n }\n // Drain the input audio fully before emitting the scripted events,\n // so consumers can assert on what bytes were pushed.\n return Stream.drain(audioIn).pipe(Stream.concat(Stream.fromIterable(scripted[i]!)))\n }),\n ),\n }\n return service\n })\n\n/**\n * Returns a Layer that provides both the `Transcriber` service and the\n * `SttStreaming` capability marker. Use when the code under test calls\n * `streamTranscriptionFrom`.\n */\nexport const layer = (\n script: MockTranscriberScript,\n): {\n readonly layer: Layer.Layer<Transcriber | SttStreaming>\n readonly recorder: Effect.Effect<MockTranscriberRecorder>\n} => {\n const tCalls = Ref.makeUnsafe<ReadonlyArray<CommonTranscribeRequest>>([])\n const sCalls = Ref.makeUnsafe<ReadonlyArray<CommonStreamTranscribeRequest>>([])\n const transcriberLayer = Layer.effect(\n Transcriber,\n makeService(script, {\n transcribe: (req) => Ref.update(tCalls, (xs) => [...xs, req]),\n stream: (req) => Ref.update(sCalls, (xs) => [...xs, req]),\n }),\n )\n const live = Layer.merge(transcriberLayer, Layer.succeed(SttStreaming, undefined))\n return {\n layer: live,\n recorder: Effect.gen(function* () {\n const transcribeCalls = yield* Ref.get(tCalls)\n const streamCalls = yield* Ref.get(sCalls)\n return { transcribeCalls, streamCalls }\n }),\n }\n}\n\n/**\n * Variant that omits the `SttStreaming` marker — use to test that\n * consumers calling `streamTranscriptionFrom` fail to compile against\n * a non-streaming provider.\n */\nexport const layerSyncOnly = (\n script: MockTranscriberScript,\n): {\n readonly layer: Layer.Layer<Transcriber>\n readonly recorder: Effect.Effect<MockTranscriberRecorder>\n} => {\n const tCalls = Ref.makeUnsafe<ReadonlyArray<CommonTranscribeRequest>>([])\n const sCalls = Ref.makeUnsafe<ReadonlyArray<CommonStreamTranscribeRequest>>([])\n const live = Layer.effect(\n Transcriber,\n makeService(script, {\n transcribe: (req) => Ref.update(tCalls, (xs) => [...xs, req]),\n stream: (req) => Ref.update(sCalls, (xs) => [...xs, req]),\n }),\n )\n return {\n layer: live,\n recorder: Effect.gen(function* () {\n const transcribeCalls = yield* Ref.get(tCalls)\n const streamCalls = yield* Ref.get(sCalls)\n return { transcribeCalls, streamCalls }\n }),\n }\n}\n"],"mappings":";;;;AA0BA,MAAM,eACJ,QACA,WAKA,OAAO,IAAI,aAAa;CACtB,MAAM,UAAU,OAAO,IAAI,KAAK,CAAC;CACjC,MAAM,UAAU,OAAO,IAAI,KAAK,CAAC;CAuCjC,OAAO;EArCL,aAAa,YACX,OAAO,IAAI,aAAa;GACtB,OAAO,OAAO,WAAW,OAAO;GAChC,MAAM,IAAI,OAAO,IAAI,aAAa,UAAU,MAAM,IAAI,CAAC;GACvD,MAAM,WAAW,OAAO,eAAe,CAAC;GACxC,IAAI,KAAK,SAAS,QAChB,OAAO,OAAO,IAAIA,eAAuB;IACvC,UAAU;IACV,KAAK,8BAA8B,SAAS,OAAO,kCAAkC,IAAI,EAAE;GAC7F,CAAC;GAEH,OAAO,SAAS;EAClB,CAAC;EACH,0BACE,SACA,YAEA,OAAO,OACL,OAAO,IAAI,aAAa;GACtB,OAAO,OAAO,OAAO,OAAO;GAC5B,MAAM,IAAI,OAAO,IAAI,aAAa,UAAU,MAAM,IAAI,CAAC;GACvD,MAAM,WAAW,OAAO,WAAW,CAAC;GACpC,IAAI,KAAK,SAAS,QAOhB,OAN0E,OAAO,KAC/E,IAAIA,eAAuB;IACzB,UAAU;IACV,KAAK,8BAA8B,SAAS,OAAO,8BAA8B,IAAI,EAAE;GACzF,CAAC,CAEY;GAIjB,OAAO,OAAO,MAAM,OAAO,CAAC,CAAC,KAAK,OAAO,OAAO,OAAO,aAAa,SAAS,EAAG,CAAC,CAAC;EACpF,CAAC,CACH;CAES;AACf,CAAC;;;;;;AAOH,MAAa,SACX,WAIG;CACH,MAAM,SAAS,IAAI,WAAmD,CAAC,CAAC;CACxE,MAAM,SAAS,IAAI,WAAyD,CAAC,CAAC;CAC9E,MAAM,mBAAmB,MAAM,OAC7B,aACA,YAAY,QAAQ;EAClB,aAAa,QAAQ,IAAI,OAAO,SAAS,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC;EAC5D,SAAS,QAAQ,IAAI,OAAO,SAAS,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC;CAC1D,CAAC,CACH;CAEA,OAAO;EACL,OAFW,MAAM,MAAM,kBAAkB,MAAM,QAAQ,cAAc,KAAA,CAAS,CAEpE;EACV,UAAU,OAAO,IAAI,aAAa;GAGhC,OAAO;IAAE,iBAAA,OAFsB,IAAI,IAAI,MAAM;IAEnB,aAAA,OADC,IAAI,IAAI,MAAM;GACH;EACxC,CAAC;CACH;AACF;;;;;;AAOA,MAAa,iBACX,WAIG;CACH,MAAM,SAAS,IAAI,WAAmD,CAAC,CAAC;CACxE,MAAM,SAAS,IAAI,WAAyD,CAAC,CAAC;CAQ9E,OAAO;EACL,OARW,MAAM,OACjB,aACA,YAAY,QAAQ;GAClB,aAAa,QAAQ,IAAI,OAAO,SAAS,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC;GAC5D,SAAS,QAAQ,IAAI,OAAO,SAAS,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC;EAC1D,CAAC,CAGS;EACV,UAAU,OAAO,IAAI,aAAa;GAGhC,OAAO;IAAE,iBAAA,OAFsB,IAAI,IAAI,MAAM;IAEnB,aAAA,OADC,IAAI,IAAI,MAAM;GACH;EACxC,CAAC;CACH;AACF"}
|
package/dist/tool/Approval.d.mts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { v as ToolCall } from "../Items-
|
|
2
|
-
import { t as ToolResult } from "../ToolResult-
|
|
3
|
-
import { t as ToolEvent } from "../ToolEvent-
|
|
1
|
+
import { v as ToolCall } from "../Items-SfUabNuB.mjs";
|
|
2
|
+
import { t as ToolResult } from "../ToolResult-Cj8ChodL.mjs";
|
|
3
|
+
import { t as ToolEvent } from "../ToolEvent-D99MGQYH.mjs";
|
|
4
4
|
import { Data, Duration, Effect, Queue, Scope, Stream } from "effect";
|
|
5
|
-
import * as _$effect_Unify0 from "effect/Unify";
|
|
6
|
-
|
|
7
|
-
//#region src/tool/Approval.d.ts
|
|
8
5
|
declare namespace Approval_d_exports {
|
|
9
6
|
export { ApprovalDecision, ApprovalMapEntry, FromQueueOptions, ToolCallPlan, Verdict, approvalRequested, approve, fromMap, fromQueue, reject, splitApprovalDecisions };
|
|
10
7
|
}
|
|
@@ -21,14 +18,6 @@ type ApprovalDecision = Data.TaggedEnum<{
|
|
|
21
18
|
};
|
|
22
19
|
}>;
|
|
23
20
|
declare const ApprovalDecision: {
|
|
24
|
-
readonly Approved: Data.TaggedEnum.ConstructorFrom<{
|
|
25
|
-
readonly _tag: "Approved";
|
|
26
|
-
readonly call: ToolCall;
|
|
27
|
-
}, "_tag">;
|
|
28
|
-
readonly Rejected: Data.TaggedEnum.ConstructorFrom<{
|
|
29
|
-
readonly _tag: "Rejected";
|
|
30
|
-
readonly result: ToolResult;
|
|
31
|
-
}, "_tag">;
|
|
32
21
|
readonly $is: <Tag extends "Approved" | "Rejected">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
33
22
|
readonly _tag: "Approved";
|
|
34
23
|
readonly call: ToolCall;
|
|
@@ -41,39 +30,45 @@ declare const ApprovalDecision: {
|
|
|
41
30
|
readonly _tag: Tag;
|
|
42
31
|
}>;
|
|
43
32
|
readonly $match: {
|
|
44
|
-
<Cases extends {
|
|
45
|
-
readonly
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
readonly
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}>(cases: Cases): (value: {
|
|
33
|
+
<Cases extends { readonly [Tag in "Approved" | "Rejected"]: (args: Extract<{
|
|
34
|
+
readonly _tag: "Approved";
|
|
35
|
+
readonly call: ToolCall;
|
|
36
|
+
} | {
|
|
37
|
+
readonly _tag: "Rejected";
|
|
38
|
+
readonly result: ToolResult;
|
|
39
|
+
}, {
|
|
40
|
+
readonly _tag: Tag;
|
|
41
|
+
}>) => any; }>(cases: Cases): (value: {
|
|
54
42
|
readonly _tag: "Approved";
|
|
55
43
|
readonly call: ToolCall;
|
|
56
44
|
} | {
|
|
57
45
|
readonly _tag: "Rejected";
|
|
58
46
|
readonly result: ToolResult;
|
|
59
|
-
}) =>
|
|
60
|
-
<Cases extends {
|
|
61
|
-
readonly Approved: (args: {
|
|
62
|
-
readonly _tag: "Approved";
|
|
63
|
-
readonly call: ToolCall;
|
|
64
|
-
}) => any;
|
|
65
|
-
readonly Rejected: (args: {
|
|
66
|
-
readonly _tag: "Rejected";
|
|
67
|
-
readonly result: ToolResult;
|
|
68
|
-
}) => any;
|
|
69
|
-
}>(value: {
|
|
47
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["Approved" | "Rejected"]>>;
|
|
48
|
+
<Cases extends { readonly [Tag in "Approved" | "Rejected"]: (args: Extract<{
|
|
70
49
|
readonly _tag: "Approved";
|
|
71
50
|
readonly call: ToolCall;
|
|
72
51
|
} | {
|
|
73
52
|
readonly _tag: "Rejected";
|
|
74
53
|
readonly result: ToolResult;
|
|
75
|
-
},
|
|
54
|
+
}, {
|
|
55
|
+
readonly _tag: Tag;
|
|
56
|
+
}>) => any; }>(value: {
|
|
57
|
+
readonly _tag: "Approved";
|
|
58
|
+
readonly call: ToolCall;
|
|
59
|
+
} | {
|
|
60
|
+
readonly _tag: "Rejected";
|
|
61
|
+
readonly result: ToolResult;
|
|
62
|
+
}, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Approved" | "Rejected"]>>;
|
|
76
63
|
};
|
|
64
|
+
readonly Approved: Data.TaggedEnum.ConstructorFrom<{
|
|
65
|
+
readonly _tag: "Approved";
|
|
66
|
+
readonly call: ToolCall;
|
|
67
|
+
}, "_tag">;
|
|
68
|
+
readonly Rejected: Data.TaggedEnum.ConstructorFrom<{
|
|
69
|
+
readonly _tag: "Rejected";
|
|
70
|
+
readonly result: ToolResult;
|
|
71
|
+
}, "_tag">;
|
|
77
72
|
};
|
|
78
73
|
declare const approve: (call: ToolCall) => ApprovalDecision;
|
|
79
74
|
declare const reject: (result: ToolResult) => ApprovalDecision;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Approval.d.mts","names":[],"sources":["../../src/tool/Approval.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"Approval.d.mts","names":[],"sources":["../../src/tool/Approval.ts"],"mappings":";;;;;;;KAYY;WACD,UAAU,cAAc;WACxB,UAAU,cAAc;;KAGvB,mBAAmB,KAAK;EAClC;aAAqB,MAAM;;EAC3B;aAAqB,QAAQ;;;cAGlB;;;aAJgB,MAAA;;;;;aACE,QAAA;;;;;;;eADF,MAAA;;;eACE,QAAA;;;;;;;;;;;;eADF,MAAA;;;eACE,QAAA;;;;;;;;;;;;;aADF,MAAA;;;;aACE,QAAA;;;cAKlB,UAAO,MAAU,aAAW;cAE5B,SAAM,QAAY,eAAa;cAG/B,yBAAsB,WAAe,cAAc,sBAAoB;cASvE,oBAAiB,MAAU,aAAW;KAWvC;WACD;WACA;WACA;;KAGC;;;;;;WAMD,UAAU,SAAS;;;;;;;;;;;cAYjB,YAAS,YAEN,MAAM,sBAAoB,UAC5B,MAAM,QAAQ,UAAQ,UACtB,sBAAgB,OAGnB,cAAc,cACpB,OAAO;WAEG,UAAU,cAAc;WACxB,WAAW,OAAO,OAAO;WACzB,kBAAkB,OAAO,OAAO;UAG3C,MAAM;KA8EE;WACG;;WACA;WAA2B;;cAE7B,UAAO,YACL,MAAM,sBAAoB,WAAa,oBAAoB,uBAAiB,OACjF,cAAc,cAAY"}
|
package/dist/tool/Approval.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as __exportAll } from "../
|
|
1
|
+
import { n as __exportAll } from "../rolldown-runtime-EAsCxrDo.mjs";
|
|
2
2
|
import { cancelled, denied } from "./ToolResult.mjs";
|
|
3
3
|
import { ToolEvent } from "./ToolEvent.mjs";
|
|
4
4
|
import { Data, Deferred, Effect, Queue, Ref, Stream } from "effect";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Approval.mjs","names":[],"sources":["../../src/tool/Approval.ts"],"sourcesContent":["/**\n * Approval helpers for the two transport flavors.\n *\n * These helpers only decide which calls are approved and which synthetic\n * results must be returned to the model. Tool execution stays explicit at\n * the recipe boundary via `Toolkit.run`.\n */\nimport { Data, Deferred, type Duration, Effect, Queue, Ref, Scope, Stream } from \"effect\"\nimport type { ToolCall } from \"../domain/Items.js\"\nimport { type ToolResult, cancelled, denied } from \"./ToolResult.js\"\nimport { ToolEvent } from \"./ToolEvent.js\"\n\nexport type ToolCallPlan = {\n readonly approved: ReadonlyArray<ToolCall>\n readonly rejected: ReadonlyArray<ToolResult>\n}\n\nexport type ApprovalDecision = Data.TaggedEnum<{\n Approved: { readonly call: ToolCall }\n Rejected: { readonly result: ToolResult }\n}>\n\nexport const ApprovalDecision = Data.taggedEnum<ApprovalDecision>()\n\nexport const approve = (call: ToolCall): ApprovalDecision => ApprovalDecision.Approved({ call })\n\nexport const reject = (result: ToolResult): ApprovalDecision =>\n ApprovalDecision.Rejected({ result })\n\nexport const splitApprovalDecisions = (decisions: ReadonlyArray<ApprovalDecision>): ToolCallPlan =>\n decisions.reduce<ToolCallPlan>(\n (acc, decision) =>\n decision._tag === \"Approved\"\n ? { ...acc, approved: [...acc.approved, decision.call] }\n : { ...acc, rejected: [...acc.rejected, decision.result] },\n { approved: [], rejected: [] },\n )\n\nexport const approvalRequested = (call: ToolCall): ToolEvent =>\n ToolEvent.ApprovalRequested({\n call_id: call.call_id,\n tool: call.name,\n arguments: call.arguments,\n })\n\n// ---------------------------------------------------------------------------\n// Verdict queue (WebSocket-style transport).\n// ---------------------------------------------------------------------------\n\nexport type Verdict = {\n readonly call_id: string\n readonly decision: \"approve\" | \"deny\"\n readonly reason?: string\n}\n\nexport type FromQueueOptions = {\n /**\n * Resolve any still-pending gated call as `cancelled` once this elapses,\n * so a lost or never-sent verdict cannot hang the decisions stream\n * forever. Omit to wait indefinitely.\n */\n readonly timeout?: Duration.Input\n}\n\n/**\n * Queue-backed approval planner. Safe calls are returned immediately in\n * `approved`; gated calls emit `ApprovalRequested` events and later produce\n * one `ApprovalDecision` when their matching verdict arrives.\n *\n * The verdict queue is a single-consumer transport: run one approval round\n * at a time (the router retires once every gated call of the round is\n * resolved). For overlapping rounds, use one queue per round.\n */\nexport const fromQueue =\n (\n predicate: (call: ToolCall) => boolean,\n verdicts: Queue.Dequeue<Verdict>,\n options?: FromQueueOptions,\n ) =>\n (\n calls: ReadonlyArray<ToolCall>,\n ): Effect.Effect<\n {\n readonly approved: ReadonlyArray<ToolCall>\n readonly decisions: Stream.Stream<ApprovalDecision>\n readonly approvalRequests: Stream.Stream<ToolEvent>\n },\n never,\n Scope.Scope\n > =>\n Effect.gen(function* () {\n const gated = calls.filter(predicate)\n const approved = calls.filter((call) => !predicate(call))\n\n const entries = yield* Effect.forEach(gated, (call) =>\n Deferred.make<ApprovalDecision>().pipe(Effect.map((d) => [call.call_id, d] as const)),\n )\n const deferreds: ReadonlyMap<string, Deferred.Deferred<ApprovalDecision>> = new Map(entries)\n const callById: ReadonlyMap<string, ToolCall> = new Map(gated.map((c) => [c.call_id, c]))\n const outstanding = yield* Ref.make(gated.length)\n\n // Complete a deferred once; decrement only on the winning transition so\n // a late verdict for an already-resolved call is a no-op.\n const resolve = (call: ToolCall, decision: ApprovalDecision) =>\n Deferred.succeed(deferreds.get(call.call_id)!, decision).pipe(\n Effect.flatMap((won) => (won ? Ref.update(outstanding, (n) => n - 1) : Effect.void)),\n )\n\n // Router retires once every gated call is resolved (by verdict or\n // timeout), so sequential rounds over one queue do not accumulate\n // racing routers. Recursive + suspended: stack-safe, no imperative loop.\n const route: Effect.Effect<void> = Effect.suspend(() =>\n Ref.get(outstanding).pipe(\n Effect.flatMap((n) =>\n n <= 0\n ? Effect.void\n : Queue.take(verdicts).pipe(\n Effect.flatMap((v) => {\n const call = callById.get(v.call_id)\n return (\n call === undefined\n ? Effect.void\n : resolve(\n call,\n v.decision === \"approve\"\n ? approve(call)\n : reject(denied(call, v.reason)),\n )\n ).pipe(Effect.andThen(route))\n }),\n ),\n ),\n ),\n )\n\n if (gated.length > 0) {\n yield* Effect.forkScoped(route)\n if (options?.timeout !== undefined) {\n yield* Effect.forkScoped(\n Effect.sleep(options.timeout).pipe(\n Effect.andThen(\n Effect.forEach(gated, (call) =>\n resolve(call, reject(cancelled(call, \"approval timed out\"))),\n ),\n ),\n ),\n )\n }\n }\n\n const decisions = Stream.fromIterable(gated).pipe(\n Stream.flatMap((call) => Stream.fromEffect(Deferred.await(deferreds.get(call.call_id)!)), {\n concurrency: \"unbounded\",\n }),\n )\n\n const approvalRequests = Stream.fromIterable<ToolEvent>(gated.map(approvalRequested))\n\n return { approved, decisions, approvalRequests }\n })\n\n// ---------------------------------------------------------------------------\n// Approval map (HTTP-style transport). Verdicts arrive synchronously\n// bundled in the request payload. Missing entries → cancelled.\n// ---------------------------------------------------------------------------\n\nexport type ApprovalMapEntry =\n | { readonly decision: \"approve\" }\n | { readonly decision: \"deny\"; readonly reason?: string }\n\nexport const fromMap =\n (predicate: (call: ToolCall) => boolean, approvals: ReadonlyMap<string, ApprovalMapEntry>) =>\n (calls: ReadonlyArray<ToolCall>): ToolCallPlan =>\n splitApprovalDecisions(\n calls.map((call) => {\n if (!predicate(call)) return approve(call)\n const v = approvals.get(call.call_id)\n if (v === undefined) return reject(cancelled(call))\n return v.decision === \"approve\" ? approve(call) : reject(denied(call, v.reason))\n }),\n )\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,mBAAmB,KAAK,
|
|
1
|
+
{"version":3,"file":"Approval.mjs","names":[],"sources":["../../src/tool/Approval.ts"],"sourcesContent":["/**\n * Approval helpers for the two transport flavors.\n *\n * These helpers only decide which calls are approved and which synthetic\n * results must be returned to the model. Tool execution stays explicit at\n * the recipe boundary via `Toolkit.run`.\n */\nimport { Data, Deferred, type Duration, Effect, Queue, Ref, Scope, Stream } from \"effect\"\nimport type { ToolCall } from \"../domain/Items.js\"\nimport { type ToolResult, cancelled, denied } from \"./ToolResult.js\"\nimport { ToolEvent } from \"./ToolEvent.js\"\n\nexport type ToolCallPlan = {\n readonly approved: ReadonlyArray<ToolCall>\n readonly rejected: ReadonlyArray<ToolResult>\n}\n\nexport type ApprovalDecision = Data.TaggedEnum<{\n Approved: { readonly call: ToolCall }\n Rejected: { readonly result: ToolResult }\n}>\n\nexport const ApprovalDecision = Data.taggedEnum<ApprovalDecision>()\n\nexport const approve = (call: ToolCall): ApprovalDecision => ApprovalDecision.Approved({ call })\n\nexport const reject = (result: ToolResult): ApprovalDecision =>\n ApprovalDecision.Rejected({ result })\n\nexport const splitApprovalDecisions = (decisions: ReadonlyArray<ApprovalDecision>): ToolCallPlan =>\n decisions.reduce<ToolCallPlan>(\n (acc, decision) =>\n decision._tag === \"Approved\"\n ? { ...acc, approved: [...acc.approved, decision.call] }\n : { ...acc, rejected: [...acc.rejected, decision.result] },\n { approved: [], rejected: [] },\n )\n\nexport const approvalRequested = (call: ToolCall): ToolEvent =>\n ToolEvent.ApprovalRequested({\n call_id: call.call_id,\n tool: call.name,\n arguments: call.arguments,\n })\n\n// ---------------------------------------------------------------------------\n// Verdict queue (WebSocket-style transport).\n// ---------------------------------------------------------------------------\n\nexport type Verdict = {\n readonly call_id: string\n readonly decision: \"approve\" | \"deny\"\n readonly reason?: string\n}\n\nexport type FromQueueOptions = {\n /**\n * Resolve any still-pending gated call as `cancelled` once this elapses,\n * so a lost or never-sent verdict cannot hang the decisions stream\n * forever. Omit to wait indefinitely.\n */\n readonly timeout?: Duration.Input\n}\n\n/**\n * Queue-backed approval planner. Safe calls are returned immediately in\n * `approved`; gated calls emit `ApprovalRequested` events and later produce\n * one `ApprovalDecision` when their matching verdict arrives.\n *\n * The verdict queue is a single-consumer transport: run one approval round\n * at a time (the router retires once every gated call of the round is\n * resolved). For overlapping rounds, use one queue per round.\n */\nexport const fromQueue =\n (\n predicate: (call: ToolCall) => boolean,\n verdicts: Queue.Dequeue<Verdict>,\n options?: FromQueueOptions,\n ) =>\n (\n calls: ReadonlyArray<ToolCall>,\n ): Effect.Effect<\n {\n readonly approved: ReadonlyArray<ToolCall>\n readonly decisions: Stream.Stream<ApprovalDecision>\n readonly approvalRequests: Stream.Stream<ToolEvent>\n },\n never,\n Scope.Scope\n > =>\n Effect.gen(function* () {\n const gated = calls.filter(predicate)\n const approved = calls.filter((call) => !predicate(call))\n\n const entries = yield* Effect.forEach(gated, (call) =>\n Deferred.make<ApprovalDecision>().pipe(Effect.map((d) => [call.call_id, d] as const)),\n )\n const deferreds: ReadonlyMap<string, Deferred.Deferred<ApprovalDecision>> = new Map(entries)\n const callById: ReadonlyMap<string, ToolCall> = new Map(gated.map((c) => [c.call_id, c]))\n const outstanding = yield* Ref.make(gated.length)\n\n // Complete a deferred once; decrement only on the winning transition so\n // a late verdict for an already-resolved call is a no-op.\n const resolve = (call: ToolCall, decision: ApprovalDecision) =>\n Deferred.succeed(deferreds.get(call.call_id)!, decision).pipe(\n Effect.flatMap((won) => (won ? Ref.update(outstanding, (n) => n - 1) : Effect.void)),\n )\n\n // Router retires once every gated call is resolved (by verdict or\n // timeout), so sequential rounds over one queue do not accumulate\n // racing routers. Recursive + suspended: stack-safe, no imperative loop.\n const route: Effect.Effect<void> = Effect.suspend(() =>\n Ref.get(outstanding).pipe(\n Effect.flatMap((n) =>\n n <= 0\n ? Effect.void\n : Queue.take(verdicts).pipe(\n Effect.flatMap((v) => {\n const call = callById.get(v.call_id)\n return (\n call === undefined\n ? Effect.void\n : resolve(\n call,\n v.decision === \"approve\"\n ? approve(call)\n : reject(denied(call, v.reason)),\n )\n ).pipe(Effect.andThen(route))\n }),\n ),\n ),\n ),\n )\n\n if (gated.length > 0) {\n yield* Effect.forkScoped(route)\n if (options?.timeout !== undefined) {\n yield* Effect.forkScoped(\n Effect.sleep(options.timeout).pipe(\n Effect.andThen(\n Effect.forEach(gated, (call) =>\n resolve(call, reject(cancelled(call, \"approval timed out\"))),\n ),\n ),\n ),\n )\n }\n }\n\n const decisions = Stream.fromIterable(gated).pipe(\n Stream.flatMap((call) => Stream.fromEffect(Deferred.await(deferreds.get(call.call_id)!)), {\n concurrency: \"unbounded\",\n }),\n )\n\n const approvalRequests = Stream.fromIterable<ToolEvent>(gated.map(approvalRequested))\n\n return { approved, decisions, approvalRequests }\n })\n\n// ---------------------------------------------------------------------------\n// Approval map (HTTP-style transport). Verdicts arrive synchronously\n// bundled in the request payload. Missing entries → cancelled.\n// ---------------------------------------------------------------------------\n\nexport type ApprovalMapEntry =\n | { readonly decision: \"approve\" }\n | { readonly decision: \"deny\"; readonly reason?: string }\n\nexport const fromMap =\n (predicate: (call: ToolCall) => boolean, approvals: ReadonlyMap<string, ApprovalMapEntry>) =>\n (calls: ReadonlyArray<ToolCall>): ToolCallPlan =>\n splitApprovalDecisions(\n calls.map((call) => {\n if (!predicate(call)) return approve(call)\n const v = approvals.get(call.call_id)\n if (v === undefined) return reject(cancelled(call))\n return v.decision === \"approve\" ? approve(call) : reject(denied(call, v.reason))\n }),\n )\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|