Skip to content

feat(plugins): support manifests and static asset directories - #5685

Merged
WillemJiang merged 6 commits into
bytedance:mainfrom
AnnaSuSu:feat/plugin-manifest-assets
Sep 23, 2026
Merged

WillemJiang merged 6 commits into
bytedance:mainfrom
AnnaSuSu:feat/plugin-manifest-assets

Conversation

@AnnaSuSu

@AnnaSuSu AnnaSuSu commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #5647; related to #5510 and #5539.

Why

The initial full-stack plugin API requires one self-contained browser module. A larger plugin cannot split its JavaScript or resolve CSS/images relative to import.meta.url. This implements the manifest + static resource direction discussed in #5647 while retaining the existing page/action/tool contracts and single-file transport.

What changed

  • Add BrowserAssets(module, root, manifest="ui_manifest.json") in extension-api 0.2.3, alongside BrowserModule. A versioned manifest identifies the entry module and explicitly lists allowed files; deployment still owns installation and activation.
  • Validate and snapshot packaged resources at registration. Namespace/content-revision URLs serve only listed files with authentication, correct MIME types, nosniff, a document sandbox policy and private immutable caching. The sandbox restricts direct document navigation (including SVG); plugin scripts loaded into the host page remain trusted code. Traversal, symlinked roots/files, missing resources, unsupported formats and oversized packages are rejected. File/package limits are 4/16 MiB, with at most 256 files.
  • Preserve assets-v1 / inline-v1 through discovery. Packaged entries load as native credentialed module graphs, supporting relative static/lazy imports without Blob URL rewriting, with explicit Webpack/Turbopack ignore markers. Unknown transports and loading failures are isolated per plugin; host waiting for loading/evaluation has a 30-second deadline. This cannot cancel native evaluation or later top-level side effects.
  • Migrate the bookmarks example to a manifest, entry module, separate page module, CSS and SVG. Existing inline modules keep their authenticated fetch/Blob path. Compatible plugins do not require rebuilding the host frontend.

The existing deployment/restart/manual-refresh lifecycle remains. Historical server snapshots are not retained; cached code is not instantly revoked on uninstall. Split-origin deployments require credentialed CORS/cookies, and plugins must handle credentials for their own non-JS resources. CSS font/background limitations and CSP requirements are documented in docs/full-stack-plugins.md.

Surface area

  • Frontend UI — page / component / setting / interaction under frontend/
  • Backend API — endpoint / SSE event / request-response shape under backend/app
  • Agents / LangGraph — agent node, graph wiring, langgraph.json, or prompt change
  • Sandbox — docker/ or sandboxed execution
  • Skills — change under skills/
  • Dependencies — extension-api 0.2.2 → 0.2.3, matching exact harness pin; no new third-party dependency
  • Default behavior change — changes existing behavior without the user opting in
  • Docs / tests / CI only — no runtime behavior change

Frontend changes concern module loading/discovery. There are no new host screens or controls; the existing bookmarks example exercises packaged CSS and an SVG in its page.

Validation

  • Backend make format, make lint: passed.
  • Backend make test: 18,398 passed, 206 skipped, 14 failed. The same 14 browser/web-fetch address-guard failures reproduce on the unmodified base 61a99a1e; they report private/loopback-address refusals in this environment.
  • Focused manifest, plugin contribution, bookmark and API contract tests: 79 passed, including additional resource boundary coverage.
  • make test-blocking-io: 149 passed.
  • Frontend pnpm format, pnpm check, full unit suite: 1,939 passed; production build passed.
  • Browser E2E: 35 passed (30 against the new Webpack production build plus 5 native-asset tests) across bookmarks, native asset loading, business plugins, capability center, plugin icons and notifications. Native-loader tests use actual same/split-origin HTTP servers and cookie-gated static/lazy chunks; the bookmark flow uses real Python actions/SQLite with synthetic identity and scripted ToolNode calls.
  • Review regressions: root-symlink cases failed before the first fix; direct SVG script execution and the missing sandbox response header were reproduced before hardening. The native-loader suite now has 5 passing tests, including the real Gateway SVG response and an unsandboxed positive control. 9 bookmark E2E tests passed against the actual Turbo development server; the default bookmark flow also passed before the explicit Turbo marker on this pinned Next.js version.
  • Built the bookmarks wheel and registered it from its extracted contents: manifest and all four static files load successfully.
  • Agent guidance checks and git diff --check: passed (2 soft-budget warnings). A wording-only Gateway guidance reduction keeps the latest-main merge view below its hard limit; all 13 guidance tests passed.

