Skip to content

feat: 统一 Skill 状态目录 + legacy 路径兼容层 #147

@jorben

Description

@jorben

背景

当前 skill 生态中,部分 skill(如 $Code)运行时会在硬编码的路径下写持久化文件(例如 ~/code/memory.md),但这些路径不在 Agent 的 writable_roots 中,导致 tool.path.outside_workspace 拒绝。

现状分析

权限模型

  • 内建可写根:~/.agents~/.tiy~/.cache/tmp$TMPDIR
  • 全局混合模型:read / write / edit / search / list / find 共用同一组 writable_roots
  • Path 解析器只认绝对路径或相对 workspace 的路径,不展开 ~${VAR}

Prompt 注入

SkillsProvider 只暴露每个 skill 的 SKILL.md 文件路径,没有 stateDir、没有兼容规则、没有"本轮激活的是哪个 skill"的结构化信息

痛点

$Code skill 要求读写 ~/code/memory.md,但该路径不在任何 writable_roots 中。按当前设计,每遇到一个这样的 skill 就要手动给用户开白名单——不可持续。

方案

核心思路:不继续给未知目录开 writable_roots,而是把 skill 自有状态统一收敛到 ~/.tiy/skills-state/<skill-id>/~/.tiy 已是内建可写根),再做 legacy 路径识别与定向重写。

1. 数据模型扩展

SkillRecordDtosrc-tauri/src/model/extensions.rs)新增字段:

pub state_dir: String,              // ~/.tiy/skills-state/<sanitized-id>/
pub legacy_state_paths: Vec<String>, // 从 SKILL.md 启发式提取,如 "~/code"

前端类型同步。

2. Skill 加载时生成 state 信息

parse_skill_markdown 中:

  • 生成 state_dir = ~/.tiy/skills-state/<id>/
  • 启发式扫描:从 SKILL.md body 提取 ~/xxx$HOME/xxxmkdir -p ~/xxx 等模式
  • 排除已有权限的路径(skill 自身目录、/tmp、workspace)

3. Prompt 注入升级

SkillsProvider::collect 输出每个 skill 的 state 目录 + 兼容规则:

- Code: Coding workflow... (file: .../SKILL.md, state: ~/.tiy/skills-state/code-1.0.4/)

并增加兼容指引:

### Skill state rules
- Skill installation directories are READ-ONLY. Never modify files inside a skill's own directory.
- Each skill has a dedicated writable state directory listed above. Write all skill-owned persistent files there.
- If a SKILL.md references legacy paths like ~/code/memory.md, treat them as examples. Use the skill's state directory instead.

4. 工具层兼容拒绝与重写

当 file 工具返回 tool.path.outside_workspace 时:

if 被拒绝的路径 ∈ 某个已启用 skill 的 legacy_state_paths:
    返回 enhanced error + suggested redirect

不自动静默重写,但给 agent 足够信息自行修正。

5. 后续可选增强

  • 支持 skill://state/<skill-id>/memory.md 伪协议路径
  • 按当前激活 skill 动态追加 state root 到 additional_roots

改造清单(最小可行版)

文件 改动
src-tauri/src/model/extensions.rs SkillRecordDtostate_dirlegacy_state_paths
src/shared/types/extensions.ts SkillRecord 同步加字段
src-tauri/src/extensions/mod.rs parse_skill_markdown:生成 state_dir,启发式扫描 legacy 路径
src-tauri/src/core/prompt/providers.rs SkillsProvider::collect:输出 state 目录 + 兼容规则
src-tauri/src/core/policy_engine.rs / tool_gateway.rs outside 拒绝时检查 legacy_state_paths,返回 suggested redirect

优点

  • 不需要用户手动加路径到 writable_roots
  • 不需要预测每个 skill 会用什么路径
  • ~/.tiy 已是内建可写根,无需额外权限
  • 统一、可审计、可按 skill 隔离

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions