feat(plugins): add managed runtime SDK and release 2.4.5
This commit is contained in:
1 parent
506e3bf1c5
commit
2a3bbaa3fb
45 files changed
+3348
-98
No files matched your search
@@ -0,0 +1,121 @@
|
||||
# 插件运行辅助接口与市场检查
|
||||
|
||||
这些接口从 ARTEMIS **2.4.5** 开始提供,位于 `artemis.plugins.sdk`;使用 ARTEMIS 2.4.5 或更新版本的 Python,并在插件清单中声明 `artemis_version: ">=2.4.5,<3.0.0"`。它们不属于远程薄客户端 `artemis-client`,不启动后台、模型或第二套任务队列。现有不依赖这些辅助接口的插件仍按自身清单兼容旧宿主;采用这些接口才需要提高最低版本。
|
||||
|
||||
## 已批准文件必须绑定实际使用的字节
|
||||
|
||||
```python
|
||||
from artemis.plugins.sdk import snapshot_file
|
||||
|
||||
snapshot = snapshot_file(authorized_path, approved_sha256)
|
||||
# 此后只消费快照。连接建立、预检或等待期间原文件发生变化,也不会改变快照。
|
||||
for chunk in snapshot.chunks():
|
||||
transport.sendall(chunk)
|
||||
```
|
||||
|
||||
`approved_sha256` 必须来自已批准的内容身份,不能在执行时把新文件的摘要自动当作新的批准。制作快照、验证设备身份和写入授权都应在真正写操作前完成。使用 `.data` 或只读的 `.chunks()`,不要校验之后重新打开原路径;传输后才发现摘要不符不能撤销已经发生的写入。
|
||||
|
||||
`snapshot_file` 一次读取授权的普通文件并核对 SHA256,拒绝链接、特殊文件、大小超限和摘要不符。默认上限 64 MiB,副本是不可变内存 bytes;参数 `max_bytes` 仅作用于本次调用,选择更大上限须评估内存。它不负责用户授权或路径沙箱,插件仍要限制允许读取的范围。大文件如采用磁盘快照或流式方案,应提供等价的防修改保证并另行验证,不能恢复到“检查后重开路径”。
|
||||
|
||||
`VerifiedFile.size`、`.sha256` 对应快照本身。原路径同长度覆盖、替换、增长或截断后,快照仍保持原内容。对写入端到端的回归必须检查实际发送字节,不能只断言预检函数被调用。
|
||||
|
||||
## 外部工具的生命周期
|
||||
|
||||
```python
|
||||
from artemis.plugins.sdk import ManagedProcessError, run_process
|
||||
|
||||
try:
|
||||
result = await run_process(
|
||||
fixed_argv,
|
||||
cwd=authorized_directory,
|
||||
timeout=20,
|
||||
input_data=snapshot.data,
|
||||
)
|
||||
except ManagedProcessError as exc:
|
||||
# 用稳定的 error_code 返回部分执行/未知;不回显命令或原始敏感输出,不自动重试。
|
||||
...
|
||||
```
|
||||
|
||||
`fixed_argv` 必须是业务层构造的固定参数列表,不能向模型暴露任意 shell;`cwd` 为明确的现有目录。可通过 `env` 提供所需的环境白名单;未提供时继承当前插件环境,插件应自行排除不需要的密钥和工具日志变量。宿主的凭据管理不会自动证明外部程序的输出安全。
|
||||
|
||||
`timeout` 必须为有限正数,作用于本次工具运行;标准输入最多 64 MiB。stdout、stderr 分别返回 bytes,共用默认 4 MiB 上限,可通过本次调用的 `max_output_bytes` 调整。输出超限立即停止;部分输出不作有效结果返回。`ProcessResult.returncode` 保留非零退出码,不据此生成业务用例通过结论。
|
||||
|
||||
运行器在 Windows 先将等待启动的引导进程加入不可继承的 Job Object,再允许启动实际工具,防止快速派生后代漏出归属;父进程退出时 Job Object 同样回收其树。POSIX 使用独立会话,正常退出、超时、取消和输出超限均回收所属进程组。它是生命周期辅助,不是恶意程序沙箱,不能承诺回收主动逃离会话的程序。
|
||||
|
||||
调用结束后不保留后台子进程。需要常驻驱动的插件应实现会话级归属、关闭与验证,不借本接口后台化工具。取消在清理后继续传播,不能吞掉 `CancelledError`。清理另有固定的有界等待;未确认完成时抛出 `process_cleanup_unconfirmed`,插件必须返回未知状态并核对资源,不声明已全部停止。
|
||||
|
||||
常见错误码:`process_timeout`、`process_output_limit`、`process_launch_failed`、`process_ownership_failed`、`process_worker_interrupted`、`process_invalid_status`、`process_cleanup_unconfirmed`。`may_have_started=false` 只表示已知工具尚未启动;其余情况不能授权重发写操作。运行器从不重试。原始输出可能包含密钥,返回给模型前按业务字段白名单处理。
|
||||
|
||||
宿主负责整体任务与 MCP 调用预算,插件负责内部操作及其后代。内部超时先发生时,必须在向宿主返回前收尾;宿主未超时、或只关闭 MCP 连接,都不能替代这一步。
|
||||
|
||||
## 可运行回归
|
||||
|
||||
```sh
|
||||
<ARTEMIS_PYTHON> -B -m artemis.plugins.sdk_checks --json-output sdk-faults.json
|
||||
<ARTEMIS_PYTHON> -B <受管工具模板目录>/test_contract.py
|
||||
```
|
||||
|
||||
模板目录在源码仓库的 `examples/plugins/managed-tool-demo`,或本 Skill 的 `assets/managed-tool-demo`;先复制到用户指定开发目录。以下检查均使用 ARTEMIS 2.4.5 或更新版本的 Python。
|
||||
|
||||
第一条只在临时目录创建合成文件和自己的测试进程,覆盖文件同长度覆盖、截断/增长、错误摘要、二进制 stdin、非零退出码、正常结束/超时/取消后的后代回收、输出超限、附件摘要与路径拒绝,以及部分/未知执行不能授权重试。它不连接设备、不调用模型、不修改现有任务或配置。结果明确标为 `sdk_faults_only`。
|
||||
|
||||
第二条额外通过真实 stdio MCP 调用模板工具,核对有效输入、错误摘要、目录穿越拒绝及宿主附件回读,结果标为 `template_offline_contract`。模板见插件开发文档的受管工具示例。
|
||||
|
||||
改写插件后,必须扩展模板测试到自己的实际入口:在最后一次预检与真正消费文件之间替换输入、让实际工具创建后代再超时/取消,并检查实际传输和进程存活。SDK 自测通过不能证明插件调用正确。还需覆盖错误/部分执行、证据丢失或摘要不符、权限、资源互斥以及真实任务时间线。
|
||||
|
||||
## 开发检查与市场材料检查
|
||||
|
||||
统一入口为仓库/Skill 的 `check_plugin.py`,也可执行 `python -m artemis.plugins.validation`。两处脚本调用同一个实现,不单独维护规则。
|
||||
|
||||
```sh
|
||||
# 日常本地开发,无需公开分发许可;--live 只握手,不派发工具。
|
||||
<ARTEMIS_PYTHON> <检查脚本> <插件目录或ZIP> --live --values '{"python":"ARTEMIS解释器绝对路径"}'
|
||||
|
||||
# 在清洁候选目录生成草稿;所有结论未验证、许可未知,不覆盖已有 MARKET.json。
|
||||
<ARTEMIS_PYTHON> <检查脚本> <插件目录> --prepare-market
|
||||
|
||||
# 对最终候选 ZIP 检查市场材料;包含只读 MCP 声明握手,不调用业务工具。
|
||||
<ARTEMIS_PYTHON> <检查脚本> <最终ZIP> --market --values '{"python":"ARTEMIS解释器绝对路径"}' --json-output market-check.json
|
||||
```
|
||||
|
||||
路径与 JSON 按当前 Shell 引用;密钥不放进 `--values`。`--json-output` 必须在候选目录外,不能覆盖输入 ZIP。ZIP 沿用宿主的安全解压和大小限制,检查不向已有插件库安装。`--market` 先核对材料,再握手,普通开发检查仍保持轻量。
|
||||
|
||||
市场检查拒绝缓存、`.pyc`、虚拟环境、版本库目录、个人 `.env` 配置、链接和冲突路径。环境样例可保留 `.env.example`。不要为通过检查而清理用户运行目录;从可分发输入构建独立候选。
|
||||
|
||||
## MARKET.json
|
||||
|
||||
此文件随包提交,版本固定为 1,不增加 `plugin.json` 字段,旧宿主仍可安装符合其能力的插件。草稿由检查脚本生成,包含:
|
||||
|
||||
| 字段 | 要求 |
|
||||
| --- | --- |
|
||||
| `plugin_id`、`plugin_version` | 与候选清单完全一致 |
|
||||
| `content_sha256` | 全部包内文件路径及 SHA256 的确定性摘要,只排除根层 `MARKET.json` 自身 |
|
||||
| `traits.uses_subprocesses` | 明确 true/false,不能保留 null |
|
||||
| `traits.file_inputs_for_writes` | 是否消费文件执行写操作,明确 true/false |
|
||||
| `components` | 分开列原始实现、适配层、第三方依赖/参考资料的许可和来源 |
|
||||
| `checks` | 下表全部验收项目,保留实际范围和证据 |
|
||||
|
||||
每个组件包含 `name`、`paths`、`license`、`permission`、`license_file`、`source_file`。`paths` 为精确包内相对路径或目录前缀,`.` 表示整个包;全部可分发文件须被覆盖。只有明确的 `permission: "permitted"` 才接受,`local_only` 和 `unknown` 均阻止市场提交。许可和来源文件必须实际在包内;适配层开源许可不会重新许可原始代码、用例、协议或内部资料。可使用合适的 LicenseRef,不强制统一开源,但具体分发授权必须可审查。
|
||||
|
||||
每条验收记录包含 `status`、`evidence_file`、`evidence_sha256`、`notes`。`passed` 必须指向真实的非空包内文档及其正确摘要,notes 说明验证环境、范围与结果;缺失、失败和未验证均拒绝。不能把历史记录、SDK 自测或模拟设备改写为本次真实业务通过。
|
||||
|
||||
| 验收项目 | 要记录的内容 |
|
||||
| --- | --- |
|
||||
| `installation` | 安装、升级、平台和依赖兼容 |
|
||||
| `tool_contract` | 实际 MCP Schema、能力和参数限制 |
|
||||
| `failure_partial` | 错误、部分执行、未知副作用与不盲目重试 |
|
||||
| `timeout_cancel` | 实际工具入口超时与取消 |
|
||||
| `process_tree` | 工具创建后代后,正常/超时/取消的回收验证 |
|
||||
| `file_binding` | 已批准文件被替换、截断、增长时的实际消费字节 |
|
||||
| `resource_conflicts` | 同设备/桌面资源互斥 |
|
||||
| `evidence_export` | 导出索引、中文路径、摘要不符与缺失附件 |
|
||||
| `history_uninstall` | 卸载/升级后历史仍可读 |
|
||||
| `secret_redaction` | 配置、命令、输出和回执脱敏 |
|
||||
| `real_task` | 经宿主正常任务的操作、时间线、暂停/恢复、证据和报告 |
|
||||
| `source_review` | 对本次包的源码审查、来源与分发授权核对 |
|
||||
|
||||
仅在声明无相应行为时,`process_tree`、`file_binding` 可 `not_applicable`;无 MCP 时 `tool_contract`、`timeout_cancel` 可不适用;无声明资源时 `resource_conflicts` 可不适用。仍须在 notes 中给出理由,不能把未测写成不适用。其他项目须有真实通过记录。
|
||||
|
||||
更新任何文件后,旧材料会失效。先重跑受影响验证,再用 `--fingerprint` 查看新的内容摘要,并更新记录;不要只刷新摘要来沿用过期结论。握手结束后重新核对内容与市场记录,检查期间改动会使检查失败。最终 ZIP 检查结果另包含实际解压检查的同一份字节的 SHA256,不重新打开原路径来记录替换文件;公开发布时仍需核对固定附件、市场索引与无认证下载字节。
|
||||
|
||||
市场材料检查验证字段、覆盖范围、摘要和证据存在性,不证明声明真实、不替作者确认权利归属,也不自动发布。源码审查和实际业务验收仍由发布方核对;文档、静态检查、握手、SDK 故障自测与真实业务结论分别记录。
|
||||
Reference in new issue
Block a user