-
Notifications
You must be signed in to change notification settings - Fork 3k
feat(daemon): enumerate installed extension skills locally #11281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+866
−31
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
bb28cd1
feat(daemon): enumerate installed extension skills locally
callmeYe d90e39c
fix(daemon): resolve local extension skill display names by locale
callmeYe 813893b
fix(daemon): harden local skills catalog cache and failure domains (#…
284fcb4
fix(daemon): close skills-catalog probe, tier, and latch gaps from re…
eef154c
fix(daemon): restore scoped skill enumeration contracts
callmeYe 8d5907e
fix(daemon): skip absent extension stores during skill reads
callmeYe cf66404
docs(daemon): record skills-catalog read contract and later-stage ite…
3c594cf
Merge branch 'main' into codex/daemon-extension-skill-catalog
qwen-code-dev-bot 084ec76
Merge branch 'main' into codex/daemon-extension-skill-catalog
qwen-code-dev-bot 9585979
fix(daemon): resolve skill state from the loaded extension snapshot
callmeYe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| # Daemon extension Skill catalog | ||
|
|
||
| [English](daemon-extension-skill-catalog.md) | [简体中文](daemon-extension-skill-catalog.zh-CN.md) | ||
|
|
||
| This implements stage 2 of #11274. Previously the daemon-local workspace Skill | ||
| provider supplied an empty active-extension list, so its first response omitted | ||
| installed extension Skills when no child snapshot existed. | ||
|
|
||
| Use an unbound `ExtensionManager` for the selected workspace to load installed | ||
| extensions through the existing consistent store reader. Supply active | ||
| extensions to `SkillManager`, preserving project > user > extension > bundled | ||
| precedence. Append inactive extension Skills as management entries with the | ||
| existing `inactive_extension` status, retaining their identity and metadata. | ||
| Resolve settings and extension Skill defaults/overrides with the existing | ||
| parsers. A settings opt-in does not enable an inactive parent extension. | ||
| Resolve each extension's Skill defaults from its own manifest, with workspace | ||
| Skill overrides from the same consistent store snapshot used to load it. Cache | ||
| those booleans by the loaded extension object and normalized Skill name. This | ||
| avoids selecting the wrong owner through a colliding ID; it does not create a | ||
| new identity namespace or change store policy. Explicit opt-ins and hard | ||
| settings disablements retain their existing precedence. | ||
| Resolve localized extension names with the existing language setting and locale | ||
| helpers on every response, without changing the daemon process language or | ||
| rebuilding the directory cache when only the language changes. | ||
|
|
||
| Keep the lightweight Config surface: do not construct a runtime Config, start a | ||
| child, initialize MCP, execute hooks, or install watchers. Honor safe mode, | ||
| disabled discovery levels and workspace trust; inert untrusted inventory must | ||
| not load workspace settings or extension runtime context. Directory failures | ||
| continue to return an uninitialized error status. | ||
|
|
||
| The implementation and collocated regressions live in the daemon-local provider. | ||
| Tests cover real manifests, active/inactive state, source collisions, persisted | ||
| Skill settings, safe/untrusted contexts and explicit cache invalidation. E2E | ||
| evidence uses an isolated home and a daemon with no child session. | ||
|
|
||
| The facade still prefers child snapshots in this stage. Replacing that source, | ||
| changing toggle/refresh semantics, adding configured-state fields and changing | ||
| Web Shell projections belong to later PRs. No public schema changes are needed. | ||
|
|
||
| Discovery-level disabling suppresses active extension Skills through | ||
| `SkillManager`; inactive extension management entries are still appended, as in | ||
| the child producer. Safe mode and untrusted contexts never load extensions. | ||
|
|
||
| An absent extensions root is an empty inventory; no extension store is created. | ||
| With a present root the read reconciles through the shared store: a missing or | ||
| drifted store is initialized in place under the store's exclusive lock, | ||
| rewriting `extension-store/state.json`, creating its `state.previous.json` | ||
| rollback copy, and rewriting the legacy `extension-enablement.json` projection. | ||
| Cache hits reuse the loaded managers. A later rebuild still takes the lock and | ||
| may perform directory/permission maintenance even if policy is unchanged. | ||
| Unreadable roots and errors propagated by the shared store/loader return | ||
| `initialized: false` with explicit errors. That failure is deliberately | ||
| all-or-nothing: one bad extension | ||
| artifact (for example a dangling directory entry) fails the entire catalog, | ||
| project, user and bundled Skills included, until the artifact is repaired. | ||
| Individual artifact handling remains owned by the shared loader: malformed | ||
| manifests are skipped with its diagnostic, whereas a dangling extension entry | ||
| propagates an error. This stage does not add per-artifact diagnostics or | ||
| per-level degradation to the response, or change the loader's failure policy. | ||
| Existing facade caching, source preference and invalidation behavior remain | ||
| unchanged; the tracking issue assigns cache lifecycle and concurrency changes | ||
| to stage 4. | ||
|
|
||
| **Known later-stage items (recorded during review, deliberately not in this | ||
| stage):** | ||
|
|
||
| - The sibling `/workspace/extensions` route resolves its locale through | ||
| `loadSettings` without `skipLoadEnvironment`, so a workspace `.env` | ||
| `QWEN_CODE_LANG` can diverge from this provider's language resolution. | ||
| - The inactive-entry append and sort assembly duplicates the child producer's | ||
| (`acpAgent.ts`). Here a per-extension name set removes duplicate inactive | ||
| Skill names; it provides the same source separation without that string key. | ||
| - Extension mutations do not invalidate the config-catalog providers this stage | ||
| populates, so a committed install, update, enable/disable or uninstall can | ||
| leave stale extension Skill state on the skills config routes until an | ||
| unrelated skill mutation, a workspace removal, or a restart. The | ||
| invalidation wiring and `refreshCacheIfSourcesChanged` revalidation belong | ||
| to stage 4. | ||
| - The active-Skill `enabled` judgment mirrors `Config.isSkillEnabled` by hand. | ||
| - Shared store/API handling of duplicate extension IDs remains separate. This | ||
| provider preserves per-manifest defaults instead of querying an ambiguous | ||
| ID for its owner; it still consumes workspace overrides by the store's ID key. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| # Daemon extension Skill 目录 | ||
|
|
||
| [English](daemon-extension-skill-catalog.md) | [简体中文](daemon-extension-skill-catalog.zh-CN.md) | ||
|
|
||
| 本设计实现 #11274 的第 2 阶段。此前 daemon 本地 workspace Skill provider | ||
| 提供空的 active extension 列表,因此没有 child 快照时,首次响应缺少已安装 | ||
| extension 的 Skill。 | ||
|
|
||
| 为选定 workspace 使用不绑定运行时 Config 的 `ExtensionManager`,通过现有 | ||
| store 一致性读取加载扩展。将 active extension 交给 `SkillManager`,保留 | ||
| project > user > extension > bundled 优先级。将 inactive extension 的 Skill | ||
| 作为管理条目追加,使用既有 `inactive_extension` 状态,保留身份和元数据。 | ||
| 设置、extension Skill 默认值与覆盖沿用现有解析器。settings 显式启用不会启用 | ||
| inactive 父扩展。 | ||
|
|
||
| 每个 extension 的 Skill 默认值来自其自身 manifest;workspace Skill 覆盖来自 | ||
| 加载扩展时使用的同一份 store 一致性快照。按已加载 extension 对象和归一化 | ||
| Skill 名称缓存布尔结果,避免通过冲突 ID 再次选错 owner。这不新增身份命名空间, | ||
| 也不修改 store 策略。显式启用与 settings 硬禁用保持原有优先级。 | ||
|
|
||
| 每次响应通过现有语言设置和 locale helper 解析本地化扩展名,不修改 daemon | ||
| 进程语言,也不因单独的语言变化重建目录缓存。 | ||
|
|
||
| 保持轻量 Config 接口:不构造运行时 Config、不启动 child、不初始化 MCP、 | ||
| 不执行 hooks、不安装 watcher。遵守 safe mode、发现层级禁用及 workspace 信任 | ||
| 规则;未信任目录的静态盘点不加载 workspace 设置或 extension 运行时上下文。 | ||
| 目录读取失败继续返回未初始化错误状态。 | ||
|
|
||
| 实现与相邻回归测试位于 daemon 本地 provider。测试覆盖真实 manifest、 | ||
| active/inactive 状态、来源冲突、持久化 Skill 设置、safe/untrusted 上下文及 | ||
| 显式缓存失效。E2E 使用隔离 home 和无 child 会话的 daemon。 | ||
|
|
||
| 本阶段 facade 仍优先使用 child 快照。替换来源、修改开关或刷新语义、增加配置 | ||
| 状态字段及修改 Web Shell 投影属于后续 PR。不修改公共响应 schema。 | ||
|
|
||
| 发现层级禁用通过 `SkillManager` 隐藏 active extension Skill;与 child producer | ||
| 一致,仍追加 inactive extension 管理条目。safe mode 和未信任上下文不加载扩展。 | ||
|
|
||
| extensions 根目录不存在代表空目录,不创建 extension store。根目录存在时, | ||
| 通过共享 store 协调读取:缺失或发生漂移的 store 在独占锁内初始化,写入 | ||
| `extension-store/state.json`、生成 `state.previous.json` 回滚副本,并写入旧格式 | ||
| `extension-enablement.json` 投影。命中缓存时复用已加载 manager;后续重建仍会 | ||
| 获取锁,即使策略未变也可能维护目录与权限。 | ||
|
|
||
| 不可读根目录和共享 store/loader 抛出的错误返回 `initialized: false` 及明确 | ||
| 错误。该失败有意影响整份目录:例如一个悬空 extension 目录条目会使 project、 | ||
| user 和 bundled Skill 一同不可用,直到制品被修复。单个制品的处理仍由共享 | ||
| loader 决定:损坏 manifest 被跳过并记录其诊断,悬空条目则向外抛错。本阶段不 | ||
| 增加逐制品诊断或逐层降级,不修改 loader 失败策略。 | ||
|
|
||
| 现有 facade 缓存、来源优先级及失效行为不变;跟踪 issue 将缓存生命周期和并发 | ||
| 改造安排在第 4 阶段。 | ||
|
|
||
| **评审中记录、明确不在本阶段实现的后续事项:** | ||
|
|
||
| - 相邻 `/workspace/extensions` 路由调用 `loadSettings` 时未设置 | ||
| `skipLoadEnvironment`,workspace `.env` 的 `QWEN_CODE_LANG` 可能使其语言 | ||
| 解析与本 provider 不同。 | ||
| - inactive 条目追加及排序与 child producer(`acpAgent.ts`)存在重复。这里按 | ||
| extension 分别对 Skill 名去重,与字符串 `level:extensionName:name` key | ||
| 提供相同的来源区分,并未使用该字符串 key。 | ||
| - extension 变更没有失效本阶段填充的 config-catalog provider,因此安装、更新、 | ||
| 启停或卸载提交后,skills config 路由可能保留旧状态,直到无关 Skill 变更、 | ||
| workspace 移除或重启。失效接线与 `refreshCacheIfSourcesChanged` 重新验证 | ||
| 属于第 4 阶段。 | ||
| - active Skill 的 `enabled` 判断沿用 `Config.isSkillEnabled` 的规则,尚未共享 | ||
| 判定实现。 | ||
| - 共享 store/API 的重复 extension ID 处理另行跟进。本 provider 从各自 manifest | ||
| 保留默认值,不通过歧义 ID 查询 owner;workspace 覆盖仍使用 store 的 ID key。 |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.