petfishframework 0.2.4__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.4 → petfishframework-0.3.0}/CHANGELOG.md +11 -0
  3. {petfishframework-0.2.4 → petfishframework-0.3.0}/PKG-INFO +8 -5
  4. {petfishframework-0.2.4 → petfishframework-0.3.0}/README.md +6 -4
  5. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/api.md +84 -2
  6. petfishframework-0.3.0/docs/release-checklist.md +73 -0
  7. {petfishframework-0.2.4 → petfishframework-0.3.0}/pyproject.toml +4 -2
  8. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/__init__.py +4 -1
  9. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/core/environment.py +21 -0
  10. petfishframework-0.3.0/src/petfishframework/credentials/__init__.py +7 -0
  11. petfishframework-0.3.0/src/petfishframework/credentials/broker.py +70 -0
  12. petfishframework-0.3.0/src/petfishframework/credentials/token.py +37 -0
  13. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/permissions/model.py +2 -0
  14. petfishframework-0.3.0/src/petfishframework/policies/__init__.py +12 -0
  15. petfishframework-0.3.0/src/petfishframework/policies/conditions.py +176 -0
  16. petfishframework-0.3.0/src/petfishframework/policies/engine.py +130 -0
  17. petfishframework-0.3.0/src/petfishframework/policies/rule.py +32 -0
  18. petfishframework-0.3.0/src/petfishframework/policies/test_runner.py +167 -0
  19. petfishframework-0.3.0/tests/policies/enterprise-expense.tests.yaml +45 -0
  20. petfishframework-0.3.0/tests/test_credential_broker.py +81 -0
  21. petfishframework-0.3.0/tests/test_credential_integration.py +107 -0
  22. petfishframework-0.3.0/tests/test_yaml_policy.py +325 -0
  23. petfishframework-0.3.0/tests/test_yaml_policy_suite.py +81 -0
  24. {petfishframework-0.2.4 → petfishframework-0.3.0}/uv.lock +69 -1
  25. {petfishframework-0.2.4 → petfishframework-0.3.0}/.env.example +0 -0
  26. {petfishframework-0.2.4 → petfishframework-0.3.0}/.github/workflows/ci.yml +0 -0
  27. {petfishframework-0.2.4 → petfishframework-0.3.0}/.github/workflows/publish.yml +0 -0
  28. {petfishframework-0.2.4 → petfishframework-0.3.0}/.gitignore +0 -0
  29. {petfishframework-0.2.4 → petfishframework-0.3.0}/.sisyphus/plans/v0.1.6-plan.md +0 -0
  30. {petfishframework-0.2.4 → petfishframework-0.3.0}/.sisyphus/plans/v0.2.0-plan.md +0 -0
  31. {petfishframework-0.2.4 → petfishframework-0.3.0}/AGENTS.md +0 -0
  32. {petfishframework-0.2.4 → petfishframework-0.3.0}/AGENTS.md.new +0 -0
  33. {petfishframework-0.2.4 → petfishframework-0.3.0}/CONTRIBUTING.md +0 -0
  34. {petfishframework-0.2.4 → petfishframework-0.3.0}/LICENSE +0 -0
  35. {petfishframework-0.2.4 → petfishframework-0.3.0}/SECURITY.md +0 -0
  36. {petfishframework-0.2.4 → petfishframework-0.3.0}/conftest.py +0 -0
  37. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/architecture.md +0 -0
  38. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/benchmark-results.md +0 -0
  39. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/development-plan.md +0 -0
  40. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/development.md +0 -0
  41. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/enterprise-expense-demo.md +0 -0
  42. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_0_1_4_playground_recommendations.md +0 -0
  43. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_playground_report.md +0 -0
  44. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_production_readiness_roadmap.md +0 -0
  45. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_recommendations.md +0 -0
  46. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_v0_1_6_playground_review.md +0 -0
  47. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_v0_1_7_review.md +0 -0
  48. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_v0_1_8_review.md +0 -0
  49. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/feedbacks/archives/petfishframework_v0_2_1_review.md +0 -0
  50. {petfishframework-0.2.4/docs/feedbacks → petfishframework-0.3.0/docs/feedbacks/archives}/petfishframework_v0_2_2_review.md +0 -0
  51. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/research/competitor-analysis/README.md +0 -0
  52. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/research/competitor-analysis/competitor-matrix.md +0 -0
  53. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/research/competitor-analysis/market-brief.md +0 -0
  54. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/research/competitor-analysis/positioning-map.md +0 -0
  55. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/research/competitor-analysis/swot-analysis.md +0 -0
  56. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/research/literature-review/inclusion-exclusion-criteria.md +0 -0
  57. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/research/literature-review/literature-matrix.md +0 -0
  58. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/research/literature-review/literature-review.md +0 -0
  59. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/research/literature-review/search-strategy.md +0 -0
  60. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/research/reference-repos-absorption.md +0 -0
  61. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/skeleton-completeness-checklist.md +0 -0
  62. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/test-results-report.md +0 -0
  63. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/usage-guide.md +0 -0
  64. {petfishframework-0.2.4 → petfishframework-0.3.0}/docs/validation-roadmap.md +0 -0
  65. {petfishframework-0.2.4 → petfishframework-0.3.0}/initialization-report.md +0 -0
  66. {petfishframework-0.2.4 → petfishframework-0.3.0}/mcp/README.md +0 -0
  67. {petfishframework-0.2.4 → petfishframework-0.3.0}/mcp/connection-checklist.md +0 -0
  68. {petfishframework-0.2.4 → petfishframework-0.3.0}/mcp/mcp-config.example.json +0 -0
  69. {petfishframework-0.2.4 → petfishframework-0.3.0}/opencode.json +0 -0
  70. {petfishframework-0.2.4 → petfishframework-0.3.0}/qa/code-review-checklist.md +0 -0
  71. {petfishframework-0.2.4 → petfishframework-0.3.0}/qa/qa-review.md +0 -0
  72. {petfishframework-0.2.4 → petfishframework-0.3.0}/qa/qc-gate-decision.md +0 -0
  73. {petfishframework-0.2.4 → petfishframework-0.3.0}/qa/test-plan.md +0 -0
  74. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/config.py +0 -0
  75. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/core/__init__.py +0 -0
  76. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/core/agent.py +0 -0
  77. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/core/compiled.py +0 -0
  78. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/core/contracts.py +0 -0
  79. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/core/conversation.py +0 -0
  80. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/core/events.py +0 -0
  81. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/core/session.py +0 -0
  82. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/core/structured.py +0 -0
  83. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/core/types.py +0 -0
  84. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/mcp/__init__.py +0 -0
  85. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/mcp/client.py +0 -0
  86. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/mcp/server.py +0 -0
  87. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/mcp/stdio_transport.py +0 -0
  88. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/mcp/wrapper.py +0 -0
  89. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/models/__init__.py +0 -0
  90. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/models/anthropic.py +0 -0
  91. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/models/fake.py +0 -0
  92. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/models/openai.py +0 -0
  93. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/observability/__init__.py +0 -0
  94. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/observability/sinks.py +0 -0
  95. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/permissions/__init__.py +0 -0
  96. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/reasoning/__init__.py +0 -0
  97. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/reasoning/lats.py +0 -0
  98. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/reasoning/llm_plus_p.py +0 -0
  99. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/reasoning/react.py +0 -0
  100. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/reliability/__init__.py +0 -0
  101. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/reliability/audit_report.py +0 -0
  102. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/reliability/cost.py +0 -0
  103. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/reliability/cost_report.py +0 -0
  104. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/reliability/pass_at_k.py +0 -0
  105. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/reliability/replay.py +0 -0
  106. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/reliability/retry.py +0 -0
  107. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/reliability/timeout.py +0 -0
  108. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/retrieval/__init__.py +0 -0
  109. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/retrieval/adaptive.py +0 -0
  110. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/retrieval/crag.py +0 -0
  111. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/retrieval/memory_store.py +0 -0
  112. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/tools/__init__.py +0 -0
  113. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/tools/agent_tool.py +0 -0
  114. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/tools/base.py +0 -0
  115. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/tools/calculator.py +0 -0
  116. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/tools/path_planner.py +0 -0
  117. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/tools/registry.py +0 -0
  118. {petfishframework-0.2.4 → petfishframework-0.3.0}/src/petfishframework/tools/word_sorter.py +0 -0
  119. {petfishframework-0.2.4 → petfishframework-0.3.0}/tasks/backlog.md +0 -0
  120. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/integration/__init__.py +0 -0
  121. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/integration/test_anthropic_integration.py +0 -0
  122. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/integration/test_openai_integration.py +0 -0
  123. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_agent_tool.py +0 -0
  124. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_anthropic_adapter.py +0 -0
  125. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_api_agent_lifecycle.py +0 -0
  126. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_api_edge_cases.py +0 -0
  127. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_api_integration.py +0 -0
  128. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_api_public_surface.py +0 -0
  129. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_api_type_safety.py +0 -0
  130. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_async.py +0 -0
  131. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_audit_report_v2.py +0 -0
  132. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_conversation.py +0 -0
  133. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_degrade.py +0 -0
  134. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_degrade_failclosed.py +0 -0
  135. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_enterprise_demo.py +0 -0
  136. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_m2_m3_m4.py +0 -0
  137. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_mcp.py +0 -0
  138. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_openai_adapter.py +0 -0
  139. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_pass_at_k.py +0 -0
  140. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_permission_semantics.py +0 -0
  141. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_quickstart_smoke.py +0 -0
  142. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_replay.py +0 -0
  143. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_retrieval.py +0 -0
  144. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_retry.py +0 -0
  145. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_skeleton.py +0 -0
  146. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_stdio_transport.py +0 -0
  147. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_streaming.py +0 -0
  148. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_structured.py +0 -0
  149. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_tool_registry.py +0 -0
  150. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_v016_semantics.py +0 -0
  151. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_v018.py +0 -0
  152. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_v019.py +0 -0
  153. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_v2_interface_compatibility.py +0 -0
  154. {petfishframework-0.2.4 → petfishframework-0.3.0}/tests/test_v2_lats.py +0 -0
  155. {petfishframework-0.2.4 → 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.4
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
 
@@ -216,9 +217,9 @@ Run: `python examples/05_enterprise_expense.py`
216
217
 
217
218
  ## Roadmap
218
219
 
219
- - **v0.1.x** (current): Core runtime, permission semantics, quickstart verified
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
 
@@ -187,9 +187,9 @@ Run: `python examples/05_enterprise_expense.py`
187
187
 
188
188
  ## Roadmap
189
189
 
190
- - **v0.1.x** (current): Core runtime, permission semantics, quickstart verified
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
 
@@ -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.2.2. Every signature, field, and example below is derived from the source code and from the tests that exercise it.
3
+ This document is the authoritative reference for the public API of `petfishFramework` v0.2.5. 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
 
@@ -327,7 +327,7 @@ class Session:
327
327
 
328
328
  `run` builds the `RuntimeEnvironment` and `RunContext`, emits `session.start`, executes the strategy, attaches accumulated `Usage` and `session_id`, and emits `session.end`.
329
329
 
330
- `replay` accepts an optional `ReplayMode` argument. In v0.2.2 all modes return the stored event log; full deterministic replay via `RecordingEnvironment` and `ReplayEnvironment` is available in `petfishframework.reliability.replay`.
330
+ `replay` accepts an optional `ReplayMode` argument. In v0.2.5 all modes return the stored event log; full deterministic replay via `RecordingEnvironment` and `ReplayEnvironment` is available in `petfishframework.reliability.replay`.
331
331
 
332
332
  ```python
333
333
  agent = Agent(model=FakeModel(responses=(ModelResponse(content="ok"),)))
@@ -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.
@@ -0,0 +1,73 @@
1
+ # Release Checklist
2
+
3
+ > Run before every release tag. Prevents document drift and version inconsistencies.
4
+
5
+ ## Pre-release checks
6
+
7
+ ```bash
8
+ # 1. No stale version references
9
+ grep -R "v0\.1\b" README.md docs/ pyproject.toml src/
10
+ # Expected: only CHANGELOG historical entries
11
+
12
+ # 2. No "coming in" placeholders
13
+ grep -R "coming in" README.md docs/ examples/
14
+ # Expected: no output
15
+
16
+ # 3. Badge URL matches test count
17
+ grep "badge/tests" README.md
18
+ # Expected: current test count in both text and URL
19
+
20
+ # 4. api.md version matches release version
21
+ grep "authoritative reference" docs/api.md
22
+ # Expected: current version number
23
+
24
+ # 5. All tests pass
25
+ uv run pytest tests/ -q
26
+ # Expected: all passed
27
+
28
+ # 6. Ruff clean
29
+ uv run ruff check src/ tests/ examples/
30
+ # Expected: All checks passed
31
+ ```
32
+
33
+ ## Release process (Trusted Publishing — NO API TOKEN)
34
+
35
+ ```bash
36
+ # 1. Bump version in pyproject.toml + src/petfishframework/__init__.py
37
+ # 2. Update CHANGELOG.md
38
+ # 3. Commit
39
+ git add -A && git commit -m "release: vX.Y.Z"
40
+
41
+ # 4. Push master
42
+ git push origin master
43
+
44
+ # 5. Tag (MUST start with 'v')
45
+ git tag vX.Y.Z
46
+ git push origin vX.Y.Z
47
+
48
+ # → GitHub Actions CI runs tests
49
+ # → GitHub Actions publish.yml builds + publishes via OIDC
50
+ # → PyPI receives the package with NO API TOKEN
51
+ # → Verify at https://pypi.org/project/petfishframework/
52
+ ```
53
+
54
+ ## Post-release checks
55
+
56
+ ```bash
57
+ # 1. PyPI shows new version
58
+ curl -s https://pypi.org/pypi/petfishframework/json | python -c "import sys,json; print(json.load(sys.stdin)['info']['version'])"
59
+
60
+ # 2. pip install works
61
+ pip install petfishframework==X.Y.Z
62
+
63
+ # 3. GitHub Actions both green
64
+ gh run list --repo kylecui/petfishFramework --limit 2
65
+ # Expected: CI success + Publish success
66
+ ```
67
+
68
+ ## NEVER
69
+
70
+ - ❌ Do NOT use twine (Trusted Publishing replaces it)
71
+ - ❌ Do NOT use API tokens (revoked; OIDC only)
72
+ - ❌ Do NOT tag without `v` prefix (workflow only triggers on `v*`)
73
+ - ❌ Do NOT skip the pre-release grep checks
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "petfishframework"
7
- version = "0.2.4"
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.4"
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",