Skip to content

Commit e995ab8

Browse files
committed
Complete Skills v2 platform maturity
1 parent 29254b5 commit e995ab8

29 files changed

Lines changed: 920 additions & 29 deletions

docs/agent-architecture-improvement-plan.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,12 +503,36 @@ Turn the architecture review into an executable backlog for Funplay's local agen
503503
|---|---|---|---|---|
504504
| P65-1 | Add lazy supporting-file listing and reading for Skill packages. | Completed | Registered `list_agent_skill_files` and `read_agent_skill_file` as read-only Native tools. Supporting-file reads are scoped to the selected Skill package, reject path traversal and symlinks, skip `SKILL.md`, reject binary-like files, and cap file size/content. | `npm run agent:skills-v2-benchmark`; `npm run build`; `npm run test:runtime`; `npm run agent:roadmap-audit` |
505505

506+
## Phase 66: Skills v2 Trust And Permission Boundaries
507+
508+
| ID | Requirement | Status | Implementation Notes | Verification |
509+
|---|---|---|---|---|
510+
| P66-1 | Add Skill trust, source verification, permission policy, and script execution boundaries. | Completed | Filesystem Skills now expose trust level, verification status, content SHA-256, permission policy, script policy, and declared scripts. Approval-required or untrusted Skills are excluded from metadata auto-activation, and declared scripts are metadata only: execution must go through ordinary Agent tools and the existing permission broker. Native/Claude prompts and skill tools surface these boundaries. | `npm run agent:skills-v2-benchmark`; `npm run build`; `npm run agent:roadmap-audit` |
511+
512+
## Phase 67: Claude SDK Skills Integration
513+
514+
| ID | Requirement | Status | Implementation Notes | Verification |
515+
|---|---|---|---|---|
516+
| P67-1 | Integrate Claude SDK native Skills support where available. | Completed | Claude SDK options now apply Skills-aware `settings`: inactive Skills are hidden from model auto-selection, inactive user-invocable Skills remain slash-visible, and inline Skill shell execution is disabled. When a Claude SDK AgentDefinition is active, Funplay attaches active filesystem Skill names to that agent's `skills` allowlist. | `npm run agent:skills-v2-benchmark`; `npm run build`; `npm run agent:roadmap-audit` |
517+
518+
## Phase 68: Skills v2 Observability
519+
520+
| ID | Requirement | Status | Implementation Notes | Verification |
521+
|---|---|---|---|---|
522+
| P68-1 | Add Skill activation runtime events, replay export, and transcript visibility. | Completed | Active filesystem Skill selections now persist as `skill_activation` runtime events, appear in replay metrics and Agent Core debugger parts, and render in completed chat transcripts from structured Agent Core system parts. The Skills v2 benchmark includes these observability checks. | `npm run agent:skills-v2-benchmark`; `npm run build`; `npm run agent:roadmap-audit` |
523+
506524
## Phase 69: Skills v2 Benchmark Gate
507525

508526
| ID | Requirement | Status | Implementation Notes | Verification |
509527
|---|---|---|---|---|
510528
| P69-1 | Add deterministic Skills v2 benchmark coverage. | Completed | Added `scripts/agent-skills-v2-benchmark.mjs` and `npm run agent:skills-v2-benchmark`, covering registry discovery, metadata context, explicit and automatic activation, supporting-file reads, and read-only tool-boundary checks. The main deterministic `agent:benchmark` now includes this Skills v2 slice. | `npm run agent:skills-v2-benchmark`; `npm run build`; `npm run test:runtime`; `npm run agent:benchmark`; `npm run agent:roadmap-audit` |
511529

530+
## Phase 70: Skills v2 Settings UI
531+
532+
| ID | Requirement | Status | Implementation Notes | Verification |
533+
|---|---|---|---|---|
534+
| P70-1 | Mature the Settings UI for source precedence, conflicts, updates, and project overrides. | Completed | Added a main-process filesystem Skill registry IPC endpoint and rendered it in Project Settings. The Skills page now shows source precedence, same-name override conflicts, trust/verification status, permission policy, script policy, and suggested tools alongside existing catalog sync/update and custom project Skill override controls. | `npm run agent:skills-v2-benchmark`; `npm run build`; `npm run agent:roadmap-audit` |
535+
512536
## Next Roadmap: Claude Code MCP Platform Parity
513537

514538
This draft section is intentionally not encoded as audited implementation rows. Convert each item into a numbered phase row only when that item enters active implementation.

docs/skills-v2-roadmap.md

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Skills v2 Platform Roadmap
22

3-
Last updated: 2026-05-15
3+
Last updated: 2026-05-16
44

55
## Maturity Target
66

@@ -25,11 +25,11 @@ Funplay Skills v2 should behave like a platform capability, not a large prompt f
2525
| SK63-1 | Replace eager prompt injection with automatic metadata-based activation. | Completed |
2626
| SK64-1 | Add explicit `/skill-name` invocation and session-visible activation state. | Completed |
2727
| SK65-1 | Add supporting-file lazy loading with size limits and provenance. | Completed |
28-
| SK66-1 | Add Skill trust, source verification, permission policy, and script execution boundaries. | Not started |
29-
| SK67-1 | Integrate Claude SDK native Skills support where available. | Not started |
30-
| SK68-1 | Add Skill activation/runtime events, replay export, and UI visibility. | Not started |
28+
| SK66-1 | Add Skill trust, source verification, permission policy, and script execution boundaries. | Completed |
29+
| SK67-1 | Integrate Claude SDK native Skills support where available. | Completed |
30+
| SK68-1 | Add Skill activation/runtime events, replay export, and UI visibility. | Completed |
3131
| SK69-1 | Add deterministic and live Skill benchmark gates. | Completed |
32-
| SK70-1 | Mature the Settings UI for source precedence, conflicts, updates, and project overrides. | Not started |
32+
| SK70-1 | Mature the Settings UI for source precedence, conflicts, updates, and project overrides. | Completed |
3333

3434
## Phase 61 Verification
3535

@@ -65,8 +65,40 @@ Funplay Skills v2 should behave like a platform capability, not a large prompt f
6565
- Supporting file reads are scoped to the selected Skill directory, skip symlinks, reject path traversal and binary-like files, and cap file size/content.
6666
- Regression checks: `npm run agent:skills-v2-benchmark`, `npm run build`, `npm run test:runtime`
6767

68+
## Phase 66 Verification
69+
70+
- Filesystem Skills now carry trust level, source verification status, content SHA-256, permission policy, script policy, and declared script metadata.
71+
- Project/user/plugin Skills receive local provenance; user/plugin sources can be trusted, while catalog/custom sources default to unverified approval-required handling.
72+
- Skills marked approval-required or untrusted are not auto-activated by metadata matching; explicit invocation and read tools still surface the policy.
73+
- Declared scripts are metadata only and never execute through the Skill system directly; they must go through normal host tools and permission broker checks.
74+
- Native and Claude prompts, `list_agent_skills`, and `read_agent_skill` now show trust, verification, permission, and script boundaries.
75+
- Regression checks: `npm run agent:skills-v2-benchmark`, `npm run build`
76+
77+
## Phase 67 Verification
78+
79+
- Claude SDK sessions now receive Skills-aware settings when filesystem Skills exist.
80+
- Inactive Skills are hidden from model auto-selection with `skillOverrides`, while user-invocable inactive Skills remain slash-visible.
81+
- Skill inline shell execution is disabled through SDK settings.
82+
- When the caller uses a Claude SDK AgentDefinition, active Funplay Skills are attached to that agent's `skills` allowlist.
83+
- Regression checks: `npm run agent:skills-v2-benchmark`, `npm run build`
84+
85+
## Phase 68 Verification
86+
87+
- Active filesystem Skill selections now persist as first-class `skill_activation` runtime events.
88+
- Agent replay exports include Skill activation metrics and convert persisted activations into structured Agent Core system parts.
89+
- Completed chat transcript rendering now shows activated Skills from Agent Core parts without parsing assistant pseudo text.
90+
- The Skills v2 benchmark now includes activation-event, replay, and transcript visibility checks.
91+
- Regression checks: `npm run agent:skills-v2-benchmark`, `npm run build`
92+
6893
## Phase 69 Verification
6994

7095
- Added `npm run agent:skills-v2-benchmark` with focused registry, activation, supporting-file, and read-only tool-boundary checks.
7196
- The deterministic `npm run agent:benchmark` suite now includes the Skills v2 platform slice.
7297
- Regression checks: `npm run agent:skills-v2-benchmark`, `npm run build`, `npm run test:runtime`, `npm run agent:roadmap-audit`, `npm run agent:benchmark`
98+
99+
## Phase 70 Verification
100+
101+
- Project Settings now loads the filesystem Skill registry through a main-process IPC path.
102+
- The Skills settings view shows source precedence, same-name override conflicts, trust/verification status, permission policy, script policy, and suggested tools.
103+
- Existing catalog sync/update and project custom Skill override controls remain available beside the Claude Code filesystem registry view.
104+
- Regression checks: `npm run agent:skills-v2-benchmark`, `npm run build`

