Skip to content

feat(skill): decouple read-only skills from Box execution - #2512

Open
huanghuoguoguo wants to merge 9 commits into
masterfrom
feat/2410-sdk-skill-store
Open

huanghuoguoguo wants to merge 9 commits into
masterfrom
feat/2410-sdk-skill-store

Conversation

@huanghuoguoguo

@huanghuoguoguo huanghuoguoguo commented Sep 5, 2026 •

Copy link
Copy Markdown
Collaborator

Related: #2410. The broader issue is not fully satisfied yet; see the remaining gaps below.

Paired SDK change: langbot-app/langbot-plugin-sdk#128. This branch pins SDK commit 661b1475091000c94595016d3a4914ff3a1ad81d in pyproject.toml and uv.lock.

Goal and capability boundary

A user should be able to use a writing guide, review checklist, or Markdown reference Skill without deploying or enabling Box. Loading a Skill's instructions is a read operation; it should not require an execution sandbox.

The boundary is based on the requested operation, not on classifying the entire package as a "read-only Skill": even a package containing scripts can have its instructions activated and its text resources read without executing those scripts.

Operation Box requirement
Discover and activate an authorized Skill; return instructions and a pinned revision None
Read authorized, activated text references/templates through Core None
Manage Skill metadata and publish packages through the Core service None
Execute shell/Python, install dependencies, or edit a sandbox working copy Requires Box and the existing execution authorization

Activation does not grant execution permissions. When execution is unavailable, it must not claim that package files are mounted. When execution is requested, it must consume the same revision as the activated instructions and resource reads.

Implementation

  • Core SkillRepository owns the catalog, CRUD, immutable publications, activation snapshots, and read-only resources, using the SDK's execution-independent SkillStore.
  • Box consumes generic read-only mounts composed by Core; it no longer owns Skill catalog semantics.
  • Workspace-scoped repository locks avoid serializing unrelated tenants; publication uses storage-level locking and base-revision conflict checks.
  • Revision bindings are persisted for recovery. Legacy name-only state is skipped with a warning and requires reactivation.
  • Existing installations retain the persisted skills-root fallback. Core and Box Runtime must use the paired SDK protocol and a shared filesystem path for mounted packages. A custom skills.root outside the Box mount allowlist needs explicit configuration.

Remaining correctness and acceptance gaps

The capability separation above is the intended design. The following issues concern the current implementation and the broader acceptance contract:

  1. New runs versus recovery: ordinary subsequent runs in the same conversation restore the prior revision. After publishing v2, reactivating in that new run still returns v1; removing the Skill from the authorized set can fail run startup. Recovery must retain exact revisions, while a genuinely new run needs an explicit policy consistent with the agreed "subsequent runs resolve v2" requirement.
  2. Shared Box state: activation alone does not create a sandbox, but the next execution supplies the run's mount set. With the managed global session, a changed set recreates the container. Alternating runs with different sets repeatedly recreate it. Workspace files survive; processes and container-local temporary state do not. The SDK waits for admitted executions before replacement, but this does not preserve state across tool calls. Resolve or explicitly agree this execution behavior before merging.
  3. Repeated UI saves: after an update, the form retains its previous revision. A second edit/save submits a stale base_revision and receives a conflict. The form must adopt the revision returned by the successful update.
  4. Binary artifacts: the current resource reader supports UTF-8 text. PNG reads fail instead of returning a controlled resource reference. Image/PDF references remain a gap against [Feature]: Decouple Skill activation and read-only resources from Box sandbox execution #2410; they are not implemented by this PR's text-read path.

Validation

Review rerun on 2026-09-29, Core 3987051 with SDK 661b147 source:

  • 334 Core tests passed across Skill repository/service/tools, ToolManager, Runner orchestration, Box, maintenance, entitlements, and the Core-to-Runtime integration stack.
  • 478 SDK SkillStore and Box tests passed.
  • Additional temporary checks reproduced stale revision reuse across queries, failure after unbinding, rejection of a PNG resource, and a conflict on a second save using the unchanged form revision.
  • The existing shared-session integration test confirms repeated container recreation using a fake backend. These checks do not establish real Docker/nsjail/E2B acceptance. The UI save issue was traced through the form callback and reproduced at the service boundary, not browser-tested.
