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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions