bensz-skill-kernel 0.14.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bensz_skill_kernel-0.14.1/LICENSE +21 -0
- bensz_skill_kernel-0.14.1/MANIFEST.in +5 -0
- bensz_skill_kernel-0.14.1/PKG-INFO +159 -0
- bensz_skill_kernel-0.14.1/README.md +119 -0
- bensz_skill_kernel-0.14.1/pyproject.toml +43 -0
- bensz_skill_kernel-0.14.1/setup.cfg +4 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/__init__.py +159 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/atomic_verifiers.py +93 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/builtins.py +114 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/cli.py +640 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/contract_packs.py +864 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/contracts.py +121 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/packs.py +215 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/runtime.py +1090 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/state_ids.py +39 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/states/active/STATE.md +42 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/states/cancelled/STATE.md +32 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/states/checking/STATE.md +45 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/states/completed/STATE.md +35 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/states/delivering/STATE.md +43 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/states/failed/STATE.md +33 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/states/index.json +16 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/states/planned/STATE.md +41 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/states/waiting/STATE.md +41 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/states/workspace-closed/STATE.md +37 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/states/workspace-ready/STATE.md +42 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/states.py +706 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifier_ids.py +34 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/artifact-file-exists/VERIFIER.md +4 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/artifact-file-exists/scripts/verify.py +24 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/citation-truth-and-fit/VERIFIER.md +6 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/contract-conformance/VERIFIER.md +3 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/contract-conformance/scripts/verify.py +5 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/diff-scope/VERIFIER.md +3 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/diff-scope/scripts/verify.py +5 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/event-integrity/VERIFIER.md +3 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/event-integrity/scripts/verify.py +5 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/evidence-provenance/VERIFIER.md +3 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/evidence-provenance/scripts/verify.py +5 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/index.json +18 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/markdown-link-integrity/VERIFIER.md +8 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/markdown-link-integrity/scripts/collector.py +261 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/markdown-link-integrity/scripts/verify.py +47 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/path-scope/VERIFIER.md +3 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/path-scope/scripts/verify.py +5 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/schema-conformance/VERIFIER.md +3 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/schema-conformance/scripts/verify.py +5 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/secret-redaction/VERIFIER.md +3 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/secret-redaction/scripts/verify.py +5 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/state-transition/VERIFIER.md +3 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/state-transition/scripts/verify.py +5 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/task-completeness/VERIFIER.md +3 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers/task-completeness/scripts/verify.py +5 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/verifiers.py +896 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel/workspace.py +269 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel.egg-info/PKG-INFO +159 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel.egg-info/SOURCES.txt +59 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel.egg-info/dependency_links.txt +1 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel.egg-info/entry_points.txt +2 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel.egg-info/requires.txt +1 -0
- bensz_skill_kernel-0.14.1/src/bensz_skill_kernel.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bensz Conan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bensz-skill-kernel
|
|
3
|
+
Version: 0.14.1
|
|
4
|
+
Summary: Append-only lifecycle runtime for Bensz Agent Skills
|
|
5
|
+
Author: Bensz Conan
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Bensz Conan
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Repository, https://github.com/huangwb8/skills
|
|
29
|
+
Classifier: Programming Language :: Python :: 3
|
|
30
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Operating System :: OS Independent
|
|
35
|
+
Requires-Python: >=3.11
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
License-File: LICENSE
|
|
38
|
+
Requires-Dist: PyYAML<7,>=6.0
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# bensz-skill-kernel
|
|
42
|
+
|
|
43
|
+
轻量的 Agent Skill 状态、工作区与 verifier 生命周期内核。
|
|
44
|
+
|
|
45
|
+
## Python 支持
|
|
46
|
+
|
|
47
|
+
- 最低支持版本:Python 3.11。
|
|
48
|
+
- 已验证测试矩阵:Python 3.11、3.12、3.13。
|
|
49
|
+
- 推荐运行版本:Python 3.12。
|
|
50
|
+
|
|
51
|
+
内核运行时除 PyYAML(用于读取 Skill 的 `config.yaml`)外仅依赖 Python 标准库;新 Python 版本会在测试矩阵验证后纳入官方支持范围。
|
|
52
|
+
|
|
53
|
+
State 与 Verifier 都采用目录化 Contract Pack:一个 Markdown 契约、索引元数据和零个或多个执行组件。`contract_packs.py` 在 `packs.py` 的发现与 JSON-stdio 边界之上统一描述并编排 `script`、`agent`、`human` 组件,绑定契约/计划/组件哈希、证据、依赖顺序、run/attempt 和执行者。State 仍由状态图/迁移适配器解释组件结果,Verifier 仍由 verdict/Gate 适配器解释组件结果,二者不会因共享执行层而混淆语义。
|
|
54
|
+
|
|
55
|
+
Verifier ID 的 canonical 命名、版本和 alias 迁移规则见仓库级 [`docs/verifier-id-naming.md`](../../docs/verifier-id-naming.md);State ID 对应规则见 [`docs/state-id-naming.md`](../../docs/state-id-naming.md)。
|
|
56
|
+
|
|
57
|
+
状态定义采用目录化协议:`states/index.json` 是 State 包目录清单和属性索引,每个元状态目录包含一个 `STATE.md`,可选附带 JSON-stdio
|
|
58
|
+
检查或演示脚本。内置状态可以通过统一命令发现;`--root` 会在内置状态之外叠加 Skill
|
|
59
|
+
状态包,而非替换它:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
bsk state list
|
|
63
|
+
bsk state describe bensz.workspace.ready
|
|
64
|
+
bsk state list --root path/to/skill/states
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Kernel 自己的八个生命周期状态均直接位于 `states/<state>/STATE.md`:`planned`、
|
|
68
|
+
`active`、`waiting`、`checking`、`delivering`、`completed`、`failed`、`cancelled`。
|
|
69
|
+
目录契约用于发现与人工审核,`runtime.py` 的事件 reducer 是可执行转移语义;测试会阻止两者漂移。
|
|
70
|
+
`states/workspace-ready/` 与 `states/workspace-closed/` 保存工作区系统状态。领域 Skill 的业务阶段仍在自身 `references/states/`,
|
|
71
|
+
不进入 kernel 生命周期目录。
|
|
72
|
+
|
|
73
|
+
一个 Skill 现在在根目录 `config.yaml` 的 `runtime` 节声明它采用哪些状态包、初始状态、可用状态
|
|
74
|
+
和 Verifier 子集;旧版 `state-machine.json` 仍可只读兼容。kernel 只校验该格式,不理解领域动作。
|
|
75
|
+
Agent 先读取状态契约,再用同一命令检查或持久化转移。旧 Pack 的单一 `entrypoint` 继续按原 JSON-stdio 协议执行;新 Pack 可在索引中声明组件计划。required 组件只有全部完成并通过时才允许对应阶段条件成立:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
bsk state list --skill-root path/to/skill
|
|
79
|
+
bsk state describe bensz.workspace.ready --skill-root path/to/skill
|
|
80
|
+
bsk state check bensz.workspace.ready org.example.skill.collecting --skill-root path/to/skill
|
|
81
|
+
bsk state transition .bensz-api/task-YYYYMMDD-HHMM-demo skill-name org.example.skill.collecting \
|
|
82
|
+
--skill-root path/to/skill --context-json '{"input":"report.md"}'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
状态操作统一返回 `bensz-meta-state-v1` JSON,包含操作、状态、结果、可选脚本回执和
|
|
86
|
+
持久化快照。每个 Skill 的当前元状态写入自身 `log/meta-state.json`;它与任务级
|
|
87
|
+
`events.ndjson` / `state.json` 分层,后者仍只记录生命周期、验证与交付事实。
|
|
88
|
+
成功的 Skill 状态转移同时以 `state.transition`(`state_domain: skill`)追加到任务事件账本,
|
|
89
|
+
`bsk rebuild` 会将其投影到 `skill_states`/`skill_state_transitions`,并核验最新元状态快照的稳定字段哈希;快照缺失时仍可据事件账本恢复,漂移则返回 `integrity_error`。
|
|
90
|
+
|
|
91
|
+
状态 `invariants` 默认是面向领域的说明;Kernel 只执行有明确协议的通用 invariant。
|
|
92
|
+
当前支持 `verifier-result-recorded`:离开声明该 invariant 的状态前,任务事件账本必须
|
|
93
|
+
同时包含 `verification.result` 和 `verification.gate`。未满足时 CLI 返回结构化
|
|
94
|
+
`rejected`,不会写入新的状态快照;领域专属 invariant 仍由 Skill helper 或人工复核负责。
|
|
95
|
+
事件带有运行身份时,`run_id` 与 `attempt_id` 必须成对传入,避免重试之间串用验证证据。
|
|
96
|
+
|
|
97
|
+
内置 State 的 ID、版本、kind、aliases、classification、tags、`mode`、`assurance_tier` 和 `components` 由 `states/index.json` 单独管理;
|
|
98
|
+
`STATE.md` 只保留 `description`、`entry_conditions`、`invariants`、`transitions` 等状态工作契约及正文说明。
|
|
99
|
+
没有 `index.json` 的外部兼容目录仍可在 `STATE.md` frontmatter 声明完整属性。可选 `entrypoint` 是相对于该 `STATE.md` 目录的脚本:stdin 接收
|
|
100
|
+
`{"protocol":"bensz-meta-state-v1","state":...,"request":...}`,stdout 只能输出一个
|
|
101
|
+
JSON 对象,其中 `verdict` 是 `pass`、`fail`、`uncertain`、`unchecked`、`error`、
|
|
102
|
+
`timed_out` 或 `skipped`,并可带 `summary`、`facts`、`evidence_refs`。只有成功执行且
|
|
103
|
+
`verdict=pass` 的 helper 会允许持久化转移。显式 `agent`/`human` 组件会先返回带契约、组件、计划和运行身份的 handoff;外部执行者按 handoff 回传标准组件结果后,`StateContractAdapter` 才会把公共结果解释为阶段是否满足。没有新组件声明的旧 instruction-only 状态仍保持兼容行为。
|
|
104
|
+
|
|
105
|
+
每个逻辑任务先初始化一个不可变的 BenszAPI 工作区;Skill 不应自行拼接路径,而应通过
|
|
106
|
+
工作区命令取得自己的 `input`、`output` 和 `log` 边界:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
bsk workspace init . --description citation-review
|
|
110
|
+
bsk workspace path .bensz-api/task-YYYYMMDD-HHMM-citation-review validate-md-ref input
|
|
111
|
+
bsk workspace status .bensz-api/task-YYYYMMDD-HHMM-citation-review
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
初始化结果中的 `bensz.workspace.ready` 是所有 Skill 状态的系统级前置状态,旧 ID `workspace.ready` 作为 alias 保留。工作区还会创建
|
|
115
|
+
共享 `shared/input|output|log` 边界;工作区清单保存协议版本和初始状态,事件账本仍由
|
|
116
|
+
下方的生命周期命令追加和重放;两者保持分层,避免把 Skill 领域状态硬编码进核心 reducer。
|
|
117
|
+
|
|
118
|
+
安装后,Skill 通过 `bsk` 发现和调用包内 `bensz_skill_kernel/verifiers/` 目录中的 verifier。`verifiers/index.json` 是 Verifier 包目录清单和执行计划的单一来源;每个 verifier 由一个 `VERIFIER.md` 契约和 `components` 组成。脚本组件通过 stdin/stdout 交换 JSON,Agent/人工组件由 Kernel 准备 handoff、由外部宿主执行并回传绑定结果:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
bsk verifier list --tag citation
|
|
122
|
+
bsk verifier describe bensz.evidence.citation-truth-fit --version 1.0.0
|
|
123
|
+
bsk verifier run bensz.document.markdown-link-integrity --input README.md
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
内置 Verifier 的 ID、版本、aliases、classification、tags、契约路径、mode、assurance 和组件由
|
|
127
|
+
`verifiers/index.json` 单独管理;`VERIFIER.md` 专注判断目标、证据边界与执行说明。
|
|
128
|
+
没有 `index.json` 的外部兼容目录仍可使用原 YAML frontmatter;旧单入口 Pack 会兼容解释为一个脚本组件,旧 instruction-only Pack 会给出缺少显式组件元数据的诊断。脚本入口遵循:stdin 一个请求 JSON,stdout
|
|
129
|
+
一个结果 JSON(`verdict` 为 `pass`、`fail`、`uncertain`、`unchecked`、`error`、
|
|
130
|
+
`timed_out` 或 `skipped`)。kernel 负责超时、异常、非法 JSON 和结果字段归一化。
|
|
131
|
+
|
|
132
|
+
当前内置示例:
|
|
133
|
+
|
|
134
|
+
- `bensz.artifact.file-existence`:确认本地产物是现有普通文件;旧 ID `artifact.file-exists` 作为 alias 保留。
|
|
135
|
+
- `bensz.document.markdown-link-integrity`:Markdown 链接和锚点完整性检查,标签 `common`、`markdown`、`links`、`deterministic`;旧 ID `markdown.link-integrity`、`markdown.references` 作为 alias 保留。
|
|
136
|
+
- `bensz.evidence.citation-truth-fit`:格式无关的引用真实性与适切性契约;索引将它显式声明为 `agent` 组件,Kernel 不捆绑模型,未回传绑定结果时保持 `unchecked`/`wait`;旧 ID `citation.truth-and-fit` 作为 alias 保留。
|
|
137
|
+
|
|
138
|
+
首批通用原子 Pack 还包括合同一致性、路径范围、Schema、diff 范围、敏感信息脱敏、证据来源、事件完整性、状态转移和任务完整性。每个 Pack 都位于 `verifiers/<slug>/`,包含可审查的 `VERIFIER.md` 与可选 `scripts/verify.py`;共享实现位于 kernel 模块层的 `atomic_verifiers.py`,不占用 Verifier 清单目录,也不再把规则主体塞进 `builtins.py`。它们只接受通用事实,不包含领域规则。`index.json` 是属性的单一来源;注册表会校验其目录、契约和入口真实存在,且没有漏列或陈旧条目。
|
|
139
|
+
|
|
140
|
+
代码只定义发现、调用、超时、结果归一化和事件记录协议,不内置领域判断流程。Markdown、LaTeX、Word 等格式适配器可各自选择适用 verifier。
|
|
141
|
+
|
|
142
|
+
需要审计时,为 `run` 增加 `--events EVENTS --run-id RUN_ID`;命令会输出统一 `results`、`gate` 和兼容的 `verification` 字段。Agent/人工组件未执行时还会在顶层输出完整 handoff,但 handoff 不进入持久化 `results`,避免把契约正文或原始上下文写入事件账本。Python Adapter 使用 `FilesystemVerifierRegistry.run_contract()` 接收外部组件提交。生命周期底层命令仍可通过 `bsk status/rebuild/append/transition/artifact/validation/delivery` 使用。
|
|
143
|
+
|
|
144
|
+
# 运行边界与审计
|
|
145
|
+
|
|
146
|
+
Pack helper 默认以受信本地进程运行,但内核会限制输入、stdout/stderr 体积、环境变量和执行时长,并在超时后终止整个进程组;调用不可信 Pack 时应显式传入 `trusted=False`,此时会 fail-closed。该边界是进程级资源与路径约束,不等同于容器或操作系统沙箱。
|
|
147
|
+
|
|
148
|
+
事件账本除状态投影外还保留可选的运行契约快照、授权链和执行审计轨迹;`reduce_events()` 仅用于离线状态投影重放,不会重新调用模型或工具。`verification-v2` 结果会在记录和完成门禁处复核组件唯一性、哈希、证据引用、run/attempt、执行者/模型及人工确认,调用方自报的 aggregate pass 不能覆盖 required 失败或漏跑。`summarize_metrics()` 额外汇总组件绑定率和执行者身份覆盖率。
|
|
149
|
+
|
|
150
|
+
## 发布到 PyPI
|
|
151
|
+
|
|
152
|
+
仓库根目录的发布助手默认只构建 wheel/sdist 并运行 `twine check`;只有显式增加 `--upload` 才会使用 Twine 的标准本机鉴权上传到正式 PyPI:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
python3 tests/publish_bsk_pypi.py
|
|
156
|
+
python3 tests/publish_bsk_pypi.py --upload
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
构建产物保存在 `tmp/bsk-pypi/`,脚本不会读取、复制或记录 PyPI 凭据。
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# bensz-skill-kernel
|
|
2
|
+
|
|
3
|
+
轻量的 Agent Skill 状态、工作区与 verifier 生命周期内核。
|
|
4
|
+
|
|
5
|
+
## Python 支持
|
|
6
|
+
|
|
7
|
+
- 最低支持版本:Python 3.11。
|
|
8
|
+
- 已验证测试矩阵:Python 3.11、3.12、3.13。
|
|
9
|
+
- 推荐运行版本:Python 3.12。
|
|
10
|
+
|
|
11
|
+
内核运行时除 PyYAML(用于读取 Skill 的 `config.yaml`)外仅依赖 Python 标准库;新 Python 版本会在测试矩阵验证后纳入官方支持范围。
|
|
12
|
+
|
|
13
|
+
State 与 Verifier 都采用目录化 Contract Pack:一个 Markdown 契约、索引元数据和零个或多个执行组件。`contract_packs.py` 在 `packs.py` 的发现与 JSON-stdio 边界之上统一描述并编排 `script`、`agent`、`human` 组件,绑定契约/计划/组件哈希、证据、依赖顺序、run/attempt 和执行者。State 仍由状态图/迁移适配器解释组件结果,Verifier 仍由 verdict/Gate 适配器解释组件结果,二者不会因共享执行层而混淆语义。
|
|
14
|
+
|
|
15
|
+
Verifier ID 的 canonical 命名、版本和 alias 迁移规则见仓库级 [`docs/verifier-id-naming.md`](../../docs/verifier-id-naming.md);State ID 对应规则见 [`docs/state-id-naming.md`](../../docs/state-id-naming.md)。
|
|
16
|
+
|
|
17
|
+
状态定义采用目录化协议:`states/index.json` 是 State 包目录清单和属性索引,每个元状态目录包含一个 `STATE.md`,可选附带 JSON-stdio
|
|
18
|
+
检查或演示脚本。内置状态可以通过统一命令发现;`--root` 会在内置状态之外叠加 Skill
|
|
19
|
+
状态包,而非替换它:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bsk state list
|
|
23
|
+
bsk state describe bensz.workspace.ready
|
|
24
|
+
bsk state list --root path/to/skill/states
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Kernel 自己的八个生命周期状态均直接位于 `states/<state>/STATE.md`:`planned`、
|
|
28
|
+
`active`、`waiting`、`checking`、`delivering`、`completed`、`failed`、`cancelled`。
|
|
29
|
+
目录契约用于发现与人工审核,`runtime.py` 的事件 reducer 是可执行转移语义;测试会阻止两者漂移。
|
|
30
|
+
`states/workspace-ready/` 与 `states/workspace-closed/` 保存工作区系统状态。领域 Skill 的业务阶段仍在自身 `references/states/`,
|
|
31
|
+
不进入 kernel 生命周期目录。
|
|
32
|
+
|
|
33
|
+
一个 Skill 现在在根目录 `config.yaml` 的 `runtime` 节声明它采用哪些状态包、初始状态、可用状态
|
|
34
|
+
和 Verifier 子集;旧版 `state-machine.json` 仍可只读兼容。kernel 只校验该格式,不理解领域动作。
|
|
35
|
+
Agent 先读取状态契约,再用同一命令检查或持久化转移。旧 Pack 的单一 `entrypoint` 继续按原 JSON-stdio 协议执行;新 Pack 可在索引中声明组件计划。required 组件只有全部完成并通过时才允许对应阶段条件成立:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bsk state list --skill-root path/to/skill
|
|
39
|
+
bsk state describe bensz.workspace.ready --skill-root path/to/skill
|
|
40
|
+
bsk state check bensz.workspace.ready org.example.skill.collecting --skill-root path/to/skill
|
|
41
|
+
bsk state transition .bensz-api/task-YYYYMMDD-HHMM-demo skill-name org.example.skill.collecting \
|
|
42
|
+
--skill-root path/to/skill --context-json '{"input":"report.md"}'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
状态操作统一返回 `bensz-meta-state-v1` JSON,包含操作、状态、结果、可选脚本回执和
|
|
46
|
+
持久化快照。每个 Skill 的当前元状态写入自身 `log/meta-state.json`;它与任务级
|
|
47
|
+
`events.ndjson` / `state.json` 分层,后者仍只记录生命周期、验证与交付事实。
|
|
48
|
+
成功的 Skill 状态转移同时以 `state.transition`(`state_domain: skill`)追加到任务事件账本,
|
|
49
|
+
`bsk rebuild` 会将其投影到 `skill_states`/`skill_state_transitions`,并核验最新元状态快照的稳定字段哈希;快照缺失时仍可据事件账本恢复,漂移则返回 `integrity_error`。
|
|
50
|
+
|
|
51
|
+
状态 `invariants` 默认是面向领域的说明;Kernel 只执行有明确协议的通用 invariant。
|
|
52
|
+
当前支持 `verifier-result-recorded`:离开声明该 invariant 的状态前,任务事件账本必须
|
|
53
|
+
同时包含 `verification.result` 和 `verification.gate`。未满足时 CLI 返回结构化
|
|
54
|
+
`rejected`,不会写入新的状态快照;领域专属 invariant 仍由 Skill helper 或人工复核负责。
|
|
55
|
+
事件带有运行身份时,`run_id` 与 `attempt_id` 必须成对传入,避免重试之间串用验证证据。
|
|
56
|
+
|
|
57
|
+
内置 State 的 ID、版本、kind、aliases、classification、tags、`mode`、`assurance_tier` 和 `components` 由 `states/index.json` 单独管理;
|
|
58
|
+
`STATE.md` 只保留 `description`、`entry_conditions`、`invariants`、`transitions` 等状态工作契约及正文说明。
|
|
59
|
+
没有 `index.json` 的外部兼容目录仍可在 `STATE.md` frontmatter 声明完整属性。可选 `entrypoint` 是相对于该 `STATE.md` 目录的脚本:stdin 接收
|
|
60
|
+
`{"protocol":"bensz-meta-state-v1","state":...,"request":...}`,stdout 只能输出一个
|
|
61
|
+
JSON 对象,其中 `verdict` 是 `pass`、`fail`、`uncertain`、`unchecked`、`error`、
|
|
62
|
+
`timed_out` 或 `skipped`,并可带 `summary`、`facts`、`evidence_refs`。只有成功执行且
|
|
63
|
+
`verdict=pass` 的 helper 会允许持久化转移。显式 `agent`/`human` 组件会先返回带契约、组件、计划和运行身份的 handoff;外部执行者按 handoff 回传标准组件结果后,`StateContractAdapter` 才会把公共结果解释为阶段是否满足。没有新组件声明的旧 instruction-only 状态仍保持兼容行为。
|
|
64
|
+
|
|
65
|
+
每个逻辑任务先初始化一个不可变的 BenszAPI 工作区;Skill 不应自行拼接路径,而应通过
|
|
66
|
+
工作区命令取得自己的 `input`、`output` 和 `log` 边界:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
bsk workspace init . --description citation-review
|
|
70
|
+
bsk workspace path .bensz-api/task-YYYYMMDD-HHMM-citation-review validate-md-ref input
|
|
71
|
+
bsk workspace status .bensz-api/task-YYYYMMDD-HHMM-citation-review
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
初始化结果中的 `bensz.workspace.ready` 是所有 Skill 状态的系统级前置状态,旧 ID `workspace.ready` 作为 alias 保留。工作区还会创建
|
|
75
|
+
共享 `shared/input|output|log` 边界;工作区清单保存协议版本和初始状态,事件账本仍由
|
|
76
|
+
下方的生命周期命令追加和重放;两者保持分层,避免把 Skill 领域状态硬编码进核心 reducer。
|
|
77
|
+
|
|
78
|
+
安装后,Skill 通过 `bsk` 发现和调用包内 `bensz_skill_kernel/verifiers/` 目录中的 verifier。`verifiers/index.json` 是 Verifier 包目录清单和执行计划的单一来源;每个 verifier 由一个 `VERIFIER.md` 契约和 `components` 组成。脚本组件通过 stdin/stdout 交换 JSON,Agent/人工组件由 Kernel 准备 handoff、由外部宿主执行并回传绑定结果:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
bsk verifier list --tag citation
|
|
82
|
+
bsk verifier describe bensz.evidence.citation-truth-fit --version 1.0.0
|
|
83
|
+
bsk verifier run bensz.document.markdown-link-integrity --input README.md
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
内置 Verifier 的 ID、版本、aliases、classification、tags、契约路径、mode、assurance 和组件由
|
|
87
|
+
`verifiers/index.json` 单独管理;`VERIFIER.md` 专注判断目标、证据边界与执行说明。
|
|
88
|
+
没有 `index.json` 的外部兼容目录仍可使用原 YAML frontmatter;旧单入口 Pack 会兼容解释为一个脚本组件,旧 instruction-only Pack 会给出缺少显式组件元数据的诊断。脚本入口遵循:stdin 一个请求 JSON,stdout
|
|
89
|
+
一个结果 JSON(`verdict` 为 `pass`、`fail`、`uncertain`、`unchecked`、`error`、
|
|
90
|
+
`timed_out` 或 `skipped`)。kernel 负责超时、异常、非法 JSON 和结果字段归一化。
|
|
91
|
+
|
|
92
|
+
当前内置示例:
|
|
93
|
+
|
|
94
|
+
- `bensz.artifact.file-existence`:确认本地产物是现有普通文件;旧 ID `artifact.file-exists` 作为 alias 保留。
|
|
95
|
+
- `bensz.document.markdown-link-integrity`:Markdown 链接和锚点完整性检查,标签 `common`、`markdown`、`links`、`deterministic`;旧 ID `markdown.link-integrity`、`markdown.references` 作为 alias 保留。
|
|
96
|
+
- `bensz.evidence.citation-truth-fit`:格式无关的引用真实性与适切性契约;索引将它显式声明为 `agent` 组件,Kernel 不捆绑模型,未回传绑定结果时保持 `unchecked`/`wait`;旧 ID `citation.truth-and-fit` 作为 alias 保留。
|
|
97
|
+
|
|
98
|
+
首批通用原子 Pack 还包括合同一致性、路径范围、Schema、diff 范围、敏感信息脱敏、证据来源、事件完整性、状态转移和任务完整性。每个 Pack 都位于 `verifiers/<slug>/`,包含可审查的 `VERIFIER.md` 与可选 `scripts/verify.py`;共享实现位于 kernel 模块层的 `atomic_verifiers.py`,不占用 Verifier 清单目录,也不再把规则主体塞进 `builtins.py`。它们只接受通用事实,不包含领域规则。`index.json` 是属性的单一来源;注册表会校验其目录、契约和入口真实存在,且没有漏列或陈旧条目。
|
|
99
|
+
|
|
100
|
+
代码只定义发现、调用、超时、结果归一化和事件记录协议,不内置领域判断流程。Markdown、LaTeX、Word 等格式适配器可各自选择适用 verifier。
|
|
101
|
+
|
|
102
|
+
需要审计时,为 `run` 增加 `--events EVENTS --run-id RUN_ID`;命令会输出统一 `results`、`gate` 和兼容的 `verification` 字段。Agent/人工组件未执行时还会在顶层输出完整 handoff,但 handoff 不进入持久化 `results`,避免把契约正文或原始上下文写入事件账本。Python Adapter 使用 `FilesystemVerifierRegistry.run_contract()` 接收外部组件提交。生命周期底层命令仍可通过 `bsk status/rebuild/append/transition/artifact/validation/delivery` 使用。
|
|
103
|
+
|
|
104
|
+
# 运行边界与审计
|
|
105
|
+
|
|
106
|
+
Pack helper 默认以受信本地进程运行,但内核会限制输入、stdout/stderr 体积、环境变量和执行时长,并在超时后终止整个进程组;调用不可信 Pack 时应显式传入 `trusted=False`,此时会 fail-closed。该边界是进程级资源与路径约束,不等同于容器或操作系统沙箱。
|
|
107
|
+
|
|
108
|
+
事件账本除状态投影外还保留可选的运行契约快照、授权链和执行审计轨迹;`reduce_events()` 仅用于离线状态投影重放,不会重新调用模型或工具。`verification-v2` 结果会在记录和完成门禁处复核组件唯一性、哈希、证据引用、run/attempt、执行者/模型及人工确认,调用方自报的 aggregate pass 不能覆盖 required 失败或漏跑。`summarize_metrics()` 额外汇总组件绑定率和执行者身份覆盖率。
|
|
109
|
+
|
|
110
|
+
## 发布到 PyPI
|
|
111
|
+
|
|
112
|
+
仓库根目录的发布助手默认只构建 wheel/sdist 并运行 `twine check`;只有显式增加 `--upload` 才会使用 Twine 的标准本机鉴权上传到正式 PyPI:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
python3 tests/publish_bsk_pypi.py
|
|
116
|
+
python3 tests/publish_bsk_pypi.py --upload
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
构建产物保存在 `tmp/bsk-pypi/`,脚本不会读取、复制或记录 PyPI 凭据。
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "bensz-skill-kernel"
|
|
7
|
+
version = "0.14.1"
|
|
8
|
+
description = "Append-only lifecycle runtime for Bensz Agent Skills"
|
|
9
|
+
readme = {file = "README.md", content-type = "text/markdown"}
|
|
10
|
+
license = {file = "LICENSE"}
|
|
11
|
+
authors = [{name = "Bensz Conan"}]
|
|
12
|
+
requires-python = ">=3.11"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.11",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"PyYAML>=6.0,<7",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
bsk = "bensz_skill_kernel.runtime:main"
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Repository = "https://github.com/huangwb8/skills"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
33
|
+
include = ["bensz_skill_kernel*"]
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.package-data]
|
|
36
|
+
bensz_skill_kernel = ["states/*", "states/**/*", "verifiers/*", "verifiers/**/*"]
|
|
37
|
+
|
|
38
|
+
[tool.setuptools.exclude-package-data]
|
|
39
|
+
"*" = ["__pycache__/*", "*.pyc", "**/__pycache__/*", "**/*.pyc"]
|
|
40
|
+
|
|
41
|
+
[tool.pytest.ini_options]
|
|
42
|
+
testpaths = ["tests"]
|
|
43
|
+
pythonpath = ["src"]
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Minimal, append-only runtime kernel for Agent Skill task lifecycles."""
|
|
2
|
+
|
|
3
|
+
from .runtime import (
|
|
4
|
+
CompletionError,
|
|
5
|
+
EventEnvelope,
|
|
6
|
+
EventLog,
|
|
7
|
+
IntegrityError,
|
|
8
|
+
IdempotencyConflict,
|
|
9
|
+
AuthorizationError,
|
|
10
|
+
InvalidTransition,
|
|
11
|
+
KernelError,
|
|
12
|
+
VALID_STATES,
|
|
13
|
+
reduce_events,
|
|
14
|
+
)
|
|
15
|
+
from .contracts import Artifact, Authorization, Contract, Effect, Requirement, Subject, RUNTIME_PROTOCOL_VERSION
|
|
16
|
+
from .contract_packs import (
|
|
17
|
+
COMPONENT_RESULT_PROTOCOL,
|
|
18
|
+
CONTRACT_EXECUTION_PROTOCOL,
|
|
19
|
+
ComponentHandoff,
|
|
20
|
+
ComponentResult,
|
|
21
|
+
ContractBindingError,
|
|
22
|
+
ContractComponent,
|
|
23
|
+
ContractExecutionError,
|
|
24
|
+
ContractExecutionReport,
|
|
25
|
+
ContractPack,
|
|
26
|
+
ContractPackExecutor,
|
|
27
|
+
)
|
|
28
|
+
from .verifiers import (
|
|
29
|
+
Evidence,
|
|
30
|
+
CombinedVerifierRegistry,
|
|
31
|
+
FilesystemVerifierRegistry,
|
|
32
|
+
GateDecision,
|
|
33
|
+
PackRegistry,
|
|
34
|
+
VerifierPack,
|
|
35
|
+
VerifierRunner,
|
|
36
|
+
VerifierSpec,
|
|
37
|
+
VerifierDefinition,
|
|
38
|
+
VerifierRegistry,
|
|
39
|
+
VerificationRequest,
|
|
40
|
+
VerificationResult,
|
|
41
|
+
VerifierContractAdapter,
|
|
42
|
+
VerifierContractExecution,
|
|
43
|
+
apply_gate,
|
|
44
|
+
builtin_verifier_root,
|
|
45
|
+
normalize_result,
|
|
46
|
+
snapshot_evidence,
|
|
47
|
+
summarize_metrics,
|
|
48
|
+
normalize_requirements,
|
|
49
|
+
)
|
|
50
|
+
from .builtins import CITATION_TRUTH_FIT_SPEC, FILE_SPEC, build_builtin_registry, collect_markdown
|
|
51
|
+
from .verifier_ids import validate_verifier_id
|
|
52
|
+
from .state_ids import validate_state_id
|
|
53
|
+
from .states import CombinedStateRegistry, FilesystemStateRegistry, META_STATE_PROTOCOL_VERSION, SKILL_STATE_DECLARATION_VERSION, SkillStateDeclaration, StateContractAdapter, StateDefinition, StateDefinitionError, StateExecutionError, StateExecutionResult, StateTransitionError, StateMachine, build_builtin_state_registry, build_state_registry, check_state_invariants, execute_state
|
|
54
|
+
from .workspace import META_STATE_SNAPSHOT_VERSION, TaskWorkspace, WorkspaceError, WorkspacePaths, WORKSPACE_KINDS, WORKSPACE_PROTOCOL_VERSION, state_snapshot_hash, workspace_path
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
"CompletionError",
|
|
58
|
+
"EventEnvelope",
|
|
59
|
+
"EventLog",
|
|
60
|
+
"IntegrityError",
|
|
61
|
+
"IdempotencyConflict",
|
|
62
|
+
"AuthorizationError",
|
|
63
|
+
"InvalidTransition",
|
|
64
|
+
"KernelError",
|
|
65
|
+
"VALID_STATES",
|
|
66
|
+
"reduce_events",
|
|
67
|
+
"Artifact",
|
|
68
|
+
"Authorization",
|
|
69
|
+
"Contract",
|
|
70
|
+
"Effect",
|
|
71
|
+
"Requirement",
|
|
72
|
+
"Subject",
|
|
73
|
+
"RUNTIME_PROTOCOL_VERSION",
|
|
74
|
+
"COMPONENT_RESULT_PROTOCOL",
|
|
75
|
+
"CONTRACT_EXECUTION_PROTOCOL",
|
|
76
|
+
"ComponentHandoff",
|
|
77
|
+
"ComponentResult",
|
|
78
|
+
"ContractBindingError",
|
|
79
|
+
"ContractComponent",
|
|
80
|
+
"ContractExecutionError",
|
|
81
|
+
"ContractExecutionReport",
|
|
82
|
+
"ContractPack",
|
|
83
|
+
"ContractPackExecutor",
|
|
84
|
+
"Evidence",
|
|
85
|
+
"CombinedVerifierRegistry",
|
|
86
|
+
"FilesystemVerifierRegistry",
|
|
87
|
+
"VerifierRegistry",
|
|
88
|
+
"GateDecision",
|
|
89
|
+
"PackRegistry",
|
|
90
|
+
"VerifierPack",
|
|
91
|
+
"VerifierRunner",
|
|
92
|
+
"VerifierSpec",
|
|
93
|
+
"VerifierDefinition",
|
|
94
|
+
"VerificationRequest",
|
|
95
|
+
"VerificationResult",
|
|
96
|
+
"VerifierContractAdapter",
|
|
97
|
+
"VerifierContractExecution",
|
|
98
|
+
"apply_gate",
|
|
99
|
+
"builtin_verifier_root",
|
|
100
|
+
"normalize_result",
|
|
101
|
+
"snapshot_evidence",
|
|
102
|
+
"summarize_metrics",
|
|
103
|
+
"normalize_requirements",
|
|
104
|
+
"CITATION_TRUTH_FIT_SPEC",
|
|
105
|
+
"FILE_SPEC",
|
|
106
|
+
"build_builtin_registry",
|
|
107
|
+
"collect_markdown",
|
|
108
|
+
"validate_verifier_id",
|
|
109
|
+
"validate_state_id",
|
|
110
|
+
"FilesystemStateRegistry",
|
|
111
|
+
"CombinedStateRegistry",
|
|
112
|
+
"META_STATE_PROTOCOL_VERSION",
|
|
113
|
+
"SKILL_STATE_DECLARATION_VERSION",
|
|
114
|
+
"SkillStateDeclaration",
|
|
115
|
+
"StateDefinition",
|
|
116
|
+
"StateContractAdapter",
|
|
117
|
+
"StateDefinitionError",
|
|
118
|
+
"StateExecutionError",
|
|
119
|
+
"StateExecutionResult",
|
|
120
|
+
"StateTransitionError",
|
|
121
|
+
"StateMachine",
|
|
122
|
+
"build_builtin_state_registry",
|
|
123
|
+
"build_state_registry",
|
|
124
|
+
"execute_state",
|
|
125
|
+
"check_state_invariants",
|
|
126
|
+
"TaskWorkspace",
|
|
127
|
+
"WorkspaceError",
|
|
128
|
+
"WorkspacePaths",
|
|
129
|
+
"WORKSPACE_KINDS",
|
|
130
|
+
"WORKSPACE_PROTOCOL_VERSION",
|
|
131
|
+
"META_STATE_SNAPSHOT_VERSION",
|
|
132
|
+
"workspace_path",
|
|
133
|
+
"state_snapshot_hash",
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
def _distribution_version() -> str:
|
|
137
|
+
"""Resolve the runtime version from the source metadata or installed wheel."""
|
|
138
|
+
from pathlib import Path
|
|
139
|
+
import importlib.metadata
|
|
140
|
+
import re
|
|
141
|
+
|
|
142
|
+
# During source-tree execution package metadata may describe an older
|
|
143
|
+
# globally installed wheel. Prefer the adjacent pyproject as the source
|
|
144
|
+
# of truth in that case.
|
|
145
|
+
pyproject = Path(__file__).resolve().parents[2] / "pyproject.toml"
|
|
146
|
+
try:
|
|
147
|
+
text = pyproject.read_text(encoding="utf-8")
|
|
148
|
+
match = re.search(r"(?m)^version\s*=\s*['\"]([^'\"]+)['\"]", text)
|
|
149
|
+
if match:
|
|
150
|
+
return match.group(1)
|
|
151
|
+
except OSError:
|
|
152
|
+
pass
|
|
153
|
+
try:
|
|
154
|
+
return importlib.metadata.version("bensz-skill-kernel")
|
|
155
|
+
except importlib.metadata.PackageNotFoundError:
|
|
156
|
+
return "0.0.0"
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
__version__ = _distribution_version()
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Implementations for the small, domain-neutral atomic verifier packs.
|
|
2
|
+
|
|
3
|
+
This module lives beside the directory contracts so reviewers can inspect the
|
|
4
|
+
rules independently from the registry and runner plumbing. Each directory's
|
|
5
|
+
``scripts/verify.py`` is a thin JSON-stdio adapter around ``run_atomic``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Mapping
|
|
14
|
+
|
|
15
|
+
from bensz_skill_kernel.runtime import ALLOWED_TRANSITIONS, EventLog
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _parts(request: Any) -> tuple[Mapping[str, Any], Mapping[str, Any]]:
|
|
19
|
+
if hasattr(request, "subject"):
|
|
20
|
+
return request.subject, request.context
|
|
21
|
+
return request.get("subject") or {}, request.get("context") or {}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _evidence_map(request: Any, evidence: Mapping[str, Any] | None) -> Mapping[str, Any]:
|
|
25
|
+
if evidence:
|
|
26
|
+
return evidence
|
|
27
|
+
if hasattr(request, "evidence"):
|
|
28
|
+
return {item.ref: item for item in request.evidence}
|
|
29
|
+
raw = request.get("evidence") or []
|
|
30
|
+
return {str(item.get("ref", index)): item for index, item in enumerate(raw) if isinstance(item, Mapping)}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def run_atomic(name: str, request: Any, evidence: Mapping[str, Any] | None = None) -> Mapping[str, Any]:
|
|
34
|
+
subject, context = _parts(request)
|
|
35
|
+
evidence = _evidence_map(request, evidence)
|
|
36
|
+
if name == "contract-conformance":
|
|
37
|
+
required = tuple(context.get("required_fields", ()))
|
|
38
|
+
missing = [field for field in required if field not in subject]
|
|
39
|
+
return _result(not missing, {"required_fields": list(required), "missing": missing}, "missing-field", missing)
|
|
40
|
+
if name == "path-scope":
|
|
41
|
+
raw_paths = subject.get("paths") or ([subject["path"]] if subject.get("path") else [])
|
|
42
|
+
allowed = [Path(item).expanduser().resolve() for item in context.get("allowed_paths", ())]
|
|
43
|
+
violations = []
|
|
44
|
+
for raw in raw_paths:
|
|
45
|
+
target = Path(raw).expanduser().resolve()
|
|
46
|
+
# Scope is lexical after ``resolve()`` and must not depend on an
|
|
47
|
+
# allowed directory already existing. Requiring ``is_dir()``
|
|
48
|
+
# made valid output paths fail before their parent was created.
|
|
49
|
+
if not any(target == root or root in target.parents for root in allowed):
|
|
50
|
+
violations.append(str(target))
|
|
51
|
+
return _result(not violations, {"paths": list(raw_paths), "violations": violations}, "path-out-of-scope", violations)
|
|
52
|
+
if name == "schema-conformance":
|
|
53
|
+
data = subject.get("data", subject)
|
|
54
|
+
schema = context.get("schema", {})
|
|
55
|
+
required = schema.get("required", ()) if isinstance(schema, Mapping) else ()
|
|
56
|
+
missing = [key for key in required if not isinstance(data, Mapping) or key not in data]
|
|
57
|
+
return _result(not missing, {"missing": missing}, "schema-required-field", missing)
|
|
58
|
+
if name == "diff-scope":
|
|
59
|
+
changed = set(subject.get("changed_paths", ()))
|
|
60
|
+
violations = sorted(changed - set(context.get("allowed_paths", ())))
|
|
61
|
+
return _result(not violations, {"changed": sorted(changed), "violations": violations}, "unexpected-change", violations)
|
|
62
|
+
if name == "secret-redaction":
|
|
63
|
+
raw = json.dumps(subject, ensure_ascii=False, default=str)
|
|
64
|
+
patterns = (r"[\"']?(?:api[_-]?key|token|password|cookie)[\"']?\s*[:=]\s*[\"']?[^,\s}\"']+", r"sk-[A-Za-z0-9_-]{8,}")
|
|
65
|
+
matches = [pattern for pattern in patterns if re.search(pattern, raw, re.IGNORECASE)]
|
|
66
|
+
return {"verdict": "fail" if matches else "pass", "facts": {"matched_patterns": len(matches)}, "findings": [{"id": "secret-detected", "verdict": "fail"}] if matches else []}
|
|
67
|
+
if name == "evidence-provenance":
|
|
68
|
+
def value(item: Any, key: str) -> Any:
|
|
69
|
+
return item.get(key) if isinstance(item, Mapping) else getattr(item, key, None)
|
|
70
|
+
invalid = [ref for ref, item in evidence.items() if not value(item, "source_type") or not value(item, "content_hash") or not value(item, "collected_at")]
|
|
71
|
+
return _result(not invalid, {"evidence_count": len(evidence)}, "missing-provenance", invalid)
|
|
72
|
+
if name == "event-integrity":
|
|
73
|
+
path = subject.get("path")
|
|
74
|
+
if not path:
|
|
75
|
+
return _result(False, {}, "missing-events-path", ["events"])
|
|
76
|
+
try:
|
|
77
|
+
count = len(EventLog(path).read())
|
|
78
|
+
except Exception as exc:
|
|
79
|
+
return {"verdict": "fail", "uncertainty_reason": str(exc), "findings": [{"id": "event-integrity", "verdict": "fail"}]}
|
|
80
|
+
return {"verdict": "pass", "facts": {"event_count": count}, "findings": []}
|
|
81
|
+
if name == "state-transition":
|
|
82
|
+
current, target = subject.get("current_state"), subject.get("target_state")
|
|
83
|
+
allowed = target in ALLOWED_TRANSITIONS.get(current, ())
|
|
84
|
+
return _result(allowed, {"current_state": current, "target_state": target}, "illegal-transition", [target] if not allowed else [])
|
|
85
|
+
if name == "task-completeness":
|
|
86
|
+
required = tuple(context.get("required_fields", ("artifacts", "verifications", "delivery_report")))
|
|
87
|
+
missing = [key for key in required if not subject.get(key)]
|
|
88
|
+
return _result(not missing, {"missing": missing}, "task-incomplete", missing)
|
|
89
|
+
raise ValueError(f"unknown atomic verifier: {name}")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _result(ok: bool, facts: Mapping[str, Any], finding_id: str, values: list[Any]) -> Mapping[str, Any]:
|
|
93
|
+
return {"verdict": "pass" if ok else "fail", "facts": dict(facts), "findings": [{"id": finding_id, "value": value, "verdict": "fail"} for value in values]}
|