Skip to content

feat(runtime): expose the resolved plugin root as request.plugin and hand it to providers (#468) - #532

Merged
ScriptedAlchemy merged 4 commits into
mainfrom
fix/468-plugin-root-context
Sep 4, 2026
Merged

feat(runtime): expose the resolved plugin root as request.plugin and hand it to providers (#468)#532
ScriptedAlchemy merged 4 commits into
mainfrom
fix/468-plugin-root-context

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Fixes #468.

Why

The request context had no plugin-root axis: workspace is the caller's cwd, and the durable-state anchor was computed inline — three times in entry-shell.ts (state kernel, notice ledger, lineage journal) and once more in the standalone hook wrapper — so a plugin with its own on-disk files (movie-library's scripts/state-root.ts, src/providers/tracker-session.ts) re-derived AGENT_BUNDLE_PLUGIN_ROOT with its own fallback heuristics and ended up with two anchors for one install.

Design

  • One resolutionresolvePluginRoot({ fallback }) in @agent-bundle/runtime (plugin-root.ts): AGENT_BUNDLE_PLUGIN_ROOT when set to a non-empty, expanded value (source: 'native'); otherwise the shell's fallback ('derived') — the artifact root (parent of mcp/, bin/, hooks/) for artifact shells, $PWD/.agent-bundle for the npm bin. A value still carrying a ${…} token is treated as unset and reported once on stderr, never joined into a path. Both roots are made absolute; stateRoot is <root>/state. Returns { root, stateRoot, source, identity } where identity is the Observed axis.
  • Request axisAgentRequestContext.plugin: Observed<AgentPluginIdentity> ({ root, stateRoot }), AgentRequestInit.plugin; unavailable('not-provided') outside a generated scope. AGENT_REQUEST_STORE_VERSION bumped to 5 (same rule as when lineage and terminal joined; see landing notes).
  • Generated shells collapse onto it — every generated module declares const pluginRoot = resolvePluginRoot({ fallback: … }) once; the SQLite kernel (generatedStateOwner), the notice-delivery store, the lineage journal, and the standalone hook's retireLineage all open pluginRoot.stateRoot; every runAgentRequest the module opens publishes pluginRoot.identity. The MCP entry hands it to createGeneratedRouteMcpServer({ pluginRoot }); requestIdentity publishes it per tool call / resource read / prompt get and the shared event runtime per event; the Flight worker receives it on the render message (message.plugin ?? pluginRoot.identity). docs/entry-conventions.md and entry-shell.test.ts pin that kernel, ledger, journal, and plugin.stateRoot are one value.
  • ProvidersAgentProviderContext.plugin (structural AgentProviderObservedPluginRoot, so agent-bundle root declarations stay runtime-import-free), passed by every generated provider loop and by executeProviders in the harness. Pass request identity, lineage, and read-only state/notices handles to context providers #459 is untouched: the anchor reaches providers the way invocation already does, nothing else.
  • HarnessrenderRoute, invokeCli, runScript, and openInMemoryMcpServer resolve the axis with the same runtime function (fallback <project root>/.agent-bundle; module targets fall back to cwd) and accept context.plugin like every other axis. Harness state still mounts in a temp dir; the doc comment says so.
  • Not changed — the Workbench dev surfaces render through the generated artifact and inherit this; dev/** is untouched. pluginRootEnvAnchor (the env contract) is unchanged; PLUGIN_ROOT_ENV_ANCHOR is the runtime's spelling of the same name.

Tests

  • rsc-runtime/tests/plugin-root.test.ts — native / relative / unset / blank / unexpanded-token resolution, the warning text, default process.env, and the request axis (not-provided by default, frozen identity when supplied).
  • tests/route-unit/render-route.test.ts — the new plugin-root fixture tool under AGENT_BUNDLE_PLUGIN_ROOT set (native), unset (derived <project>/.agent-bundle), and an unexpanded token (derived), plus the context.plugin seam.
  • tests/projection/mcp-in-memory.test.ts — the real generated server publishes the anchor it resolved when it opened; a context override is forwarded.
  • tests/entry-shell.test.ts — every generated shell declares one pluginRoot, mounts state / notices / lineage on pluginRoot.stateRoot, no AGENT_BUNDLE_PLUGIN_ROOT literal remains, providers receive plugin; worker source hash pin updated.
  • Fixture lists updated for the new tool (mcp-in-memory, cli-dispatch, packed-stdio, manifest, contract matrix).

Docs

website/docs/{en,zh}/guide/authoring/mcp.mdx (plugin-root anchor section: the plugin axis, sources, providers, harness), website/docs/{en,zh}/reference/runtime-environment.mdx, docs/entry-conventions.md (state section, provider contract), docs/diagnostics.md (AB4940 recovery text). pnpm docs:site:build green.

Verification

pnpm typecheck, pnpm lint, pnpm test:unit (3251 passed), pnpm test:route-unit, pnpm test:projection, pnpm docs:site:build.

Review status

  • Codex reviewed 158fcb2 (PR opened) and left one P2 thread on rsc-runtime/src/plugin-root.ts: trimming the configured AGENT_BUNDLE_PLUGIN_ROOT moved the anchor for a path with surrounding whitespace. Fixed in 87a82ebbc: only the blank check reads a trimmed copy; the path is resolved exactly as written (pinned by a new plugin-root.test.ts case). No PR comments are posted from this side; the thread is answered here.
  • Head 87a82ebbc is not yet re-reviewed by the connector.

Landing notes (rebase over main after #534)

@changeset-bot

changeset-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8681171

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@agent-bundle/runtime Patch
agent-bundle Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

ScriptedAlchemy added a commit that referenced this pull request Sep 4, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-04T06:03:24.788693Z 158fcb2 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pkg-pr-new

pkg-pr-new Bot commented Sep 4, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@532
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@532
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@532

commit: 8681171

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 158fcb2774

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/rsc-runtime/src/plugin-root.ts Outdated
*/
export const resolvePluginRoot = (options: ResolvePluginRootOptions): ResolvedPluginRoot => {
const env = options.env ?? process.env;
const declared = env[PLUGIN_ROOT_ENV_ANCHOR]?.trim() ?? '';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve whitespace in the configured plugin-root path

When a plugin is installed in a valid path with leading or trailing whitespace, such as /opt/curator , trimming the environment value changes the filesystem location before both root and stateRoot are derived. Generated state then opens a different directory and routes/providers receive an incorrect install root. Preserve the original value for resolve() and use a trimmed copy only to determine whether the variable is blank.

Useful? React with 👍 / 👎.

ScriptedAlchemy added a commit that referenced this pull request Sep 4, 2026
@ScriptedAlchemy
ScriptedAlchemy force-pushed the fix/468-plugin-root-context branch from 89d6174 to faa6599 Compare September 4, 2026 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant