feat(agent-core-v2): watch user-level skill roots so the catalog stays fresh - #3608
Conversation
…s fresh UserFileSkillSource previously only re-scanned the user skill roots (KIMI_CODE_HOME/skills and ~/.agents/skills) on catalog load, so a skill created while the daemon was running never appeared until a restart. Mirror the workspace-root source: watch both user-level base directories with a subtree filter limited to the skill-root candidates, debounce (200ms) change events, and fire onDidChange so the catalog reloads the 'user' source automatically. Watch handles are registered on the service's DisposableStore and disposed with the App scope. Supersedes the skills:reload endpoint approach (#3597).
🦋 Changeset detectedLatest commit: 917eec4 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
commit: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 31c14d1fb5
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const handle = watch(base, { | ||
| ignored: subtreeWatchFilter(base, candidates), | ||
| signal: true, |
There was a problem hiding this comment.
Watch resolved user skill roots behind symlinks
On the chokidar-backed platforms, this watches only the parent base while the shared watcher is configured with followSymlinks: false. If skills or .agents/skills is a symlink—common when managing home configuration through a dotfiles directory—initial discovery succeeds because userRoots() resolves the symlink, but subsequent edits in its target produce no event and the catalog remains stale; the resolved discovered roots also need watcher coverage.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
确认为已知边界,本 PR 不修复。followSymlinks: false 是共享 watch 基础设施(#human/utils/watch)的统一配置,项目级 WorkspaceRootSkillSource 对符号链接的 .agents/skills 也存在同样限制,并非本 PR 引入的回归。正确修法是在每次 load 后对 realpath 出的 scannedRoots 轮换 watch(类似项目级的 watch handoff),属于基础设施增强,影响面限于用 dotfiles 符号链接管理 skills 目录的用户(重启/重载后首次扫描仍正常)。建议记录为后续 issue。
— Mira(代 liukx0205 分诊处理)
There was a problem hiding this comment.
Acknowledged as a known boundary, not fixing in this PR. followSymlinks: false is the shared watch infrastructure's uniform setting (#human/utils/watch), and the project-level WorkspaceRootSkillSource has the same limitation for symlinked .agents/skills — not a regression introduced here. The proper fix is rotating watches onto the realpath-d scannedRoots after each load (similar to the project-level watch handoff), which is infrastructure-level work. Impact is limited to dotfiles-symlinked skill dirs, and the initial scan on (re)load still resolves symlinks correctly. Suggest tracking as a follow-up issue.
…mments Codex review on #3608: - When KIMI_CODE_HOME equals the OS home dir, deduplicating watch bases skipped the .agents/skills candidate entirely. Group candidates by base instead so one watch covers both roots. - Remove // phase labels from the catalog test: agent-core-v2 is a comment-free zone enforced by scripts/check-no-comments.mjs (lint was red on CI).
Problem
Skills created in the user-level skill directories (
~/.kimi-code/skills,~/.agents/skills) while the daemon is running are not visible to any session:UserFileSkillSourceonly re-scans those directories when the workspace skill catalog is (re)loaded, which in practice means a daemon restart. The project-level roots did not have this problem —WorkspaceRootSkillSourcewatches the project root and reloads on change.What changed
Mirror the project-level watch pattern in
UserFileSkillSource:bootstrap.homeDir(KIMI_CODE_HOME, coveringskills/) andbootstrap.osHomeDir(covering.agents/skills/), each with asubtreeWatchFilterthat prunes everything outside the skill-root candidates so the rest of the home directory is neither traversed nor reported.onDidChange, which makesWorkspaceSkillCatalogServicereload the'user'source — soGET /sessions/{id}/skillsis always fresh.load()awaits watch readiness (same race-avoidance as the workspace source), and the watch handles are registered on the service'sDisposableStore, so they are disposed with the App scope.skillDirsare configured the user source is inactive, so no watches are installed.Tests in
packages/agent-core-v2/test/features/skill/workspace/skillCatalog.test.ts: watch registration and filter pruning for both roots, no watches with explicitskillDirs, disposal with the App scope, and an end-to-end real-watcher test that creates / modifies / deletes aSKILL.mdunder tmpdir home roots (including.agents/skills) and asserts the catalog updates after the debounce.Relationship to #3597
This supersedes #3597 (
POST /sessions/{id}/skills:reload): with the catalog kept fresh by the watcher, no reload endpoint is needed.Checklist
/approve).gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.Verified locally:
agent-core-v2skill feature tests (135 tests) and the broadertest/features test/app test/workspace test/sessionsuites (2716 tests),kap-serverskills tests (20 tests),tsc --noEmitfor both packages, and repo-wideoxlint --type-aware(0 errors).