petfishframework 0.2.5__tar.gz → 0.3.0__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.
Files changed (155) hide show
  1. petfishframework-0.3.0/.sisyphus/plans/v0.3.0-plan.md +340 -0
  2. {petfishframework-0.2.5 → petfishframework-0.3.0}/CHANGELOG.md +11 -0
  3. {petfishframework-0.2.5 → petfishframework-0.3.0}/PKG-INFO +7 -4
  4. {petfishframework-0.2.5 → petfishframework-0.3.0}/README.md +5 -3
  5. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/api.md +82 -0
  6. {petfishframework-0.2.5 → petfishframework-0.3.0}/pyproject.toml +4 -2
  7. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/__init__.py +4 -1
  8. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/core/environment.py +21 -0
  9. petfishframework-0.3.0/src/petfishframework/credentials/__init__.py +7 -0
  10. petfishframework-0.3.0/src/petfishframework/credentials/broker.py +70 -0
  11. petfishframework-0.3.0/src/petfishframework/credentials/token.py +37 -0
  12. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/permissions/model.py +2 -0
  13. petfishframework-0.3.0/src/petfishframework/policies/__init__.py +12 -0
  14. petfishframework-0.3.0/src/petfishframework/policies/conditions.py +176 -0
  15. petfishframework-0.3.0/src/petfishframework/policies/engine.py +130 -0
  16. petfishframework-0.3.0/src/petfishframework/policies/rule.py +32 -0
  17. petfishframework-0.3.0/src/petfishframework/policies/test_runner.py +167 -0
  18. petfishframework-0.3.0/tests/policies/enterprise-expense.tests.yaml +45 -0
  19. petfishframework-0.3.0/tests/test_credential_broker.py +81 -0
  20. petfishframework-0.3.0/tests/test_credential_integration.py +107 -0
  21. petfishframework-0.3.0/tests/test_yaml_policy.py +325 -0
  22. petfishframework-0.3.0/tests/test_yaml_policy_suite.py +81 -0
  23. {petfishframework-0.2.5 → petfishframework-0.3.0}/uv.lock +73 -5
  24. {petfishframework-0.2.5 → petfishframework-0.3.0}/.env.example +0 -0
  25. {petfishframework-0.2.5 → petfishframework-0.3.0}/.github/workflows/ci.yml +0 -0
  26. {petfishframework-0.2.5 → petfishframework-0.3.0}/.github/workflows/publish.yml +0 -0
  27. {petfishframework-0.2.5 → petfishframework-0.3.0}/.gitignore +0 -0
  28. {petfishframework-0.2.5 → petfishframework-0.3.0}/.sisyphus/plans/v0.1.6-plan.md +0 -0
  29. {petfishframework-0.2.5 → petfishframework-0.3.0}/.sisyphus/plans/v0.2.0-plan.md +0 -0
  30. {petfishframework-0.2.5 → petfishframework-0.3.0}/AGENTS.md +0 -0
  31. {petfishframework-0.2.5 → petfishframework-0.3.0}/AGENTS.md.new +0 -0
  32. {petfishframework-0.2.5 → petfishframework-0.3.0}/CONTRIBUTING.md +0 -0
  33. {petfishframework-0.2.5 → petfishframework-0.3.0}/LICENSE +0 -0
  34. {petfishframework-0.2.5 → petfishframework-0.3.0}/SECURITY.md +0 -0
  35. {petfishframework-0.2.5 → petfishframework-0.3.0}/conftest.py +0 -0
  36. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/architecture.md +0 -0
  37. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/benchmark-results.md +0 -0
  38. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/development-plan.md +0 -0
  39. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/development.md +0 -0
  40. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/enterprise-expense-demo.md +0 -0
  41. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_0_1_4_playground_recommendations.md +0 -0
  42. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_playground_report.md +0 -0
  43. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_production_readiness_roadmap.md +0 -0
  44. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_recommendations.md +0 -0
  45. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_v0_1_6_playground_review.md +0 -0
  46. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_v0_1_7_review.md +0 -0
  47. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_v0_1_8_review.md +0 -0
  48. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_v0_2_1_review.md +0 -0
  49. {petfishframework-0.2.5/docs/feedbacks → petfishframework-0.3.0/docs/feedbacks/archives}/petfishframework_v0_2_2_review.md +0 -0
  50. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/release-checklist.md +0 -0
  51. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/research/competitor-analysis/README.md +0 -0
  52. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/research/competitor-analysis/competitor-matrix.md +0 -0
  53. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/research/competitor-analysis/market-brief.md +0 -0
  54. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/research/competitor-analysis/positioning-map.md +0 -0
  55. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/research/competitor-analysis/swot-analysis.md +0 -0
  56. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/research/literature-review/inclusion-exclusion-criteria.md +0 -0
  57. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/research/literature-review/literature-matrix.md +0 -0
  58. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/research/literature-review/literature-review.md +0 -0
  59. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/research/literature-review/search-strategy.md +0 -0
  60. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/research/reference-repos-absorption.md +0 -0
  61. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/skeleton-completeness-checklist.md +0 -0
  62. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/test-results-report.md +0 -0
  63. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/usage-guide.md +0 -0
  64. {petfishframework-0.2.5 → petfishframework-0.3.0}/docs/validation-roadmap.md +0 -0
  65. {petfishframework-0.2.5 → petfishframework-0.3.0}/initialization-report.md +0 -0
  66. {petfishframework-0.2.5 → petfishframework-0.3.0}/mcp/README.md +0 -0
  67. {petfishframework-0.2.5 → petfishframework-0.3.0}/mcp/connection-checklist.md +0 -0
  68. {petfishframework-0.2.5 → petfishframework-0.3.0}/mcp/mcp-config.example.json +0 -0
  69. {petfishframework-0.2.5 → petfishframework-0.3.0}/opencode.json +0 -0
  70. {petfishframework-0.2.5 → petfishframework-0.3.0}/qa/code-review-checklist.md +0 -0
  71. {petfishframework-0.2.5 → petfishframework-0.3.0}/qa/qa-review.md +0 -0
  72. {petfishframework-0.2.5 → petfishframework-0.3.0}/qa/qc-gate-decision.md +0 -0
  73. {petfishframework-0.2.5 → petfishframework-0.3.0}/qa/test-plan.md +0 -0
  74. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/config.py +0 -0
  75. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/core/__init__.py +0 -0
  76. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/core/agent.py +0 -0
  77. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/core/compiled.py +0 -0
  78. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/core/contracts.py +0 -0
  79. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/core/conversation.py +0 -0
  80. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/core/events.py +0 -0
  81. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/core/session.py +0 -0
  82. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/core/structured.py +0 -0
  83. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/core/types.py +0 -0
  84. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/mcp/__init__.py +0 -0
  85. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/mcp/client.py +0 -0
  86. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/mcp/server.py +0 -0
  87. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/mcp/stdio_transport.py +0 -0
  88. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/mcp/wrapper.py +0 -0
  89. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/models/__init__.py +0 -0
  90. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/models/anthropic.py +0 -0
  91. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/models/fake.py +0 -0
  92. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/models/openai.py +0 -0
  93. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/observability/__init__.py +0 -0
  94. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/observability/sinks.py +0 -0
  95. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/permissions/__init__.py +0 -0
  96. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/reasoning/__init__.py +0 -0
  97. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/reasoning/lats.py +0 -0
  98. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/reasoning/llm_plus_p.py +0 -0
  99. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/reasoning/react.py +0 -0
  100. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/reliability/__init__.py +0 -0
  101. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/reliability/audit_report.py +0 -0
  102. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/reliability/cost.py +0 -0
  103. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/reliability/cost_report.py +0 -0
  104. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/reliability/pass_at_k.py +0 -0
  105. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/reliability/replay.py +0 -0
  106. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/reliability/retry.py +0 -0
  107. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/reliability/timeout.py +0 -0
  108. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/retrieval/__init__.py +0 -0
  109. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/retrieval/adaptive.py +0 -0
  110. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/retrieval/crag.py +0 -0
  111. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/retrieval/memory_store.py +0 -0
  112. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/tools/__init__.py +0 -0
  113. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/tools/agent_tool.py +0 -0
  114. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/tools/base.py +0 -0
  115. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/tools/calculator.py +0 -0
  116. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/tools/path_planner.py +0 -0
  117. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/tools/registry.py +0 -0
  118. {petfishframework-0.2.5 → petfishframework-0.3.0}/src/petfishframework/tools/word_sorter.py +0 -0
  119. {petfishframework-0.2.5 → petfishframework-0.3.0}/tasks/backlog.md +0 -0
  120. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/integration/__init__.py +0 -0
  121. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/integration/test_anthropic_integration.py +0 -0
  122. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/integration/test_openai_integration.py +0 -0
  123. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_agent_tool.py +0 -0
  124. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_anthropic_adapter.py +0 -0
  125. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_api_agent_lifecycle.py +0 -0
  126. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_api_edge_cases.py +0 -0
  127. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_api_integration.py +0 -0
  128. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_api_public_surface.py +0 -0
  129. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_api_type_safety.py +0 -0
  130. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_async.py +0 -0
  131. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_audit_report_v2.py +0 -0
  132. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_conversation.py +0 -0
  133. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_degrade.py +0 -0
  134. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_degrade_failclosed.py +0 -0
  135. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_enterprise_demo.py +0 -0
  136. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_m2_m3_m4.py +0 -0
  137. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_mcp.py +0 -0
  138. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_openai_adapter.py +0 -0
  139. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_pass_at_k.py +0 -0
  140. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_permission_semantics.py +0 -0
  141. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_quickstart_smoke.py +0 -0
  142. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_replay.py +0 -0
  143. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_retrieval.py +0 -0
  144. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_retry.py +0 -0
  145. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_skeleton.py +0 -0
  146. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_stdio_transport.py +0 -0
  147. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_streaming.py +0 -0
  148. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_structured.py +0 -0
  149. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_tool_registry.py +0 -0
  150. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_v016_semantics.py +0 -0
  151. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_v018.py +0 -0
  152. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_v019.py +0 -0
  153. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_v2_interface_compatibility.py +0 -0
  154. {petfishframework-0.2.5 → petfishframework-0.3.0}/tests/test_v2_lats.py +0 -0
  155. {petfishframework-0.2.5 → petfishframework-0.3.0}/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.2.5
3
+ Version: 0.3.0
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
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
35
36
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/kylecui/petfishFramework/blob/master/LICENSE)
36
- [![Tests: 234](https://img.shields.io/badge/tests-234-brightgreen.svg)](https://github.com/kylecui/petfishFramework/tree/master/tests/)
37
+ [![Tests: 259](https://img.shields.io/badge/tests-259-brightgreen.svg)](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
 
@@ -218,7 +219,7 @@ Run: `python examples/05_enterprise_expense.py`
218
219
 
219
220
  - **v0.2.x** (current): Core runtime, permission semantics, enterprise PoC, Trusted Publishing ✅
220
221
  - **v0.2.x**: Enterprise agent examples, structured audit reports
221
- - **v0.3.x**: Policy engine (YAML), credential broker
222
+ - **v0.3.x**: Policy engine (YAML), credential broker
222
223
  - **v0.4.x**: Production hardening, deployment guides
223
224
 
224
225
  ## Current Limitations
@@ -239,6 +240,8 @@ petfishFramework is **Alpha**. API may change before v1.0.
239
240
  | MCP client stdio | ✅ Available |
240
241
  | MCP server mode | 📋 Planned |
241
242
  | Structured output / conversation memory | ✅ Available |
243
+ | YAML Policy Engine | ✅ Available |
244
+ | Credential Broker | ✅ Available |
242
245
  | LATS / LLM+P | ⚠️ Lightweight implementations |
243
246
  | CRAG / Adaptive-RAG | ⚠️ Lightweight reference implementations |
244
247
 
@@ -248,7 +251,7 @@ petfishFramework is **Alpha**. API may change before v1.0.
248
251
  git clone https://github.com/kylecui/petfishFramework.git
249
252
  cd petfishFramework
250
253
  uv sync --all-extras
251
- uv run pytest # 234 tests
254
+ uv run pytest # 259 tests
252
255
  uv run ruff check src/ tests/
253
256
  ```
254
257
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/kylecui/petfishFramework/blob/master/LICENSE)
7
- [![Tests: 234](https://img.shields.io/badge/tests-234-brightgreen.svg)](https://github.com/kylecui/petfishFramework/tree/master/tests/)
7
+ [![Tests: 259](https://img.shields.io/badge/tests-259-brightgreen.svg)](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
 
@@ -189,7 +189,7 @@ Run: `python examples/05_enterprise_expense.py`
189
189
 
190
190
  - **v0.2.x** (current): Core runtime, permission semantics, enterprise PoC, Trusted Publishing ✅
191
191
  - **v0.2.x**: Enterprise agent examples, structured audit reports
192
- - **v0.3.x**: Policy engine (YAML), credential broker
192
+ - **v0.3.x**: Policy engine (YAML), credential broker
193
193
  - **v0.4.x**: Production hardening, deployment guides
194
194
 
195
195
  ## Current Limitations
@@ -210,6 +210,8 @@ petfishFramework is **Alpha**. API may change before v1.0.
210
210
  | MCP client stdio | ✅ Available |
211
211
  | MCP server mode | 📋 Planned |
212
212
  | Structured output / conversation memory | ✅ Available |
213
+ | YAML Policy Engine | ✅ Available |
214
+ | Credential Broker | ✅ Available |
213
215
  | LATS / LLM+P | ⚠️ Lightweight implementations |
214
216
  | CRAG / Adaptive-RAG | ⚠️ Lightweight reference implementations |
215
217
 
@@ -219,7 +221,7 @@ petfishFramework is **Alpha**. API may change before v1.0.
219
221
  git clone https://github.com/kylecui/petfishFramework.git
220
222
  cd petfishFramework
221
223
  uv sync --all-extras
222
- uv run pytest # 234 tests
224
+ uv run pytest # 259 tests
223
225
  uv run ruff check src/ tests/
224
226
  ```
225
227
 
@@ -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.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "petfishframework"
7
- version = "0.2.5"
7
+ version = "0.3.0"
8
8
  description = "A lightweight runtime framework for reliable, auditable, budget-aware, and permission-aware AI agents."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -21,7 +21,9 @@ classifiers = [
21
21
  "Programming Language :: Python :: 3.12",
22
22
  "Topic :: Software Development :: Libraries :: Application Frameworks",
23
23
  ]
24
- dependencies = []
24
+ dependencies = [
25
+ "pyyaml>=6.0.3",
26
+ ]
25
27
 
26
28
  [project.urls]
27
29
  Homepage = "https://github.com/kylecui/petfishFramework"
@@ -10,11 +10,12 @@ from .core.agent import Agent
10
10
  from .core.contracts import Tool
11
11
  from .core.types import Budget, BudgetExceeded, Result, Task
12
12
  from .permissions.model import DecisionEffect
13
+ from .policies import PolicyRule, YamlPolicy
13
14
  from .reasoning import LATS, LLMPlusP, ReAct
14
15
  from .reliability.replay import ReplayMode
15
16
  from .tools.base import BaseTool
16
17
 
17
- __version__ = "0.2.5"
18
+ __version__ = "0.3.0"
18
19
 
19
20
  __all__ = [
20
21
  "Agent",
@@ -25,8 +26,10 @@ __all__ = [
25
26
  "FrameworkConfig",
26
27
  "LATS",
27
28
  "LLMPlusP",
29
+ "PolicyRule",
28
30
  "ReAct",
29
31
  "ReplayMode",
32
+ "YamlPolicy",
30
33
  "Result",
31
34
  "Task",
32
35
  "Tool",
@@ -10,6 +10,7 @@ import copy
10
10
  from dataclasses import dataclass
11
11
  from typing import Any
12
12
 
13
+ from petfishframework.credentials import CredentialBroker
13
14
  from petfishframework.permissions.model import (
14
15
  AccessContext,
15
16
  Action,
@@ -79,6 +80,7 @@ class RuntimeEnvironment(Environment):
79
80
  policy: PermissionPolicy
80
81
  session_id: str = ""
81
82
  _accountant: CostAccountant | None = None
83
+ _credential_broker: CredentialBroker | None = None
82
84
 
83
85
  def __post_init__(self) -> None:
84
86
  if self._accountant is None:
@@ -133,6 +135,7 @@ class RuntimeEnvironment(Environment):
133
135
  effect, executed=False,
134
136
  )
135
137
  fallback_args = decision.fallback_args if decision.fallback_args is not None else args
138
+ self._maybe_inject_credential(fallback, fallback_args)
136
139
  result = fallback.execute(fallback_args)
137
140
  self.events.emit(
138
141
  "tool.degraded",
@@ -176,6 +179,7 @@ class RuntimeEnvironment(Environment):
176
179
  # Execute tool (ALLOW, PARTIAL_ALLOW, DEGRADE-without-fallback, MASK all execute)
177
180
  import time as _time
178
181
 
182
+ self._maybe_inject_credential(tool, args)
179
183
  start = _time.time()
180
184
  try:
181
185
  result = tool.execute(args)
@@ -221,6 +225,7 @@ class RuntimeEnvironment(Environment):
221
225
  effect, executed=False,
222
226
  )
223
227
  fallback_args = decision.fallback_args if decision.fallback_args is not None else args
228
+ self._maybe_inject_credential(fallback, fallback_args)
224
229
  if asyncio.iscoroutinefunction(fallback.execute):
225
230
  result = await fallback.execute(fallback_args)
226
231
  else:
@@ -263,6 +268,7 @@ class RuntimeEnvironment(Environment):
263
268
  if effect == DecisionEffect.MASK and decision.input_mask_fields:
264
269
  args = _apply_mask_to_dict(args, decision.input_mask_fields)
265
270
 
271
+ self._maybe_inject_credential(tool, args)
266
272
  if asyncio.iscoroutinefunction(tool.execute):
267
273
  result = await tool.execute(args)
268
274
  else:
@@ -316,6 +322,21 @@ class RuntimeEnvironment(Environment):
316
322
  return t
317
323
  return None
318
324
 
325
+ def _maybe_inject_credential(self, tool: Tool, args: dict) -> None:
326
+ """Issue and inject a scoped credential token when a tool requires one.
327
+
328
+ The token value is never materialized as a plain string inside the
329
+ environment; it is stored as a ScopedToken whose repr/str hide the
330
+ underlying secret. If no broker is configured, the tool proceeds
331
+ without credentials (graceful degradation).
332
+ """
333
+ if not getattr(tool, "requires_credentials", False):
334
+ return
335
+ if self._credential_broker is None:
336
+ return
337
+ token = self._credential_broker.issue_token(tool.name, tool_name=tool.name)
338
+ args["_credential_token"] = token
339
+
319
340
  def _prepare_tool_call(self, ref: ToolRef, args: dict) -> tuple[Tool | None, Any]:
320
341
  """Permission gate for tool calls; shared by sync and async paths."""
321
342
  tool = self._find_tool(ref.name)
@@ -0,0 +1,7 @@
1
+ """CredentialBroker public API."""
2
+ from __future__ import annotations
3
+
4
+ from .broker import CredentialBroker
5
+ from .token import ScopedToken
6
+
7
+ __all__ = ["CredentialBroker", "ScopedToken"]