β¨ feat(skills): recognize project-level skills in the homogeneous agent runtime#15110
Conversation
β¦nt runtime Port of the project-skill server-side recognition originally landed on feat/cc-api-provider-mode (39ffca7). That branch isn't merging soon so the work has been lifted onto canary and re-applied against the existing agent-document skill registry. Surface filesystem skills (`.agents/skills` / `.claude/skills`) to the homogeneous agent runtime and let it activate them on demand: - **Discovery**: client scans the active working directory at send time (`resolveProjectSkills` in `gateway.ts`) and passes the skill list (frontmatter + SKILL.md path + file tree) as a new `projectSkills` op param. Wired all the way through `services/aiAgent.ts`, the lambda router, and `RuntimeExecutors` into `ToolExecutionContext`. - **Injection**: project skills are merged into the OperationSkillSet alongside the agent-document skill registry. Dedupe precedence on name collision is `project > db > agent-skills > builtin`. They render in `<available_skills>` with `source="project" location="β¦"`, and `skillsPrompts` appends a hint that tells the model to readFile the SKILL.md before following its instructions. - **Activation**: `activateSkill` / `readReference` in `SkillsExecutionRuntime` resolve project skills by reading SKILL.md and reference files through a new `deviceFileAccess.readFile`. The server wires that to `LocalSystemApiName.readFile` over `deviceProxy.executeToolCall` β no new file-read primitive, the existing local-system capability over the gateway does the work. - **Type system**: new `ProjectSkillMeta` in `@lobechat/types`, new `SkillSource` (`builtin | db | project`) in `@lobechat/prompts`, and `files` / `location` / `source` added to `SkillMeta` in the context-engine so the metadata survives end to end. Plus 4 new `SkillsExecutionRuntime` tests for the project-skill paths (activate by name, fall back when no device access, readReference by relative path, error when device missing). The 6 pre-existing failures in that file are about `execScript` and reproduce on canary HEAD too. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
π‘ Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dd4a4e7595
βΉοΈ 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".
| const fullPath = joinPath(getDirname(projectSkill.location), path); | ||
| const content = await this.deviceFileAccess.readFile(fullPath); |
There was a problem hiding this comment.
Restrict project readReference to declared skill files
The project-skill branch reads readReference paths directly via deviceFileAccess.readFile(fullPath) after only blocking .., but it never verifies that the requested path is one of projectSkill.files. In practice, a model can request any guessed relative filename (for example hidden files like .env) and read content that was not exposed in the advertised resource list, which can leak sensitive project data from the skill directory. Validate path against the discovered file list before reading.
Useful? React with πΒ / π.
| const skills = [...projectMetas, ...dbMetas, ...agentSkillMetas, ...builtinMetas].filter( | ||
| (skill) => { | ||
| if (seenNames.has(skill.name)) return false; | ||
| seenNames.add(skill.name); | ||
| return true; |
There was a problem hiding this comment.
Align deduped skill precedence with activation behavior
This dedupe now enforces project > db > agent-skills > builtin by name, so a DB skill can be the only one shown in <available_skills> when it collides with a builtin name. But activateSkill still resolves builtins before DB skills, so runSkill may execute a different skill than the one surfaced to the model. The mismatch is triggered specifically on name collisions and leads to incorrect skill execution despite the new precedence comment.
Useful? React with πΒ / π.
Codecov Reportβ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## canary #15110 +/- ##
==========================================
- Coverage 70.68% 70.64% -0.04%
==========================================
Files 3126 3127 +1
Lines 310653 310864 +211
Branches 32682 33615 +933
==========================================
+ Hits 219579 219625 +46
- Misses 90907 91072 +165
Partials 167 167
Flags with carried forward coverage won't be shown. Click here to find out more.
π New features to boost your workflow:
|
β¦ model enumerate The earlier port carried a `files: string[]` directory tree end to end (`ProjectSkillMeta` β SkillEngine β operation skill set β server context β activateSkill resource prompt). That's redundant payload β the model already has `local-system.listFiles` available, so it can discover the skill's directory on demand. Removed `files` from: - `ProjectSkillMeta` (`@lobechat/types`) - `SkillMeta` (`@lobechat/context-engine`) - `ProjectSkillRuntimeItem` (`@lobechat/builtin-tool-skills` ExecutionRuntime) - `ToolExecutionContext.projectSkills` (server tool-exec types) - The map in `RuntimeExecutors.ts` - `aiAgent/index.ts` SkillEngine projectMetas - `gateway.ts resolveProjectSkills` client-side mapping - `services/aiAgent.ts` + lambda router zod schema `SkillsExecutionRuntime.activateSkill` now reads SKILL.md and appends a short hint pointing at the skill's directory + recommending the model call `local-system.listFiles` itself. No second deviceProxy round-trip at activation. Tests updated. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
β¦t bare /name Project skill chips previously serialized to bare `/<skill-name>` text because the hetero CLI agent (Claude Code) recognized its own slash invocation. Now that project skills are in the homogeneous runtime's `<available_skills>` registry too (see the SkillEngine + activateSkill port a few commits up), the model needs them as `<skill name="β¦" />` tags so the activation path can match. Markdown writer for `projectSkill` now mirrors `skill` / `agentSkill`: `<skill name="<skill-name>" label="β¦" />`. LiteXML editor save format is unchanged β `<projectSkill>` still round-trips category info so the chip color / tooltip survive reload. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
β¦l inspector The activateSkill inspector previously displayed the raw identifier (e.g. `agent-skills:lobe-annotation-cleanup`) and used a single "Activate Skill" label regardless of where the skill came from. For agent-document skill bundles this looked like a debug artifact instead of the friendly title shown elsewhere in the UI. - Carry an optional `title` on `BuiltinSkill` so the synthetic entries built from `AgentDocumentsService.getAgentSkills` can preserve the agent-document title. - Tag `activateSkill` results with `source: 'agent' | 'builtin' | 'project' | 'user'` (renamed from `db` β these skills are user-level) and surface the `title` in `ActivateSkillState`. - Both `RunSkillInspector` and `ActivateSkillInspector` now prefer the `title` for display and pick the label by source: "Activate Agent Skill" / "Activate Project Skill" / "Activate Skill". During args streaming the agent prefix (`agent-skills:`) is used as a best-effort fallback so the label flips early. - New i18n keys `builtins.lobe-skills.apiName.activateAgentSkill` and `activateProjectSkill` (EN + ZH-CN populated for dev preview). Also cleans up a stale comment in `aiAgent/index.ts` left over from the previous `projectSkills.files` drop β the directory tree is enumerated lazily, not carried in the op param. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
β¦king sidebar Slash menu now includes agent-document skill bundles and user-installed skills alongside the previous commands + project-skill set, so the `/` trigger covers every skill source the runtime can resolve. The project skills gate also drops `isHetero` (homogeneous runtime accepts them now). Working sidebar's Skills tab gains a User skills section (sourced from `agent_skill` table β market imports + custom user skills only, no builtin tools / LobeHub MCP servers). All three sections (agent / project / user) self-hide when empty; when exactly one source has items the group header is dropped and the list renders flat to avoid a redundant "User skills 1" label above a single row. Bottom padding keeps the last row off the panel edge. Also rewrites the Web-tab empty copy to match the tab's actual content (crawled webpages, not generic documents). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two related issues in the project / agent-document skill flow.
P1 β project `readReference` only blocked literal `..` substrings before
reading the joined path via `deviceFileAccess.readFile`. A model could
request any guessed filename under the skill directory β including
hidden files like `.env` or `node_modules/β¦` β and exfiltrate content
the discovery layer never advertised.
- Add `listFiles(dir)` to `DeviceFileAccess`, wired on the server side
to `local-system.globFiles` with `**/*` + the skill dir as scope.
- Normalize the requested path (POSIX, no leading `./`), reject empty
paths, traversal substrings, and any hidden segment up front.
- Enumerate the skill directory live and reject paths not present in
the result set. The device-side enumerator already strips hidden
files; the runtime re-checks segments as defense in depth.
- Live enumeration is the right cost trade β one extra round-trip per
`readReference` call, no payload bloat for the (much more common)
activation that never reads references.
P2 β the dedupe in `aiAgent/index.ts` sets precedence to
`project > user > agent-skills > builtin`, but `activateSkill` /
`readReference` were checking builtins before falling back to DB
lookups. On a name collision the model saw the user skill in
`<available_skills>` but activated the shadowed builtin.
- Reorder both paths to do `service.findByName` before scanning
`builtinSkills`. Costs one indexed DB lookup per activation, which
is fine β activations aren't on a hot path.
- Builtin lookup remains the fallback so agent-document skill
bundles (`agent-skills:` prefix) and true builtins still resolve.
Tests: existing project-skill tests updated to provide the new
`listFiles` mock, plus four new tests covering rejected paths, hidden
segments, DB-over-builtin precedence, and the builtin fallback.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
# π LobeHub Release (20260528) **Release Date:** May 28, 2026 **Since v2.2.0:** 220 merged PRs Β· 15 contributors > This cycle brings heterogeneous "platform agents" you can dispatch to local or remote devices, a rebuilt onboarding flow, document-centric chat, and a unified model-runtime error model β with new DeepSeek V4 and Gemini 3.5 Flash support along the way. --- ## β¨ Highlights - **More Hetero Agents (OpenClaw / Hermes)** β Create heterogeneous agents and dispatch them to local or remote devices through the device gateway, with an execution-target switcher in the composer and persistent CLI sessions. (#15065, #15179, #15022) - **iMessage on Desktop** β New iMessage setup and bridge on desktop, plus bot attachments across every platform. (#15228, #15227, #15029) - **Skills in the Composer** β Drag skill chips into chat, trigger installed skills from the slash menu mid-line, and surface project-level skills in the homogeneous agent runtime. (#15095, #15061, #15110) - **New Models** β DeepSeek V4 Flash/Pro and Gemini 3.5 Flash across providers, with thinking params for structured output and chat cost estimates. (#15031, #15001, #15051, #14876) - **Agent Runtime Observability** β OpenTelemetry GenAI semantic conventions plus per-call generation tracing. (#15123, #15124) --- ## π€ Agents & Heterogeneous Runtime - **Platform agent creation** β OpenClaw/Hermes creation UI, device guard, and remote dispatch backend. (#15065) - **Execution-target switcher** β Pick local vs remote execution directly in the composer; device-selection UX with actionable guidance. (#15179, #15111) - **CLI hetero dispatch** β OpenClaw/Hermes dispatch with persistent sessions and a notify protocol. (#15022) - **Gateway snapshot as source of truth** β Consume the gateway `uiMessages` snapshot at step boundaries to keep chat state consistent. (#15153, #15152) - **Client sub-agent as a normal tool call** β Simplifies the sub-agent execution path. (#15281) - **Hermes agent chain** β Implements the Hermes agent chain logic. (#15189) - **Device registry** β TRPC endpoints to register, list, update, and remove devices. (#15299) - **Desktop device routing** β Route gateway agent runs through `lh hetero exec`; restore `userId` in gateway dispatch and gate local-system by execution target. (#15132, #15232) - **Agent signals** β Anchor agent-signal receipts to messages and isolate memory-agent messages into a child thread. (#14969, #14921) --- ## π Onboarding - **Simplified first screen** β Defer topic creation to first send. (#15090) - **Market Agent Picker** β Added as a classic onboarding step, with template prefetch. (#14980, #15041) - **Welcome guidance** β Show agent welcome guidance on first run. (#15098) - **Mobile** β Adapt agent onboarding UI and restore Classic-step padding on mobile. (#15019, #15032) - **Discovery** β Streamline discovery to a single profession question. (#14987) - **Analytics** β Track onboarding step events and create-agent modal source. (#15133, #15028) --- ## π Documents, Pages & Knowledge - **Thread chat in preview** β Embed thread chat in the document preview portal. (#15216) - **Non-markdown rendering** β Render non-markdown docs as a read-only highlight. (#15272) - **Multi-select** β Multi-select delete in the document tree. (#15125) - **Page-agent streaming** β Preview `initPage` streaming arguments. (#15039) - **Per-agent topics** β Per-agent topic management page. (#15207) - **Server-side category** β Derive document category server-side and drop frontend predicates. (#15076) --- ## π§© Skills & Tools - **Drag skill chips** β Drag skills into chat input and register agent-document skills. (#15095) - **Slash menu** β Installed skills appear in the slash menu with a mid-line trigger. (#15061) - **Project skills** β Recognize project-level skills in the homogeneous agent runtime and surface them regardless of active device. (#15110, #15177) - **VFS archiving** β Archive oversized tool results to VFS instead of truncating. (#15074) - **@localfile mentions** β Drag folders into chat input as `@localFile` mentions on desktop. (#15071) --- ## π§ Model Runtime & Providers - **Error spec registry** β Unify error codes into a spec + pattern registry, split `ProviderBizError` into finer codes, classify Cloud-only codes via a tier digit, and add `DatabasePersistError`. (#15262, #15286, #15278, #15279) - **New models** β DeepSeek V4 Flash/Pro (opencode-go) and Gemini 3.5 Flash; DeepSeek V4 Pro on SiliconCloud. (#15031, #15001, #15017, #15267) - **Structured output** β Thinking params for structured output, Bedrock structured generation, and DeepSeek `generateObject` tool choice. (#15051, #15174, #15054) - **Cost** β Chat cost estimate support; preserve usage cost in custom streams. (#14876, #15218) --- ## π¬ Chat & User Experience - **Follow-up chips** β Extend follow-up chip suggestions to general chat with scene-specific model config. (#15101, #14797) - **Input drafts** β Persist unsent input drafts across tab switches and prevent repeated draft restore. (#14992, #15024) - **Command menu** β Order topic/message search by recency and promote inline type filters. (#15094, #14986) - **Zoom HUD** β Show a zoom-level HUD on Cmd +/β and Cmd 0. (#15294) - **Copy** β Unescape markdown escapes when copying user messages. (#15253) --- ## π₯οΈ Desktop - **App Nap fix** β Prevent App Nap from dropping the gateway WebSocket during display sleep. (#14994) - **File preview** β Preview `.cjs`/`.mjs`/no-extension files instead of binary fallback and expand `~` when opening local files. (#15168, #15284) - **Cross-platform settings** β Open settings via main-window navigation on Windows/Linux and restore the route after an update restart. (#15036, #14922) - **Token refresh** β Prevent frequent logout from token-refresh retries. (#14928) --- ## π Observability - **OTel GenAI** β Instrument Agent Runtime with OpenTelemetry GenAI semantic conventions. (#15123) - **Generation tracing** β Per-call `llm_generation_tracing` with a pre-allocated tracingId and recordFeedback router. (#15124, #15146) - **Error classification** β Persist `ERROR_CODE_SPECS` classification on operation errors. (#15273) --- ## ποΈ Database Migrations - **Batch migrations** β Topic usage stats, push tokens, `tasks.editor_data`, and document shares. (#15280) - **Tracing & eval tables** β Add `llm_generation_tracing` and agent eval experiment tables. (#15126) > Self-hosted operators should run the database migration (`pnpm db:migrate`, or restart with auto-migrate enabled) after upgrading. The changes are additive and backwards-compatible. --- ## π Security & Reliability - **Security:** Remove the `getPlaintextCred` tool to prevent plaintext credential exposure. (#14998) - **Security:** Prompt account selection for Google OAuth and add `prompt=consent` to the OIDC authorization URL to fix missing refresh tokens. (#15234, #15010) - **Reliability:** Preserve streamed content across a mid-stream cancel. (#15173) - **Reliability:** Bound the Redis command timeout and configure the Anthropic client timeout. (#15091, #15042) - **Reliability:** Prevent infinite recursion in the assistant chain. (#15288) --- ## π₯ Contributors Huge thanks to **15 contributors** who shipped **220 merged PRs** this cycle. @AnotiaWang Β· @sxjeru Β· @algojogacor Β· @hardy-one Β· @arvinxx Β· @Innei Β· @tjx666 Β· @lijian Β· @AmAzing129 Β· @rdmclin2 Β· @neko Β· @cy948 Β· @CanisMinor Β· @sudongyuer Β· @rivertwilight Plus @lobehubbot and renovate[bot] for maintenance. --- **Full Changelog**: v2.2.0...release/weekly-20260528
Summary
Port of the project-skill server-side recognition originally landed on
feat/cc-api-provider-mode(39ffca7). That branch isn't merging soon so this lifts the work onto canary and re-applies it against the now-merged agent-document skill registry (#15095).Surfaces filesystem skills (
.agents/skills/.claude/skills) to the homogeneous agent runtime and lets it activate them on demand:resolveProjectSkillsingateway.ts) and passes the skill list (frontmatter + absolute SKILL.md path + file tree) as a newprojectSkillsop param. Wired throughservices/aiAgent.ts, the lambda router, andRuntimeExecutorsintoToolExecutionContext.project > db > agent-skills > builtin. They render in<available_skills>withsource="project" location="β¦", andskillsPromptsappends a hint telling the model toreadFilethe SKILL.md before following its instructions.activateSkill/readReferenceinSkillsExecutionRuntimeresolve project skills by reading SKILL.md and reference files through a newdeviceFileAccess.readFile. The server wires that toLocalSystemApiName.readFileoverdeviceProxy.executeToolCallβ no new file-read primitive, the existing local-system capability over the gateway does the work.ProjectSkillMetain@lobechat/types, newSkillSource(builtin | db | project) in@lobechat/prompts, andfiles/location/sourceadded toSkillMetain@lobechat/context-engineso the metadata survives end to end.Plus 4 new
SkillsExecutionRuntimetests for the project-skill paths (activate by name, fall back when no device access, readReference by relative path, error when device missing). The 6 pre-existing failures in that file are aboutexecScriptand reproduce on canary HEAD too.Also adds 6
AgentDocumentsService.getAgentSkillsunit tests verifying the agent-skill identifier construction + bundle/index-child resolution that landed in #15095.Test plan
.agents/skills/test/SKILL.md. Send a chat. Verify<available_skills>contains<skill name="test" source="project" location="β¦/SKILL.md">and the projectHint sentence. Have the modelactivateSkill('test')β content from SKILL.md returned.<available_skills>(gated onactiveDeviceId).references/usage.md. Model callsreadReference({ id: 'test', path: 'references/usage.md' })β content returned via deviceProxy.readFile.lobe-skills(collides with builtin). Verify the project version wins.agent-skills:<filename>still resolves correctly via the existing builtinSkills channel.π€ Generated with Claude Code