Skip to content

[codex] Keep harness runtime importable at the edge#124

Merged
minpeter merged 3 commits into
mainfrom
codex/fix-minimal-agent-dev-watch
Apr 27, 2026
Merged

[codex] Keep harness runtime importable at the edge#124
minpeter merged 3 commits into
mainfrom
codex/fix-minimal-agent-dev-watch

Conversation

@minpeter
Copy link
Copy Markdown
Owner

@minpeter minpeter commented Apr 27, 2026

Summary

  • Keep @ai-sdk-tool/harness/runtime free of unconditional Node-only imports so edge consumers do not pull node:fs, node:path, or node:crypto through the runtime subpath.
  • Move Node dotenv discovery/loading into the explicit @ai-sdk-tool/harness/env-node subpath.
  • Load skills and MCP plumbing lazily only when the runtime configuration actually uses those features.
  • Preserve Node CLI behavior by calling the Node dotenv loader from minimal-agent and tgbot entrypoints.
  • Keep the minimal-agent dev watch fix for the local EMFILE failure.
  • Add a browser-condition, process-less runtime import/openSession smoke to catch external package conditional-export regressions.

Root Cause

The first local fix made minimal-agent runnable but moved Node filesystem imports into harness/env.ts. That broke the existing edge-runtime intent because createAgentRuntime() imports runtime internals that should be usable from Cloudflare Workers without probing local .env files or statically importing filesystem-backed skills/MCP code.

An Oracle review also found that the harness source graph alone is not enough: the external AI SDK path can resolve to a Node-oriented @vercel/oidc build unless browser conditions are active. The new smoke test covers that package-resolution requirement without adding Wrangler as a project dependency.

Worker-Safe Minimum

The supported minimal edge path is intentionally narrow: import @ai-sdk-tool/harness/runtime, pass Worker bindings into the provider directly, call createAgentRuntime({ cwd: "/" }), open a session, and run a turn without file persistence, skills, MCP, TUI/headless adapters, or @ai-sdk-tool/harness/env-node.

FileSnapshotStore, file-backed preferences, local skills discovery, stdio MCP, and Node dotenv loading remain Node-oriented surfaces. A real Cloudflare Worker app still needs its bundler/deploy path to resolve browser conditions correctly; if Cloudflare support becomes a first-class guarantee, add a Wrangler/Workers bundle smoke on top of this package-level test.

Validation

  • pnpm -F "@ai-sdk-tool/harness" test -- src/runtime/runtime-edge-imports.test.ts
  • pnpm -F "@ai-sdk-tool/harness" typecheck
  • pnpm -F "@ai-sdk-tool/harness" test
  • pnpm -F "@ai-sdk-tool/harness" build
  • pnpm -F "@plugsuits/minimal-agent" typecheck
  • pnpm -F "@plugsuits/minimal-agent" test
  • pnpm -F "@plugsuits/minimal-agent" dev
  • pnpm -F "@plugsuits/tgbot" typecheck
  • pnpm -F "@plugsuits/tgbot" test
  • pnpm run check
  • Runtime import/openSession smoke via the minimal-agent workspace context

Notes

The new runtime-edge-imports.test.ts guards the runtime static import graph against Node built-ins and verifies the external dependency path under browser conditions with process removed.

The local dev entrypoint failed before rendering the TUI because Node watch attempted to observe too much of the workspace and hit macOS file-watch limits. A second source-condition run exposed a Node 24 ESM ambiguity in the harness dotenv loader, so the loader now uses static Node imports instead of require inside an ESM graph.

Constraint: minimal-agent dev must keep using source-condition TypeScript entrypoints for workspace packages

Constraint: Node 24 rejects require inside this top-level-await ESM execution graph

Rejected: Watch sibling workspace source directories from minimal-agent dev | Node watch still hits EMFILE in this environment

Confidence: high

Scope-risk: narrow

Directive: Do not broaden minimal-agent dev watch paths without validating macOS file-watch behavior

Tested: pnpm -F "@plugsuits/minimal-agent" dev

Tested: pnpm -F "@ai-sdk-tool/harness" typecheck

Tested: pnpm -F "@ai-sdk-tool/harness" test

Tested: pnpm -F "@plugsuits/minimal-agent" typecheck

Tested: pnpm -F "@plugsuits/minimal-agent" test

Tested: pnpm run check

Not-tested: Watching harness/tui/headless sibling source trees from the minimal-agent dev script
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

Important

Review skipped

Auto reviews are disabled on this repository. To trigger a review, include @crb review in the PR description. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c2174c15-6599-49ca-bba4-59dd8624e0d0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-minimal-agent-dev-watch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request addresses dotenv loading issues on Node 24 by refactoring imports in the harness package and updating the development script in the minimal-agent package. Review feedback highlights that converting dynamic requires to top-level static imports for Node-specific modules breaks compatibility with edge runtimes. Additionally, the use of hardcoded file paths in the watch script is noted as a maintenance risk that could lead to missed updates when new files are added.

Comment thread packages/harness/src/env.ts Outdated
Comment on lines +1 to +2
import { existsSync } from "node:fs";
import { dirname, resolve } from "node:path";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Moving node:fs and node:path to top-level static imports breaks the "Safe to omit in edge runtimes" guarantee mentioned on line 7. Edge environments (e.g., Cloudflare Workers, Vercel Edge) typically do not support node:fs and will throw a module resolution error at import time, even if loadDotEnvFilesIfAvailable is never called. If cross-platform compatibility is still a goal, consider using dynamic import() or update the documentation to reflect that this module is now Node-only.

