petfishframework 0.2.5__tar.gz → 0.3.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.
- petfishframework-0.3.1/.sisyphus/plans/v0.3.0-plan.md +340 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/CHANGELOG.md +11 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/PKG-INFO +37 -6
- {petfishframework-0.2.5 → petfishframework-0.3.1}/README.md +35 -5
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/api.md +83 -1
- petfishframework-0.3.1/docs/feedbacks/petfishframework_v0_3_0_review.md +879 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/pyproject.toml +4 -2
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/__init__.py +4 -1
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/core/agent.py +2 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/core/environment.py +33 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/core/session.py +3 -0
- petfishframework-0.3.1/src/petfishframework/credentials/__init__.py +7 -0
- petfishframework-0.3.1/src/petfishframework/credentials/broker.py +70 -0
- petfishframework-0.3.1/src/petfishframework/credentials/token.py +37 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/permissions/model.py +2 -0
- petfishframework-0.3.1/src/petfishframework/policies/__init__.py +12 -0
- petfishframework-0.3.1/src/petfishframework/policies/conditions.py +176 -0
- petfishframework-0.3.1/src/petfishframework/policies/engine.py +130 -0
- petfishframework-0.3.1/src/petfishframework/policies/rule.py +32 -0
- petfishframework-0.3.1/src/petfishframework/policies/test_runner.py +167 -0
- petfishframework-0.3.1/tests/policies/enterprise-expense.tests.yaml +45 -0
- petfishframework-0.3.1/tests/test_credential_broker.py +81 -0
- petfishframework-0.3.1/tests/test_credential_integration.py +107 -0
- petfishframework-0.3.1/tests/test_yaml_policy.py +325 -0
- petfishframework-0.3.1/tests/test_yaml_policy_suite.py +81 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/uv.lock +69 -1
- {petfishframework-0.2.5 → petfishframework-0.3.1}/.env.example +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/.github/workflows/ci.yml +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/.github/workflows/publish.yml +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/.gitignore +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/.sisyphus/plans/v0.1.6-plan.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/.sisyphus/plans/v0.2.0-plan.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/AGENTS.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/AGENTS.md.new +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/CONTRIBUTING.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/LICENSE +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/SECURITY.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/conftest.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/architecture.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/benchmark-results.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/development-plan.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/development.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/enterprise-expense-demo.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/feedbacks/archives/petfishframework_0_1_4_playground_recommendations.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/feedbacks/archives/petfishframework_playground_report.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/feedbacks/archives/petfishframework_production_readiness_roadmap.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/feedbacks/archives/petfishframework_recommendations.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/feedbacks/archives/petfishframework_v0_1_6_playground_review.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/feedbacks/archives/petfishframework_v0_1_7_review.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/feedbacks/archives/petfishframework_v0_1_8_review.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/feedbacks/archives/petfishframework_v0_2_1_review.md +0 -0
- {petfishframework-0.2.5/docs/feedbacks → petfishframework-0.3.1/docs/feedbacks/archives}/petfishframework_v0_2_2_review.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/release-checklist.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/research/competitor-analysis/README.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/research/competitor-analysis/competitor-matrix.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/research/competitor-analysis/market-brief.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/research/competitor-analysis/positioning-map.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/research/competitor-analysis/swot-analysis.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/research/literature-review/inclusion-exclusion-criteria.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/research/literature-review/literature-matrix.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/research/literature-review/literature-review.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/research/literature-review/search-strategy.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/research/reference-repos-absorption.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/skeleton-completeness-checklist.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/test-results-report.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/usage-guide.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/docs/validation-roadmap.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/initialization-report.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/mcp/README.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/mcp/connection-checklist.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/mcp/mcp-config.example.json +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/opencode.json +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/qa/code-review-checklist.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/qa/qa-review.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/qa/qc-gate-decision.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/qa/test-plan.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/config.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/core/__init__.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/core/compiled.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/core/contracts.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/core/conversation.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/core/events.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/core/structured.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/core/types.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/mcp/__init__.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/mcp/client.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/mcp/server.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/mcp/stdio_transport.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/mcp/wrapper.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/models/__init__.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/models/anthropic.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/models/fake.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/models/openai.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/observability/__init__.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/observability/sinks.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/permissions/__init__.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/reasoning/__init__.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/reasoning/lats.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/reasoning/llm_plus_p.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/reasoning/react.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/reliability/__init__.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/reliability/audit_report.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/reliability/cost.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/reliability/cost_report.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/reliability/pass_at_k.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/reliability/replay.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/reliability/retry.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/reliability/timeout.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/retrieval/__init__.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/retrieval/adaptive.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/retrieval/crag.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/retrieval/memory_store.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/tools/__init__.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/tools/agent_tool.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/tools/base.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/tools/calculator.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/tools/path_planner.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/tools/registry.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/src/petfishframework/tools/word_sorter.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tasks/backlog.md +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/integration/__init__.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/integration/test_anthropic_integration.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/integration/test_openai_integration.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_agent_tool.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_anthropic_adapter.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_api_agent_lifecycle.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_api_edge_cases.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_api_integration.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_api_public_surface.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_api_type_safety.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_async.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_audit_report_v2.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_conversation.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_degrade.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_degrade_failclosed.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_enterprise_demo.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_m2_m3_m4.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_mcp.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_openai_adapter.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_pass_at_k.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_permission_semantics.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_quickstart_smoke.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_replay.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_retrieval.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_retry.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_skeleton.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_stdio_transport.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_streaming.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_structured.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_tool_registry.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_v016_semantics.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_v018.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_v019.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_v2_interface_compatibility.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_v2_lats.py +0 -0
- {petfishframework-0.2.5 → petfishframework-0.3.1}/tests/test_v2_llm_plus_p.py +0 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# petfishFramework v0.3.0 开发计划
|
|
2
|
+
|
|
3
|
+
> 基于 v0.2.5 状态 + 6 轮用户反馈 + 生产可用路线图
|
|
4
|
+
> 目标:从代码策略进入配置化策略 + 凭证治理 MVP
|
|
5
|
+
> 原则:**TDD 先行,YAML-first,向后兼容**
|
|
6
|
+
|
|
7
|
+
## v0.3.0 使命
|
|
8
|
+
|
|
9
|
+
> 让安全/运维团队通过 YAML 文件配置 Agent 权限策略,无需写 Python 代码。
|
|
10
|
+
> 让 Agent 永不持有真实凭证——通过 CredentialBroker 签发 scoped 短期令牌。
|
|
11
|
+
|
|
12
|
+
## v0.2.5 → v0.3.0 状态跃迁
|
|
13
|
+
|
|
14
|
+
| 维度 | v0.2.5(当前) | v0.3.0(目标) |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| 策略定义 | Python class (PermissionPolicy) | **YAML 文件 + Python fallback** |
|
|
17
|
+
| 策略测试 | 手写 pytest | **YAML test suite + pytest runner** |
|
|
18
|
+
| 策略版本 | 无 | **policy_version 字段 + 审计追踪** |
|
|
19
|
+
| 凭证 | Agent 持有 API key | **CredentialBroker scoped 令牌** |
|
|
20
|
+
| 策略组合 | 单一 policy | **多规则 deny-overrides 合并** |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Phase A: YAML Policy Engine
|
|
25
|
+
|
|
26
|
+
### A1: YAML 策略解析器 + 匹配引擎
|
|
27
|
+
|
|
28
|
+
**目标**:从 YAML 文件加载策略规则,按优先级匹配,生成 Decision。
|
|
29
|
+
|
|
30
|
+
**文件**:`src/petfishframework/policies/` (新模块)
|
|
31
|
+
|
|
32
|
+
**YAML 格式**:
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
version: "1.0"
|
|
36
|
+
name: "enterprise-expense-policy"
|
|
37
|
+
|
|
38
|
+
rules:
|
|
39
|
+
- name: deny-non-finance-approval
|
|
40
|
+
priority: 100
|
|
41
|
+
when:
|
|
42
|
+
action.tool_name: approve_payment
|
|
43
|
+
subject.role_not_in: [finance, admin]
|
|
44
|
+
effect: DENY
|
|
45
|
+
reason: "only finance/admin can approve payments"
|
|
46
|
+
|
|
47
|
+
- name: require-approval-for-side-effects
|
|
48
|
+
priority: 90
|
|
49
|
+
when:
|
|
50
|
+
tool.side_effect: true
|
|
51
|
+
effect: REQUIRE_APPROVAL
|
|
52
|
+
reason: "side-effect tool requires approval"
|
|
53
|
+
|
|
54
|
+
- name: degrade-large-payments
|
|
55
|
+
priority: 80
|
|
56
|
+
when:
|
|
57
|
+
action.tool_name: approve_payment
|
|
58
|
+
action.args.amount_gt: 5000
|
|
59
|
+
effect: DEGRADE
|
|
60
|
+
reason: "large payment degraded to dry-run"
|
|
61
|
+
fallback_tool: dry_run_payment
|
|
62
|
+
|
|
63
|
+
- name: mask-pii-in-payments
|
|
64
|
+
priority: 70
|
|
65
|
+
when:
|
|
66
|
+
action.tool_name: approve_payment
|
|
67
|
+
effect: MASK
|
|
68
|
+
reason: "mask PII"
|
|
69
|
+
input_mask_fields: [recipient]
|
|
70
|
+
output_mask_fields: [recipient]
|
|
71
|
+
event_mask_fields: [recipient]
|
|
72
|
+
|
|
73
|
+
- name: partial-allow-policy-check
|
|
74
|
+
priority: 60
|
|
75
|
+
when:
|
|
76
|
+
action.tool_name: check_policy
|
|
77
|
+
effect: PARTIAL_ALLOW
|
|
78
|
+
reason: "only amount needed"
|
|
79
|
+
allowed_fields: [amount]
|
|
80
|
+
|
|
81
|
+
- name: default-allow
|
|
82
|
+
priority: 0
|
|
83
|
+
when: {}
|
|
84
|
+
effect: ALLOW
|
|
85
|
+
reason: "default allow"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### TDD 测试先行
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# tests/test_yaml_policy.py
|
|
92
|
+
|
|
93
|
+
def test_yaml_policy_loads_from_file():
|
|
94
|
+
"""YAML 文件成功加载为 PolicyEngine。"""
|
|
95
|
+
|
|
96
|
+
def test_yaml_policy_deny_rule_blocks():
|
|
97
|
+
"""deny-non-finance 规则阻断非 finance 用户。"""
|
|
98
|
+
|
|
99
|
+
def test_yaml_policy_priority_ordering():
|
|
100
|
+
"""高 priority 规则先匹配。"""
|
|
101
|
+
|
|
102
|
+
def test_yaml_policy_deny_overrides_allow():
|
|
103
|
+
"""deny 规则覆盖 default-allow。"""
|
|
104
|
+
|
|
105
|
+
def test_yaml_policy_mask_rule_sets_fields():
|
|
106
|
+
"""mask 规则正确设置 input/output/event mask fields。"""
|
|
107
|
+
|
|
108
|
+
def test_yaml_policy_degrade_rule_sets_fallback():
|
|
109
|
+
"""degrade 规则正确设置 fallback_tool。"""
|
|
110
|
+
|
|
111
|
+
def test_yaml_policy_partial_allow_sets_allowed_fields():
|
|
112
|
+
"""partial_allow 规则正确设置 allowed_fields。"""
|
|
113
|
+
|
|
114
|
+
def test_yaml_policy_no_match_returns_default():
|
|
115
|
+
"""无规则匹配时返回默认 Decision。"""
|
|
116
|
+
|
|
117
|
+
def test_yaml_policy_condition_amount_gt():
|
|
118
|
+
"""amount_gt 条件正确比较数值。"""
|
|
119
|
+
|
|
120
|
+
def test_yaml_policy_condition_role_not_in():
|
|
121
|
+
"""role_not_in 条件正确检查角色。"""
|
|
122
|
+
|
|
123
|
+
def test_yaml_policy_condition_tool_side_effect():
|
|
124
|
+
"""tool.side_effect 条件匹配工具元数据。"""
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### 实现
|
|
128
|
+
|
|
129
|
+
1. `policies/__init__.py` — 导出
|
|
130
|
+
2. `policies/rule.py` — `PolicyRule` dataclass (name, priority, when, effect, reason, mask_fields, fallback_tool, allowed_fields)
|
|
131
|
+
3. `policies/conditions.py` — 条件匹配器:
|
|
132
|
+
- `action.tool_name` — 精确匹配
|
|
133
|
+
- `action.args.amount_gt` — 数值大于
|
|
134
|
+
- `action.args.amount_lt` — 数值小于
|
|
135
|
+
- `subject.role_in` — 角色在列表中
|
|
136
|
+
- `subject.role_not_in` — 角色不在列表中
|
|
137
|
+
- `tool.side_effect` — 工具元数据匹配
|
|
138
|
+
- `tool.external_egress` — 工具元数据匹配
|
|
139
|
+
4. `policies/engine.py` — `YamlPolicy` class:
|
|
140
|
+
- `from_file(path) -> YamlPolicy`
|
|
141
|
+
- `from_string(yaml_str) -> YamlPolicy`
|
|
142
|
+
- `evaluate(subject, action, resource, context) -> Decision`
|
|
143
|
+
- 按 priority 降序遍历规则,deny-overrides 语义
|
|
144
|
+
- 需要工具元数据 → 接受 `tool_registry` 或在 evaluate 时接收 tool 对象
|
|
145
|
+
|
|
146
|
+
### A2: YAML 策略测试套件
|
|
147
|
+
|
|
148
|
+
**目标**:在 YAML 中定义测试用例,pytest 自动运行。
|
|
149
|
+
|
|
150
|
+
**YAML 格式**:
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
# tests/policies/enterprise-expense.tests.yaml
|
|
154
|
+
policy_file: enterprise-expense.yaml
|
|
155
|
+
|
|
156
|
+
tests:
|
|
157
|
+
- name: analyst-cannot-approve
|
|
158
|
+
input:
|
|
159
|
+
subject.roles: [analyst]
|
|
160
|
+
action.tool_name: approve_payment
|
|
161
|
+
action.args.amount: 300
|
|
162
|
+
expect:
|
|
163
|
+
effect: DENY
|
|
164
|
+
|
|
165
|
+
- name: finance-small-payment-partial-allow
|
|
166
|
+
input:
|
|
167
|
+
subject.roles: [finance]
|
|
168
|
+
action.tool_name: check_policy
|
|
169
|
+
action.args.amount: 350
|
|
170
|
+
expect:
|
|
171
|
+
effect: PARTIAL_ALLOW
|
|
172
|
+
|
|
173
|
+
- name: large-payment-degrades
|
|
174
|
+
input:
|
|
175
|
+
subject.roles: [finance]
|
|
176
|
+
action.tool_name: approve_payment
|
|
177
|
+
action.args.amount: 8000
|
|
178
|
+
expect:
|
|
179
|
+
effect: REQUIRE_APPROVAL # side_effect 先匹配
|
|
180
|
+
|
|
181
|
+
- name: unknown-tool-default-allow
|
|
182
|
+
input:
|
|
183
|
+
subject.roles: [analyst]
|
|
184
|
+
action.tool_name: unknown_tool
|
|
185
|
+
expect:
|
|
186
|
+
effect: ALLOW
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### TDD 测试先行
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
# tests/test_yaml_policy_suite.py
|
|
193
|
+
|
|
194
|
+
def test_policy_suite_runs_all_tests():
|
|
195
|
+
"""YAML 测试套件中所有用例通过。"""
|
|
196
|
+
|
|
197
|
+
def test_policy_suite_reports_failures():
|
|
198
|
+
"""测试套件中失败的用例被报告。"""
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### 实现
|
|
202
|
+
|
|
203
|
+
- `policies/test_runner.py` — 加载 `.tests.yaml`,构造 Subject/Action/Resource/Context,调用 policy.evaluate,比较 effect
|
|
204
|
+
- pytest fixture 自动发现 `tests/policies/*.tests.yaml`
|
|
205
|
+
|
|
206
|
+
### A3: 策略版本 + 审计元数据
|
|
207
|
+
|
|
208
|
+
**目标**:策略带版本号,决策事件记录策略版本。
|
|
209
|
+
|
|
210
|
+
#### TDD 测试先行
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
def test_policy_has_version():
|
|
214
|
+
"""YAML policy 有 version 字段。"""
|
|
215
|
+
|
|
216
|
+
def test_decision_records_policy_version():
|
|
217
|
+
"""Decision 记录 policy_version。"""
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### 实现
|
|
221
|
+
|
|
222
|
+
- `PolicyRule` 增加 `version: str` 字段
|
|
223
|
+
- `YamlPolicy` 从 YAML `version` 读取
|
|
224
|
+
- `Decision` 增加 `policy_version: str | None = None` 字段
|
|
225
|
+
- `YamlPolicy.evaluate()` 在返回 Decision 时填充 policy_version
|
|
226
|
+
- Environment 事件记录 policy_version
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Phase B: CredentialBroker MVP
|
|
231
|
+
|
|
232
|
+
### B1: Scoped 令牌签发 + 过期
|
|
233
|
+
|
|
234
|
+
**目标**:Agent 永不持有真实 API key。CredentialBroker 签发短期限时令牌。
|
|
235
|
+
|
|
236
|
+
**文件**:`src/petfishframework/credentials/` (新模块)
|
|
237
|
+
|
|
238
|
+
#### TDD 测试先行
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
# tests/test_credential_broker.py
|
|
242
|
+
|
|
243
|
+
def test_broker_issues_scoped_token():
|
|
244
|
+
"""Broker 发放 scoped 令牌(限 tool + scope)。"""
|
|
245
|
+
|
|
246
|
+
def test_broker_token_has_ttl():
|
|
247
|
+
"""令牌有 TTL,过期后失效。"""
|
|
248
|
+
|
|
249
|
+
def test_broker_token_not_in_event_log():
|
|
250
|
+
"""令牌不出现在事件日志中。"""
|
|
251
|
+
|
|
252
|
+
def test_broker_revokes_token():
|
|
253
|
+
"""Broker 可以撤销令牌。"""
|
|
254
|
+
|
|
255
|
+
def test_broker_token_scoped_to_tool():
|
|
256
|
+
"""令牌限定了可调用的工具。"""
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
#### 实现
|
|
260
|
+
|
|
261
|
+
1. `credentials/__init__.py` — 导出
|
|
262
|
+
2. `credentials/broker.py` — `CredentialBroker`:
|
|
263
|
+
- `register_credential(name, secret, scope)` — 注册真实凭证
|
|
264
|
+
- `issue_token(name, tool_name, ttl_s) -> ScopedToken` — 签发限时令牌
|
|
265
|
+
- `validate_token(token) -> bool` — 验证令牌有效性
|
|
266
|
+
- `revoke_token(token_id)` — 撤销令牌
|
|
267
|
+
3. `credentials/token.py` — `ScopedToken` dataclass:
|
|
268
|
+
- `token_id: str` — 唯一 ID
|
|
269
|
+
- `tool_name: str` — 限定的工具
|
|
270
|
+
- `expires_at: float` — 过期时间戳
|
|
271
|
+
- `_secret: str` — 内部引用(不暴露在事件日志中)
|
|
272
|
+
- `is_valid() -> bool`
|
|
273
|
+
- `__repr__` 脱敏(显示 `[REDACTED]`)
|
|
274
|
+
|
|
275
|
+
### B2: 与 Environment 集成
|
|
276
|
+
|
|
277
|
+
**目标**:工具通过 Broker 获取凭证,不直接持有 API key。
|
|
278
|
+
|
|
279
|
+
#### 实现
|
|
280
|
+
|
|
281
|
+
- `Environment` 增加 `credential_broker: CredentialBroker | None`
|
|
282
|
+
- 工具的 `requires_credentials: True` 时,Environment 在 `call()` 前通过 Broker 签发令牌
|
|
283
|
+
- 令牌传递给工具 `execute()` 的 args 中(字段名 `_credential_token`)
|
|
284
|
+
- 事件日志中令牌值脱敏
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Phase C: 企业 PoC 集成 + 文档
|
|
289
|
+
|
|
290
|
+
### C1: 企业报销审批 YAML 策略
|
|
291
|
+
|
|
292
|
+
**目标**:用 YAML 文件替代 v0.2.x 的 Python ExpensePolicy。
|
|
293
|
+
|
|
294
|
+
**文件**:`examples/policies/enterprise-expense.yaml`
|
|
295
|
+
|
|
296
|
+
### C2: 企业 demo 升级
|
|
297
|
+
|
|
298
|
+
**目标**:`examples/05_enterprise_expense.py` 升级为 YAML 策略驱动。
|
|
299
|
+
|
|
300
|
+
### C3: 文档更新
|
|
301
|
+
|
|
302
|
+
- `docs/api.md` 增加 YAML Policy + CredentialBroker 章节
|
|
303
|
+
- `docs/usage-guide.md` 增加「YAML 策略配置」+「凭证治理」段落
|
|
304
|
+
- `CHANGELOG.md` v0.3.0
|
|
305
|
+
- `README.md` 更新 Current Limitations
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## 执行顺序与依赖
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
A1 (YAML engine) → A2 (test suite) → A3 (version metadata)
|
|
313
|
+
↓
|
|
314
|
+
B1 (Broker MVP) → B2 (Environment integration)
|
|
315
|
+
↓
|
|
316
|
+
C1 (YAML policy file) → C2 (demo upgrade) → C3 (docs)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## 不做清单
|
|
320
|
+
|
|
321
|
+
- ❌ OpenTelemetry(v0.4.0)
|
|
322
|
+
- ❌ 确定性回放 RESUME/RERUN(v0.4.0)
|
|
323
|
+
- ❌ MCP server mode(v0.5.0)
|
|
324
|
+
- ❌ Vault 集成(v0.4.0+)
|
|
325
|
+
- ❌ Policy 热重载(v0.4.0+)
|
|
326
|
+
|
|
327
|
+
## 验收标准
|
|
328
|
+
|
|
329
|
+
| # | 标准 | 验证方法 |
|
|
330
|
+
|---|---|---|
|
|
331
|
+
| V1 | YAML policy 从文件加载并正确匹配 | `test_yaml_policy.py` 全通过 |
|
|
332
|
+
| V2 | 6 种 DecisionEffect 全部可从 YAML 配置 | YAML 规则覆盖全部 effect |
|
|
333
|
+
| V3 | deny-overrides 语义正确 | `test_yaml_policy_deny_overrides` |
|
|
334
|
+
| V4 | YAML test suite 自动运行 | `pytest tests/policies/` |
|
|
335
|
+
| V5 | CredentialBroker 签发 scoped 令牌 | `test_credential_broker.py` |
|
|
336
|
+
| V6 | 令牌不出现在事件日志 | `test_broker_token_not_in_event_log` |
|
|
337
|
+
| V7 | 企业 demo 使用 YAML 策略 | `python examples/05_enterprise_expense.py` |
|
|
338
|
+
| V8 | 所有 234+ 现有测试仍通过 | pytest |
|
|
339
|
+
| V9 | ruff clean | ruff check |
|
|
340
|
+
| V10 | Trusted Publishing 发布成功 | git tag v0.3.0 |
|
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to petfishFramework will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.3.0] — 2026-07-08
|
|
6
|
+
|
|
7
|
+
### v0.3.0 Policy Engine + Credential Broker Integration
|
|
8
|
+
|
|
9
|
+
- YAML Policy Engine (Phase A1): load authorization rules from YAML via `YamlPolicy.from_file` / `from_string`
|
|
10
|
+
- Condition matchers: `action.tool_name`, `subject.role_in`, `subject.role_not_in`, `action.args.amount_gt`, `action.args.amount_lt`, `tool.side_effect`, `tool.external_egress`
|
|
11
|
+
- Enterprise expense YAML policy example (`examples/policies/enterprise-expense.yaml`)
|
|
12
|
+
- `CredentialBroker` + `ScopedToken`: issue scoped, time-limited tokens that hide secrets in `repr` / `str`
|
|
13
|
+
- `RuntimeEnvironment` integration: tools with `requires_credentials=True` receive a scoped token via `_credential_token` before execution
|
|
14
|
+
- 3 new TDD tests covering credential injection, non-credential tools, and event-log safety
|
|
15
|
+
|
|
5
16
|
## [0.2.0] — 2026-07-08
|
|
6
17
|
|
|
7
18
|
### v0.2.0 Enterprise PoC Release
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: petfishframework
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: A lightweight runtime framework for reliable, auditable, budget-aware, and permission-aware AI agents.
|
|
5
5
|
Project-URL: Homepage, https://github.com/kylecui/petfishFramework
|
|
6
6
|
Project-URL: Documentation, https://github.com/kylecui/petfishFramework/tree/master/docs
|
|
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
21
21
|
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
22
23
|
Provides-Extra: anthropic
|
|
23
24
|
Requires-Dist: anthropic>=0.40; extra == 'anthropic'
|
|
24
25
|
Provides-Extra: mcp
|
|
@@ -33,7 +34,7 @@ Description-Content-Type: text/markdown
|
|
|
33
34
|
|
|
34
35
|
[](https://www.python.org/downloads/)
|
|
35
36
|
[](https://github.com/kylecui/petfishFramework/blob/master/LICENSE)
|
|
36
|
-
[](https://github.com/kylecui/petfishFramework/tree/master/tests/)
|
|
37
38
|
|
|
38
39
|
**Status: Alpha** — API may change. Core runtime works; see [Roadmap](#roadmap).
|
|
39
40
|
|
|
@@ -214,11 +215,39 @@ See `examples/05_enterprise_expense.py` and `tests/test_enterprise_demo.py` for
|
|
|
214
215
|
|
|
215
216
|
Run: `python examples/05_enterprise_expense.py`
|
|
216
217
|
|
|
218
|
+
## YAML Policy Engine
|
|
219
|
+
|
|
220
|
+
Configure permissions via YAML instead of Python:
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from petfishframework.policies import YamlPolicy
|
|
224
|
+
|
|
225
|
+
policy = YamlPolicy.from_file("examples/policies/enterprise-expense.yaml")
|
|
226
|
+
policy.register_tools(tools)
|
|
227
|
+
|
|
228
|
+
agent = Agent(model=model, reasoning=ReAct(), tools=tools, permission_policy=policy)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Supported conditions: `action.tool_name`, `subject.role_in`, `subject.role_not_in`, `action.args.amount_gt/lt`, `tool.side_effect`, `tool.external_egress`.
|
|
232
|
+
|
|
233
|
+
## CredentialBroker
|
|
234
|
+
|
|
235
|
+
Tools never hold raw API keys — they receive scoped, time-limited tokens:
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
from petfishframework.credentials import CredentialBroker
|
|
239
|
+
|
|
240
|
+
broker = CredentialBroker()
|
|
241
|
+
broker.register_credential("github_tool", os.environ["GITHUB_TOKEN"])
|
|
242
|
+
|
|
243
|
+
agent = Agent(model=model, reasoning=ReAct(), tools=tools, credential_broker=broker)
|
|
244
|
+
# Tools with requires_credentials=True receive a ScopedToken (repr-safe, auto-expiring)
|
|
245
|
+
```
|
|
246
|
+
|
|
217
247
|
## Roadmap
|
|
218
248
|
|
|
219
|
-
- **v0.2.x
|
|
220
|
-
- **v0.
|
|
221
|
-
- **v0.3.x**: Policy engine (YAML), credential broker
|
|
249
|
+
- **v0.2.x**: Core runtime, permission semantics, enterprise PoC, Trusted Publishing ✅
|
|
250
|
+
- **v0.3.x** (current): YAML Policy Engine, CredentialBroker ✅
|
|
222
251
|
- **v0.4.x**: Production hardening, deployment guides
|
|
223
252
|
|
|
224
253
|
## Current Limitations
|
|
@@ -239,6 +268,8 @@ petfishFramework is **Alpha**. API may change before v1.0.
|
|
|
239
268
|
| MCP client stdio | ✅ Available |
|
|
240
269
|
| MCP server mode | 📋 Planned |
|
|
241
270
|
| Structured output / conversation memory | ✅ Available |
|
|
271
|
+
| YAML Policy Engine | ✅ Available |
|
|
272
|
+
| Credential Broker | ✅ Available |
|
|
242
273
|
| LATS / LLM+P | ⚠️ Lightweight implementations |
|
|
243
274
|
| CRAG / Adaptive-RAG | ⚠️ Lightweight reference implementations |
|
|
244
275
|
|
|
@@ -248,7 +279,7 @@ petfishFramework is **Alpha**. API may change before v1.0.
|
|
|
248
279
|
git clone https://github.com/kylecui/petfishFramework.git
|
|
249
280
|
cd petfishFramework
|
|
250
281
|
uv sync --all-extras
|
|
251
|
-
uv run pytest #
|
|
282
|
+
uv run pytest # 259 tests
|
|
252
283
|
uv run ruff check src/ tests/
|
|
253
284
|
```
|
|
254
285
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://www.python.org/downloads/)
|
|
6
6
|
[](https://github.com/kylecui/petfishFramework/blob/master/LICENSE)
|
|
7
|
-
[](https://github.com/kylecui/petfishFramework/tree/master/tests/)
|
|
8
8
|
|
|
9
9
|
**Status: Alpha** — API may change. Core runtime works; see [Roadmap](#roadmap).
|
|
10
10
|
|
|
@@ -185,11 +185,39 @@ See `examples/05_enterprise_expense.py` and `tests/test_enterprise_demo.py` for
|
|
|
185
185
|
|
|
186
186
|
Run: `python examples/05_enterprise_expense.py`
|
|
187
187
|
|
|
188
|
+
## YAML Policy Engine
|
|
189
|
+
|
|
190
|
+
Configure permissions via YAML instead of Python:
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from petfishframework.policies import YamlPolicy
|
|
194
|
+
|
|
195
|
+
policy = YamlPolicy.from_file("examples/policies/enterprise-expense.yaml")
|
|
196
|
+
policy.register_tools(tools)
|
|
197
|
+
|
|
198
|
+
agent = Agent(model=model, reasoning=ReAct(), tools=tools, permission_policy=policy)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Supported conditions: `action.tool_name`, `subject.role_in`, `subject.role_not_in`, `action.args.amount_gt/lt`, `tool.side_effect`, `tool.external_egress`.
|
|
202
|
+
|
|
203
|
+
## CredentialBroker
|
|
204
|
+
|
|
205
|
+
Tools never hold raw API keys — they receive scoped, time-limited tokens:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from petfishframework.credentials import CredentialBroker
|
|
209
|
+
|
|
210
|
+
broker = CredentialBroker()
|
|
211
|
+
broker.register_credential("github_tool", os.environ["GITHUB_TOKEN"])
|
|
212
|
+
|
|
213
|
+
agent = Agent(model=model, reasoning=ReAct(), tools=tools, credential_broker=broker)
|
|
214
|
+
# Tools with requires_credentials=True receive a ScopedToken (repr-safe, auto-expiring)
|
|
215
|
+
```
|
|
216
|
+
|
|
188
217
|
## Roadmap
|
|
189
218
|
|
|
190
|
-
- **v0.2.x
|
|
191
|
-
- **v0.
|
|
192
|
-
- **v0.3.x**: Policy engine (YAML), credential broker
|
|
219
|
+
- **v0.2.x**: Core runtime, permission semantics, enterprise PoC, Trusted Publishing ✅
|
|
220
|
+
- **v0.3.x** (current): YAML Policy Engine, CredentialBroker ✅
|
|
193
221
|
- **v0.4.x**: Production hardening, deployment guides
|
|
194
222
|
|
|
195
223
|
## Current Limitations
|
|
@@ -210,6 +238,8 @@ petfishFramework is **Alpha**. API may change before v1.0.
|
|
|
210
238
|
| MCP client stdio | ✅ Available |
|
|
211
239
|
| MCP server mode | 📋 Planned |
|
|
212
240
|
| Structured output / conversation memory | ✅ Available |
|
|
241
|
+
| YAML Policy Engine | ✅ Available |
|
|
242
|
+
| Credential Broker | ✅ Available |
|
|
213
243
|
| LATS / LLM+P | ⚠️ Lightweight implementations |
|
|
214
244
|
| CRAG / Adaptive-RAG | ⚠️ Lightweight reference implementations |
|
|
215
245
|
|
|
@@ -219,7 +249,7 @@ petfishFramework is **Alpha**. API may change before v1.0.
|
|
|
219
249
|
git clone https://github.com/kylecui/petfishFramework.git
|
|
220
250
|
cd petfishFramework
|
|
221
251
|
uv sync --all-extras
|
|
222
|
-
uv run pytest #
|
|
252
|
+
uv run pytest # 259 tests
|
|
223
253
|
uv run ruff check src/ tests/
|
|
224
254
|
```
|
|
225
255
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# petfishFramework API Reference
|
|
2
2
|
|
|
3
|
-
This document is the authoritative reference for the public API of `petfishFramework` v0.
|
|
3
|
+
This document is the authoritative reference for the public API of `petfishFramework` v0.3.1. Every signature, field, and example below is derived from the source code and from the tests that exercise it.
|
|
4
4
|
|
|
5
5
|
## 1. Overview
|
|
6
6
|
|
|
@@ -1212,3 +1212,85 @@ Tool functions that raise internally are caught by `BaseTool` and `MCPToolWrappe
|
|
|
1212
1212
|
### Replay divergence
|
|
1213
1213
|
|
|
1214
1214
|
`ReplayEnvironment` raises `RuntimeError` when a replayed strategy makes more calls than were recorded. This detects when the execution path has diverged from the original run. `ResumableEnvironment` switches to the live environment after the checkpoint; calling it beyond the checkpoint continues live execution and may produce a different answer.
|
|
1215
|
+
|
|
1216
|
+
## 15. YAML Policy Engine
|
|
1217
|
+
|
|
1218
|
+
Authorization rules can be loaded from YAML and evaluated through `YamlPolicy`, which implements the same `PermissionPolicy` protocol as custom Python policies.
|
|
1219
|
+
|
|
1220
|
+
```python
|
|
1221
|
+
from petfishframework import YamlPolicy
|
|
1222
|
+
|
|
1223
|
+
policy = YamlPolicy.from_file("policies/enterprise-expense.yaml")
|
|
1224
|
+
policy.register_tools(tools)
|
|
1225
|
+
|
|
1226
|
+
decision = policy.evaluate(subject, action, resource, context)
|
|
1227
|
+
print(decision.effect, decision.reason)
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
### Loading policies
|
|
1231
|
+
|
|
1232
|
+
```python
|
|
1233
|
+
from petfishframework import YamlPolicy
|
|
1234
|
+
|
|
1235
|
+
policy = YamlPolicy.from_file("path/to/policy.yaml")
|
|
1236
|
+
policy = YamlPolicy.from_string(yaml_text)
|
|
1237
|
+
```
|
|
1238
|
+
|
|
1239
|
+
`YamlPolicy.from_file` reads UTF-8 YAML and returns a policy instance. `register_tools(tools)` must be called if rules reference tool metadata such as `side_effect` or `external_egress`.
|
|
1240
|
+
|
|
1241
|
+
### Policy file format
|
|
1242
|
+
|
|
1243
|
+
```yaml
|
|
1244
|
+
version: "1.0"
|
|
1245
|
+
name: "enterprise-expense-policy"
|
|
1246
|
+
rules:
|
|
1247
|
+
- name: deny-non-finance-approval
|
|
1248
|
+
priority: 100
|
|
1249
|
+
when:
|
|
1250
|
+
action.tool_name: approve_payment
|
|
1251
|
+
subject.role_not_in: [finance, admin]
|
|
1252
|
+
effect: DENY
|
|
1253
|
+
reason: "only finance/admin can approve payments"
|
|
1254
|
+
|
|
1255
|
+
- name: require-approval-for-side-effects
|
|
1256
|
+
priority: 90
|
|
1257
|
+
when:
|
|
1258
|
+
tool.side_effect: true
|
|
1259
|
+
effect: REQUIRE_APPROVAL
|
|
1260
|
+
reason: "side-effect tool requires approval"
|
|
1261
|
+
|
|
1262
|
+
- name: default-allow
|
|
1263
|
+
priority: 0
|
|
1264
|
+
when: {}
|
|
1265
|
+
effect: ALLOW
|
|
1266
|
+
reason: "default allow"
|
|
1267
|
+
```
|
|
1268
|
+
|
|
1269
|
+
Rules are evaluated in descending `priority` order. The first fully matching rule wins. `version` and `name` are optional metadata carried on the resulting `Decision`.
|
|
1270
|
+
|
|
1271
|
+
### Condition matchers
|
|
1272
|
+
|
|
1273
|
+
Each key under `when:` selects a matcher. Matchers are fail-closed: an unknown key never matches.
|
|
1274
|
+
|
|
1275
|
+
| Condition key | Meaning | Example value |
|
|
1276
|
+
|---|---|---|
|
|
1277
|
+
| `action.tool_name` | Exact tool name match | `"approve_payment"` |
|
|
1278
|
+
| `subject.role_in` | Subject has any listed role | `[finance, admin]` |
|
|
1279
|
+
| `subject.role_not_in` | Subject has none of the listed roles | `[finance, admin]` |
|
|
1280
|
+
| `action.args.amount_gt` | `action.args["amount"] > value` | `5000` |
|
|
1281
|
+
| `action.args.amount_lt` | `action.args["amount"] < value` | `100` |
|
|
1282
|
+
| `tool.side_effect` | Match tool metadata flag | `true` |
|
|
1283
|
+
| `tool.external_egress` | Match tool metadata flag | `true` |
|
|
1284
|
+
|
|
1285
|
+
Compound conditions use AND semantics: every key in a `when:` block must match for the rule to apply.
|
|
1286
|
+
|
|
1287
|
+
### Policy version
|
|
1288
|
+
|
|
1289
|
+
The `version` field in YAML is exposed on every `Decision` produced by the policy:
|
|
1290
|
+
|
|
1291
|
+
```python
|
|
1292
|
+
decision.policy_version # "1.0"
|
|
1293
|
+
decision.policy_name # "enterprise-expense-policy"
|
|
1294
|
+
```
|
|
1295
|
+
|
|
1296
|
+
This lets audit logs trace each decision back to the policy file version that produced it.
|