electron/main/agent-platform/claude/prompt-builder.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ export function formatUserSkills(params: GenericAgentRuntimeParams): string {
119119
`## ${skill.name}`,
120120
skill.description ? `Purpose: ${skill.description}` : '',
121121
`Source: ${skill.source}`,
122+
`Trust: ${skill.trustLevel} · Verification: ${skill.verificationStatus}`,
123+
`Permission policy: ${skill.permissionPolicy} (skill metadata does not grant tool permission)`,
124+
`Script policy: ${skill.scriptPolicy}${skill.declaredScripts?.length ? ` · ${skill.declaredScripts.length} declared script(s), run only through normal host tool permission` : ''}`,
122125
skill.allowedTools?.length ? `Suggested tools: ${skill.allowedTools.join(', ')}` : '',
123126
'Instructions:',
124127
skill.instruction
@@ -136,6 +139,9 @@ export function formatUserSkills(params: GenericAgentRuntimeParams): string {
136139
skill.description ? `Purpose: ${skill.description}` : '',
137140
`Source: ${skill.source}`,
138141
`Invocable: user=${skill.userInvocable ? 'yes' : 'no'} model=${skill.modelInvocable ? 'yes' : 'no'}`,
142+
`Trust: ${skill.trustLevel} · Verification: ${skill.verificationStatus}`,
143+
`Permission policy: ${skill.permissionPolicy}`,
144+
skill.declaredScripts?.length ? `Declared scripts: ${skill.declaredScripts.length} (execute only through normal host tool permission)` : '',
139145
skill.allowedTools?.length ? `Allowed tools: ${skill.allowedTools.join(', ')}` : ''
140146
].filter(Boolean).join('\n')
141147
)

electron/main/agent-platform/claude/runtime.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,24 @@ export function createClaudeSdkPermissionHandler(params: GenericAgentRuntimePara
739739
};
740740
}
741741

