Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/harness-worker-edge-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ai-sdk-tool/harness": patch
---

Verify the runtime subpath in a real Cloudflare Worker bundle and keep optional Node-only MCP and skills modules out of edge bundles unless those features are configured.
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,29 @@ jobs:

- name: Test
run: SKIP_E2E=1 pnpm run test

edge-safe:
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.32.1

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 24
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Check Cloudflare Worker runtime compatibility
run: pnpm run test:edge-safe
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"typecheck:root": "pnpm -F @ai-sdk-tool/harness exec tsc --noEmit -p ../../tsconfig.root.json",
"check": "pnpm exec ultracite check .",
"lint": "pnpm exec ultracite fix .",
"test:edge-safe": "pnpm -F @ai-sdk-tool/harness build && node scripts/cloudflare-worker-edge-smoke/run.mjs",
"test:headless-edit": "node --import tsx scripts/test-headless-edit-ops.ts",
"test:headless-edit-edge": "node --import tsx scripts/test-headless-edit-edge-cases.ts",
"changeset": "changeset",
Expand Down Expand Up @@ -45,6 +46,7 @@
"tsx": "^4.21.0",
"turbo": "^2.9.6",
"ultracite": "^7.6.0",
"vitest": "^4.1.5"
"vitest": "^4.1.5",
"wrangler": "^4.85.0"
}
}
39 changes: 39 additions & 0 deletions packages/harness/edge-smoke/worker.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { createAgentRuntime, defineAgent } from "@ai-sdk-tool/harness/runtime";

export default {
async fetch() {
if ("process" in globalThis) {
return json({ ok: false, error: "process global is present" }, 500);
}

const runtime = await createAgentRuntime({
name: "cf-worker-edge-smoke",
cwd: "/",
agents: [
defineAgent({
name: "bot",
agent: { model: {}, instructions: "edge smoke" },
}),
],
});

try {
const session = await runtime.openSession({
sessionId: "cf-worker-edge-smoke-session",
});
return json({
ok: session.sessionId === "cf-worker-edge-smoke-session",
sessionId: session.sessionId,
});
} finally {
await runtime.close();
}
},
};

function json(body, status = 200) {
return new Response(JSON.stringify(body), {
headers: { "content-type": "application/json" },
status,
});
}
7 changes: 7 additions & 0 deletions packages/harness/edge-smoke/wrangler.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "../../../node_modules/wrangler/config-schema.json",
"name": "plugsuits-harness-runtime-edge-smoke",
"main": "./worker.mjs",
"compatibility_date": "2026-04-27",
"workers_dev": true
}
6 changes: 5 additions & 1 deletion packages/harness/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export interface StopConditionInput {
}
export type StopCondition = (input: StopConditionInput) => boolean;

const MCP_INIT_MODULE = "./mcp-init.js";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use extensionless specifier for MCP init dynamic import

This change introduces "./mcp-init.js" in source TS, which conflicts with the root AGENTS.md guideline requiring extensionless source imports and risks module-not-found errors for source-condition users/toolchains that don’t map .js back to .ts; that failure path is triggered specifically when config.mcp is provided. Keep the source import extensionless and apply extension fixes in compiled dist instead.

Useful? React with 👍 / 👎.


const toToolSet = async (toolSources: ToolSource[] | undefined) => {
if (!toolSources || toolSources.length === 0) {
return {};
Expand Down Expand Up @@ -235,7 +237,9 @@ export async function createAgent(config: AgentConfig): Promise<Agent> {
};

if (config.mcp !== undefined) {
const { resolveMCPOption } = await import("./mcp-init");
const { resolveMCPOption }: typeof import("./mcp-init") = await import(
MCP_INIT_MODULE
);
const resolved = await resolveMCPOption(config.mcp, mergedTools);
mergedTools = resolved.tools;
const previousClose = closeFn;
Expand Down
6 changes: 5 additions & 1 deletion packages/harness/src/runtime/create-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import type {
DefinedAgent,
} from "./types";

const SKILLS_MODULE = "../skills.js";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use extensionless specifier for skills dynamic import

The new "../skills.js" specifier in source TypeScript violates the repo rule in AGENTS.md (root) that source imports must stay extensionless, and it can break @ai-sdk-tool/source consumers that execute/compile TS sources directly (where skills.js does not exist alongside skills.ts), causing runtime resolution failures when definition.skills is enabled. Please keep the source specifier extensionless and handle .js rewriting only in build output.

Useful? React with 👍 / 👎.


const getDefaultCwd = (): string => {
if (typeof process === "undefined") {
return "/";
Expand All @@ -29,7 +31,9 @@ const getDefaultCwd = (): string => {
const loadConfiguredSkills = async (
config: AgentSkillsConfig
): Promise<SkillInfo[]> => {
const { SkillsEngine } = await import("../skills");
const { SkillsEngine }: typeof import("../skills") = await import(
SKILLS_MODULE
);
return await new SkillsEngine(config).loadAllSkills();
};

Expand Down
Loading
Loading