@skybridge/test 0.0.0-dev.9a3a28d → 0.0.0-dev.b9466d5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,19 +4,19 @@ Evals for [Skybridge](https://skybridge.tech) MCP apps: they answer whether a
4
4
  prompt reaches the right tool with the right arguments, which no unit test can
5
5
  check.
6
6
 
7
- **Beta.** The API is still settling, so it publishes under the `beta`
8
- dist-tag and may change between releases.
7
+ **Beta.** The API is still settling and may change between releases.
9
8
 
10
9
  Add the `evals` option to the Skybridge Vite plugin, then write scenarios with
11
10
  vitest's own `it` and `expect`.
12
11
 
13
12
  ```ts
13
+ import { anthropic } from "@ai-sdk/anthropic";
14
14
  import { expect, it } from "vitest";
15
15
  import { start } from "@skybridge/test";
16
16
  import type { AppType } from "../src/server.js";
17
17
 
18
18
  it("finds products by category", async () => {
19
- const chat = await start<AppType>();
19
+ const chat = await start<AppType>({ model: anthropic("claude-sonnet-4-5") });
20
20
  await chat.send("I am looking for ski goggles");
21
21
 
22
22
  expect.chat(chat).toHaveCalledToolWith("search-products", {
@@ -27,64 +27,39 @@ it("finds products by category", async () => {
27
27
 
28
28
  ## Choosing a model
29
29
 
30
- `model` is a `provider/model-id` string. Only the provider you name is
31
- imported, so install just that one as a dev dependency, and the key is read
32
- from its own environment variable.
33
-
34
- | prefix | package | key |
35
- | --- | --- | --- |
36
- | `anthropic` | `@ai-sdk/anthropic` | `ANTHROPIC_API_KEY` |
37
- | `openai` | `@ai-sdk/openai` | `OPENAI_API_KEY` |
38
- | `mistral` | `@ai-sdk/mistral` | `MISTRAL_API_KEY` |
39
- | `google` | `@ai-sdk/google` | `GOOGLE_GENERATIVE_AI_API_KEY` |
30
+ `start` takes any AI SDK model instance, so the provider and its credentials
31
+ are the project's own. Install the provider package you want alongside `ai`.
40
32
 
41
33
  ```ts
42
- skybridge({
43
- evals: {
44
- model: "anthropic/claude-sonnet-4-5",
45
- project: { cwd: ".", command: ["node", "dist/server.js"] },
46
- },
47
- });
48
- ```
49
-
50
- `command` has to start the server process directly, since wrappers such as
51
- `pnpm run` do not forward the channel the runner uses to learn the port.
52
-
53
- Any other prefix is passed to the AI SDK untouched, so a string such as
54
- `openai/gpt-5` also resolves through the Vercel AI Gateway if you have
55
- `AI_GATEWAY_API_KEY` set and no provider package installed.
34
+ import { anthropic } from "@ai-sdk/anthropic";
35
+ import { start } from "@skybridge/test";
56
36
 
57
- ## Models the prefixes cannot reach
37
+ const chat = await start<AppType>({ model: anthropic("claude-sonnet-4-5") });
38
+ ```
58
39
 
59
- For a local runtime, a company gateway, or a model that needs middleware,
60
- build the model yourself and register it from a vitest setup file. It takes
61
- precedence over `model`, which can then be left out.
40
+ A local runtime or a company gateway works the same way, through the AI SDK's
41
+ own `createOpenAICompatible` or `customProvider`.
62
42
 
63
43
  ```ts
64
- // eval-model.ts
65
44
  import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
66
- import { defineEvalModel } from "@skybridge/test";
67
-
68
- defineEvalModel(
69
- createOpenAICompatible({ baseURL: "http://localhost:11434/v1" })("llama3.1"),
70
- );
71
- ```
72
45
 
73
- ```ts
74
- // vitest.config.ts
75
- export default defineConfig({
76
- plugins: [skybridge({ evals: { project: { ... } } })],
77
- test: { setupFiles: ["./eval-model.ts"] },
46
+ const ollama = createOpenAICompatible({
47
+ name: "ollama",
48
+ baseURL: "http://localhost:11434/v1",
78
49
  });
50
+ const chat = await start<AppType>({ model: ollama("llama3.1") });
79
51
  ```
80
52
 
81
- Tool calls the server rejected do not count towards a positive assertion, so a
82
- scenario cannot pass on a call that errored. To assert an error on purpose, use
83
- `toHaveFailedToolCall`.
53
+ ## Booting the server
84
54
 
85
55
  ```ts
86
- expect.chat(chat).toHaveFailedToolCall("create-checkout");
56
+ skybridge({
57
+ evals: { project: { cwd: ".", command: ["node", "dist/server.js"] } },
58
+ });
87
59
  ```
88
60
 
61
+ `command` has to start the server process directly, since wrappers such as
62
+ `pnpm run` do not forward the channel the runner uses to learn the port.
63
+
89
64
  See the [documentation](https://docs.skybridge.tech) for setup and the full
90
65
  matcher list.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  export type { ChatAssertion, ChatMatchers, } from "@skybridge/vite-plugin/evals";
2
2
  export type { Chat } from "./chat.js";
3
- export { defineEvalModel } from "./model.js";
4
3
  export { start } from "./session-registry.js";
5
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,YAAY,GACb,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,YAAY,GACb,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,2 @@
1
- export { defineEvalModel } from "./model.js";
2
1
  export { start } from "./session-registry.js";
3
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC"}
@@ -1,3 +1,4 @@
1
+ import type { LanguageModel } from "ai";
1
2
  import { Chat } from "./chat.js";
2
3
  /**
3
4
  * Opens a fresh MCP session and conversation against the server the plugin
@@ -9,8 +10,11 @@ import { Chat } from "./chat.js";
9
10
  * `start<AppType>()` returns a `Chat<AppType>`, and `expect.chat` infers the
10
11
  * tool names and argument shapes from it.
11
12
  */
12
- export declare function start<App>(overrides?: {
13
+ export declare function start<App>(options: {
14
+ /** Any AI SDK model instance, built by the project. */
15
+ model: LanguageModel;
13
16
  systemPrompt?: string;
14
17
  temperature?: number;
18
+ maxSteps?: number;
15
19
  }): Promise<Chat<App>>;
16
20
  //# sourceMappingURL=session-registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-registry.d.ts","sourceRoot":"","sources":["../src/session-registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC;;;;;;;;;GASG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAgBrB"}
1
+ {"version":3,"file":"session-registry.d.ts","sourceRoot":"","sources":["../src/session-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAExC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;;;;GASG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE;IACxC,uDAAuD;IACvD,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAgBrB"}
@@ -1,6 +1,5 @@
1
1
  import { inject, onTestFinished } from "vitest";
2
2
  import { Chat } from "./chat.js";
3
- import { resolveModel } from "./model.js";
4
3
  /**
5
4
  * Opens a fresh MCP session and conversation against the server the plugin
6
5
  * started. The session is closed when the current test finishes, so scenarios
@@ -11,17 +10,17 @@ import { resolveModel } from "./model.js";
11
10
  * `start<AppType>()` returns a `Chat<AppType>`, and `expect.chat` infers the
12
11
  * tool names and argument shapes from it.
13
12
  */
14
- export async function start(overrides) {
15
- const config = inject("skybridgeEvals");
13
+ export async function start(options) {
16
14
  const url = inject("skybridgeEvalsUrl");
17
- if (config === undefined || url === undefined) {
15
+ if (url === undefined) {
18
16
  throw new Error("No eval server is running. Add `skybridge({ evals: {...} })` to the vitest config.");
19
17
  }
18
+ const config = inject("skybridgeEvals");
20
19
  const chat = await Chat.open(url, {
21
- model: await resolveModel(config.model),
22
- temperature: overrides?.temperature ?? config.temperature,
23
- systemPrompt: overrides?.systemPrompt ?? config.systemPrompt,
24
- maxSteps: config.maxSteps,
20
+ model: options.model,
21
+ temperature: options.temperature ?? config?.temperature,
22
+ systemPrompt: options.systemPrompt ?? config?.systemPrompt,
23
+ maxSteps: options.maxSteps ?? config?.maxSteps,
25
24
  });
26
25
  onTestFinished(() => chat.close());
27
26
  return chat;
@@ -1 +1 @@
1
- {"version":3,"file":"session-registry.js","sourceRoot":"","sources":["../src/session-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAM,SAGhC;IACC,MAAM,MAAM,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACxC,IAAI,MAAM,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,oFAAoF,CACrF,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAM,GAAG,EAAE;QACrC,KAAK,EAAE,MAAM,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC;QACvC,WAAW,EAAE,SAAS,EAAE,WAAW,IAAI,MAAM,CAAC,WAAW;QACzD,YAAY,EAAE,SAAS,EAAE,YAAY,IAAI,MAAM,CAAC,YAAY;QAC5D,QAAQ,EAAE,MAAM,CAAC,QAAQ;KAC1B,CAAC,CAAC;IACH,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"session-registry.js","sourceRoot":"","sources":["../src/session-registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAM,OAMhC;IACC,MAAM,GAAG,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACxC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,oFAAoF,CACrF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAM,GAAG,EAAE;QACrC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,MAAM,EAAE,WAAW;QACvD,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,MAAM,EAAE,YAAY;QAC1D,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,MAAM,EAAE,QAAQ;KAC/C,CAAC,CAAC;IACH,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC;AACd,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skybridge/test",
3
- "version": "0.0.0-dev.9a3a28d",
3
+ "version": "0.0.0-dev.b9466d5",
4
4
  "description": "Eval runner for Skybridge MCP apps: does a prompt reach the right tool with the right arguments",
5
5
  "license": "ISC",
6
6
  "type": "module",
@@ -12,10 +12,6 @@
12
12
  ".": {
13
13
  "types": "./dist/index.d.ts",
14
14
  "default": "./dist/index.js"
15
- },
16
- "./vitest-config": {
17
- "types": "./dist/vitest-config.d.ts",
18
- "default": "./dist/vitest-config.js"
19
15
  }
20
16
  },
21
17
  "files": [
@@ -23,47 +19,23 @@
23
19
  ],
24
20
  "dependencies": {
25
21
  "@modelcontextprotocol/sdk": "^1.29.0",
26
- "ai": "^5.0.0",
27
- "@skybridge/vite-plugin": "0.0.0-dev.9a3a28d"
22
+ "@skybridge/vite-plugin": "0.0.0-dev.b9466d5"
28
23
  },
29
24
  "peerDependencies": {
30
- "@ai-sdk/anthropic": "^2.0.0",
31
- "@ai-sdk/google": "^2.0.0",
32
- "@ai-sdk/mistral": "^2.0.0",
33
- "@ai-sdk/openai": "^2.0.0",
25
+ "ai": "^5.0.0",
34
26
  "skybridge": "^2.0.0",
35
27
  "vitest": "^4.1.0"
36
28
  },
37
- "peerDependenciesMeta": {
38
- "@ai-sdk/anthropic": {
39
- "optional": true
40
- },
41
- "@ai-sdk/google": {
42
- "optional": true
43
- },
44
- "@ai-sdk/mistral": {
45
- "optional": true
46
- },
47
- "@ai-sdk/openai": {
48
- "optional": true
49
- }
50
- },
51
29
  "devDependencies": {
52
- "@ai-sdk/anthropic": "^2.0.0",
53
- "@ai-sdk/google": "^2.0.0",
54
- "@ai-sdk/mistral": "^2.0.0",
55
- "@ai-sdk/openai": "^2.0.0",
56
30
  "@types/node": "^24.13.3",
31
+ "ai": "^5.0.0",
57
32
  "typescript": "^5.9.3",
58
33
  "vitest": "^4.1.10",
59
- "skybridge": "0.0.0-dev.9a3a28d"
34
+ "skybridge": "0.0.0-dev.b9466d5"
60
35
  },
61
36
  "engines": {
62
37
  "node": ">=22.23.1"
63
38
  },
64
- "publishConfig": {
65
- "tag": "beta"
66
- },
67
39
  "scripts": {
68
40
  "build": "tsc -p tsconfig.json",
69
41
  "format": "biome check --write --error-on-warnings",
package/dist/model.d.ts DELETED
@@ -1,15 +0,0 @@
1
- import type { LanguageModel } from "ai";
2
- /**
3
- * Registers a model instance from a setup file, taking precedence over the
4
- * configured `model` string. The escape hatch for anything the provider
5
- * prefixes cannot express, such as a local runtime or a company LLM gateway.
6
- */
7
- export declare function defineEvalModel(model: LanguageModel): void;
8
- /**
9
- * Turns a `provider/model-id` string into a model instance, importing only the
10
- * provider named. Each provider package reads its own conventional API key
11
- * variable, so no key configuration is needed. An unrecognised prefix is
12
- * returned untouched for the AI SDK's default provider to resolve.
13
- */
14
- export declare function resolveModel(spec: string | undefined): Promise<LanguageModel>;
15
- //# sourceMappingURL=model.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAaxC;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAE1D;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,OAAO,CAAC,aAAa,CAAC,CAwBxB"}
package/dist/model.js DELETED
@@ -1,43 +0,0 @@
1
- const PROVIDERS = {
2
- anthropic: () => import("@ai-sdk/anthropic").then((m) => m.anthropic),
3
- google: () => import("@ai-sdk/google").then((m) => m.google),
4
- mistral: () => import("@ai-sdk/mistral").then((m) => m.mistral),
5
- openai: () => import("@ai-sdk/openai").then((m) => m.openai),
6
- };
7
- let registered;
8
- /**
9
- * Registers a model instance from a setup file, taking precedence over the
10
- * configured `model` string. The escape hatch for anything the provider
11
- * prefixes cannot express, such as a local runtime or a company LLM gateway.
12
- */
13
- export function defineEvalModel(model) {
14
- registered = model;
15
- }
16
- /**
17
- * Turns a `provider/model-id` string into a model instance, importing only the
18
- * provider named. Each provider package reads its own conventional API key
19
- * variable, so no key configuration is needed. An unrecognised prefix is
20
- * returned untouched for the AI SDK's default provider to resolve.
21
- */
22
- export async function resolveModel(spec) {
23
- if (registered !== undefined) {
24
- return registered;
25
- }
26
- if (spec === undefined) {
27
- throw new Error("No model configured. Set `model` on the evals plugin, or call defineEvalModel() from a setup file.");
28
- }
29
- const separator = spec.indexOf("/");
30
- if (separator === -1) {
31
- return spec;
32
- }
33
- const name = spec.slice(0, separator);
34
- const load = PROVIDERS[name];
35
- if (load === undefined) {
36
- return spec;
37
- }
38
- const provider = await load().catch(() => {
39
- throw new Error(`The model "${spec}" needs @ai-sdk/${name}@^2. Install it as a dev dependency.`);
40
- });
41
- return provider(spec.slice(separator + 1));
42
- }
43
- //# sourceMappingURL=model.js.map
package/dist/model.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAIA,MAAM,SAAS,GAAmD;IAChE,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACrE,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IAC5D,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IAC/D,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;CAC7D,CAAC;AAEF,IAAI,UAAqC,CAAC;AAE1C;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAoB;IAClD,UAAU,GAAG,KAAK,CAAC;AACrB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAwB;IAExB,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,oGAAoG,CACrG,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;QACvC,MAAM,IAAI,KAAK,CACb,cAAc,IAAI,mBAAmB,IAAI,sCAAsC,CAChF,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;AAC7C,CAAC"}