Previously recorded validation (2026-09-28)
  • Core Python 3.13 unit suite with SDK [BUG]若回复过长,会导致回复消息不可见,但在!prompt指令执行后是可见的 #128 commit 661b147 source: 5,237 passed, 11 skipped.
  • Core Skill, Runner, Box, repository, maintenance, and entitlement targeted suite: 341 passed, 12 skipped. The mount-change integration test exercises the Core to SDK Runtime path with a fake nsjail backend.
  • Independent host 186 Python 3.11 environment with the same SDK revision installed: 136 targeted Core tests passed; SDK Box and SkillStore suite: 478 passed.
  • uv lock --check passed locally (offline, uv 0.12.15) and on host 186 (uv 0.12.17). Ruff checks and formatting passed. Frontend build and 189 unit tests passed; frontend lint had 0 errors and 31 warnings.

Merge readiness

Do not treat passing CI as full acceptance of #2410. Address the correctness issues above, agree the shared-session behavior and remaining issue scope, and merge the paired SDK change before coordinated deployment. At the 2026-09-29 review, GitHub reported conflicts with master; resolve them and rerun the relevant checks.

@huanghuoguoguo
huanghuoguoguo marked this pull request as ready for review September 6, 2026 13:59
@RockChinQ

Copy link
Copy Markdown
Member

@dadachann Analyze and check if this PR is okay for merging, if the design is good, fix the CI errors.

@dadachann

Copy link
Copy Markdown
Collaborator

👋 I’m dadachann, the LangBot community maintenance agent. I’ve picked up the review/task on this pull request and will share findings and next steps here.

@RockChinQ

Copy link
Copy Markdown
Member

We agree with moving centralized Skill management into Core. To complete this separation, we propose the following consistency contract for the paired Core/SDK changes. These are design requirements, not claims that the current PR already implements them.

Ownership and publication

  • Core owns the authoritative Skill registry: metadata, the current revision pointer, and immutable published package revisions. Manual installations also publish into this registry.
  • A sandbox consumes a specific published revision. Agent edits happen in a separate writable working copy; they must not mutate the published package or implicitly synchronize back to Core.
  • Agents explicitly call registration/update tools to publish their changes. Updates include base_revision; Core rejects conflicting updates instead of silently overwriting another writer's changes.
  • Publish into a temporary location, validate the complete package and compute its content digest, then atomically publish the revision and advance the current pointer. Compute the digest at publication rather than scanning all package contents on every resource read. Multiple Core writers need storage-level concurrency control, not only process-local locks.

Activation and sandbox materialization

  • Activation pins a revision for the run. Instructions, resource reads, and script execution must all use that same revision.
  • Activation remains available without Box. Materialize the pinned package when execution needs it; activation alone must not require creating a sandbox.
  • Use a read-only mount when the execution node can access the same immutable revision directory and the backend supports reliable read-only mounts.
  • Otherwise copy/download that revision into a dedicated sandbox package directory. Verify the complete package before marking it ready. Failed or partial transfers must block execution; do not silently continue with stale or mixed files.
  • Keep published packages separate from writable drafts, for example /skills/<skill-id>/<revision>/ versus /workspace/skill-drafts/<draft-id>/. A copied execution package must provide the same immutability guarantee as a read-only mount; if a backend cannot enforce this, it must not claim that guarantee.
  • New sandboxes should materialize only the Skills activated and authorized for the run, rather than every pipeline-bound Skill.

Updates and sandbox reuse

  • Publishing v2 notifies sandboxes using the Skill that a new revision is available; they may prefetch it. It must not overwrite v1 underneath an active run. Existing runs continue on v1; subsequent runs resolve v2.
  • Notifications accelerate refresh but are not the correctness mechanism. Before execution, reconcile the requested revision with the sandbox's prepared package manifest, so offline sandboxes and missed notifications are safe.
  • Sandbox reuse must account for the pinned revisions and mount set. Concurrent runs must not switch each other's package versions. Keep writable Workspace data separate from disposable execution containers, and scope dependency caches by revision and relevant environment inputs.
  • Docker cannot add bind mounts to an existing container. When its mount set changes, safely rebuild/select an execution container while preserving Workspace data; do not destroy one with active work. Remote/copy-based backends prepare a fresh verified revision directory instead of incrementally overwriting a live package.

Restart, deletion, and recovery

  • Persist the current revision pointer, publication manifests, and revision bindings for recoverable runs. After Core/Box restart, validate the sandbox manifest against those bindings or recreate the execution environment.
  • Never restore a run by Skill name alone and silently resolve the latest revision. If the pinned revision is unavailable, return an explicit recovery error.
  • Normal deletion prevents new use, while revisions referenced by active/recoverable runs remain until safe garbage collection. Emergency revocation is a separate explicit operation that rejects or terminates affected execution.

Gaps to address in the current implementation

The current stat-v1 token detects metadata changes but does not provide an immutable package snapshot. Execution mounts use mutable package paths, and the Box mount signature compares paths/modes rather than package contents. This can pair old activation instructions with newly modified scripts. The E2B best-effort upload path can also leave partial uploads or obsolete remote files. These paths need the same revision contract described above.

Minimum acceptance coverage should include: activating v1 then publishing v2 still reads/executes v1 in the existing run; a new run uses v2; interrupted transfer never executes partial content; Core-only and Box-only restarts preserve or explicitly reject the pinned revision; concurrent runs do not switch each other's versions; conflicting updates are rejected; and deleted files do not survive in a newly prepared revision.

@huanghuoguoguo
huanghuoguoguo marked this pull request as draft September 28, 2026 03:09
@huanghuoguoguo
huanghuoguoguo force-pushed the feat/2410-sdk-skill-store branch from bbde0f7 to 3987051 Compare September 28, 2026 03:12
@huanghuoguoguo
huanghuoguoguo marked this pull request as ready for review September 28, 2026 03:49
@huanghuoguoguo
huanghuoguoguo requested review from RockChinQ, TyperBody and dadachann and removed request for RockChinQ September 29, 2026 03:33
@huanghuoguoguo

Copy link
Copy Markdown
Collaborator Author

@RockChinQ 想结合当前 Agent 与 Pipeline 同级的架构,重新确认一下 #2410 的真实需求和本 PR 的设计边界。

我的判断是:Skill 从 Box 解耦的方向合理,需要调整的是公共接入层和运行状态管理,不需要推翻“没有 Box 也能使用 Skill”这个目标。

真实需求

用户安装写作规范、检查清单、Markdown 参考资料等 Skill 后,即使没有部署 Box、关闭了 Box,或没有沙盒执行权限,也应能通过已授权的 Agent / Pipeline 发现、激活和读取它。

这里应按操作划分能力:activate 加载指令,资源工具读取资料;真正执行 shell/Python、安装依赖、修改沙盒文件时才要求 Box。包含脚本的 Skill 也能先读取说明,激活本身不授予执行权限。远程 MCP 等已有工具继续按自己的权限工作,不必全部经过 Box。

Agent 与 Pipeline 应使用同一套 Skill 语义

Agent 和 Pipeline 是同级入口,Skill 应属于 Workspace 的公共资源,不应由 Pipeline 拥有。两种入口分别把自己的资源选择投影为运行时授权,再由公共层完成目录加载、授权过滤和激活。

当前 AgentRunOrchestrator.run_from_query() 会进入共同的 run(),但目录的 ensure_loaded() 只出现在 Pipeline 预处理;AgentResourceBuilder 直接读取缓存。多 Workspace 启动无法选出唯一 Workspace 时,独立 Agent 可能拿到空目录。本地检查已复现“Repository 有 Skill,冷缓存资源构建却为空”。建议在共同的 run 资源构建入口加载,而不是给 Agent 再补一条 Pipeline 专用路径。内部仍叫 _pipeline_bound_skills 的变量也需要审视其语义,但本次无需做无关的大规模重命名。

多 Agent / SaaS 的共享边界

建议明确为:共享已发布 Skill,隔离运行状态;发布新版不改写在用旧版。

  • 同一 Workspace 的多个 Agent / Pipeline 可以共享同名 Skill、同一不可变 revision;激活记录、执行状态和输出不因共享包而合并。
  • 不同 Workspace 的同名 Skill 相互隔离。同一 Workspace 重复安装同名包应明确报冲突;希望独立维护的变体使用不同名称。
  • 更新共享 Skill 是发布操作,应与使用权限区分。更新携带 base_revision,冲突时拒绝,不能最后写入者静默覆盖。
  • 固定版本的边界需要明确:恢复同一个未完成 run 应沿用原 revision;真正的新 run 默认选择当前版本。如果产品希望整段会话固定版本,也可以,但需要定义刷新/切换方式,不能把普通后续消息一律当成旧 run 恢复。当前实现会让发布 v2 后的新 query 再次 activate 仍得到 v1,解绑后还可能阻塞后续启动。

现有不可变发布和冲突检查值得保留。本地临时验证已确认:跨 Workspace 同名包隔离、重复创建拒绝、两个 Repository 并发更新只有一个成功、发布 v2 后仍可读取 v1。

自部署与 Cloud 需要补齐的部分

  1. 目录一致性。 单 Core 可以直接读本地库;多进程共享库时,当前 ensure_loaded() 不会刷新已有缓存,另一个进程新建 Skill 后旧进程仍看不到。建议先在新 run 构建资源时刷新目录,性能确有需要再引入目录版本号/失效通知。
  2. Box 执行一致性。 激活本身不创建容器,但之后的 exec 会携带当前 run 的挂载集合。Cloud 的 global session 遇到不同集合会重建,影响其他任务跨工具调用保留的进程和临时状态。等待单次 exec 结束并不等于保护整个任务;需要确认共享 Box 到底承诺保留哪些状态,再选择执行容器/版本目录方案。无 Box 的只读路径不应被这个问题绑住。
  3. 可写文件边界。 Skill 包只读,不代表脚本输出不会互相覆盖。共享 Workspace 中两个 run 都写 result.json 仍会冲突;草稿和输出应使用运行专属目录,显式需要共享时才共用。
  4. 部署契约。 当前执行挂载要求 Core/Box 在相同路径访问发布包。Cloud 多副本需要共享权威存储及有效的存储锁;跨 instance 迁移还需处理 Skill 注册信息和被引用版本,因为 namespace 包含 instance 与 Workspace。这里是代码要求,不是已经核实了线上拓扑。

另外,表单保存后未更新 revision 导致连续保存冲突,是需要直接修复的实现 Bug。图片/PDF 的受控引用是 #2410 更广的资源需求,建议单独确认是否本次交付,不应成为纯文本 Skill 脱离 Box 的前置条件。

想请你确认:本次是否以“自部署和 Cloud 中,Agent / Pipeline 均可在无 Box 时使用已授权的指令与文本资源 Skill,且不引入跨运行/租户干扰”为核心验收?版本固定按 run 还是会话?共享 Box 的状态保留契约是什么? 确认后可以据此收敛改动,避免为了只读激活一次性扩展成完整的分布式 Skill 执行系统。

检查基于 Core 3987051 / SDK 661b147。此前已运行 334 项 Core 与 478 项 SDK 测试,全部通过;挂载重建证据来自 fake backend 集成测试,缓存/隔离问题来自本地临时检查,尚未做真实 Cloud 部署验收。

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants