Skip to content

🤖 feat: add Mux Extension Platform v1#3255

Draft
ThomasK33 wants to merge 145 commits into
mainfrom
extensions-main
Draft

🤖 feat: add Mux Extension Platform v1#3255
ThomasK33 wants to merge 145 commits into
mainfrom
extensions-main

Conversation

@ThomasK33
Copy link
Copy Markdown
Member

@ThomasK33 ThomasK33 commented May 8, 2026

Summary

Adds the Mux Extension Platform v1 behind the EXTENSION_PLATFORM experiment and documents the platform's architectural pivot to Extension Modules: extension folders with a single extension.ts, statically extractable manifests, QuickJS-based discovery/activation, skill-style root precedence, source locks, and Mux-owned trust/capability state. The implementation is now partially refactored toward that model: trusted roots can discover direct child Extension Module folders via static extension.ts manifest extraction, local authoring roots use ~/.mux/extensions/local, and source-lock schemas now model git/vendored extension sources without carrying trust state.

Background

This PR grew out of the need to consolidate Mux's extension surfaces across skills, tools, agents, policies, themes, and future runtime contributions. During review, the design moved away from npm-package identity and repo-stored project approvals toward a Go-modules-like Extension Module model. The updated docs capture that decision, the code hardens the current scaffold so repositories cannot provide security authority, and the latest slices begin moving discovery/root layout/source metadata from package manifests to static Extension Module manifests and locks.

Implementation

  • Adds extension manifest/domain schemas, descriptor validation, permission calculation, drift detection, conflict resolution, provenance-aware telemetry helpers, global state, project-scoped state, and snapshot caching.
  • Adds transitional Extension Name support alongside old dotted package identities so kebab-case module names can appear in registry snapshots, IPC payloads, and persisted state keys.
  • Adds static extension.ts manifest extraction for export const manifest = defineManifest({ ... }) or a literal object export, rejecting dynamic manifest values without executing extension code.
  • Adds trusted-root Extension Module discovery for direct child folders containing extension.ts, including folder-name validation, manifest.name mismatch diagnostics, project-local pre-trust no-read behavior, and static capability validation.
  • Points the user-global local authoring root at ~/.mux/extensions/local and updates initializeUserRoot to create that folder instead of a package-root package.json.
  • Adds source-lock schemas for global git locks and project git/vendored locks, with strict rejection of repository-provided trust/approval state.
  • Adds node discovery/registry services, root watching, bundled-root assembly, ORPC routes, service-container wiring, and bun run debug extensions.
  • Threads extension-contributed skills through skill discovery, slash dispatch, stream context construction, and agent_skill_* tool reads, including hardened skill-body reads that reject symlinks and TOCTOU path swaps.
  • Adds the Extensions settings UI, Extension cards, consent shortcut/granular controls, diagnostics surfacing, palette entries, and keyboard shortcuts.
  • Adds a bundled demo extension package and deterministic bundled-extension assembly tooling for the current scaffold.
  • Updates docs, RFC, PRD, ADRs, release checklist, telemetry docs, and authoring docs to describe Extension Modules (extension.ts) as the target architecture.
  • Stores project-local extension trust/enablement/grants in Mux-owned global storage under ~/.mux/extensions/project-state/<project-hash>/, not inside the target repository.

Validation

  • make static-check
  • make test -j1
  • Latest TDD slices:
    • bun test src/node/extensions/extensionRoots.test.ts src/node/orpc/extensionsRouter.test.ts src/common/extensions/sourceLocks.test.ts src/node/extensions/staticManifestExtractor.test.ts src/node/extensions/extensionDiscoveryService.test.ts
  • Earlier targeted tests during review cleanup:
    • bun test src/common/extensions/conflictResolver.test.ts src/common/extensions/permissionCalculator.test.ts
    • bun test src/node/extensions/bundledExtensionsAssemble.test.ts
    • bun test src/node/extensions/projectExtensionStateService.test.ts
    • bun test src/node/orpc/extensionsRouter.test.ts
    • bun test src/cli/debug/extensions.test.ts
    • bun test src/browser/features/Settings/Sections/ExtensionCard.test.tsx src/browser/features/Settings/Sections/ExtensionsSection.test.tsx

Risks

This is a large additive subsystem touching startup wiring, settings UI, package assembly, telemetry, and skill discovery. The primary rollback lever is the default-on EXTENSION_PLATFORM experiment. The highest remaining architectural risk is that full QuickJS Registration Discovery/Full Activation and git install/store materialization are still follow-up work; the current module-discovery slice intentionally publishes no module-registered skills until that runtime path exists.

Pains

This PR required several review and merge cycles: resolving older security findings, integrating concurrent main changes around heartbeat/image-generation skill filtering, aligning extension skill IDs with agent skill schemas, moving project extension state out of repositories after review identified the trust-injection vulnerability, and beginning the package-to-Extension-Module refactor while preserving transitional compatibility.


Generated with mux • Model: openai:gpt-5.5 • Thinking: off • Cost: $916.09

ThomasK33 added 30 commits May 8, 2026 10:44
Capture the extension platform glossary, ADRs, and PRD generated from the design grilling session.

---

_Generated with `mux` • Model: `openai:gpt-5.5` • Thinking: `high` • Cost: `729323{MUX_COSTS_USD:-0}`_

<!-- mux-attribution: model=openai:gpt-5.5 thinking=high costs=103.38 -->
Apply six P0 fixes from advisor review and capture supporting language in CONTEXT.md and a new ADR-0005:

- Add v1 Contribution Activation Matrix (separates schema-supported from capability-consumed; clarifies that themes/layouts/runtime presets/commands are inspection-only in v1).
- Mark inspection-only contribution types as Provisional Descriptors so their schemas can evolve without bumping descriptor version.
- Tighten telemetry boundary to Provenance-gated Telemetry (reserved-prefix regex AND bundled-root provenance), with defense-in-depth.
- Make Snapshot Cache feed only the Inspection Path; Capability Path uses the live Snapshot, and cache invalidation includes Global/Project-local Extension State mtimes.
- Clarify Grant Records store the normalized granted set (inferred registration + operational); enablement does not grant registration.
- Drop the manifest icon field; inspection UI uses generic icons.
- Add P0 Acceptance Criteria section before Implementation Decisions.
- Make IPC mutators identify with { rootId, extensionId } and add BundledExtensionRootResolver as a supporting module.
- Replace bun install --no-save assembly with deterministic offline copy/pack.
- Pre-trust project-local discovery is existence-only.
- Add explicit security tests (reserved prefix, provenance-gated telemetry, capability-vs-cache separation, drift across new contribution types) and screenshot/video evidence requirements to dogfood checklist.
- Add ADR-0005 capturing the aggregate security boundary.

---

_Generated with `mux` • Model: `anthropic:claude-opus-4-7` • Thinking: `max` • Cost: `881861{MUX_COSTS_USD:-0}`_

<!-- mux-attribution: model=anthropic:claude-opus-4-7 thinking=max costs=130.54 -->
…r kill switch

ServiceContainer now resolves the bundled extension root via
detectBundledExtensionRoot() (preferring the assembled tree at build/extensions
in dev) and kicks off an initial extensions.reload() so the Settings UI paints
on cold start without a hang. SettingsPage gates the Extensions tab on the
EXTENSION_PLATFORM experiment so the kill switch hides the section without
unmounting downstream sections. Adds the e2e Electron smoke that asserts the
Demo Extension card surfaces on first paint, disappears with the kill switch,
survives a renderer reload, and reappears on re-enable without a fresh
trust/grant prompt.
…cklist)

Adds the v1 authoring quickstart + manifest reference, the full v1 telemetry
events catalog with provenance-gating notes, and the pre-release dogfood
checklist with screenshot/video evidence requirements. Wires the new pages
into docs.json navigation and regenerates the built-in mux-docs index so the
docs skill surfaces them.
Formatting-only normalization (collapse multi-line argument lists, consistent
quoting) across the extension layer plus minor test cleanup left over from
US-026/27/28 integration. Marks US-026/27/28 passes:true in tasks/prd.json.
PRD §Permission Model defines bundled Extensions as policy-granted (not
user-consented). Before this fix the Demo Extension shipped with
`granted: false`, surfaced `Pending re-grant` on the card, and the
`mux-extensions` skill never reached `Available` — breaking the
"fresh-install, no manual setup" promise of P0 #2.

Discovery now treats `isBundled` as activation-granted, and the Registry
synthesizes a matching policy Grant Record at permission-calculation time
(never persisted, recomputed on every reload, distribution-identity-aligned
so drift stays `null` across version bumps). The discovery test that
asserted `activated:false` on a bundled root without a grant has been
updated to reflect the new contract.
…ry disabled

Renderer's useExperimentValue falls back to `enabledByDefault` when
PostHog returns no assignment, but the backend's isExperimentEnabled
returned `false` in the same scenario — so dev-server / MUX_E2E builds
shipped with EXTENSION_PLATFORM (default-on) effectively off. Frontend
showed the Extensions tab while the backend reported "No extension roots
configured".

isExperimentEnabled now uses EXPERIMENTS[id].enabledByDefault as the
fallback. Existing tests for default-off experiments still assert false
because their definitions are enabledByDefault:false.
…ively

The renderer can't reproduce permissionCalculator's canonical SHA-256
without a Node `crypto` import, so both Consent Shortcut paths sent
`requestedPermissionsHash: ""`. After persistence the very next reload
read `hash !== ""` → driftStatus `permissions-changed` for an
already-fresh grant.

setGrant now overwrites the hash with hashRequestedPermissions(<live
manifest's requestedPermissions>) before persistence; falls back to
hashing grantedPermissions when no live snapshot is available (equivalent
under v1's all-or-nothing grants). The frontend-side comments are updated
to point at the canonical recompute site instead of misleading future
readers.
#2)

agentSkillsService had zero references to extensionRegistry. Even with the
Demo Extension fully Available in the Registry snapshot, its
`mux-extensions` skill never reached the slash menu — directly
contradicting PRD P0 #2 ("Demo Extension visible end-to-end … exposes
the mux-extensions skill via the existing slash menu").

Changes:
- Adds 'extension' as a fourth AgentSkillScope value (project > global >
  extension > built-in precedence so user-authored skills always shadow
  extension-provided ones).
- ExtensionRegistry.getSkillSources() returns the resolved list of
  Available skill contributions (absolute body path + display metadata)
  for agentSkillsService to consume.
- discoverAgentSkills / discoverAgentSkillsDiagnostics / readAgentSkill
  accept an optional extensionSkills array; the agentSkills router
  supplies it from context.extensionRegistry.getSkillSources() on every
  call so kill-switch flips and reload events take effect immediately.
- readAgentSkill reads the extension body via node:fs (extensions live on
  the host, not the workspace runtime) and returns it with
  scope:'extension'.
- SkillIndicator gains an "Extension" scope group; MuxMessageMetadata
  scope union widens to include 'extension' so /skill messages from
  extension skills render correctly.
- Adds YAML frontmatter to packages/mux-extension-platform-demo/SKILL.md
  so parseSkillMarkdown accepts it (the Manifest Validator only needs the
  body file to exist; the agent-skill consumer needs frontmatter).
The 'Why?' link in the Inferred Registration Permissions header pointed at
docs.mux.dev (not our domain) with an anchor that didn't exist. Repoints
to https://mux.coder.com/extensions/authoring#permissions where the v1
permissions reference actually lives.
The Shift+? hotkey hint at the top of the Extensions Settings Section was
rendered as low-contrast plain text on a transparent ghost button — hard
to read against the section background. Wraps the keybind in the same
<kbd> styling already used by ExtensionsCheatSheetModal so the trigger
visually echoes the cheat sheet itself instead of looking like a comment.
The modal panel was styled with `bg-background-primary`, but
globals.css only defines `--color-background` and
`--color-background-secondary`. The non-existent token resolved to
nothing, so the panel rendered fully transparent — only the dimmed
backdrop was visible behind it, and the shortcut list bled through
into the section content. Switches to `bg-background-secondary` to
match ConsentShortcutModal.
…atform-demo

The package shipped under `@mux/extension-platform-demo`, but `@mux` is
not Coder's npm scope. Renames the distribution identity to
`@coder/mux-extension-platform-demo` (Extension Identity `mux.platformdemo`
is unchanged — it lives under the reserved `mux.*` prefix and survives
package renames per ADR-0003).

Touches: package.json + README + SKILL.md inside the workspace, the
docs/extensions/authoring.mdx quickstart, bundled-extensions.ts comment,
test fixtures (the assemble + discovery + registry suites all rebuilt
against the new path), and the PRD/CONTEXT/prd.json planning artifacts
that referenced the old name. Regenerates the embedded mux-docs builtin
skill index so `agent_skill_read` surfaces the renamed link.
agentSkillsService.list (the slash menu) merged extension-contributed
skills correctly, but the dispatch path that actually invokes a skill
(`agentSession.ts` slash resolver, `agent_skill_read`,
`agent_skill_read_file`) called readAgentSkill without the
extensionSkills source list. Result: the slash menu showed
`mux-extensions` but `/mux-extensions` failed with
`Agent skill not found: mux-extensions` — the user-reported bug.

Wires a single getExtensionSkillSources provider from
ExtensionRegistry.getSkillSources() through three layers:
- ServiceContainer registers the provider with both WorkspaceService
  (for AgentSession.sendMessage slash resolution) and AIService (for
  the tool layer).
- WorkspaceService passes the provider into every AgentSession via a
  new option.
- AgentSession reads the live source list per slash invocation; fixes
  agentSession.ts:5253 to pass extensionSkills to readAgentSkill.
- AIService injects extensionSkills into ToolConfiguration on every
  stream so agent_skill_read / agent_skill_read_file resolve extension
  skills the same way they resolve project / global / built-in skills.

Adds a regression test in agentSession.agentSkillSnapshot.test.ts that
exercises a slash invocation against an extension-contributed skill —
fails on the pre-fix codepath, passes here.
Copy link
Copy Markdown

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

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: 26dd821cb1

ℹ️ 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 src/node/extensions/extensionRoots.ts Outdated
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Addressed the latest follow-up review threads:

  • previous-good activation fallback now only restores contributions with body activation failure diagnostics, so disposed activation registrations stay removed
  • extension root watchers now restart when a stable project-local root id points at a new root path after lock add/remove transitions

Local validation: MUX_ESLINT_CONCURRENCY=2 make static-check passed, along with targeted watcher/registry/discovery/source-lock tests.

Copy link
Copy Markdown

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

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: 53ee297929

ℹ️ 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 src/browser/features/Settings/Sections/ExtensionsSection.tsx Outdated
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Addressed the project lock sync fail-closed finding: when trusted lock sync fails, the roots provider no longer points discovery at a stale materialized active root and falls back to the project .mux inspection path instead.

Local validation: bun test src/node/extensions/extensionRoots.test.ts and MUX_ESLINT_CONCURRENCY=2 make static-check passed.

Copy link
Copy Markdown

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

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: 160628d447

ℹ️ 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 src/node/extensions/extensionRoots.ts
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Addressed the Settings root rendering finding: the Extensions section now renders every user-global root returned by the registry snapshot, including fetched installed roots such as user-global-fetched.

Local validation: bun test src/browser/features/Settings/Sections/ExtensionsSection.test.tsx and MUX_ESLINT_CONCURRENCY=2 make static-check passed.

Copy link
Copy Markdown

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

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: 436bb38e24

ℹ️ 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 src/node/extensions/extensionRootWatcher.ts Outdated
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Addressed the sync-failure fallback activation finding: project lock sync failures now mark the returned project-local root as untrusted/non-activating, so discovery cannot publish stale active-view code or repo-controlled .mux contents through the failure path.

Local validation: bun test src/node/extensions/extensionRoots.test.ts and MUX_ESLINT_CONCURRENCY=2 make static-check passed.

@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Addressed the arbitrary skill body watcher finding: module directory watchers now trigger reload for contained body file changes regardless of basename, with regression coverage for a custom review-body.md skill body.

Local validation: bun test src/node/extensions/extensionRootWatcher.test.ts, the focused custom-body watcher test, and MUX_ESLINT_CONCURRENCY=2 make static-check passed.

Copy link
Copy Markdown

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

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: 4c582725b2

ℹ️ 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 scripts/bundled-extensions.ts Outdated
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Addressed the bundled extension validation finding: bundled packages that contain extension.ts now fail validate/assemble discovery when Static Manifest extraction fails instead of being silently omitted. Added a regression test that injects a malformed bundled package via MUX_BUNDLED_EXTENSIONS_PACKAGES_DIR.

Local validation: bun test src/node/extensions/bundledExtensionsAssemble.test.ts, bun scripts/bundled-extensions.ts validate, and MUX_ESLINT_CONCURRENCY=2 make static-check passed.

Copy link
Copy Markdown

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

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: e8f84fc45d

ℹ️ 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 scripts/bundled-extensions.ts Outdated
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Addressed the malformed bundled package follow-up: a packages/ directory that contains extension.ts now fails discovery when its package.json is missing/invalid instead of being skipped. Added regression coverage for malformed package.json in the bundled validation path.

Local validation: bun test src/node/extensions/bundledExtensionsAssemble.test.ts, bun scripts/bundled-extensions.ts validate, and MUX_ESLINT_CONCURRENCY=2 make static-check passed.

Copy link
Copy Markdown

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

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: 6aa52b2f24

ℹ️ 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 src/node/services/tools/agent_skill_read_file.ts Outdated
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Addressed the extension reference file TOCTOU finding: extension skill reference reads now revalidate realpath containment after preflight, then open the validated path with no-follow before reading through the file handle. Added regression coverage that swaps an extension reference file to an escaping symlink between containment and read.

Local validation: bun test src/node/services/tools/agent_skill_read_file.test.ts and MUX_ESLINT_CONCURRENCY=2 make static-check passed.

Copy link
Copy Markdown

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

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: da074d189a

ℹ️ 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 src/node/services/tools/agent_skill_read_file.ts
@ThomasK33
Copy link
Copy Markdown
Member Author

@codex review

Addressed the opened-parent-directory TOCTOU follow-up: extension reference reads now validate the realpath of the opened file handle (via /proc/self/fd or /dev/fd where available, with fallback) before reading, so an open redirected through a swapped parent directory is rejected. Added a regression test that forces fs.open to return an outside file for a nested reference path.

Local validation: bun test src/node/services/tools/agent_skill_read_file.test.ts and MUX_ESLINT_CONCURRENCY=2 make static-check passed.

Copy link
Copy Markdown

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

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: eef3fcfab9

ℹ️ 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".

// neither fd namespace is available (for example on Windows).
}
}
return fs.realpath(fallbackPath);
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 Fail closed when opened-handle realpath is unavailable

When neither /proc/self/fd nor /dev/fd is available, this falls back to resolving fallbackPath rather than the already-open handle. In that environment, e.g. Windows, a writable extension root can still swap an intermediate parent to an escaping link for fs.open() and swap it back before this fallback realpath, so the check validates a different path than the file handle that is later read. For the TOCTOU hardening to hold, this path should reject when the opened handle cannot be canonicalized, or use another handle-bound validation.

Useful? React with 👍 / 👎.

throw new Error("Opened file resolves outside the extension skill directory.");
}
const stat = await handle.stat();
const content = stat.isDirectory() ? "" : await handle.readFile("utf8");
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Reject extension directories instead of returning empty content

For extension skills, a filePath that points at a directory now succeeds with empty content because directories skip readFile here and validateFileSize does not reject directories. The non-extension path below returns Path is a directory, not a file, so this new extension branch gives agents a misleading successful read for inputs like filePath: "refs" instead of the expected error.

Useful? React with 👍 / 👎.

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