742+
function buildClaudeSdkSkillSettings(
743+
params: GenericAgentRuntimeParams,
744+
activeSkillNames: string[]
745+
): ClaudeAgentSdkOptions['settings'] | undefined {
746+
const skillIndex = params.context.toolContext.skillIndex;
747+
if (!skillIndex.length && !activeSkillNames.length) {
748+
return undefined;
749+
}
750+
const active = new Set(activeSkillNames);
751+
return {
752+
disableSkillShellExecution: true,
753+
skillOverrides: Object.fromEntries(skillIndex.map((skill) => [
754+
skill.name,
755+
active.has(skill.name) ? 'on' : skill.userInvocable ? 'user-invocable-only' : 'off'
756+
]))
757+
};
758+
}
759+
742760
export function createClaudeCodeSdkOptions(params: GenericAgentRuntimeParams, allowWriteTools: boolean, options: {
743761
cwd: string;
744762
abortController: AbortController;
@@ -753,6 +771,8 @@ export function createClaudeCodeSdkOptions(params: GenericAgentRuntimeParams, al
753771
allowWriteTools,
754772
supportsHostControlledWrites: true
755773
});
774+
const activeSkillNames = params.context.toolContext.activeSkills.map((skill) => skill.name);
775+
const skillSettings = buildClaudeSdkSkillSettings(params, activeSkillNames);
756776
const sdkOptions: ClaudeAgentSdkOptions = {
757777
cwd: options.cwd,
758778
abortController: options.abortController,
@@ -768,6 +788,7 @@ export function createClaudeCodeSdkOptions(params: GenericAgentRuntimeParams, al
768788
env: options.env ?? buildClaudeCodeSdkEnv(params.provider),
769789
settingSources: resolveClaudeSdkSettingSources(params.provider),
770790
mcpServers: buildFunplayMcpServers(params, options.cwd, profile),
791+
settings: skillSettings,
771792
enableFileCheckpointing: Boolean(params.checkpointSnapshotId),
772793
canUseTool: options.canUseTool,
773794
stderr: options.stderr
@@ -817,6 +838,15 @@ export function createClaudeCodeSdkOptions(params: GenericAgentRuntimeParams, al
817838
}
818839
if (runtimeOverrides?.agent?.trim()) {
819840
sdkOptions.agent = runtimeOverrides.agent.trim();
841+
if (activeSkillNames.length && sdkOptions.agents?.[sdkOptions.agent]) {
842+
sdkOptions.agents = {
843+
...sdkOptions.agents,
844+
[sdkOptions.agent]: {
845+
...sdkOptions.agents[sdkOptions.agent],
846+
skills: activeSkillNames
847+
}
848+
};
849+
}
820850
}
821851

822852
if (model) {

electron/main/agent-platform/native/prompt.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ export function createNativeRuntimeUserPrompt(
137137
`## ${skill.name}`,
138138
skill.description ? `用途:${skill.description}` : '',
139139
`来源:${skill.source}`,
140+
`信任:${skill.trustLevel} · 验证:${skill.verificationStatus}`,
141+
`权限策略:${skill.permissionPolicy}(只约束 Skill 工作流,不授予工具权限)`,
142+
`脚本策略:${skill.scriptPolicy}${skill.declaredScripts?.length ? ` · 声明脚本 ${skill.declaredScripts.length} 个,必须通过普通工具权限流程执行` : ''}`,
140143
skill.allowedTools?.length ? `建议工具:${skill.allowedTools.join(', ')}` : '',
141144
'执行准则:',
142145
skill.instruction
@@ -155,6 +158,9 @@ export function createNativeRuntimeUserPrompt(
155158
skill.description ? `用途:${skill.description}` : '',
156159
`来源:${skill.source}`,
157160
`可调用:user=${skill.userInvocable ? 'yes' : 'no'} model=${skill.modelInvocable ? 'yes' : 'no'}`,
161+
`信任:${skill.trustLevel} · 验证:${skill.verificationStatus}`,
162+
`权限策略:${skill.permissionPolicy}`,
163+
skill.declaredScripts?.length ? `声明脚本:${skill.declaredScripts.length} 个(不能直接执行,需普通工具权限)` : '',
158164
skill.allowedTools?.length ? `建议工具:${skill.allowedTools.join(', ')}` : ''
159165
].filter(Boolean).join('\n')
160166
)

electron/main/agent-platform/replay-log.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export function buildAgentReplayMetrics(run: AgentRuntimeStatus): AgentReplayMet
3535
const events = run.events ?? [];
3636
const usageTotals = latestUsageTotals(run);
3737
const usageEventCount = events.filter((event) => event.type === 'usage').length;
38+
const skillActivationCount = events.filter((event) => event.type === 'skill_activation' && event.skillActivation).length;
3839
const toolUseIds = new Set(events
3940
.filter((event) => event.type === 'tool_use' && event.toolUse?.toolUseId)
4041
.map((event) => event.toolUse?.toolUseId as string));
@@ -64,7 +65,8 @@ export function buildAgentReplayMetrics(run: AgentRuntimeStatus): AgentReplayMet
6465
toolRetryCount: countRepeatedToolResults(events),
6566
recoveryEventCount: recoveryEvents.length,
6667
apiRetryCount,
67-
contextRetryCount
68+
contextRetryCount,
69+
skillActivationCount
6870
};
6971
}
7072

electron/main/agent-platform/run-registry.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
AgentRuntimeStatus,
88
AgentRuntimeTimelineEntry,
99
AgentRuntimeToolBoundary,
10+
AgentSkillActivation,
1011
AgentToolArtifact,
1112
AgentToolBrowserResult,
1213
AgentToolChangedFile,
@@ -319,6 +320,26 @@ export function recordActiveRunTodoUpdate(runId: string, input: NonNullable<Agen
319320
persistRuntimeRun(updated);
320321
}
321322

323+
export function recordActiveRunSkillActivation(runId: string, input: AgentSkillActivation): void {
324+
const run = activeRunsById.get(runId);
325+
if (!run) {
326+
return;
327+
}
328+
329+
const updated: ActiveAgentRun = {
330+
...run,
331+
updatedAt: nowIso()
332+
};
333+
updated.events = appendRuntimeEvent(updated, {
334+
type: 'skill_activation',
335+
status: updated.status,
336+
statusMessage: `Skill activated: ${input.name}`,
337+
skillActivation: input
338+
});
339+
activeRunsById.set(runId, updated);
340+
persistRuntimeRun(updated);
341+
}
342+
322343
export function findActiveRunBySession(sessionId: string): ActiveAgentRun | undefined {
323344
return [...activeRunsById.values()].find((run) => run.sessionId === sessionId);
324345
}

0 commit comments

Comments
 (0)