AI assistance

Tool(s) used: Codex.

How you used it: Implementation, documentation, regression tests, local browser verification and PR preparation. Changes were inspected and validated against the existing plugin contracts; test limitations are reported above.

  • I've read and understand every line of this change and take responsibility for it — it's not unreviewed AI output.

@AnnaSuSu
AnnaSuSu requested review from Vanzeren and zhfeng and a lite review from Copilot September 22, 2026 04:08
@github-actions github-actions Bot added area:backend Gateway / runtime / core backend under backend/ area:deps Dependency manifests / lockfiles area:docs Documentation and Markdown only area:frontend Next.js frontend under frontend/ needs-validation Touches front/back contract surface; needs real-path validation risk:high High risk: backend API, agents, sandbox, auth, deps, CI size/XL PR changes 700+ lines labels Sep 22, 2026

Copilot AI left a comment

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.

Copilot review overview

🟡 Changes recommended

A critical root-symlink validation issue and a moderate native-module timeout limitation remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity

Open (1)
What changed in this PR

Adds manifest-backed static assets for plugins, including authenticated serving, native module loading, and a migrated bookmarks example.

Changes:

  • Adds BrowserAssets manifests, validation, snapshots, MIME handling, and asset routes.
  • Adds native credentialed loading for packaged modules with relative imports.
  • Updates the bookmarks example, documentation, dependencies, and test coverage.
File Summary
frontend/​tests/​unit/​core/​extensions/​registry.test.ts Transport-aware loader unit tests
frontend/​tests/​e2e/​plugin-assets.spec.ts Packaged asset loading E2E tests
frontend/​tests/​e2e/​bookmark-plugin.spec.ts Updated bookmark plugin E2E coverage
frontend/​src/​core/​extensions/​registry.ts Transport-aware plugin loading
frontend/​src/​core/​extensions/​contracts.ts Frontend transport contracts
frontend/​src/​core/​extensions/​asset-module.ts Native credentialed asset loader; timeout does not cancel late module evaluation
frontend/​src/​core/​extensions/​api.ts Transport discovery parsing
frontend/​AGENTS.md Plugin development guidance
examples/​deerflow-extension-bookmarks/​README.md Example usage documentation
examples/​deerflow-extension-bookmarks/​pyproject.toml Example version and dependency updates
examples/​deerflow-extension-bookmarks/​deerflow_extension_bookmarks/​ui_manifest.json Packaged asset manifest
examples/​deerflow-extension-bookmarks/​deerflow_extension_bookmarks/​static/​dist/​styles.css Packaged stylesheet
examples/​deerflow-extension-bookmarks/​deerflow_extension_bookmarks/​static/​dist/​index.mjs Plugin entry module
examples/​deerflow-extension-bookmarks/​deerflow_extension_bookmarks/​static/​dist/​chunks/​bookmarks.mjs Lazy bookmark page module
examples/​deerflow-extension-bookmarks/​deerflow_extension_bookmarks/​static/​dist/​bookmark.svg Packaged bookmark icon
examples/​deerflow-extension-bookmarks/​deerflow_extension_bookmarks/​__init__.py Example asset registration
docs/​full-stack-plugins.md Packaged asset documentation
backend/​uv.lock Dependency lock update
backend/​tests/​test_extension_api_contracts.py Extension API contract tests
backend/​tests/​test_browser_assets.py Asset security and boundary tests
backend/​packages/​harness/​pyproject.toml Extension API pin update
backend/​packages/​harness/​deerflow/​extensions/​registry.py Browser asset registration
backend/​packages/​harness/​deerflow/​extensions/​browser_assets.py Manifest validation and snapshots; root symlinks can escape package confinement
backend/​packages/​extension-api/​pyproject.toml Extension API package version
backend/​packages/​extension-api/​deerflow_extension_api/​plugins.py BrowserAssets public API
backend/​packages/​extension-api/​deerflow_extension_api/​__init__.py Public export and version update
backend/​app/​gateway/​routers/​plugins.py Authenticated static asset serving

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread backend/packages/harness/deerflow/extensions/browser_assets.py Outdated
@AnnaSuSu AnnaSuSu changed the title feat(plugins): manifest-backed assets / 插件 manifest 与静态资源目录 feat(plugins): support manifests and static asset directories Sep 22, 2026

@willem-bd willem-bd left a comment

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.

Reviewed at 50d8214. No blocking findings. The boundary work checks out: manifest allowlist with strict typing (bool-as-int rejected), per-component symlink rejection plus package-confined resolve(strict=True), deterministic revision digest over manifest+paths+content, namespace+revision scoping on the asset route (the cross-namespace 404 is covered), auth enforced before lookup, and per-plugin transport isolation on the loader side. The root-symlink thread from the Copilot review is fixed at this head with regression coverage, and the untouched reference example keeps loading because the loader's api check is a range, not an exact pin. Two non-blocking suggestions below.

script.onload = () => {
// The credentialed script populated the document's module map. Reuse that
// module instance to obtain its exports; do not fetch it as a Blob.
void import(/* webpackIgnore: true */ absoluteURL).then(

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.

Non-blocking: the repo also documents DEER_FLOW_DEV_BUNDLER=turbo as a supported dev path, and Turbopack's handling of webpackIgnore on dynamic imports has been inconsistent across versions (turbopackIgnore is its native marker). The new e2e transpiles this file standalone and the unit tests mock the importer, so only the webpack production build exercises this line. Consider adding /* turbopackIgnore: true */ next to webpackIgnore so import(absoluteURL) stays a native dynamic import under the turbo dev bundler too.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in ccfb567.

Added turbopackIgnore: true alongside webpackIgnore and documented how to run the bookmark E2E against the actual Turbo development server.

I also checked the existing head before this change: the default bookmark flow already passed with this pinned Next.js version. With both markers, all 9 bookmark browser tests passed against DEER_FLOW_DEV_BUNDLER=turbo pnpm dev, exercising the real compiled loader, packaged entry/lazy chunk, styling and actions. This is explicit bundler compatibility plus a verified dev path, rather than a claim that the previous head was broken on the current version.

The Webpack production build, 30 related production-browser tests, frontend lint/typecheck and all 1,939 frontend unit tests also passed.

return Response(
asset.content,
media_type=asset.media_type,
headers={

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.

Non-blocking hardening: image/svg+xml is served from the authenticated Gateway origin, and an SVG loaded by direct navigation runs as a document in that origin (scripts inside it execute there, with cookies available), while the docs present "HTML ... not served" as the serving boundary. For a trusted operator-installed package the marginal risk is small, but Content-Security-Policy: sandbox on these responses would de-fang direct navigation without affecting subresource use (<img>, CSS, fonts, module-script fetches ignore a response's sandbox directive) — or at least add the SVG caveat next to that docs line.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in ccfb567.

Added Content-Security-Policy: sandbox to packaged asset responses and documented the direct-navigation boundary. The restriction applies when an asset becomes a document; it does not isolate plugin JavaScript intentionally loaded into the host page.

The new Chromium regression uses the real Python registration and Gateway asset route with an SVG containing a script. Before the fix, direct navigation executed it. After the fix, the SVG still renders as an image and direct navigation cannot execute the script. A positive control removes only the response's sandbox header and confirms that the same SVG executes again. All 5 native asset browser tests passed, including same/split-origin authenticated static/lazy module imports, CSS and images; backend route tests check the header across served asset types.

@github-actions github-actions Bot added the area:ci GitHub Actions, CI config, repo tooling label Sep 22, 2026
@WillemJiang
WillemJiang merged commit 2a9beb3 into bytedance:main Sep 23, 2026
18 checks passed
@WillemJiang WillemJiang added this to the 2.2.0 milestone Sep 23, 2026
WillemJiang pushed a commit that referenced this pull request Sep 23, 2026
…5769)

* docs(extensions): add an eleven-page extension developer manual

Add a harness/extensions/ section in English and Chinese documenting the
deerflow-extension-api 0.2.3 contract:

  index                when to write an extension vs a tool, MCP server or
                       skill; contribution kinds; loading; failure and
                       trust model; versioning
  quick-start          build, test, install and remove a working extension
  runtime              load sequence, API version rules, scopes and
                       ExtensionData, fail-open and cancellation, diagnostics
  middleware           placements and where each lands, scope, ordering,
                       observe-only wrap hooks, failure isolation
  observers            task lifecycle, system model calls, agent assembly
                       and fingerprint, context compaction
  services-and-routes  service lifecycle, router rejection rules, auth,
                       principals
  run-evidence         service and request-scoped readers (#5727), cursor
                       semantics, deletions, redaction, store differences
  plugins              experimental full-stack plugins, inline modules and
                       manifest-based browser assets (#5685)
  operations           extension manager CLI, sources, upgrade, rollback,
                       Docker, Helm, recovery
  troubleshooting      indexed by the exact log and error strings
  reference            every public name and the contract version history

Also fix stale descriptions: the contribution kinds listed in AGENTS.md
and deerflow/extensions/AGENTS.md (now eight, including plugins), the
run-evidence redaction claim (only auth_token is removed), and the plugin
mount return value in docs/full-stack-plugins.md ({ dispose }, not a
function). Changelog entries added in both languages.

* docs(agents): trim root AGENTS.md to keep inherited chains under the size limit

* docs(extensions): scope the manual to the 0.2.1 contract

Plugins and request-scoped run evidence are not in 2.1.x-dev yet; they
move to a stacked follow-up so this change can be cherry-picked.

* docs(extensions): clarify PAT rejection on contributed routes
WillemJiang pushed a commit that referenced this pull request Sep 23, 2026
…5769)

* docs(extensions): add an eleven-page extension developer manual

Add a harness/extensions/ section in English and Chinese documenting the
deerflow-extension-api 0.2.3 contract:

  index                when to write an extension vs a tool, MCP server or
                       skill; contribution kinds; loading; failure and
                       trust model; versioning
  quick-start          build, test, install and remove a working extension
  runtime              load sequence, API version rules, scopes and
                       ExtensionData, fail-open and cancellation, diagnostics
  middleware           placements and where each lands, scope, ordering,
                       observe-only wrap hooks, failure isolation
  observers            task lifecycle, system model calls, agent assembly
                       and fingerprint, context compaction
  services-and-routes  service lifecycle, router rejection rules, auth,
                       principals
  run-evidence         service and request-scoped readers (#5727), cursor
                       semantics, deletions, redaction, store differences
  plugins              experimental full-stack plugins, inline modules and
                       manifest-based browser assets (#5685)
  operations           extension manager CLI, sources, upgrade, rollback,
                       Docker, Helm, recovery
  troubleshooting      indexed by the exact log and error strings
  reference            every public name and the contract version history

Also fix stale descriptions: the contribution kinds listed in AGENTS.md
and deerflow/extensions/AGENTS.md (now eight, including plugins), the
run-evidence redaction claim (only auth_token is removed), and the plugin
mount return value in docs/full-stack-plugins.md ({ dispose }, not a
function). Changelog entries added in both languages.

* docs(agents): trim root AGENTS.md to keep inherited chains under the size limit

* docs(extensions): scope the manual to the 0.2.1 contract

Plugins and request-scoped run evidence are not in 2.1.x-dev yet; they
move to a stacked follow-up so this change can be cherry-picked.

* docs(extensions): clarify PAT rejection on contributed routes
WillemJiang added a commit that referenced this pull request Sep 23, 2026
…run evidence (#5775)

* docs(extensions): add an eleven-page extension developer manual

Add a harness/extensions/ section in English and Chinese documenting the
deerflow-extension-api 0.2.3 contract:

  index                when to write an extension vs a tool, MCP server or
                       skill; contribution kinds; loading; failure and
                       trust model; versioning
  quick-start          build, test, install and remove a working extension
  runtime              load sequence, API version rules, scopes and
                       ExtensionData, fail-open and cancellation, diagnostics
  middleware           placements and where each lands, scope, ordering,
                       observe-only wrap hooks, failure isolation
  observers            task lifecycle, system model calls, agent assembly
                       and fingerprint, context compaction
  services-and-routes  service lifecycle, router rejection rules, auth,
                       principals
  run-evidence         service and request-scoped readers (#5727), cursor
                       semantics, deletions, redaction, store differences
  plugins              experimental full-stack plugins, inline modules and
                       manifest-based browser assets (#5685)
  operations           extension manager CLI, sources, upgrade, rollback,
                       Docker, Helm, recovery
  troubleshooting      indexed by the exact log and error strings
  reference            every public name and the contract version history

Also fix stale descriptions: the contribution kinds listed in AGENTS.md
and deerflow/extensions/AGENTS.md (now eight, including plugins), the
run-evidence redaction claim (only auth_token is removed), and the plugin
mount return value in docs/full-stack-plugins.md ({ dispose }, not a
function). Changelog entries added in both languages.

* docs(agents): trim root AGENTS.md to keep inherited chains under the size limit

* docs(extensions): scope the manual to the 0.2.1 contract

Plugins and request-scoped run evidence are not in 2.1.x-dev yet; they
move to a stacked follow-up so this change can be cherry-picked.

* docs(extensions): cover full-stack plugins and request-scoped run evidence

Brings the extension manual up to deerflow-extension-api 0.2.3. Stacked on
the 0.2.1 manual so that part can be cherry-picked to 2.1.x-dev alone.

* docs(extensions): clarify PAT rejection on contributed routes

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
vieenrose pushed a commit to vieenrose/deer-flow that referenced this pull request Sep 27, 2026
…ytedance#5769)

* docs(extensions): add an eleven-page extension developer manual

Add a harness/extensions/ section in English and Chinese documenting the
deerflow-extension-api 0.2.3 contract:

  index                when to write an extension vs a tool, MCP server or
                       skill; contribution kinds; loading; failure and
                       trust model; versioning
  quick-start          build, test, install and remove a working extension
  runtime              load sequence, API version rules, scopes and
                       ExtensionData, fail-open and cancellation, diagnostics
  middleware           placements and where each lands, scope, ordering,
                       observe-only wrap hooks, failure isolation
  observers            task lifecycle, system model calls, agent assembly
                       and fingerprint, context compaction
  services-and-routes  service lifecycle, router rejection rules, auth,
                       principals
  run-evidence         service and request-scoped readers (bytedance#5727), cursor
                       semantics, deletions, redaction, store differences
  plugins              experimental full-stack plugins, inline modules and
                       manifest-based browser assets (bytedance#5685)
  operations           extension manager CLI, sources, upgrade, rollback,
                       Docker, Helm, recovery
  troubleshooting      indexed by the exact log and error strings
  reference            every public name and the contract version history

Also fix stale descriptions: the contribution kinds listed in AGENTS.md
and deerflow/extensions/AGENTS.md (now eight, including plugins), the
run-evidence redaction claim (only auth_token is removed), and the plugin
mount return value in docs/full-stack-plugins.md ({ dispose }, not a
function). Changelog entries added in both languages.

* docs(agents): trim root AGENTS.md to keep inherited chains under the size limit

* docs(extensions): scope the manual to the 0.2.1 contract

Plugins and request-scoped run evidence are not in 2.1.x-dev yet; they
move to a stacked follow-up so this change can be cherry-picked.

* docs(extensions): clarify PAT rejection on contributed routes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:backend Gateway / runtime / core backend under backend/ area:ci GitHub Actions, CI config, repo tooling area:deps Dependency manifests / lockfiles area:docs Documentation and Markdown only area:frontend Next.js frontend under frontend/ needs-validation Touches front/back contract surface; needs real-path validation risk:high High risk: backend API, agents, sandbox, auth, deps, CI size/XL PR changes 700+ lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants