@yolk-sdk/emulators 0.1.0-canary.96
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/LICENSE +21 -0
- package/README.md +2135 -0
- package/dist/anthropic.d.mts +269 -0
- package/dist/anthropic.d.mts.map +1 -0
- package/dist/anthropic.mjs +177 -0
- package/dist/anthropic.mjs.map +1 -0
- package/dist/chat-completions.d.mts +288 -0
- package/dist/chat-completions.d.mts.map +1 -0
- package/dist/chat-completions.mjs +451 -0
- package/dist/chat-completions.mjs.map +1 -0
- package/dist/codex.d.mts +265 -0
- package/dist/codex.d.mts.map +1 -0
- package/dist/codex.mjs +199 -0
- package/dist/codex.mjs.map +1 -0
- package/dist/dropbox/api.d.mts +63 -0
- package/dist/dropbox/api.d.mts.map +1 -0
- package/dist/dropbox/api.mjs +565 -0
- package/dist/dropbox/api.mjs.map +1 -0
- package/dist/dropbox/state.d.mts +136 -0
- package/dist/dropbox/state.d.mts.map +1 -0
- package/dist/dropbox/state.mjs +209 -0
- package/dist/dropbox/state.mjs.map +1 -0
- package/dist/dropbox.d.mts +79 -0
- package/dist/dropbox.d.mts.map +1 -0
- package/dist/dropbox.mjs +124 -0
- package/dist/dropbox.mjs.map +1 -0
- package/dist/email-fixtures.d.mts +36 -0
- package/dist/email-fixtures.d.mts.map +1 -0
- package/dist/email-fixtures.mjs +1080 -0
- package/dist/email-fixtures.mjs.map +1 -0
- package/dist/email.d.mts +160 -0
- package/dist/email.d.mts.map +1 -0
- package/dist/email.mjs +608 -0
- package/dist/email.mjs.map +1 -0
- package/dist/emulator-compose.d.mts +40 -0
- package/dist/emulator-compose.d.mts.map +1 -0
- package/dist/emulator-compose.mjs +91 -0
- package/dist/emulator-compose.mjs.map +1 -0
- package/dist/emulator-http.d.mts +53 -0
- package/dist/emulator-http.d.mts.map +1 -0
- package/dist/emulator-http.mjs +117 -0
- package/dist/emulator-http.mjs.map +1 -0
- package/dist/emulator-kernel.d.mts +176 -0
- package/dist/emulator-kernel.d.mts.map +1 -0
- package/dist/emulator-kernel.mjs +413 -0
- package/dist/emulator-kernel.mjs.map +1 -0
- package/dist/fixture-route.d.mts +129 -0
- package/dist/fixture-route.d.mts.map +1 -0
- package/dist/fixture-route.mjs +257 -0
- package/dist/fixture-route.mjs.map +1 -0
- package/dist/fortnox/api.d.mts +92 -0
- package/dist/fortnox/api.d.mts.map +1 -0
- package/dist/fortnox/api.mjs +750 -0
- package/dist/fortnox/api.mjs.map +1 -0
- package/dist/fortnox/state.d.mts +533 -0
- package/dist/fortnox/state.d.mts.map +1 -0
- package/dist/fortnox/state.mjs +612 -0
- package/dist/fortnox/state.mjs.map +1 -0
- package/dist/fortnox.d.mts +128 -0
- package/dist/fortnox.d.mts.map +1 -0
- package/dist/fortnox.mjs +403 -0
- package/dist/fortnox.mjs.map +1 -0
- package/dist/gateway-evaluate-recordings.d.mts +12 -0
- package/dist/gateway-evaluate-recordings.d.mts.map +1 -0
- package/dist/gateway-evaluate-recordings.mjs +128 -0
- package/dist/gateway-evaluate-recordings.mjs.map +1 -0
- package/dist/gateway.d.mts +272 -0
- package/dist/gateway.d.mts.map +1 -0
- package/dist/gateway.mjs +400 -0
- package/dist/gateway.mjs.map +1 -0
- package/dist/github/api.d.mts +63 -0
- package/dist/github/api.d.mts.map +1 -0
- package/dist/github/api.mjs +527 -0
- package/dist/github/api.mjs.map +1 -0
- package/dist/github/state.d.mts +272 -0
- package/dist/github/state.d.mts.map +1 -0
- package/dist/github/state.mjs +303 -0
- package/dist/github/state.mjs.map +1 -0
- package/dist/github.d.mts +77 -0
- package/dist/github.d.mts.map +1 -0
- package/dist/github.mjs +180 -0
- package/dist/github.mjs.map +1 -0
- package/dist/google/calendar.d.mts +10 -0
- package/dist/google/calendar.d.mts.map +1 -0
- package/dist/google/calendar.mjs +252 -0
- package/dist/google/calendar.mjs.map +1 -0
- package/dist/google/drive.d.mts +8 -0
- package/dist/google/drive.d.mts.map +1 -0
- package/dist/google/drive.mjs +252 -0
- package/dist/google/drive.mjs.map +1 -0
- package/dist/google/gmail.d.mts +10 -0
- package/dist/google/gmail.d.mts.map +1 -0
- package/dist/google/gmail.mjs +591 -0
- package/dist/google/gmail.mjs.map +1 -0
- package/dist/google/shared.d.mts +107 -0
- package/dist/google/shared.d.mts.map +1 -0
- package/dist/google/shared.mjs +146 -0
- package/dist/google/shared.mjs.map +1 -0
- package/dist/google/state.d.mts +385 -0
- package/dist/google/state.d.mts.map +1 -0
- package/dist/google/state.mjs +496 -0
- package/dist/google/state.mjs.map +1 -0
- package/dist/google.d.mts +75 -0
- package/dist/google.d.mts.map +1 -0
- package/dist/google.mjs +210 -0
- package/dist/google.mjs.map +1 -0
- package/dist/linkedin-search/api.d.mts +45 -0
- package/dist/linkedin-search/api.d.mts.map +1 -0
- package/dist/linkedin-search/api.mjs +181 -0
- package/dist/linkedin-search/api.mjs.map +1 -0
- package/dist/linkedin-search/state.d.mts +123 -0
- package/dist/linkedin-search/state.d.mts.map +1 -0
- package/dist/linkedin-search/state.mjs +261 -0
- package/dist/linkedin-search/state.mjs.map +1 -0
- package/dist/linkedin-search.d.mts +69 -0
- package/dist/linkedin-search.d.mts.map +1 -0
- package/dist/linkedin-search.mjs +158 -0
- package/dist/linkedin-search.mjs.map +1 -0
- package/dist/mcp/api.d.mts +44 -0
- package/dist/mcp/api.d.mts.map +1 -0
- package/dist/mcp/api.mjs +557 -0
- package/dist/mcp/api.mjs.map +1 -0
- package/dist/mcp/recordings.d.mts +40 -0
- package/dist/mcp/recordings.d.mts.map +1 -0
- package/dist/mcp/recordings.mjs +2390 -0
- package/dist/mcp/recordings.mjs.map +1 -0
- package/dist/mcp/state.d.mts +71 -0
- package/dist/mcp/state.d.mts.map +1 -0
- package/dist/mcp/state.mjs +83 -0
- package/dist/mcp/state.mjs.map +1 -0
- package/dist/mcp.d.mts +84 -0
- package/dist/mcp.d.mts.map +1 -0
- package/dist/mcp.mjs +241 -0
- package/dist/mcp.mjs.map +1 -0
- package/dist/messages.d.mts +183 -0
- package/dist/messages.d.mts.map +1 -0
- package/dist/messages.mjs +532 -0
- package/dist/messages.mjs.map +1 -0
- package/dist/microsoft/api.d.mts +47 -0
- package/dist/microsoft/api.d.mts.map +1 -0
- package/dist/microsoft/api.mjs +178 -0
- package/dist/microsoft/api.mjs.map +1 -0
- package/dist/microsoft/calendar.d.mts +30 -0
- package/dist/microsoft/calendar.d.mts.map +1 -0
- package/dist/microsoft/calendar.mjs +271 -0
- package/dist/microsoft/calendar.mjs.map +1 -0
- package/dist/microsoft/drive.d.mts +36 -0
- package/dist/microsoft/drive.d.mts.map +1 -0
- package/dist/microsoft/drive.mjs +298 -0
- package/dist/microsoft/drive.mjs.map +1 -0
- package/dist/microsoft/graph.d.mts +198 -0
- package/dist/microsoft/graph.d.mts.map +1 -0
- package/dist/microsoft/graph.mjs +264 -0
- package/dist/microsoft/graph.mjs.map +1 -0
- package/dist/microsoft/mail.d.mts +48 -0
- package/dist/microsoft/mail.d.mts.map +1 -0
- package/dist/microsoft/mail.mjs +488 -0
- package/dist/microsoft/mail.mjs.map +1 -0
- package/dist/microsoft/state.d.mts +480 -0
- package/dist/microsoft/state.d.mts.map +1 -0
- package/dist/microsoft/state.mjs +625 -0
- package/dist/microsoft/state.mjs.map +1 -0
- package/dist/microsoft.d.mts +168 -0
- package/dist/microsoft.d.mts.map +1 -0
- package/dist/microsoft.mjs +483 -0
- package/dist/microsoft.mjs.map +1 -0
- package/dist/node.d.mts +47 -0
- package/dist/node.d.mts.map +1 -0
- package/dist/node.mjs +178 -0
- package/dist/node.mjs.map +1 -0
- package/dist/notion/api.d.mts +51 -0
- package/dist/notion/api.d.mts.map +1 -0
- package/dist/notion/api.mjs +507 -0
- package/dist/notion/api.mjs.map +1 -0
- package/dist/notion/state.d.mts +329 -0
- package/dist/notion/state.d.mts.map +1 -0
- package/dist/notion/state.mjs +432 -0
- package/dist/notion/state.mjs.map +1 -0
- package/dist/notion.d.mts +72 -0
- package/dist/notion.d.mts.map +1 -0
- package/dist/notion.mjs +127 -0
- package/dist/notion.mjs.map +1 -0
- package/dist/openai.d.mts +165 -0
- package/dist/openai.d.mts.map +1 -0
- package/dist/openai.mjs +141 -0
- package/dist/openai.mjs.map +1 -0
- package/dist/opencode-recordings.d.mts +7 -0
- package/dist/opencode-recordings.d.mts.map +1 -0
- package/dist/opencode-recordings.mjs +222 -0
- package/dist/opencode-recordings.mjs.map +1 -0
- package/dist/opencode.d.mts +70 -0
- package/dist/opencode.d.mts.map +1 -0
- package/dist/opencode.mjs +217 -0
- package/dist/opencode.mjs.map +1 -0
- package/dist/r2-fixtures.d.mts +36 -0
- package/dist/r2-fixtures.d.mts.map +1 -0
- package/dist/r2-fixtures.mjs +245 -0
- package/dist/r2-fixtures.mjs.map +1 -0
- package/dist/r2-guard.d.mts +43 -0
- package/dist/r2-guard.d.mts.map +1 -0
- package/dist/r2-guard.mjs +215 -0
- package/dist/r2-guard.mjs.map +1 -0
- package/dist/r2.d.mts +166 -0
- package/dist/r2.d.mts.map +1 -0
- package/dist/r2.mjs +517 -0
- package/dist/r2.mjs.map +1 -0
- package/dist/responses.d.mts +231 -0
- package/dist/responses.d.mts.map +1 -0
- package/dist/responses.mjs +556 -0
- package/dist/responses.mjs.map +1 -0
- package/dist/route-evidence.d.mts +45 -0
- package/dist/route-evidence.d.mts.map +1 -0
- package/dist/route-evidence.mjs +56 -0
- package/dist/route-evidence.mjs.map +1 -0
- package/dist/router.d.mts +93 -0
- package/dist/router.d.mts.map +1 -0
- package/dist/router.mjs +259 -0
- package/dist/router.mjs.map +1 -0
- package/dist/stateful-core.d.mts +17 -0
- package/dist/stateful-core.d.mts.map +1 -0
- package/dist/stateful-core.mjs +41 -0
- package/dist/stateful-core.mjs.map +1 -0
- package/dist/stateful-emulator.d.mts +565 -0
- package/dist/stateful-emulator.d.mts.map +1 -0
- package/dist/stateful-emulator.mjs +1228 -0
- package/dist/stateful-emulator.mjs.map +1 -0
- package/dist/stateful-secrets.d.mts +84 -0
- package/dist/stateful-secrets.d.mts.map +1 -0
- package/dist/stateful-secrets.mjs +216 -0
- package/dist/stateful-secrets.mjs.map +1 -0
- package/dist/subscription-usage-recordings.d.mts +9 -0
- package/dist/subscription-usage-recordings.d.mts.map +1 -0
- package/dist/subscription-usage-recordings.mjs +53 -0
- package/dist/subscription-usage-recordings.mjs.map +1 -0
- package/dist/subscription-usage.d.mts +53 -0
- package/dist/subscription-usage.d.mts.map +1 -0
- package/dist/subscription-usage.mjs +21 -0
- package/dist/subscription-usage.mjs.map +1 -0
- package/dist/telegram/api.d.mts +34 -0
- package/dist/telegram/api.d.mts.map +1 -0
- package/dist/telegram/api.mjs +257 -0
- package/dist/telegram/api.mjs.map +1 -0
- package/dist/telegram/state.d.mts +128 -0
- package/dist/telegram/state.d.mts.map +1 -0
- package/dist/telegram/state.mjs +154 -0
- package/dist/telegram/state.mjs.map +1 -0
- package/dist/telegram.d.mts +62 -0
- package/dist/telegram.d.mts.map +1 -0
- package/dist/telegram.mjs +127 -0
- package/dist/telegram.mjs.map +1 -0
- package/dist/todoist/api.d.mts +55 -0
- package/dist/todoist/api.d.mts.map +1 -0
- package/dist/todoist/api.mjs +415 -0
- package/dist/todoist/api.mjs.map +1 -0
- package/dist/todoist/state.d.mts +275 -0
- package/dist/todoist/state.d.mts.map +1 -0
- package/dist/todoist/state.mjs +345 -0
- package/dist/todoist/state.mjs.map +1 -0
- package/dist/todoist.d.mts +68 -0
- package/dist/todoist.d.mts.map +1 -0
- package/dist/todoist.mjs +181 -0
- package/dist/todoist.mjs.map +1 -0
- package/dist/xai.d.mts +267 -0
- package/dist/xai.d.mts.map +1 -0
- package/dist/xai.mjs +252 -0
- package/dist/xai.mjs.map +1 -0
- package/package.json +157 -0
- package/src/anthropic.ts +289 -0
- package/src/chat-completions.ts +924 -0
- package/src/codex.ts +296 -0
- package/src/dropbox/api.ts +1084 -0
- package/src/dropbox/state.ts +364 -0
- package/src/dropbox.ts +203 -0
- package/src/email-fixtures.ts +1297 -0
- package/src/email.ts +1108 -0
- package/src/emulator-compose.ts +147 -0
- package/src/emulator-http.ts +184 -0
- package/src/emulator-kernel.ts +844 -0
- package/src/fixture-route.ts +561 -0
- package/src/fortnox/api.ts +1352 -0
- package/src/fortnox/state.ts +798 -0
- package/src/fortnox.ts +801 -0
- package/src/gateway-evaluate-recordings.ts +153 -0
- package/src/gateway.ts +530 -0
- package/src/github/api.ts +986 -0
- package/src/github/state.ts +439 -0
- package/src/github.ts +271 -0
- package/src/google/calendar.ts +486 -0
- package/src/google/drive.ts +471 -0
- package/src/google/gmail.ts +1011 -0
- package/src/google/shared.ts +321 -0
- package/src/google/state.ts +686 -0
- package/src/google.ts +298 -0
- package/src/linkedin-search/api.ts +363 -0
- package/src/linkedin-search/state.ts +373 -0
- package/src/linkedin-search.ts +241 -0
- package/src/mcp/api.ts +995 -0
- package/src/mcp/recordings.ts +2665 -0
- package/src/mcp/state.ts +125 -0
- package/src/mcp.ts +319 -0
- package/src/messages.ts +844 -0
- package/src/microsoft/api.ts +426 -0
- package/src/microsoft/calendar.ts +491 -0
- package/src/microsoft/drive.ts +541 -0
- package/src/microsoft/graph.ts +550 -0
- package/src/microsoft/mail.ts +833 -0
- package/src/microsoft/state.ts +822 -0
- package/src/microsoft.ts +982 -0
- package/src/node.ts +293 -0
- package/src/notion/api.ts +976 -0
- package/src/notion/state.ts +512 -0
- package/src/notion.ts +210 -0
- package/src/openai.ts +198 -0
- package/src/opencode-recordings.ts +257 -0
- package/src/opencode.ts +271 -0
- package/src/r2-fixtures.ts +295 -0
- package/src/r2-guard.ts +323 -0
- package/src/r2.ts +890 -0
- package/src/responses.ts +901 -0
- package/src/route-evidence.ts +90 -0
- package/src/router.ts +490 -0
- package/src/stateful-core.ts +70 -0
- package/src/stateful-emulator.ts +2571 -0
- package/src/stateful-secrets.ts +299 -0
- package/src/subscription-usage-recordings.ts +77 -0
- package/src/subscription-usage.ts +68 -0
- package/src/telegram/api.ts +437 -0
- package/src/telegram/state.ts +229 -0
- package/src/telegram.ts +227 -0
- package/src/todoist/api.ts +736 -0
- package/src/todoist/state.ts +456 -0
- package/src/todoist.ts +316 -0
- package/src/xai.ts +341 -0
package/dist/mcp.d.mts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { EmulatorEvidence, EmulatorRouteEvidence, emulatorEvidenceHeader } from "./route-evidence.mjs";
|
|
2
|
+
import { StatefulCoverage, StatefulEmulatorApi, StatefulFaultMatch, StatefulFaultState, StatefulInputKind, StatefulLedgerEntry, StatefulStreamFault } from "./stateful-emulator.mjs";
|
|
3
|
+
import { McpRecordedExchange, McpRecordedFixture, McpRecordedRequest, McpRecordedResponse, mcpEmulatorFixtures } from "./mcp/recordings.mjs";
|
|
4
|
+
import { McpEmulatorModernListing, McpEmulatorSeed, McpEmulatorSession, McpEmulatorState, McpEmulatorStateSchema, mcpEmulatorLegacyPath, mcpEmulatorModernPath, mcpEmulatorOrigin, mcpEmulatorReservedInvalidCredential, mcpEmulatorSessionCap, mcpEmulatorSessionPrefix } from "./mcp/state.mjs";
|
|
5
|
+
import { McpEmulatorDrills, isMcpEmulatedRequestId, mcpEmulatorDrillKnobs } from "./mcp/api.mjs";
|
|
6
|
+
|
|
7
|
+
//#region src/mcp.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Route evidence manifest: one `RPC <origin><path>#<method>` row per recorded JSON-RPC method of
|
|
10
|
+
* each profile, plus the legacy `GET` row, with the conformance cases whose (synthetic,
|
|
11
|
+
* unverified) fixtures each answers. No row writes anything. Kept in sync with the handlers by
|
|
12
|
+
* construction (both come from one route table).
|
|
13
|
+
*/
|
|
14
|
+
declare const mcpEmulatorRoutes: ReadonlyArray<EmulatorRouteEvidence>;
|
|
15
|
+
/**
|
|
16
|
+
* Optional fault filter; an omitted field matches every request. `method` is the HTTP method
|
|
17
|
+
* (`POST` or `GET`, never a row's `RPC`); `path` is the raw request path (ending in `*`, a
|
|
18
|
+
* prefix); `route` is a manifest row path (for example
|
|
19
|
+
* `https://mcp.example.test/legacy/mcp#tools/list`), and a value naming no row is rejected when
|
|
20
|
+
* the fault is added.
|
|
21
|
+
*/
|
|
22
|
+
declare const McpFaultMatch: import("effect/Schema").Struct<{
|
|
23
|
+
readonly method: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
24
|
+
readonly path: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
25
|
+
readonly route: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
26
|
+
}>;
|
|
27
|
+
type McpFaultMatch = typeof StatefulFaultMatch.Type;
|
|
28
|
+
/**
|
|
29
|
+
* A status fault (400-599), or a `truncate-after-chunks` fault that sends the first `chunks`
|
|
30
|
+
* chunks of the prepared answer and then ends it cleanly (an SSE answer has two chunks, a JSON
|
|
31
|
+
* answer one). Only a request the emulator would answer reaches a fault: a request that is not
|
|
32
|
+
* emulated, by its shape or by the state, never uses one up. A faulted request writes nothing (a
|
|
33
|
+
* truncated `initialize` mints no session; a truncated first page issues no cursor). A truncation
|
|
34
|
+
* that cannot apply answers 500 and is not used up.
|
|
35
|
+
*/
|
|
36
|
+
declare const McpFault: import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
37
|
+
readonly kind: import("effect/Schema").Literal<"status">;
|
|
38
|
+
readonly status: import("effect/Schema").Int;
|
|
39
|
+
readonly headers: import("effect/Schema").optionalKey<import("effect/Schema").$Record<import("effect/Schema").String, import("effect/Schema").String>>;
|
|
40
|
+
readonly body: import("effect/Schema").optionalKey<import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>>;
|
|
41
|
+
readonly match: import("effect/Schema").optionalKey<import("effect/Schema").Struct<{
|
|
42
|
+
readonly method: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
43
|
+
readonly path: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
44
|
+
readonly route: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
45
|
+
}>>;
|
|
46
|
+
readonly count: import("effect/Schema").optionalKey<import("effect/Schema").Int>;
|
|
47
|
+
}>, import("effect/Schema").Struct<{
|
|
48
|
+
readonly kind: import("effect/Schema").Literal<"truncate-after-chunks">;
|
|
49
|
+
readonly chunks: import("effect/Schema").Int;
|
|
50
|
+
readonly match: import("effect/Schema").optionalKey<import("effect/Schema").Struct<{
|
|
51
|
+
readonly method: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
52
|
+
readonly path: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
53
|
+
readonly route: import("effect/Schema").optionalKey<import("effect/Schema").String>;
|
|
54
|
+
}>>;
|
|
55
|
+
readonly count: import("effect/Schema").optionalKey<import("effect/Schema").Int>;
|
|
56
|
+
}>]>;
|
|
57
|
+
type McpFault = StatefulStreamFault;
|
|
58
|
+
type McpFaultState = StatefulFaultState<McpFault>;
|
|
59
|
+
type McpLedgerEntry = StatefulLedgerEntry;
|
|
60
|
+
type McpCoverage = StatefulCoverage;
|
|
61
|
+
declare const McpEmulatorInputInvalid_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => import("effect/Cause").YieldableError & {
|
|
62
|
+
readonly _tag: "McpEmulatorInputInvalid";
|
|
63
|
+
} & Readonly<A>;
|
|
64
|
+
/** Invalid emulator input from the JS API: a seed, a fault, or an option. A programmer error. */
|
|
65
|
+
declare class McpEmulatorInputInvalid extends McpEmulatorInputInvalid_base<{
|
|
66
|
+
readonly input: StatefulInputKind;
|
|
67
|
+
readonly reason: string;
|
|
68
|
+
}> {
|
|
69
|
+
get message(): string;
|
|
70
|
+
}
|
|
71
|
+
type McpEmulatorOptions = {
|
|
72
|
+
/** Typed seed; defaults to the one-page modern listing (sessions are never seeded). */readonly seed?: McpEmulatorSeed; /** Drill knobs (tests only): make the emulator disagree with one conformance claim. */
|
|
73
|
+
readonly drills?: McpEmulatorDrills;
|
|
74
|
+
};
|
|
75
|
+
type McpEmulator = StatefulEmulatorApi<McpEmulatorState, McpEmulatorSeed, McpFault>;
|
|
76
|
+
/**
|
|
77
|
+
* Create a stateful MCP emulator on the `@emulators/core` custom runtime. Each call has its own
|
|
78
|
+
* state, ledger, faults, and session counter. Rejects with `McpEmulatorInputInvalid` for an
|
|
79
|
+
* invalid seed or option. See `src/stateful-emulator.ts` for the request precedence.
|
|
80
|
+
*/
|
|
81
|
+
declare const makeMcpEmulator: (options?: McpEmulatorOptions) => Promise<McpEmulator>;
|
|
82
|
+
//#endregion
|
|
83
|
+
export { type EmulatorEvidence, type EmulatorRouteEvidence, McpCoverage, McpEmulator, type McpEmulatorDrills, McpEmulatorInputInvalid, McpEmulatorModernListing, McpEmulatorOptions, McpEmulatorSeed, McpEmulatorSession, type McpEmulatorState, McpEmulatorStateSchema, McpFault, McpFaultMatch, McpFaultState, McpLedgerEntry, type McpRecordedExchange, type McpRecordedFixture, type McpRecordedRequest, type McpRecordedResponse, emulatorEvidenceHeader, isMcpEmulatedRequestId, makeMcpEmulator, mcpEmulatorDrillKnobs, mcpEmulatorFixtures, mcpEmulatorLegacyPath, mcpEmulatorModernPath, mcpEmulatorOrigin, mcpEmulatorReservedInvalidCredential, mcpEmulatorRoutes, mcpEmulatorSessionCap, mcpEmulatorSessionPrefix };
|
|
84
|
+
//# sourceMappingURL=mcp.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.mts","names":[],"sources":["../src/mcp.ts"],"mappings":";;;;;;;;;AA6KmE;AAUnE;;;cAVa,iBAAA,EAAmB,aAAa,CAAC,qBAAA;;;;;;;;cAUjC,aAAA,0BAAa,MAAA;EAAA;;;;KAEd,aAAA,UAAuB,kBAAA,CAAmB,IAAI;;;;AAAA;AAU1D;;;;cAAa,QAAA,0BAAQ,KAAA,mCAAA,MAAA;EAAA;;;;;;;;;;;;;;;;;;;;KAET,QAAA,GAAW,mBAAmB;AAAA,KAE9B,aAAA,GAAgB,kBAAkB,CAAC,QAAA;AAAA,KAEnC,cAAA,GAAiB,mBAAmB;AAAA,KAEpC,WAAA,GAAc,gBAAgB;AAAA,cAAA,4BAAA;;;;cAG7B,uBAAA,SAAgC,4BAAA;EAAA,SAClC,KAAA,EAAO,iBAAA;EAAA,SACP,MAAA;AAAA;EAAA,IAEI,OAAA,CAAA;AAAA;AAAA,KAKH,kBAAA;kGAED,IAAA,GAAO,eAAA;WAEP,MAAA,GAAS,iBAAiB;AAAA;AAAA,KAGzB,WAAA,GAAc,mBAAA,CAAoB,gBAAA,EAAkB,eAAA,EAAiB,QAAA;;;;;;cAgBpE,eAAA,GAAyB,OAAA,GAAS,kBAAA,KAA0B,OAAA,CAAQ,WAAA"}
|
package/dist/mcp.mjs
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { emulatorEvidenceHeader } from "./route-evidence.mjs";
|
|
2
|
+
import { isRecognisableBearerValue } from "./stateful-secrets.mjs";
|
|
3
|
+
import { StatefulFaultMatch, StatefulStreamFault, checkBooleanDrills, makeChunkedStatefulEmulator, routeManifest } from "./stateful-emulator.mjs";
|
|
4
|
+
import { statefulCoreRuntime } from "./stateful-core.mjs";
|
|
5
|
+
import { mcpEmulatorFixtures } from "./mcp/recordings.mjs";
|
|
6
|
+
import { McpEmulatorModernListing, McpEmulatorSeed, McpEmulatorSession, McpEmulatorStateSchema, buildSeedState, decodeState, makeMcpRuntime, mcpBearerDigest, mcpEmulatorLegacyPath, mcpEmulatorModernPath, mcpEmulatorOrigin, mcpEmulatorReservedInvalidCredential, mcpEmulatorSessionCap, mcpEmulatorSessionPrefix } from "./mcp/state.mjs";
|
|
7
|
+
import { isMcpEmulatedRequestId, mcpApiRoutes, mcpEmulatorDrillKnobs, mcpRecordingProblems } from "./mcp/api.mjs";
|
|
8
|
+
import { Data, Predicate } from "effect";
|
|
9
|
+
//#region src/mcp.ts
|
|
10
|
+
/**
|
|
11
|
+
* Stateful MCP emulator (the two synthetic servers of the MCP conformance fixtures: profile
|
|
12
|
+
* `synthetic-modern` on `https://mcp.example.test/modern/mcp`, stateless `2026-07-28` with JSON
|
|
13
|
+
* answers, and profile `synthetic-legacy` on `https://mcp.example.test/legacy/mcp`, an
|
|
14
|
+
* `initialize` handshake with sessions and SSE answers), built on the upstream `@emulators/core`
|
|
15
|
+
* custom runtime, with a request ledger, status and truncation faults, and an `/_emulate/*`
|
|
16
|
+
* control plane.
|
|
17
|
+
*
|
|
18
|
+
* It never imports SDK code: its answers are the sixteen synthetic `@yolk-sdk/mcp/conformance`
|
|
19
|
+
* fixtures copied as data (`src/mcp/recordings.ts`), and every manifest row names the conformance
|
|
20
|
+
* cases whose fixtures it answers (`mcpEmulatorRoutes`: one `RPC <origin><path>#<method>` row per
|
|
21
|
+
* recorded JSON-RPC method of each profile, plus the legacy `GET` row; no row writes anything in a
|
|
22
|
+
* real service). Response behaviour comes only from the fixtures, byte for byte: a JSON-RPC POST is
|
|
23
|
+
* answered only when it equals a recorded request within the latitude below, with the recorded
|
|
24
|
+
* answer, the request id substituted at exactly the recorded place (the top-level `id` of a JSON
|
|
25
|
+
* answer, or the `id` of the SSE response event's payload; notification events and SSE `id:` lines
|
|
26
|
+
* stay byte for byte; an answer that does not carry the recorded request id, such as the legacy era
|
|
27
|
+
* probe's `id: null` error, is unchanged). The legacy `initialize` mints a session
|
|
28
|
+
* `yolk-emu-session-<n>` (`n` from a counter that never resets, a form no seed holds), answered in
|
|
29
|
+
* the recorded `mcp-session-id` header; `notifications/initialized` on that session answers the
|
|
30
|
+
* recorded 202 and makes it ready; on a ready session `tools/list`, `tools/call`, and the standing
|
|
31
|
+
* `GET` (the recorded 405) answer the recordings with the session's id. At most 256 sessions are
|
|
32
|
+
* held: another `initialize` is refused before any fault. `reset` and `seed` clear the sessions.
|
|
33
|
+
* The seed only selects which recorded modern listing `tools/list` answers (`modernListing`:
|
|
34
|
+
* `one-page`, the default, or `two-pages`, whose first page issues the recorded cursor in the
|
|
35
|
+
* generation that first issues it, and `<cursor>.g<generation>` after a reset or seed; the second
|
|
36
|
+
* page answers only the cursor issued in the current generation). Every plan prepares its answer
|
|
37
|
+
* before anything is written; its commit (minting a session, readying one, issuing a cursor) runs
|
|
38
|
+
* only when no fault answers the request. Status faults (400-599) and `truncate-after-chunks`
|
|
39
|
+
* faults apply only after a request is admitted and planned, and a faulted request writes nothing:
|
|
40
|
+
* a truncation sends the prepared answer cut short and never runs the commit, so a truncated
|
|
41
|
+
* `initialize` holds no session and leaves the counter and the cap where they were, and a truncated
|
|
42
|
+
* first page issues no cursor. `match.route` selects one manifest row (a value naming no row is
|
|
43
|
+
* rejected when the fault is added); `match.method` is the HTTP method. Anything else answers one
|
|
44
|
+
* 400 not-emulated, ledgered with constant text only (`/<unrecognised>`, a standard method or
|
|
45
|
+
* `<other>`, an empty query, no headers or body, a constant reason, and the route template or row),
|
|
46
|
+
* that writes nothing and uses up no fault.
|
|
47
|
+
*
|
|
48
|
+
* Not emulated: `DELETE` (the client never sends it), `ping`, `resources/*`, `prompts/*`,
|
|
49
|
+
* `logging/*`, `completion/*`, `tasks/*`, JSON-RPC batches and client-sent responses, a JSON body
|
|
50
|
+
* repeating a key (the wrapper's opt-in `uniqueJsonKeys`), `mcp-*` headers no recording carries
|
|
51
|
+
* (such as `mcp-param-*`), cursors this emulator did not issue, any other tool or arguments, any
|
|
52
|
+
* other origin or path, and a missing `Authorization` (no fixture records the answer to one).
|
|
53
|
+
*
|
|
54
|
+
* Fail closed, on the shared wrapper's fail-closed mode as `/github`, `/google`, and
|
|
55
|
+
* `/linkedin-search` use it, with its opt-in `constantRefusals`, `guardAllHeaders`, and
|
|
56
|
+
* `guardOutput`: a request is recognised only on an emulated route shape, and any `Authorization`
|
|
57
|
+
* header is exactly `Bearer <token>` with a recognisable bearer (the RFC 6750 `b64token` syntax, at
|
|
58
|
+
* least 8 characters, a first character in `[G-Zg-z\-._~+/]` other than `n`, `r`, `t`, `u`, and a
|
|
59
|
+
* character outside `[0-9.eE+-]`). A recognised request that repeats the bearer anywhere (its raw
|
|
60
|
+
* path, the raw query or any query key or value, any request header name or value other than
|
|
61
|
+
* `Authorization`, or its body, through the wrapper's fixpoint closure of tolerant percent-decoding
|
|
62
|
+
* and JSON-unescaping, capped, where a cap refuses) is ledgered with constant text only. The
|
|
63
|
+
* prepared output is checked the same way before any fault is decided or anything is committed: an
|
|
64
|
+
* answer (every header and chunk) or a minted session id or cursor that would repeat the bearer is
|
|
65
|
+
* refused with the constant credential-repeat entry, no fault used and nothing written, so a bearer
|
|
66
|
+
* such as `yolk-emu-session-1`, or `synthetic-mcp` (inside the recorded `yolk-synthetic-mcp`),
|
|
67
|
+
* never reaches a response, the state, or `/_emulate/*`. The bearer is never stored, forwarded,
|
|
68
|
+
* ledgered, or echoed: routes see only its digest (the wrapper's opt-in `bearerDigest`; SHA-256 of
|
|
69
|
+
* the origin, a space, and the bearer), which they compare only with the digest of the public
|
|
70
|
+
* reserved invalid credential `yolk-conformance-invalid-credential-0000` (itself a recognisable
|
|
71
|
+
* bearer) to answer its recorded 401 byte for byte. Scope: the bearer is never copied from the
|
|
72
|
+
* request into a response, the state, or `/_emulate/*`; the output guard also refuses a prepared
|
|
73
|
+
* fixture answer, minted session id, or cursor that happens to contain it, but the emulator's other
|
|
74
|
+
* constants (state values such as `initializing`, wrapper headers such as `x-emulator-evidence`)
|
|
75
|
+
* and host-configured control-plane data (a fault body) may coincidentally equal a bearer and are
|
|
76
|
+
* not checked. `makeMcpEmulator` also throws when a copied recording is not canonical JSON (every
|
|
77
|
+
* JSON body and SSE `data:` payload equal to `JSON.stringify(JSON.parse(text))`), which id
|
|
78
|
+
* substitution relies on.
|
|
79
|
+
*
|
|
80
|
+
* Request-shape latitude (`/mcp`, the only accepted deviations): any bearer value in the RFC 6750
|
|
81
|
+
* `b64token` syntax (`[A-Za-z0-9\-._~+/]+=*`) of at least 8 characters, starting with a character
|
|
82
|
+
* in `[G-Zg-z\-._~+/]` other than `n`, `r`, `t`, `u`, with at least one outside `[0-9.eE+-]`, that
|
|
83
|
+
* occurs nowhere else in the request (any header name or value included) and in no answer or value
|
|
84
|
+
* the request would store (never stored or ledgered; only its digest is compared, with the digest
|
|
85
|
+
* of the public reserved invalid credential `yolk-conformance-invalid-credential-0000`, which
|
|
86
|
+
* answers the recorded 401 on the era probe); extra request headers, except `mcp-*` headers other
|
|
87
|
+
* than `mcp-method`, `mcp-name`, `mcp-protocol-version`, and `mcp-session-id`; a recorded header
|
|
88
|
+
* value sent as several headers that the HTTP layer joins into the recorded value; JSON key order;
|
|
89
|
+
* any JSON-RPC request id that is an integer from 0 to 2^53 - 1 or 1 to 64 printable ASCII
|
|
90
|
+
* characters where the recording has an id; any non-empty `name` and `version` (and no other key)
|
|
91
|
+
* in the `_meta` client info (`io.modelcontextprotocol/clientInfo`) of a modern request; a session
|
|
92
|
+
* id this emulator minted since the last reset or seed where the recording sends `mcp-session-id`
|
|
93
|
+
* (initializing for `notifications/initialized`, ready otherwise); and, with the seed's `two-pages`
|
|
94
|
+
* listing, the cursor this emulator issued in the current generation on the second page.
|
|
95
|
+
* `Authorization` must be exactly `Bearer <token>` (that spelling, one space). Everything else
|
|
96
|
+
* (another origin or path, any query, other HTTP methods such as `DELETE` or a `GET` on the modern
|
|
97
|
+
* profile, JSON-RPC methods no fixture of the profile records such as `ping`, `resources/*`, or
|
|
98
|
+
* `prompts/*`, batches and client-sent responses, other members, a JSON body repeating a key
|
|
99
|
+
* (compared after unescaping), a `null`, negative, or fractional id, other params (other tools,
|
|
100
|
+
* arguments, protocol versions, or capabilities, extra client-info keys, and a legacy `initialize`
|
|
101
|
+
* client info other than the recorded one), the MCP headers `accept`, `content-type`, `mcp-method`,
|
|
102
|
+
* `mcp-protocol-version`, `mcp-name`, and `last-event-id` other than the recorded values or present
|
|
103
|
+
* where none is recorded, any other `mcp-*` header (such as `mcp-param-*`), `mcp-session-id`
|
|
104
|
+
* missing where recorded or present where not, an unknown session or one in the wrong phase, a
|
|
105
|
+
* cursor not issued in the current generation, the reserved invalid credential on anything but the
|
|
106
|
+
* era probe, a bearer repeated anywhere in the request, and a bearer an answer or a minted session
|
|
107
|
+
* id or cursor would repeat) is not emulated.
|
|
108
|
+
*
|
|
109
|
+
* Node-only: `@emulators/core` imports Node builtins, so the core is loaded lazily by
|
|
110
|
+
* `makeMcpEmulator` (importing this module has no side effects).
|
|
111
|
+
*
|
|
112
|
+
* @experimental
|
|
113
|
+
*/
|
|
114
|
+
/**
|
|
115
|
+
* Route evidence manifest: one `RPC <origin><path>#<method>` row per recorded JSON-RPC method of
|
|
116
|
+
* each profile, plus the legacy `GET` row, with the conformance cases whose (synthetic,
|
|
117
|
+
* unverified) fixtures each answers. No row writes anything. Kept in sync with the handlers by
|
|
118
|
+
* construction (both come from one route table).
|
|
119
|
+
*/
|
|
120
|
+
const mcpEmulatorRoutes = mcpApiRoutes.flatMap(routeManifest);
|
|
121
|
+
/**
|
|
122
|
+
* Optional fault filter; an omitted field matches every request. `method` is the HTTP method
|
|
123
|
+
* (`POST` or `GET`, never a row's `RPC`); `path` is the raw request path (ending in `*`, a
|
|
124
|
+
* prefix); `route` is a manifest row path (for example
|
|
125
|
+
* `https://mcp.example.test/legacy/mcp#tools/list`), and a value naming no row is rejected when
|
|
126
|
+
* the fault is added.
|
|
127
|
+
*/
|
|
128
|
+
const McpFaultMatch = StatefulFaultMatch;
|
|
129
|
+
/**
|
|
130
|
+
* A status fault (400-599), or a `truncate-after-chunks` fault that sends the first `chunks`
|
|
131
|
+
* chunks of the prepared answer and then ends it cleanly (an SSE answer has two chunks, a JSON
|
|
132
|
+
* answer one). Only a request the emulator would answer reaches a fault: a request that is not
|
|
133
|
+
* emulated, by its shape or by the state, never uses one up. A faulted request writes nothing (a
|
|
134
|
+
* truncated `initialize` mints no session; a truncated first page issues no cursor). A truncation
|
|
135
|
+
* that cannot apply answers 500 and is not used up.
|
|
136
|
+
*/
|
|
137
|
+
const McpFault = StatefulStreamFault;
|
|
138
|
+
/** Invalid emulator input from the JS API: a seed, a fault, or an option. A programmer error. */
|
|
139
|
+
var McpEmulatorInputInvalid = class extends Data.TaggedError("McpEmulatorInputInvalid") {
|
|
140
|
+
get message() {
|
|
141
|
+
return `Invalid MCP emulator ${this.input}: ${this.reason}`;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const inputInvalid = (input, reason) => new McpEmulatorInputInvalid({
|
|
145
|
+
input,
|
|
146
|
+
reason
|
|
147
|
+
});
|
|
148
|
+
/** The constant reason of a request on no emulated route shape. */
|
|
149
|
+
const unrecognisedReason = "no emulated MCP route for this method and path";
|
|
150
|
+
/** The constant reason of a request whose `Authorization` header is not one recognisable bearer. */
|
|
151
|
+
const unrecognisedAuthorizationReason = "an unrecognisable Authorization header is not emulated";
|
|
152
|
+
/**
|
|
153
|
+
* Create a stateful MCP emulator on the `@emulators/core` custom runtime. Each call has its own
|
|
154
|
+
* state, ledger, faults, and session counter. Rejects with `McpEmulatorInputInvalid` for an
|
|
155
|
+
* invalid seed or option. See `src/stateful-emulator.ts` for the request precedence.
|
|
156
|
+
*/
|
|
157
|
+
const makeMcpEmulator = async (options = {}) => {
|
|
158
|
+
const initial = buildSeedState(options.seed ?? {});
|
|
159
|
+
if (Predicate.isString(initial)) throw inputInvalid("seed", initial);
|
|
160
|
+
checkBooleanDrills(options.drills, mcpEmulatorDrillKnobs, inputInvalid);
|
|
161
|
+
if (!isRecognisableBearerValue("yolk-conformance-invalid-credential-0000")) throw new Error("the reserved invalid MCP credential is not a recognisable bearer");
|
|
162
|
+
const recordingProblems = mcpRecordingProblems(mcpEmulatorFixtures);
|
|
163
|
+
if (recordingProblems.length > 0) throw new Error(`non-canonical MCP recordings: ${recordingProblems.join("; ")}`);
|
|
164
|
+
const drills = options.drills ?? {};
|
|
165
|
+
const runtime = makeMcpRuntime();
|
|
166
|
+
return makeChunkedStatefulEmulator({
|
|
167
|
+
routes: mcpApiRoutes,
|
|
168
|
+
env: {
|
|
169
|
+
drills: {
|
|
170
|
+
discoverCarriesErrorResponse: drills.discoverCarriesErrorResponse === true,
|
|
171
|
+
discoverWithoutResultType: drills.discoverWithoutResultType === true,
|
|
172
|
+
sessionIdNotVisibleAscii: drills.sessionIdNotVisibleAscii === true,
|
|
173
|
+
discoverAnsweredTwice: drills.discoverAnsweredTwice === true,
|
|
174
|
+
writeToolMarkedReadOnly: drills.writeToolMarkedReadOnly === true,
|
|
175
|
+
readCallAnswersToolError: drills.readCallAnswersToolError === true,
|
|
176
|
+
invalidCallAnswersRpcError: drills.invalidCallAnswersRpcError === true,
|
|
177
|
+
absentCallAnswersResult: drills.absentCallAnswersResult === true,
|
|
178
|
+
unauthorizedWithoutChallenge: drills.unauthorizedWithoutChallenge === true
|
|
179
|
+
},
|
|
180
|
+
reservedDigest: mcpBearerDigest(mcpEmulatorReservedInvalidCredential, mcpEmulatorOrigin),
|
|
181
|
+
runtime
|
|
182
|
+
},
|
|
183
|
+
initial,
|
|
184
|
+
buildSeed: buildSeedState,
|
|
185
|
+
recordHeaders: [
|
|
186
|
+
{
|
|
187
|
+
name: "accept",
|
|
188
|
+
json: false
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: "content-type",
|
|
192
|
+
json: false
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: "mcp-method",
|
|
196
|
+
json: false
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: "mcp-name",
|
|
200
|
+
json: false
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: "mcp-protocol-version",
|
|
204
|
+
json: false
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: "mcp-session-id",
|
|
208
|
+
json: false
|
|
209
|
+
}
|
|
210
|
+
],
|
|
211
|
+
failClosed: {
|
|
212
|
+
unrecognised: unrecognisedReason,
|
|
213
|
+
unrecognisedAuthorization: unrecognisedAuthorizationReason
|
|
214
|
+
},
|
|
215
|
+
bearerDigest: mcpBearerDigest,
|
|
216
|
+
constantRefusals: true,
|
|
217
|
+
guardAllHeaders: true,
|
|
218
|
+
guardOutput: true,
|
|
219
|
+
uniqueJsonKeys: true,
|
|
220
|
+
clearRuntime: () => {
|
|
221
|
+
runtime.generation += 1;
|
|
222
|
+
runtime.issuedCursor = void 0;
|
|
223
|
+
},
|
|
224
|
+
runtimeState: () => ({
|
|
225
|
+
nextSession: runtime.nextSession,
|
|
226
|
+
cursorGeneration: runtime.generation,
|
|
227
|
+
issuedCursor: runtime.issuedCursor ?? null
|
|
228
|
+
}),
|
|
229
|
+
seedSummary: (state) => ({ modernListing: state.modernListing }),
|
|
230
|
+
inputInvalid
|
|
231
|
+
}, statefulCoreRuntime({
|
|
232
|
+
name: "mcp",
|
|
233
|
+
initial,
|
|
234
|
+
decodeState,
|
|
235
|
+
inputInvalid
|
|
236
|
+
}));
|
|
237
|
+
};
|
|
238
|
+
//#endregion
|
|
239
|
+
export { McpEmulatorInputInvalid, McpEmulatorModernListing, McpEmulatorSeed, McpEmulatorSession, McpEmulatorStateSchema, McpFault, McpFaultMatch, emulatorEvidenceHeader, isMcpEmulatedRequestId, makeMcpEmulator, mcpEmulatorDrillKnobs, mcpEmulatorFixtures, mcpEmulatorLegacyPath, mcpEmulatorModernPath, mcpEmulatorOrigin, mcpEmulatorReservedInvalidCredential, mcpEmulatorRoutes, mcpEmulatorSessionCap, mcpEmulatorSessionPrefix };
|
|
240
|
+
|
|
241
|
+
//# sourceMappingURL=mcp.mjs.map
|
package/dist/mcp.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.mjs","names":[],"sources":["../src/mcp.ts"],"sourcesContent":["/**\n * Stateful MCP emulator (the two synthetic servers of the MCP conformance fixtures: profile\n * `synthetic-modern` on `https://mcp.example.test/modern/mcp`, stateless `2026-07-28` with JSON\n * answers, and profile `synthetic-legacy` on `https://mcp.example.test/legacy/mcp`, an\n * `initialize` handshake with sessions and SSE answers), built on the upstream `@emulators/core`\n * custom runtime, with a request ledger, status and truncation faults, and an `/_emulate/*`\n * control plane.\n *\n * It never imports SDK code: its answers are the sixteen synthetic `@yolk-sdk/mcp/conformance`\n * fixtures copied as data (`src/mcp/recordings.ts`), and every manifest row names the conformance\n * cases whose fixtures it answers (`mcpEmulatorRoutes`: one `RPC <origin><path>#<method>` row per\n * recorded JSON-RPC method of each profile, plus the legacy `GET` row; no row writes anything in a\n * real service). Response behaviour comes only from the fixtures, byte for byte: a JSON-RPC POST is\n * answered only when it equals a recorded request within the latitude below, with the recorded\n * answer, the request id substituted at exactly the recorded place (the top-level `id` of a JSON\n * answer, or the `id` of the SSE response event's payload; notification events and SSE `id:` lines\n * stay byte for byte; an answer that does not carry the recorded request id, such as the legacy era\n * probe's `id: null` error, is unchanged). The legacy `initialize` mints a session\n * `yolk-emu-session-<n>` (`n` from a counter that never resets, a form no seed holds), answered in\n * the recorded `mcp-session-id` header; `notifications/initialized` on that session answers the\n * recorded 202 and makes it ready; on a ready session `tools/list`, `tools/call`, and the standing\n * `GET` (the recorded 405) answer the recordings with the session's id. At most 256 sessions are\n * held: another `initialize` is refused before any fault. `reset` and `seed` clear the sessions.\n * The seed only selects which recorded modern listing `tools/list` answers (`modernListing`:\n * `one-page`, the default, or `two-pages`, whose first page issues the recorded cursor in the\n * generation that first issues it, and `<cursor>.g<generation>` after a reset or seed; the second\n * page answers only the cursor issued in the current generation). Every plan prepares its answer\n * before anything is written; its commit (minting a session, readying one, issuing a cursor) runs\n * only when no fault answers the request. Status faults (400-599) and `truncate-after-chunks`\n * faults apply only after a request is admitted and planned, and a faulted request writes nothing:\n * a truncation sends the prepared answer cut short and never runs the commit, so a truncated\n * `initialize` holds no session and leaves the counter and the cap where they were, and a truncated\n * first page issues no cursor. `match.route` selects one manifest row (a value naming no row is\n * rejected when the fault is added); `match.method` is the HTTP method. Anything else answers one\n * 400 not-emulated, ledgered with constant text only (`/<unrecognised>`, a standard method or\n * `<other>`, an empty query, no headers or body, a constant reason, and the route template or row),\n * that writes nothing and uses up no fault.\n *\n * Not emulated: `DELETE` (the client never sends it), `ping`, `resources/*`, `prompts/*`,\n * `logging/*`, `completion/*`, `tasks/*`, JSON-RPC batches and client-sent responses, a JSON body\n * repeating a key (the wrapper's opt-in `uniqueJsonKeys`), `mcp-*` headers no recording carries\n * (such as `mcp-param-*`), cursors this emulator did not issue, any other tool or arguments, any\n * other origin or path, and a missing `Authorization` (no fixture records the answer to one).\n *\n * Fail closed, on the shared wrapper's fail-closed mode as `/github`, `/google`, and\n * `/linkedin-search` use it, with its opt-in `constantRefusals`, `guardAllHeaders`, and\n * `guardOutput`: a request is recognised only on an emulated route shape, and any `Authorization`\n * header is exactly `Bearer <token>` with a recognisable bearer (the RFC 6750 `b64token` syntax, at\n * least 8 characters, a first character in `[G-Zg-z\\-._~+/]` other than `n`, `r`, `t`, `u`, and a\n * character outside `[0-9.eE+-]`). A recognised request that repeats the bearer anywhere (its raw\n * path, the raw query or any query key or value, any request header name or value other than\n * `Authorization`, or its body, through the wrapper's fixpoint closure of tolerant percent-decoding\n * and JSON-unescaping, capped, where a cap refuses) is ledgered with constant text only. The\n * prepared output is checked the same way before any fault is decided or anything is committed: an\n * answer (every header and chunk) or a minted session id or cursor that would repeat the bearer is\n * refused with the constant credential-repeat entry, no fault used and nothing written, so a bearer\n * such as `yolk-emu-session-1`, or `synthetic-mcp` (inside the recorded `yolk-synthetic-mcp`),\n * never reaches a response, the state, or `/_emulate/*`. The bearer is never stored, forwarded,\n * ledgered, or echoed: routes see only its digest (the wrapper's opt-in `bearerDigest`; SHA-256 of\n * the origin, a space, and the bearer), which they compare only with the digest of the public\n * reserved invalid credential `yolk-conformance-invalid-credential-0000` (itself a recognisable\n * bearer) to answer its recorded 401 byte for byte. Scope: the bearer is never copied from the\n * request into a response, the state, or `/_emulate/*`; the output guard also refuses a prepared\n * fixture answer, minted session id, or cursor that happens to contain it, but the emulator's other\n * constants (state values such as `initializing`, wrapper headers such as `x-emulator-evidence`)\n * and host-configured control-plane data (a fault body) may coincidentally equal a bearer and are\n * not checked. `makeMcpEmulator` also throws when a copied recording is not canonical JSON (every\n * JSON body and SSE `data:` payload equal to `JSON.stringify(JSON.parse(text))`), which id\n * substitution relies on.\n *\n * Request-shape latitude (`/mcp`, the only accepted deviations): any bearer value in the RFC 6750\n * `b64token` syntax (`[A-Za-z0-9\\-._~+/]+=*`) of at least 8 characters, starting with a character\n * in `[G-Zg-z\\-._~+/]` other than `n`, `r`, `t`, `u`, with at least one outside `[0-9.eE+-]`, that\n * occurs nowhere else in the request (any header name or value included) and in no answer or value\n * the request would store (never stored or ledgered; only its digest is compared, with the digest\n * of the public reserved invalid credential `yolk-conformance-invalid-credential-0000`, which\n * answers the recorded 401 on the era probe); extra request headers, except `mcp-*` headers other\n * than `mcp-method`, `mcp-name`, `mcp-protocol-version`, and `mcp-session-id`; a recorded header\n * value sent as several headers that the HTTP layer joins into the recorded value; JSON key order;\n * any JSON-RPC request id that is an integer from 0 to 2^53 - 1 or 1 to 64 printable ASCII\n * characters where the recording has an id; any non-empty `name` and `version` (and no other key)\n * in the `_meta` client info (`io.modelcontextprotocol/clientInfo`) of a modern request; a session\n * id this emulator minted since the last reset or seed where the recording sends `mcp-session-id`\n * (initializing for `notifications/initialized`, ready otherwise); and, with the seed's `two-pages`\n * listing, the cursor this emulator issued in the current generation on the second page.\n * `Authorization` must be exactly `Bearer <token>` (that spelling, one space). Everything else\n * (another origin or path, any query, other HTTP methods such as `DELETE` or a `GET` on the modern\n * profile, JSON-RPC methods no fixture of the profile records such as `ping`, `resources/*`, or\n * `prompts/*`, batches and client-sent responses, other members, a JSON body repeating a key\n * (compared after unescaping), a `null`, negative, or fractional id, other params (other tools,\n * arguments, protocol versions, or capabilities, extra client-info keys, and a legacy `initialize`\n * client info other than the recorded one), the MCP headers `accept`, `content-type`, `mcp-method`,\n * `mcp-protocol-version`, `mcp-name`, and `last-event-id` other than the recorded values or present\n * where none is recorded, any other `mcp-*` header (such as `mcp-param-*`), `mcp-session-id`\n * missing where recorded or present where not, an unknown session or one in the wrong phase, a\n * cursor not issued in the current generation, the reserved invalid credential on anything but the\n * era probe, a bearer repeated anywhere in the request, and a bearer an answer or a minted session\n * id or cursor would repeat) is not emulated.\n *\n * Node-only: `@emulators/core` imports Node builtins, so the core is loaded lazily by\n * `makeMcpEmulator` (importing this module has no side effects).\n *\n * @experimental\n */\nimport { Data, Predicate } from 'effect'\nimport {\n mcpApiRoutes,\n mcpEmulatorDrillKnobs,\n mcpRecordingProblems,\n type McpApiEnv,\n type McpEmulatorDrills\n} from './mcp/api.ts'\nimport { mcpEmulatorFixtures } from './mcp/recordings.ts'\nimport {\n buildSeedState,\n decodeState,\n makeMcpRuntime,\n mcpBearerDigest,\n mcpEmulatorOrigin,\n mcpEmulatorReservedInvalidCredential,\n type McpEmulatorSeed,\n type McpEmulatorState\n} from './mcp/state.ts'\nimport type { EmulatorRouteEvidence } from './route-evidence.ts'\nimport {\n StatefulFaultMatch,\n StatefulStreamFault,\n checkBooleanDrills,\n makeChunkedStatefulEmulator,\n routeManifest,\n type StatefulCoverage,\n type StatefulEmulatorApi,\n type StatefulFaultState,\n type StatefulInputKind,\n type StatefulLedgerEntry\n} from './stateful-emulator.ts'\nimport { statefulCoreRuntime } from './stateful-core.ts'\nimport { isRecognisableBearerValue } from './stateful-secrets.ts'\n\nexport type { EmulatorEvidence, EmulatorRouteEvidence } from './route-evidence.ts'\n\nexport { emulatorEvidenceHeader } from './route-evidence.ts'\n\nexport { isMcpEmulatedRequestId, mcpEmulatorDrillKnobs, type McpEmulatorDrills } from './mcp/api.ts'\n\nexport {\n McpEmulatorModernListing,\n McpEmulatorSeed,\n McpEmulatorSession,\n McpEmulatorStateSchema,\n mcpEmulatorLegacyPath,\n mcpEmulatorModernPath,\n mcpEmulatorOrigin,\n mcpEmulatorReservedInvalidCredential,\n mcpEmulatorSessionCap,\n mcpEmulatorSessionPrefix,\n type McpEmulatorState\n} from './mcp/state.ts'\n\nexport {\n mcpEmulatorFixtures,\n type McpRecordedExchange,\n type McpRecordedFixture,\n type McpRecordedRequest,\n type McpRecordedResponse\n} from './mcp/recordings.ts'\n\n/**\n * Route evidence manifest: one `RPC <origin><path>#<method>` row per recorded JSON-RPC method of\n * each profile, plus the legacy `GET` row, with the conformance cases whose (synthetic,\n * unverified) fixtures each answers. No row writes anything. Kept in sync with the handlers by\n * construction (both come from one route table).\n */\nexport const mcpEmulatorRoutes: ReadonlyArray<EmulatorRouteEvidence> =\n mcpApiRoutes.flatMap(routeManifest)\n\n/**\n * Optional fault filter; an omitted field matches every request. `method` is the HTTP method\n * (`POST` or `GET`, never a row's `RPC`); `path` is the raw request path (ending in `*`, a\n * prefix); `route` is a manifest row path (for example\n * `https://mcp.example.test/legacy/mcp#tools/list`), and a value naming no row is rejected when\n * the fault is added.\n */\nexport const McpFaultMatch = StatefulFaultMatch\n\nexport type McpFaultMatch = typeof StatefulFaultMatch.Type\n\n/**\n * A status fault (400-599), or a `truncate-after-chunks` fault that sends the first `chunks`\n * chunks of the prepared answer and then ends it cleanly (an SSE answer has two chunks, a JSON\n * answer one). Only a request the emulator would answer reaches a fault: a request that is not\n * emulated, by its shape or by the state, never uses one up. A faulted request writes nothing (a\n * truncated `initialize` mints no session; a truncated first page issues no cursor). A truncation\n * that cannot apply answers 500 and is not used up.\n */\nexport const McpFault = StatefulStreamFault\n\nexport type McpFault = StatefulStreamFault\n\nexport type McpFaultState = StatefulFaultState<McpFault>\n\nexport type McpLedgerEntry = StatefulLedgerEntry\n\nexport type McpCoverage = StatefulCoverage\n\n/** Invalid emulator input from the JS API: a seed, a fault, or an option. A programmer error. */\nexport class McpEmulatorInputInvalid extends Data.TaggedError('McpEmulatorInputInvalid')<{\n readonly input: StatefulInputKind\n readonly reason: string\n}> {\n override get message(): string {\n return `Invalid MCP emulator ${this.input}: ${this.reason}`\n }\n}\n\nexport type McpEmulatorOptions = {\n /** Typed seed; defaults to the one-page modern listing (sessions are never seeded). */\n readonly seed?: McpEmulatorSeed\n /** Drill knobs (tests only): make the emulator disagree with one conformance claim. */\n readonly drills?: McpEmulatorDrills\n}\n\nexport type McpEmulator = StatefulEmulatorApi<McpEmulatorState, McpEmulatorSeed, McpFault>\n\nconst inputInvalid = (input: StatefulInputKind, reason: string) =>\n new McpEmulatorInputInvalid({ input, reason })\n\n/** The constant reason of a request on no emulated route shape. */\nconst unrecognisedReason = 'no emulated MCP route for this method and path'\n\n/** The constant reason of a request whose `Authorization` header is not one recognisable bearer. */\nconst unrecognisedAuthorizationReason = 'an unrecognisable Authorization header is not emulated'\n\n/**\n * Create a stateful MCP emulator on the `@emulators/core` custom runtime. Each call has its own\n * state, ledger, faults, and session counter. Rejects with `McpEmulatorInputInvalid` for an\n * invalid seed or option. See `src/stateful-emulator.ts` for the request precedence.\n */\nexport const makeMcpEmulator = async (options: McpEmulatorOptions = {}): Promise<McpEmulator> => {\n const initial = buildSeedState(options.seed ?? {})\n\n if (Predicate.isString(initial)) {\n throw inputInvalid('seed', initial)\n }\n\n checkBooleanDrills(options.drills, mcpEmulatorDrillKnobs, inputInvalid)\n\n // The reserved invalid credential must reach the routes as a recognisable bearer's digest; a\n // value the bearer rule refused would never be answered its recorded 401.\n if (!isRecognisableBearerValue(mcpEmulatorReservedInvalidCredential)) {\n throw new Error('the reserved invalid MCP credential is not a recognisable bearer')\n }\n\n // Id substitution re-serialises the response message: a recording copied in a non-canonical\n // JSON form would answer other bytes, so it fails here, loudly.\n const recordingProblems = mcpRecordingProblems(mcpEmulatorFixtures)\n\n if (recordingProblems.length > 0) {\n throw new Error(`non-canonical MCP recordings: ${recordingProblems.join('; ')}`)\n }\n\n const drills = options.drills ?? {}\n const runtime = makeMcpRuntime()\n\n const env: McpApiEnv = {\n drills: {\n discoverCarriesErrorResponse: drills.discoverCarriesErrorResponse === true,\n discoverWithoutResultType: drills.discoverWithoutResultType === true,\n sessionIdNotVisibleAscii: drills.sessionIdNotVisibleAscii === true,\n discoverAnsweredTwice: drills.discoverAnsweredTwice === true,\n writeToolMarkedReadOnly: drills.writeToolMarkedReadOnly === true,\n readCallAnswersToolError: drills.readCallAnswersToolError === true,\n invalidCallAnswersRpcError: drills.invalidCallAnswersRpcError === true,\n absentCallAnswersResult: drills.absentCallAnswersResult === true,\n unauthorizedWithoutChallenge: drills.unauthorizedWithoutChallenge === true\n },\n reservedDigest: mcpBearerDigest(mcpEmulatorReservedInvalidCredential, mcpEmulatorOrigin),\n runtime\n }\n\n return makeChunkedStatefulEmulator<McpEmulatorState, McpApiEnv, McpEmulatorSeed>(\n {\n routes: mcpApiRoutes,\n env,\n initial,\n buildSeed: buildSeedState,\n recordHeaders: [\n { name: 'accept', json: false },\n { name: 'content-type', json: false },\n { name: 'mcp-method', json: false },\n { name: 'mcp-name', json: false },\n { name: 'mcp-protocol-version', json: false },\n { name: 'mcp-session-id', json: false }\n ],\n failClosed: {\n unrecognised: unrecognisedReason,\n unrecognisedAuthorization: unrecognisedAuthorizationReason\n },\n bearerDigest: mcpBearerDigest,\n constantRefusals: true,\n guardAllHeaders: true,\n guardOutput: true,\n uniqueJsonKeys: true,\n // Every reset and seed starts a cursor generation; the session counter never resets.\n clearRuntime: () => {\n runtime.generation += 1\n runtime.issuedCursor = undefined\n },\n runtimeState: () => ({\n nextSession: runtime.nextSession,\n cursorGeneration: runtime.generation,\n issuedCursor: runtime.issuedCursor ?? null\n }),\n seedSummary: state => ({ modernListing: state.modernListing }),\n inputInvalid\n },\n statefulCoreRuntime({ name: 'mcp', initial, decodeState, inputInvalid })\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6KA,MAAa,oBACX,aAAa,QAAQ,aAAa;;;;;;;;AASpC,MAAa,gBAAgB;;;;;;;;;AAY7B,MAAa,WAAW;;AAWxB,IAAa,0BAAb,cAA6C,KAAK,YAAY,yBAAyB,EAGpF;CACD,IAAa,UAAkB;EAC7B,OAAO,wBAAwB,KAAK,MAAM,IAAI,KAAK;CACrD;AACF;AAWA,MAAM,gBAAgB,OAA0B,WAC9C,IAAI,wBAAwB;CAAE;CAAO;AAAO,CAAC;;AAG/C,MAAM,qBAAqB;;AAG3B,MAAM,kCAAkC;;;;;;AAOxC,MAAa,kBAAkB,OAAO,UAA8B,CAAC,MAA4B;CAC/F,MAAM,UAAU,eAAe,QAAQ,QAAQ,CAAC,CAAC;CAEjD,IAAI,UAAU,SAAS,OAAO,GAC5B,MAAM,aAAa,QAAQ,OAAO;CAGpC,mBAAmB,QAAQ,QAAQ,uBAAuB,YAAY;CAItE,IAAI,CAAC,0BAAA,0CAA8D,GACjE,MAAM,IAAI,MAAM,kEAAkE;CAKpF,MAAM,oBAAoB,qBAAqB,mBAAmB;CAElE,IAAI,kBAAkB,SAAS,GAC7B,MAAM,IAAI,MAAM,iCAAiC,kBAAkB,KAAK,IAAI,GAAG;CAGjF,MAAM,SAAS,QAAQ,UAAU,CAAC;CAClC,MAAM,UAAU,eAAe;CAkB/B,OAAO,4BACL;EACE,QAAQ;EACR,KAAA;GAlBF,QAAQ;IACN,8BAA8B,OAAO,iCAAiC;IACtE,2BAA2B,OAAO,8BAA8B;IAChE,0BAA0B,OAAO,6BAA6B;IAC9D,uBAAuB,OAAO,0BAA0B;IACxD,yBAAyB,OAAO,4BAA4B;IAC5D,0BAA0B,OAAO,6BAA6B;IAC9D,4BAA4B,OAAO,+BAA+B;IAClE,yBAAyB,OAAO,4BAA4B;IAC5D,8BAA8B,OAAO,iCAAiC;GACxE;GACA,gBAAgB,gBAAgB,sCAAsC,iBAAiB;GACvF;EAMI;EACF;EACA,WAAW;EACX,eAAe;GACb;IAAE,MAAM;IAAU,MAAM;GAAM;GAC9B;IAAE,MAAM;IAAgB,MAAM;GAAM;GACpC;IAAE,MAAM;IAAc,MAAM;GAAM;GAClC;IAAE,MAAM;IAAY,MAAM;GAAM;GAChC;IAAE,MAAM;IAAwB,MAAM;GAAM;GAC5C;IAAE,MAAM;IAAkB,MAAM;GAAM;EACxC;EACA,YAAY;GACV,cAAc;GACd,2BAA2B;EAC7B;EACA,cAAc;EACd,kBAAkB;EAClB,iBAAiB;EACjB,aAAa;EACb,gBAAgB;EAEhB,oBAAoB;GAClB,QAAQ,cAAc;GACtB,QAAQ,eAAe,KAAA;EACzB;EACA,qBAAqB;GACnB,aAAa,QAAQ;GACrB,kBAAkB,QAAQ;GAC1B,cAAc,QAAQ,gBAAgB;EACxC;EACA,cAAa,WAAU,EAAE,eAAe,MAAM,cAAc;EAC5D;CACF,GACA,oBAAoB;EAAE,MAAM;EAAO;EAAS;EAAa;CAAa,CAAC,CACzE;AACF"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { EmulatorEvidence, EmulatorRouteEvidence } from "./route-evidence.mjs";
|
|
2
|
+
import { EmulatorApi } from "./emulator-kernel.mjs";
|
|
3
|
+
import * as Schema from "effect/Schema";
|
|
4
|
+
|
|
5
|
+
//#region src/messages.d.ts
|
|
6
|
+
/** An Anthropic wire error: `type` (for example `overloaded_error`) and a message. */
|
|
7
|
+
declare const MessagesWireError: Schema.Struct<{
|
|
8
|
+
readonly type: Schema.NonEmptyString;
|
|
9
|
+
readonly message: Schema.String;
|
|
10
|
+
}>;
|
|
11
|
+
type MessagesWireError = typeof MessagesWireError.Type;
|
|
12
|
+
/**
|
|
13
|
+
* Send `chunks` SSE events, then one `event: error` event (default `overloaded_error`) and close
|
|
14
|
+
* without `message_stop`. Streamed responses only; it must come before `message_stop`.
|
|
15
|
+
*/
|
|
16
|
+
declare const MessagesErrorEventFault: Schema.Struct<{
|
|
17
|
+
readonly match: Schema.optionalKey<Schema.Struct<{
|
|
18
|
+
readonly path: Schema.optionalKey<Schema.String>;
|
|
19
|
+
readonly model: Schema.optionalKey<Schema.String>;
|
|
20
|
+
}>>;
|
|
21
|
+
readonly count: Schema.optionalKey<Schema.Int>;
|
|
22
|
+
readonly kind: Schema.Literal<"error-event-after-chunks">;
|
|
23
|
+
readonly chunks: Schema.Int;
|
|
24
|
+
readonly error: Schema.optionalKey<Schema.Struct<{
|
|
25
|
+
readonly type: Schema.NonEmptyString;
|
|
26
|
+
readonly message: Schema.String;
|
|
27
|
+
}>>;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Wire faults: `status` (the body defaults to an Anthropic error envelope matching the status,
|
|
31
|
+
* for example `rate_limit_error` for 429 and `overloaded_error` for 529), `error-after-chunks`
|
|
32
|
+
* (a dropped connection), `truncate-after-chunks` (a clean close, for example before
|
|
33
|
+
* `message_stop`), and `error-event-after-chunks` (a mid-stream `error` event).
|
|
34
|
+
*/
|
|
35
|
+
declare const MessagesFault: Schema.Union<readonly [Schema.Struct<{
|
|
36
|
+
readonly match: Schema.optionalKey<Schema.Struct<{
|
|
37
|
+
readonly path: Schema.optionalKey<Schema.String>;
|
|
38
|
+
readonly model: Schema.optionalKey<Schema.String>;
|
|
39
|
+
}>>;
|
|
40
|
+
readonly count: Schema.optionalKey<Schema.Int>;
|
|
41
|
+
readonly kind: Schema.Literal<"status">;
|
|
42
|
+
readonly status: Schema.Int;
|
|
43
|
+
readonly headers: Schema.optionalKey<Schema.$Record<Schema.String, Schema.String>>;
|
|
44
|
+
readonly body: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
|
|
45
|
+
}>, Schema.Struct<{
|
|
46
|
+
readonly match: Schema.optionalKey<Schema.Struct<{
|
|
47
|
+
readonly path: Schema.optionalKey<Schema.String>;
|
|
48
|
+
readonly model: Schema.optionalKey<Schema.String>;
|
|
49
|
+
}>>;
|
|
50
|
+
readonly count: Schema.optionalKey<Schema.Int>;
|
|
51
|
+
readonly kind: Schema.Literal<"error-after-chunks">;
|
|
52
|
+
readonly chunks: Schema.Int;
|
|
53
|
+
}>, Schema.Struct<{
|
|
54
|
+
readonly match: Schema.optionalKey<Schema.Struct<{
|
|
55
|
+
readonly path: Schema.optionalKey<Schema.String>;
|
|
56
|
+
readonly model: Schema.optionalKey<Schema.String>;
|
|
57
|
+
}>>;
|
|
58
|
+
readonly count: Schema.optionalKey<Schema.Int>;
|
|
59
|
+
readonly kind: Schema.Literal<"truncate-after-chunks">;
|
|
60
|
+
readonly chunks: Schema.Int;
|
|
61
|
+
}>, Schema.Struct<{
|
|
62
|
+
readonly match: Schema.optionalKey<Schema.Struct<{
|
|
63
|
+
readonly path: Schema.optionalKey<Schema.String>;
|
|
64
|
+
readonly model: Schema.optionalKey<Schema.String>;
|
|
65
|
+
}>>;
|
|
66
|
+
readonly count: Schema.optionalKey<Schema.Int>;
|
|
67
|
+
readonly kind: Schema.Literal<"error-event-after-chunks">;
|
|
68
|
+
readonly chunks: Schema.Int;
|
|
69
|
+
readonly error: Schema.optionalKey<Schema.Struct<{
|
|
70
|
+
readonly type: Schema.NonEmptyString;
|
|
71
|
+
readonly message: Schema.String;
|
|
72
|
+
}>>;
|
|
73
|
+
}>]>;
|
|
74
|
+
type MessagesFault = typeof MessagesFault.Type;
|
|
75
|
+
type MessagesFaultKind = MessagesFault['kind'];
|
|
76
|
+
/** Usage for a scripted turn (sent in `message_start` and `message_delta`). */
|
|
77
|
+
declare const MessagesScriptedUsage: Schema.Struct<{
|
|
78
|
+
readonly inputTokens: Schema.Int;
|
|
79
|
+
readonly outputTokens: Schema.Int;
|
|
80
|
+
}>;
|
|
81
|
+
type MessagesScriptedUsage = typeof MessagesScriptedUsage.Type;
|
|
82
|
+
/** One scripted `tool_use` block; its JSON input streams as these `input_json_delta` fragments. */
|
|
83
|
+
declare const MessagesScriptedToolUse: Schema.Struct<{
|
|
84
|
+
readonly name: Schema.NonEmptyString;
|
|
85
|
+
readonly inputFragments: Schema.$Array<Schema.String>;
|
|
86
|
+
readonly id: Schema.optionalKey<Schema.NonEmptyString>;
|
|
87
|
+
}>;
|
|
88
|
+
type MessagesScriptedToolUse = typeof MessagesScriptedToolUse.Type;
|
|
89
|
+
/**
|
|
90
|
+
* A scripted message. Every field is exact (nothing is filled in) except: a block is sent only
|
|
91
|
+
* when its field is present; `usage` omitted is synthesized and `null` drops `usage` from the
|
|
92
|
+
* wire; `stopReason` omitted is `tool_use` with tool uses, else `end_turn`; `order` defaults to
|
|
93
|
+
* `thinking-first` (`text-first` sends the thinking block after the text).
|
|
94
|
+
*/
|
|
95
|
+
declare const MessagesScriptedMessage: Schema.Struct<{
|
|
96
|
+
readonly thinking: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
97
|
+
readonly text: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
98
|
+
readonly toolUses: Schema.optionalKey<Schema.$Array<Schema.Struct<{
|
|
99
|
+
readonly name: Schema.NonEmptyString;
|
|
100
|
+
readonly inputFragments: Schema.$Array<Schema.String>;
|
|
101
|
+
readonly id: Schema.optionalKey<Schema.NonEmptyString>;
|
|
102
|
+
}>>>;
|
|
103
|
+
readonly order: Schema.optionalKey<Schema.Literals<readonly ["thinking-first", "text-first"]>>;
|
|
104
|
+
readonly usage: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
105
|
+
readonly inputTokens: Schema.Int;
|
|
106
|
+
readonly outputTokens: Schema.Int;
|
|
107
|
+
}>>>;
|
|
108
|
+
readonly stopReason: Schema.optionalKey<Schema.NonEmptyString>;
|
|
109
|
+
}>;
|
|
110
|
+
type MessagesScriptedMessage = typeof MessagesScriptedMessage.Type;
|
|
111
|
+
/** A turn queued for the next Messages request. */
|
|
112
|
+
declare const MessagesScriptedTurn: Schema.Union<readonly [Schema.Struct<{
|
|
113
|
+
readonly error: Schema.Struct<{
|
|
114
|
+
readonly status: Schema.Int;
|
|
115
|
+
readonly body: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
116
|
+
readonly headers: Schema.optionalKey<Schema.$Record<Schema.String, Schema.String>>;
|
|
117
|
+
}>;
|
|
118
|
+
}>, Schema.Struct<{
|
|
119
|
+
readonly thinking: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
120
|
+
readonly text: Schema.optionalKey<Schema.$Array<Schema.String>>;
|
|
121
|
+
readonly toolUses: Schema.optionalKey<Schema.$Array<Schema.Struct<{
|
|
122
|
+
readonly name: Schema.NonEmptyString;
|
|
123
|
+
readonly inputFragments: Schema.$Array<Schema.String>;
|
|
124
|
+
readonly id: Schema.optionalKey<Schema.NonEmptyString>;
|
|
125
|
+
}>>>;
|
|
126
|
+
readonly order: Schema.optionalKey<Schema.Literals<readonly ["thinking-first", "text-first"]>>;
|
|
127
|
+
readonly usage: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
128
|
+
readonly inputTokens: Schema.Int;
|
|
129
|
+
readonly outputTokens: Schema.Int;
|
|
130
|
+
}>>>;
|
|
131
|
+
readonly stopReason: Schema.optionalKey<Schema.NonEmptyString>;
|
|
132
|
+
}>]>;
|
|
133
|
+
type MessagesScriptedTurn = typeof MessagesScriptedTurn.Type;
|
|
134
|
+
type MessagesLedgerEntry = {
|
|
135
|
+
/** 1-based arrival order since the last ledger clear or reset. */readonly seq: number;
|
|
136
|
+
readonly method: string;
|
|
137
|
+
readonly path: string; /** Parsed JSON request body, when it was valid JSON. */
|
|
138
|
+
readonly body?: Schema.Json;
|
|
139
|
+
readonly model?: string;
|
|
140
|
+
readonly stream?: boolean; /** The request's `max_tokens`, when it is a number (non-positive or fractional answers 400). */
|
|
141
|
+
readonly maxTokens?: number;
|
|
142
|
+
readonly thinking?: Schema.Json;
|
|
143
|
+
readonly toolChoice?: Schema.Json;
|
|
144
|
+
readonly toolNames: ReadonlyArray<string>; /** Which header carried the credential (the value is never recorded). */
|
|
145
|
+
readonly credentialHeader?: 'x-api-key' | 'authorization';
|
|
146
|
+
readonly anthropicVersion?: string;
|
|
147
|
+
readonly anthropicBeta?: string; /** Kind of the fault that shaped the response. */
|
|
148
|
+
readonly fault?: MessagesFaultKind; /** Why a matching fault could not take effect (the response was a 500 emulator error). */
|
|
149
|
+
readonly faultError?: string;
|
|
150
|
+
/**
|
|
151
|
+
* Set when the emulator could not build the planned response; the request was answered with a
|
|
152
|
+
* 500 emulator error (still evidence-tagged) and no fault was used up.
|
|
153
|
+
*/
|
|
154
|
+
readonly responseError?: string; /** Set when a scripted turn answered the request. */
|
|
155
|
+
readonly scripted?: 'message' | 'error'; /** Evidence of the matched route; `unknown-route` for requests that failed closed. */
|
|
156
|
+
readonly evidence: EmulatorEvidence | 'unknown-route';
|
|
157
|
+
readonly status: number; /** Body chunks handed to the transport so far (each produced when pulled). */
|
|
158
|
+
readonly bodyChunks: number;
|
|
159
|
+
};
|
|
160
|
+
type MessagesEmulator = EmulatorApi<MessagesScriptedTurn, MessagesFault, MessagesLedgerEntry>;
|
|
161
|
+
type MessagesEmulatorConfig = {
|
|
162
|
+
/** The Messages path, for example `/v1/messages`. */readonly path: string; /** Route evidence manifest; must list exactly the Messages route. */
|
|
163
|
+
readonly routes: ReadonlyArray<EmulatorRouteEvidence>;
|
|
164
|
+
readonly knownModels: ReadonlyArray<string>; /** Builds the error thrown by the JS API (`faults.add`, `script.enqueue`) for invalid input. */
|
|
165
|
+
readonly inputInvalid: (input: 'fault' | 'turn', reason: string) => Error;
|
|
166
|
+
};
|
|
167
|
+
/** The Anthropic error envelope. */
|
|
168
|
+
declare const messagesErrorEnvelope: (error: MessagesWireError) => Schema.JsonObject;
|
|
169
|
+
/**
|
|
170
|
+
* Create a Messages emulator from its config. Each call has independent ledger, fault, and
|
|
171
|
+
* script state.
|
|
172
|
+
*
|
|
173
|
+
* Precedence per request: authentication, a supported `anthropic-version`, JSON and request
|
|
174
|
+
* validation (including a positive integer `max_tokens`, and no `thinking` with a forced
|
|
175
|
+
* `tool_choice`), then the first matching fault if it is a `status` fault, then the next
|
|
176
|
+
* scripted turn, then model validation and defaults; a first matching body fault then shapes the
|
|
177
|
+
* body. Only the first matching fault (in insertion order) applies. Credential headers are never
|
|
178
|
+
* recorded, and credential values are never checked or stored.
|
|
179
|
+
*/
|
|
180
|
+
declare const makeMessagesEmulator: (config: MessagesEmulatorConfig) => MessagesEmulator;
|
|
181
|
+
//#endregion
|
|
182
|
+
export { MessagesEmulator, MessagesEmulatorConfig, MessagesErrorEventFault, MessagesFault, MessagesFaultKind, MessagesLedgerEntry, MessagesScriptedMessage, MessagesScriptedToolUse, MessagesScriptedTurn, MessagesScriptedUsage, MessagesWireError, makeMessagesEmulator, messagesErrorEnvelope };
|
|
183
|
+
//# sourceMappingURL=messages.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.mts","names":[],"sources":["../src/messages.ts"],"mappings":";;;;;;cAyDa,iBAAA,EAAiB,MAAA,CAAA,MAAA;EAAA;;;KAKlB,iBAAA,UAA2B,iBAAA,CAAkB,IAAI;;;;;cAMhD,uBAAA,EAAuB,MAAA,CAAA,MAAA;EAAA;;;;;;;;;;;;;AANyB;AAM7D;;;;cAaa,aAAA,EAAa,MAAA,CAAA,KAAA,WAAA,MAAA,CAAA,MAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAOd,aAAA,UAAuB,aAAA,CAAc,IAAI;AAAA,KAEzC,iBAAA,GAAoB,aAAa;;cAKhC,qBAAA,EAAqB,MAAA,CAAA,MAAA;EAAA;;;KAKtB,qBAAA,UAA+B,qBAAA,CAAsB,IAAI;;cAGxD,uBAAA,EAAuB,MAAA,CAAA,MAAA;EAAA;;;;KAMxB,uBAAA,UAAiC,uBAAA,CAAwB,IAAI;;;;AA5BzE;;;cAoCa,uBAAA,EAAuB,MAAA,CAAA,MAAA;EAAA;;;;;;;;;;;;;;KASxB,uBAAA,UAAiC,uBAAA,CAAwB,IAAI;;cAG5D,oBAAA,EAAoB,MAAA,CAAA,KAAA,WAAA,MAAA,CAAA,MAAA;EAAA;;;;;;;;;;;;;;;;;;;;KAErB,oBAAA,UAA8B,oBAAA,CAAqB,IAAI;AAAA,KAEvD,mBAAA;6EAED,GAAA;EAAA,SACA,MAAA;EAAA,SACA,IAAA;WAEA,IAAA,GAAO,MAAA,CAAO,IAAA;EAAA,SACd,KAAA;EAAA,SACA,MAAA;WAEA,SAAA;EAAA,SACA,QAAA,GAAW,MAAA,CAAO,IAAA;EAAA,SAClB,UAAA,GAAa,MAAA,CAAO,IAAA;EAAA,SACpB,SAAA,EAAW,aAAA;WAEX,gBAAA;EAAA,SACA,gBAAA;EAAA,SACA,aAAA,WArEe;EAAA,SAuEf,KAAA,GAAQ,iBAAA,EAvEO;EAAA,SAyEf,UAAA;EAzEe;;;;EAAA,SA8Ef,aAAA;WAEA,QAAA;WAEA,QAAA,EAAU,gBAAA;EAAA,SACV,MAAA;WAEA,UAAA;AAAA;AAAA,KAGC,gBAAA,GAAmB,WAAA,CAAY,oBAAA,EAAsB,aAAA,EAAe,mBAAA;AAAA,KAEpE,sBAAA;gEAED,IAAA;WAEA,MAAA,EAAQ,aAAA,CAAc,qBAAA;EAAA,SACtB,WAAA,EAAa,aAAA;WAEb,YAAA,GAAe,KAAA,oBAAyB,MAAA,aAAmB,KAAA;AAAA;;cA2XzD,qBAAA,GAAyB,KAAA,EAAO,iBAAA,KAAoB,MAAA,CAAO,UAGtE;;;;;;;;;;;;cAkFW,oBAAA,GAAwB,MAAA,EAAQ,sBAAA,KAAyB,gBAyMrE"}
|