"benchmark": "node --conditions=@ai-sdk-tool/source --import tsx benchmark.ts",
"build": "tsc && node ../../scripts/fix-esm-imports.mjs dist",
"dev": "node --watch --conditions=@ai-sdk-tool/source --import tsx index.ts",
"dev": "node --watch --watch-path=index.ts --watch-path=env.ts --watch-path=preferences.ts --conditions=@ai-sdk-tool/source --import tsx index.ts",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Hardcoding specific filenames in --watch-path is fragile and creates a maintenance burden. Any new files added to the package (e.g., a new tools.ts or utils.ts) will not be watched by default, which can lead to confusion when changes don't trigger a reload. While this approach avoids EMFILE errors, consider if watching the current directory with an ignore pattern for the session directory would be more sustainable.

The first fix made minimal-agent runnable locally but moved Node filesystem imports into the harness runtime graph. That regressed the existing edge-runtime intent, so this separates Node-only dotenv loading behind an explicit env-node subpath and keeps runtime core imports free of Node built-ins.

Constraint: Cloudflare Workers can run Node APIs only under nodejs_compat, and node:fs availability depends on newer compatibility dates or explicit flags

Constraint: minimal-agent and tgbot still need package/workspace .env loading in Node entrypoints

Rejected: Keep dotenv loading inside createAgentRuntime | edge runtimes should receive bindings/env from the host instead of probing local .env files

Rejected: Statically import SkillsEngine and MCP plumbing from runtime core | those paths pull node:fs/node:path into consumers that do not use skills or MCP

Confidence: high

Scope-risk: moderate

Directive: Do not add Node built-in imports to the @ai-sdk-tool/harness/runtime static graph; extend runtime-edge-imports.test.ts when the boundary changes

Tested: pnpm -F "@ai-sdk-tool/harness" typecheck

Tested: pnpm -F "@ai-sdk-tool/harness" test

Tested: pnpm -F "@ai-sdk-tool/harness" build

Tested: pnpm -F "@plugsuits/minimal-agent" typecheck

Tested: pnpm -F "@plugsuits/minimal-agent" test

Tested: pnpm -F "@plugsuits/minimal-agent" dev

Tested: pnpm -F "@plugsuits/tgbot" typecheck

Tested: pnpm -F "@plugsuits/tgbot" test

Tested: pnpm run check

Tested: runtime import/openSession smoke via @plugsuits/minimal-agent workspace context

Not-tested: Wrangler deploy/build against a real Cloudflare Worker project
@minpeter minpeter changed the title [codex] Keep minimal-agent dev runnable under watch limits [codex] Keep harness runtime importable at the edge Apr 27, 2026
Oracle review found that the harness runtime source graph was edge-clean but the external AI SDK path can reach @vercel/oidc's Node build unless the browser condition is active. Add a process-less import/openSession smoke under browser conditions so the PR covers that dependency-resolution requirement too.

Constraint: Do not add Wrangler as a dependency just to validate this package-level boundary

Rejected: Rely only on source static graph scanning | it misses external package conditional exports

Confidence: medium

Scope-risk: narrow

Directive: If Cloudflare support becomes a first-class promise, add a real Wrangler/Workers bundle test in addition to this browser-condition smoke

Tested: pnpm -F "@ai-sdk-tool/harness" test -- src/runtime/runtime-edge-imports.test.ts

Tested: pnpm -F "@ai-sdk-tool/harness" typecheck

Tested: pnpm -F "@ai-sdk-tool/harness" test

Tested: pnpm run check

Not-tested: Wrangler deploy/build against a real Cloudflare Worker project
@minpeter minpeter marked this pull request as ready for review April 27, 2026 16:22
@minpeter minpeter merged commit 54125d0 into main Apr 27, 2026
5 of 6 checks passed
@minpeter minpeter deleted the codex/fix-minimal-agent-dev-watch branch April 27, 2026 16:22
minpeter added a commit that referenced this pull request Apr 27, 2026
FileSnapshotStore now accepts a top-level state directory, resolves it at construction time, owns the <root>/sessions layout, and best-effort appends that root to the containing worktree's .gitignore. Consumers no longer precompute sessions directories or maintain ignore entries independently.

This keeps session storage stable even if process.cwd() changes after store construction, and it aligns the shipped release notes with the public rootDir/sessionsDir getter contract.

Constraint: Preserve #124's edge-safe runtime import changes while rebasing onto current main

Rejected: Leave relative rootDir values as-is | contradicted the resolved-path contract and made later cwd changes affect save/load paths

Confidence: high

Scope-risk: moderate

Directive: Do not reintroduce caller-managed /sessions paths without updating downstream env names and migration notes

Tested: pnpm run typecheck; pnpm run test; pnpm run build; pnpm run check

Not-tested: Live model-backed minimal-agent session
minpeter added a commit that referenced this pull request Apr 27, 2026
FileSnapshotStore now accepts a top-level state directory, resolves it at construction time, owns the <root>/sessions layout, and best-effort appends that root to the containing worktree's .gitignore. Consumers no longer precompute sessions directories or maintain ignore entries independently.

This keeps session storage stable even if process.cwd() changes after store construction, and it aligns the shipped release notes with the public rootDir/sessionsDir getter contract.

Constraint: Preserve #124's edge-safe runtime import changes while rebasing onto current main

Rejected: Leave relative rootDir values as-is | contradicted the resolved-path contract and made later cwd changes affect save/load paths

Confidence: high

Scope-risk: moderate

Directive: Do not reintroduce caller-managed /sessions paths without updating downstream env names and migration notes

Tested: pnpm run typecheck; pnpm run test; pnpm run build; pnpm run check

Not-tested: Live model-backed minimal-agent session
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant