Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d0ef6d8
feat(web-shell): support custom code block rendering
zhangxy-zju Jul 3, 2026
634772e
fix(web-shell): harden custom code block rendering
zhangxy-zju Jul 3, 2026
6d706dc
docs: add skill capability gating design
zhangxy-zju Jul 3, 2026
62dbb51
fix(web-shell): make chart skill host supplied
zhangxy-zju Jul 3, 2026
7daeebf
docs(web-shell): write chart skill in English
zhangxy-zju Jul 3, 2026
be64d78
docs(web-shell): document full-data chart payload
zhangxy-zju Jul 3, 2026
031e949
docs(web-shell): use dataset-backed chart payload
zhangxy-zju Jul 3, 2026
68bec83
feat(web-shell): add echarts full-data renderer
zhangxy-zju Jul 3, 2026
e6f228a
chore(web-shell): keep chart skill host supplied
zhangxy-zju Jul 3, 2026
a25192d
fix(web-shell): show loading for streaming chart blocks
zhangxy-zju Jul 3, 2026
bcf9c35
style(web-shell): polish echarts full-data renderer
zhangxy-zju Jul 3, 2026
eb0da5d
Merge remote-tracking branch 'origin/main' into feat/web-shell-code-b…
zhangxy-zju Jul 3, 2026
e689175
fix(web-shell): harden custom code block language parsing
zhangxy-zju Jul 3, 2026
dde6c54
fix(web-shell): harden echarts full-data renderer
zhangxy-zju Jul 3, 2026
a8dc8fb
fix(web-shell): polish echarts renderer followups
zhangxy-zju Jul 3, 2026
9464e11
fix(web-shell): reuse enhanced table for chart data
zhangxy-zju Jul 3, 2026
d43ca8f
fix(web-shell): recover chart renderer after errors
zhangxy-zju Jul 3, 2026
9e76749
Merge branch 'main' into feat/web-shell-code-block-renderer
zhangxy-zju Jul 3, 2026
56ed1d3
fix(web-shell): harden chart option handling
zhangxy-zju Jul 3, 2026
e98e20d
fix(web-shell): harden chart data rendering
zhangxy-zju Jul 3, 2026
925da8f
Merge branch 'main' into feat/web-shell-code-block-renderer
zhangxy-zju Jul 3, 2026
89ceb55
fix(web-shell): tighten chart renderer guardrails
zhangxy-zju Jul 3, 2026
66445e2
fix(web-shell): update chart fallback title
zhangxy-zju Jul 3, 2026
1c3f39c
fix(web-shell): polish chart renderer review fixes
zhangxy-zju Jul 3, 2026
52dd5cc
feat(web-shell): support compact echarts full data blocks
zhangxy-zju Jul 3, 2026
e25bc46
docs(web-shell): add chart skill template
zhangxy-zju Jul 3, 2026
ed482f9
fix(web-shell): address chart review suggestions
zhangxy-zju Jul 3, 2026
1514239
fix(web-shell): preserve punctuation language aliases
zhangxy-zju Jul 3, 2026
5e0f44e
fix(web-shell): address chart follow-up review
zhangxy-zju Jul 3, 2026
75202f3
fix(web-shell): harden chart ref resolution
zhangxy-zju Jul 4, 2026
4191476
fix(web-shell): cover chart sanitizer follow-ups
zhangxy-zju Jul 4, 2026
1dffa3b
fix(web-shell): address chart review follow-ups
zhangxy-zju Jul 4, 2026
976e0a5
fix(web-shell): address chart review leftovers
zhangxy-zju Jul 4, 2026
7ba76c3
fix(web-shell): close chart review gaps
zhangxy-zju Jul 4, 2026
31e05f7
fix(web-shell): handle latest chart review
zhangxy-zju Jul 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
342 changes: 342 additions & 0 deletions docs/design/skill-required-capabilities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,342 @@
# Skill Required Capabilities Design

Status: design note; this PR proceeds with Option B and leaves
`required-capabilities` as a future proposal.

## Context

Web Shell can render custom fenced code blocks through its markdown renderer. The
chart renderer proposal uses an `echarts-fulldata` fenced code block so the model
can return a complete ECharts option and dataset payload that Web Shell renders
as an interactive chart.

That output contract is only useful in clients that can render it. In the CLI,
ACP clients, or any other surface without a matching renderer, the same response
would appear as a large code block instead of a chart.

The initial bundled chart skill proposal relied on wording to tell the model
that the format is for Web Shell. This is a soft guard. If the skill is exposed
in a non-Web-Shell session, the model can still choose an output format that the
client cannot render.

For the current PR, Qwen Code keeps the renderer extension point in Web Shell
but does not bundle `qwencode-viz` in core. The Web Shell package includes a
copyable, non-auto-loaded skill template, and hosts should install or inject
that skill only when they also register an `echarts-fulldata` renderer.

## Problem

Qwen Code needs a clear way to decide whether a host-specific skill should be
shown to the model and to users.

For `qwencode-viz`, the concrete question is:

- Should core support a generic `required-capabilities` skill metadata field?
- Or should `qwencode-viz` not be a core bundled skill at all, and instead be
supplied only by Web Shell clients that install or inject it?

## Goals

- Prevent renderer-specific skills from being exposed when the current client
cannot satisfy their output contract.
- Keep startup skill reminders, explicit skill activation, slash-command
discovery, and skill validation consistent.
- Avoid hardcoding `qwencode-viz` as a special case.
- Preserve existing skill behavior when no capability requirement is declared.
- Keep the design extensible for future host capabilities, not only ECharts.

## Non-goals

- Implementing the ECharts renderer itself.
- Redesigning all client/server capability negotiation.
- Changing the semantics of existing skill frontmatter.
- Solving multi-client shared-session capability changes in the first version.

## Current Related Mechanisms

The codebase already has several visibility controls, but none represent client
rendering capabilities:

- `disable-model-invocation`: prevents a skill from being auto-invoked by the
model.
- `user-invocable`: controls whether a bundled skill is available as a command.
- `paths`: scopes skill availability to matching workspace paths.
- `skills.disabled`: disables configured skills.
- `allowedTools`: currently used by bundled skill loading to hide cron-oriented
skills when cron tools are unavailable.
- Slash command `supportedModes`: filters commands by execution mode.
- Daemon and ACP capability objects: describe protocol or client support, but
are not currently connected to skill exposure.

There is no existing `required-capabilities` or equivalent skill frontmatter.
Adding it would be a new skill contract.

## Option A: Add `required-capabilities`

Add a generic skill frontmatter field:

```yaml
---
name: qwencode-viz
description: Render analytical charts in Web Shell using echarts-fulldata fenced code blocks.
required-capabilities:
- markdown.codeBlock.echarts-fulldata
---
```

When the current client/session does not advertise all listed capabilities, the
skill is treated as unavailable.

### Capability Naming

Use namespaced string capabilities:

```text
markdown.codeBlock.echarts-fulldata
```

This keeps the field generic while making the contract precise:

- `markdown`: the capability belongs to rendered markdown.
- `codeBlock`: the capability applies to fenced code block rendering.
- `echarts-fulldata`: the specific language/info string supported by the
renderer.

Future examples could be:

- `markdown.codeBlock.vega-lite`
- `markdown.codeBlock.mermaid-interactive`
- `artifact.openUrl`

### Skill Metadata

Add `requiredCapabilities?: string[]` to skill configuration after parsing the
frontmatter key `required-capabilities`.

Both skill parsing paths should understand the field:

- `packages/core/src/skills/skill-load.ts`
- `packages/core/src/skills/skill-manager.ts`

The field should be optional. Missing or empty means the skill has no client
capability requirement.

### Runtime Capability Source

Add client/session capabilities to the runtime config:

```ts
interface ConfigParameters {
clientCapabilitiesProvider?: () => ReadonlySet<string>;
}
```

Expose a helper on `Config`, for example:

```ts
config.getClientCapabilities(): ReadonlySet<string>
```

Then centralize the check:

```ts
function skillMeetsRequiredCapabilities(skill: Skill, config: Config): boolean {
return skill.config.requiredCapabilities.every((capability) =>
config.getClientCapabilities().has(capability),
);
}
```

### Filtering Points

The capability filter should be applied before skills are exposed to either the
model or the user:

- `collectAvailableSkillEntries` in `packages/core/src/tools/skill-utils.ts`
should skip skills whose required capabilities are missing. This keeps startup
skill reminders, delta reminders, `SkillTool` validation, and model-invocable
activation aligned.
- `BundledSkillLoader` should skip unavailable bundled skills when creating
user-facing commands.
- `SkillCommandLoader` should skip unavailable file-system skills when creating
user-facing commands.

The important invariant is that a skill hidden from the model should not still
appear as an invocable command unless the project intentionally supports a
manual override.

### Web Shell Registration

Web Shell should advertise renderer support explicitly rather than relying on
the presence of an opaque `renderCodeBlock` callback.

For example:

```tsx
<WebShell
customization={{
markdown: {
renderableCodeBlockLanguages: ['echarts-fulldata'],
renderCodeBlock(info) {
// render custom blocks
},
},
}}
/>
```

The Web Shell client can map that to:

```text
markdown.codeBlock.echarts-fulldata
```

This makes the capability declaration stable even if the renderer callback
contains custom logic, fallbacks, or multiple supported languages.

### Daemon and ACP Propagation

For hosted or daemon-based sessions, the client capability set needs to reach
core before skills are loaded or listed. A minimal version can pass capabilities
when creating a session:

```ts
interface CreateSessionRequest {
clientCapabilities?: string[];
}
```

The daemon bridge, SDK, and ACP session creation flow can store this as
session-scoped config.

For the first version, capabilities can be session-scoped. If multiple clients
attach to the same session, the behavior should be documented as using the
capabilities from session creation time.

### Pros

- Keeps `qwencode-viz` as one canonical bundled skill.
- Prevents host-specific output contracts from leaking into unsupported
clients.
- Creates a reusable mechanism for future renderer-specific or host-specific
skills.
- Makes the dependency explicit and testable.

### Cons

- Adds a new cross-cutting skill metadata field.
- Requires client/session capability plumbing across Web Shell, daemon, SDK, and
ACP surfaces.
- Needs careful documentation for shared-session behavior.
- May be more machinery than needed if `qwencode-viz` is the only expected
capability-gated skill.

## Option B: Client-Supplied Skill

Do not add a generic `required-capabilities` field. Instead, avoid bundling
`qwencode-viz` in core. The Web Shell client, or any client that supports the
renderer, supplies the skill itself.

Possible distribution models:

- The Web Shell host installs `.qwen/skills/qwencode-viz/SKILL.md`.
- The Web Shell package ships an optional non-auto-loaded skill template that a
host can copy or install when chart rendering is enabled.
- The Web Shell integration ships an extension skill package.
- The Web Shell integration injects equivalent model instructions only when its
chart renderer is enabled.

In this model, the skill is available only because the rendering client chose to
provide it.

### Pros

- Minimal core change.
- No new global skill metadata contract.
- Capability availability is naturally owned by the client that implements the
renderer.
- Avoids daemon or ACP plumbing unless the client already has a skill injection
mechanism.

### Cons

- No canonical bundled skill unless all clients copy the same content.
- More burden on each Web Shell integrator.
- Users moving between clients may see inconsistent skill availability.
- Does not create a general safeguard for future host-specific skills.
- Harder to test in core because availability depends on external installation
or injection.

## Recommendation

For this PR, use Option B.

That keeps the core skill system unchanged and avoids exposing
`echarts-fulldata` instructions in unsupported clients. The Web Shell renderer
hook remains useful for any host-owned block renderer, while chart-specific
model instructions become an explicit host opt-in.

Longer term, discuss this as a product/API boundary decision.

Choose Option A if maintainers expect Qwen Code to support more client-rendered
output contracts over time. In that case, `required-capabilities` is a small
general contract that keeps skill exposure honest across CLI, Web Shell, ACP,
and future clients.

Choose Option B if `qwencode-viz` is expected to remain a Web-Shell-only
extension and maintainers do not want core skills to depend on client rendering
features. In that case, the current bundled skill should be removed from core
and supplied by Web Shell clients that support `echarts-fulldata`.

The recommended future default is Option A only if maintainers are comfortable
making client/session capabilities part of the skill system. Otherwise, keep
host-renderer skills client-owned.

## Open Questions

- Should capabilities be session-scoped, request-scoped, or client-scoped?
- Should missing capabilities hide user-invocable commands, or only hide
model-invocable skill activation?
- Should capability names be free-form strings or validated against a known
registry?
- Should unavailable skills be hidden entirely from `/skills`, or shown as
disabled with a reason?
- Should there be a manual override for users who intentionally want to emit raw
`echarts-fulldata` blocks in unsupported clients?
- Should the field name be `required-capabilities`, `requires-capabilities`, or
`client-capabilities`?

## Validation Plan

If Option A is implemented, add tests for:

- Frontmatter parsing in both skill parsing paths.
- `collectAvailableSkillEntries` hiding a skill when capabilities are missing.
- The same skill appearing when capabilities are present.
- Interaction with `paths`, `skills.disabled`, and `disable-model-invocation`.
- `BundledSkillLoader` and `SkillCommandLoader` command visibility.
- Web Shell mapping from supported code block languages to client capabilities.
- Daemon or ACP session creation preserving the capability set.
- Existing bundled skill integration tests, to ensure skills without
`required-capabilities` are unchanged.

## Migration

Existing skills require no migration because the new field is optional.

For the current Option B path, remove the chart skill from core bundled skills.
The Web Shell package template must not be loaded by core automatically; hosts
opt in by installing or injecting it.

If Option A is accepted, add:

```yaml
required-capabilities:
- markdown.codeBlock.echarts-fulldata
```

to a future bundled `qwencode-viz`.

If Option B is accepted, remove the chart skill from core bundled skills and
document how Web Shell clients can install or inject it when they register an
`echarts-fulldata` renderer.
42 changes: 42 additions & 0 deletions packages/web-shell/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,48 @@ export function App() {
| `language` | `'en' \| 'zh-CN' \| 'zh' \| 'zh-cn'` | UI 语言 |
| `onLanguageChange` | `(language: WebShellLanguage) => void` | `/language ui` 切换 UI 语言后触发 |

## 可选图表 Renderer

`WebShell` 支持宿主通过 `customization.markdown.renderCodeBlock` 接管特定
fenced code block 的渲染。图表类场景可以注册内置的
`echarts-fulldata` renderer:

```tsx
import { createEchartsFullDataRenderer } from '@qwen-code/web-shell';

<WebShellWithProviders
markdown={{
renderCodeBlock: createEchartsFullDataRenderer({
loadEcharts: () => window.echarts,
resolveDataRef: async (ref, meta) =>
loadControlledChartDataset(ref, meta),
}),
}}
/>;
```

renderer 会把 `echarts-fulldata` code block 替换为图表卡片,并内置图表/数据
icon 切换;ECharts runtime 由宿主通过 `loadEcharts` 提供。若启用
`data.kind="ref"` envelope,数据只能通过宿主提供的 `resolveDataRef` 解析,
renderer 不会自己读取 URL 或本地路径。

如果需要让模型主动输出 `echarts-fulldata` block,宿主应在自己的 skills 来源中
提供对应 skill,并且只在确认当前 Web Shell 宿主已经注册 renderer 时启用。
`@qwen-code/web-shell` 不内置或自动加载这个 skill;可从
`packages/web-shell/docs/examples/qwencode-viz/SKILL.md` 复制模板到宿主的
Comment thread
zhangxy-zju marked this conversation as resolved.
`.qwen/skills/qwencode-viz/SKILL.md`,或通过宿主自己的 skill 注入机制提供等价
说明。

`echarts-fulldata` 的 block body 可以是旧版纯 JSON ECharts option,也可以是
`{ "version": 1, "data": ..., "option": ... }` envelope。新版 inline envelope
使用 `data.dimensions: string[]` 和 `data.source` array-of-arrays;renderer 会先
normalize 成原生 ECharts option,并注入 `option.dataset`,再渲染图表和数据视图。
新版 ref envelope 必须使用受控 `artifact://` 或 `session-file://` ref,并提供
`data.format`(`csv` 或 `json`)和 `data.dimensions`,这些元信息会传给宿主的
`resolveDataRef(ref, meta)`。
宿主应使用 `JSON.parse` 解析,不能用 `eval`、`new Function` 或 script injection
执行模型生成内容。

## 架构说明

```text
Expand Down
Loading
Loading