petfishframework 0.3.2__tar.gz → 0.4.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 (171) hide show
  1. petfishframework-0.4.0/.sisyphus/plans/v0.4.0-plan.md +236 -0
  2. {petfishframework-0.3.2 → petfishframework-0.4.0}/CHANGELOG.md +62 -0
  3. petfishframework-0.4.0/Dockerfile +7 -0
  4. {petfishframework-0.3.2 → petfishframework-0.4.0}/PKG-INFO +12 -7
  5. {petfishframework-0.3.2 → petfishframework-0.4.0}/README.md +6 -6
  6. petfishframework-0.4.0/docker-compose.yml +8 -0
  7. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/api.md +176 -1
  8. petfishframework-0.4.0/docs/deployment.md +181 -0
  9. petfishframework-0.4.0/docs/release-protocol.md +86 -0
  10. petfishframework-0.4.0/docs/threat-model.md +189 -0
  11. {petfishframework-0.3.2 → petfishframework-0.4.0}/pyproject.toml +3 -1
  12. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/__init__.py +1 -1
  13. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/core/environment.py +12 -0
  14. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/core/session.py +38 -4
  15. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/credentials/__init__.py +2 -1
  16. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/credentials/broker.py +12 -0
  17. petfishframework-0.4.0/src/petfishframework/credentials/vault_adapter.py +87 -0
  18. petfishframework-0.4.0/src/petfishframework/observability/otel_sink.py +111 -0
  19. petfishframework-0.4.0/src/petfishframework/observability/siem_sink.py +113 -0
  20. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/reliability/__init__.py +4 -0
  21. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/reliability/replay.py +109 -0
  22. petfishframework-0.4.0/tests/test_otel_sink.py +87 -0
  23. petfishframework-0.4.0/tests/test_replay_rerun.py +162 -0
  24. petfishframework-0.4.0/tests/test_replay_resume.py +185 -0
  25. petfishframework-0.4.0/tests/test_siem_export.py +114 -0
  26. petfishframework-0.4.0/tests/test_vault_adapter.py +79 -0
  27. {petfishframework-0.3.2 → petfishframework-0.4.0}/uv.lock +67 -6
  28. {petfishframework-0.3.2 → petfishframework-0.4.0}/.env.example +0 -0
  29. {petfishframework-0.3.2 → petfishframework-0.4.0}/.github/workflows/ci.yml +0 -0
  30. {petfishframework-0.3.2 → petfishframework-0.4.0}/.github/workflows/publish.yml +0 -0
  31. {petfishframework-0.3.2 → petfishframework-0.4.0}/.gitignore +0 -0
  32. {petfishframework-0.3.2 → petfishframework-0.4.0}/.sisyphus/plans/v0.1.6-plan.md +0 -0
  33. {petfishframework-0.3.2 → petfishframework-0.4.0}/.sisyphus/plans/v0.2.0-plan.md +0 -0
  34. {petfishframework-0.3.2 → petfishframework-0.4.0}/.sisyphus/plans/v0.3.0-plan.md +0 -0
  35. {petfishframework-0.3.2 → petfishframework-0.4.0}/AGENTS.md +0 -0
  36. {petfishframework-0.3.2 → petfishframework-0.4.0}/AGENTS.md.new +0 -0
  37. {petfishframework-0.3.2 → petfishframework-0.4.0}/CONTRIBUTING.md +0 -0
  38. {petfishframework-0.3.2 → petfishframework-0.4.0}/LICENSE +0 -0
  39. {petfishframework-0.3.2 → petfishframework-0.4.0}/SECURITY.md +0 -0
  40. {petfishframework-0.3.2 → petfishframework-0.4.0}/conftest.py +0 -0
  41. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/architecture.md +0 -0
  42. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/benchmark-results.md +0 -0
  43. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/development-plan.md +0 -0
  44. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/development.md +0 -0
  45. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/enterprise-expense-demo.md +0 -0
  46. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/feedbacks/archives/petfishframework_0_1_4_playground_recommendations.md +0 -0
  47. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/feedbacks/archives/petfishframework_playground_report.md +0 -0
  48. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/feedbacks/archives/petfishframework_production_readiness_roadmap.md +0 -0
  49. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/feedbacks/archives/petfishframework_recommendations.md +0 -0
  50. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/feedbacks/archives/petfishframework_v0_1_6_playground_review.md +0 -0
  51. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/feedbacks/archives/petfishframework_v0_1_7_review.md +0 -0
  52. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/feedbacks/archives/petfishframework_v0_1_8_review.md +0 -0
  53. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/feedbacks/archives/petfishframework_v0_2_1_review.md +0 -0
  54. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/feedbacks/archives/petfishframework_v0_2_2_review.md +0 -0
  55. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/feedbacks/petfishframework_v0_3_0_review.md +0 -0
  56. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/release-checklist.md +0 -0
  57. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/research/competitor-analysis/README.md +0 -0
  58. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/research/competitor-analysis/competitor-matrix.md +0 -0
  59. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/research/competitor-analysis/market-brief.md +0 -0
  60. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/research/competitor-analysis/positioning-map.md +0 -0
  61. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/research/competitor-analysis/swot-analysis.md +0 -0
  62. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/research/literature-review/inclusion-exclusion-criteria.md +0 -0
  63. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/research/literature-review/literature-matrix.md +0 -0
  64. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/research/literature-review/literature-review.md +0 -0
  65. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/research/literature-review/search-strategy.md +0 -0
  66. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/research/reference-repos-absorption.md +0 -0
  67. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/skeleton-completeness-checklist.md +0 -0
  68. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/test-results-report.md +0 -0
  69. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/usage-guide.md +0 -0
  70. {petfishframework-0.3.2 → petfishframework-0.4.0}/docs/validation-roadmap.md +0 -0
  71. {petfishframework-0.3.2 → petfishframework-0.4.0}/initialization-report.md +0 -0
  72. {petfishframework-0.3.2 → petfishframework-0.4.0}/mcp/README.md +0 -0
  73. {petfishframework-0.3.2 → petfishframework-0.4.0}/mcp/connection-checklist.md +0 -0
  74. {petfishframework-0.3.2 → petfishframework-0.4.0}/mcp/mcp-config.example.json +0 -0
  75. {petfishframework-0.3.2 → petfishframework-0.4.0}/opencode.json +0 -0
  76. {petfishframework-0.3.2 → petfishframework-0.4.0}/qa/code-review-checklist.md +0 -0
  77. {petfishframework-0.3.2 → petfishframework-0.4.0}/qa/qa-review.md +0 -0
  78. {petfishframework-0.3.2 → petfishframework-0.4.0}/qa/qc-gate-decision.md +0 -0
  79. {petfishframework-0.3.2 → petfishframework-0.4.0}/qa/test-plan.md +0 -0
  80. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/config.py +0 -0
  81. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/core/__init__.py +0 -0
  82. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/core/agent.py +0 -0
  83. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/core/compiled.py +0 -0
  84. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/core/contracts.py +0 -0
  85. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/core/conversation.py +0 -0
  86. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/core/events.py +0 -0
  87. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/core/structured.py +0 -0
  88. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/core/types.py +0 -0
  89. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/credentials/token.py +0 -0
  90. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/mcp/__init__.py +0 -0
  91. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/mcp/client.py +0 -0
  92. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/mcp/server.py +0 -0
  93. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/mcp/stdio_transport.py +0 -0
  94. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/mcp/wrapper.py +0 -0
  95. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/models/__init__.py +0 -0
  96. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/models/anthropic.py +0 -0
  97. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/models/fake.py +0 -0
  98. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/models/openai.py +0 -0
  99. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/observability/__init__.py +0 -0
  100. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/observability/sinks.py +0 -0
  101. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/permissions/__init__.py +0 -0
  102. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/permissions/model.py +0 -0
  103. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/policies/__init__.py +0 -0
  104. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/policies/conditions.py +0 -0
  105. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/policies/engine.py +0 -0
  106. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/policies/rule.py +0 -0
  107. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/policies/test_runner.py +0 -0
  108. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/policies/validator.py +0 -0
  109. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/reasoning/__init__.py +0 -0
  110. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/reasoning/lats.py +0 -0
  111. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/reasoning/llm_plus_p.py +0 -0
  112. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/reasoning/react.py +0 -0
  113. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/reliability/audit_report.py +0 -0
  114. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/reliability/cost.py +0 -0
  115. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/reliability/cost_report.py +0 -0
  116. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/reliability/pass_at_k.py +0 -0
  117. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/reliability/retry.py +0 -0
  118. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/reliability/timeout.py +0 -0
  119. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/retrieval/__init__.py +0 -0
  120. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/retrieval/adaptive.py +0 -0
  121. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/retrieval/crag.py +0 -0
  122. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/retrieval/memory_store.py +0 -0
  123. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/tools/__init__.py +0 -0
  124. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/tools/agent_tool.py +0 -0
  125. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/tools/base.py +0 -0
  126. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/tools/calculator.py +0 -0
  127. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/tools/path_planner.py +0 -0
  128. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/tools/registry.py +0 -0
  129. {petfishframework-0.3.2 → petfishframework-0.4.0}/src/petfishframework/tools/word_sorter.py +0 -0
  130. {petfishframework-0.3.2 → petfishframework-0.4.0}/tasks/backlog.md +0 -0
  131. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/integration/__init__.py +0 -0
  132. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/integration/test_anthropic_integration.py +0 -0
  133. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/integration/test_openai_integration.py +0 -0
  134. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/policies/enterprise-expense.tests.yaml +0 -0
  135. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_agent_tool.py +0 -0
  136. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_anthropic_adapter.py +0 -0
  137. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_api_agent_lifecycle.py +0 -0
  138. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_api_edge_cases.py +0 -0
  139. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_api_integration.py +0 -0
  140. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_api_public_surface.py +0 -0
  141. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_api_type_safety.py +0 -0
  142. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_async.py +0 -0
  143. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_audit_report_v2.py +0 -0
  144. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_conversation.py +0 -0
  145. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_credential_broker.py +0 -0
  146. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_credential_integration.py +0 -0
  147. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_degrade.py +0 -0
  148. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_degrade_failclosed.py +0 -0
  149. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_enterprise_demo.py +0 -0
  150. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_m2_m3_m4.py +0 -0
  151. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_mcp.py +0 -0
  152. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_openai_adapter.py +0 -0
  153. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_pass_at_k.py +0 -0
  154. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_permission_semantics.py +0 -0
  155. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_quickstart_smoke.py +0 -0
  156. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_replay.py +0 -0
  157. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_retrieval.py +0 -0
  158. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_retry.py +0 -0
  159. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_skeleton.py +0 -0
  160. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_stdio_transport.py +0 -0
  161. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_streaming.py +0 -0
  162. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_structured.py +0 -0
  163. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_tool_registry.py +0 -0
  164. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_v016_semantics.py +0 -0
  165. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_v018.py +0 -0
  166. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_v019.py +0 -0
  167. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_v2_interface_compatibility.py +0 -0
  168. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_v2_lats.py +0 -0
  169. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_v2_llm_plus_p.py +0 -0
  170. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_yaml_policy.py +0 -0
  171. {petfishframework-0.3.2 → petfishframework-0.4.0}/tests/test_yaml_policy_suite.py +0 -0
@@ -0,0 +1,236 @@
1
+ # petfishFramework v0.4.0 开发计划
2
+
3
+ > 基于 v0.3.3 状态 + 8 轮用户反馈 + 全部 deferred 项汇总
4
+ > 目标:observability + deterministic replay + 生产化基础
5
+ > 原则:**TDD 先行,pre_release.py 门禁,文档同步作为完成条件**
6
+
7
+ ## v0.4.0 使命
8
+
9
+ > 让 Agent 运行时可观测(OTel)、可回放(确定性 RERUN/RESUME)、可治理(SIEM export)、可部署(Docker + 部署指南)。
10
+
11
+ ## Deferred 项汇总(从全部反馈中提取)
12
+
13
+ | 来源 | Deferred 项 | v0.4.0 目标 |
14
+ |---|---|---|
15
+ | v0.3.0 §9.2 | deterministic rerun / resume | ✅ 实现 |
16
+ | v0.3.0 §9.2 | MCP server mode | ⏳ v0.5.0(需完整 MCP server 实现) |
17
+ | v0.3.0 §9.2 | deployment guide | ✅ Dockerfile + 部署文档 |
18
+ | v0.3.0 §9.2 | OpenTelemetry | ✅ OTelSink |
19
+ | v0.3.0 §9.2 | SIEM export | ✅ JSON-Lines trace export |
20
+ | v0.3.0 §P2 | Vault adapter | ✅ HashiCorp Vault adapter (可选) |
21
+ | v0.3.0 §P2 | threat model | ✅ 威胁模型文档 |
22
+ | v0.3.0 §P2 | SBOM | ✅ cyclonedx 生成 |
23
+ | v0.3.0 §P2 | signed release | ✅ sigstore cosign |
24
+ | 多轮 | README Current Limitations 中 planned 项 | 清理为 available |
25
+
26
+ ---
27
+
28
+ ## Phase A: Deterministic Replay (RESUME + RERUN)
29
+
30
+ ### A1: RESUME — 检查点恢复
31
+
32
+ **目标**:从检查点恢复执行,重注入已记录的模型响应直到检查点,之后继续新鲜调用。
33
+
34
+ #### TDD 测试先行
35
+
36
+ ```python
37
+ # tests/test_replay_resume.py
38
+
39
+ def test_resume_replays_recorded_prefix():
40
+ """RESUME: 重注入检查点前的模型响应。"""
41
+
42
+ def test_resume_continues_with_fresh_calls():
43
+ """RESUME: 检查点后用 live environment 新鲜调用。"""
44
+
45
+ def test_resume_from_mid_session():
46
+ """RESUME: 从 Session 中间的检查点恢复。"""
47
+ ```
48
+
49
+ #### 实现
50
+
51
+ - `ResumableEnvironment`(已有 v0.1.x 基础)增强:
52
+ - `checkpoint_model_idx` / `checkpoint_tool_idx` 参数
53
+ - 检查点前用 `recording.model_responses[i]` 重注入
54
+ - 检查点后切换到 `live_env`
55
+ - `Session.checkpoint()` 发出 `session.checkpoint` 事件 + 记录当前 model/tool 索引
56
+ - `Session.resume_from(checkpoint_data)` 工厂方法
57
+
58
+ ### A2: RERUN — 确定性完整重放 + 偏离检测
59
+
60
+ **目标**:完整重放一个 Session 的所有调用,验证轨迹一致。偏离时报告差异。
61
+
62
+ #### TDD 测试先行
63
+
64
+ ```python
65
+ # tests/test_replay_rerun.py
66
+
67
+ def test_rerun_reproduces_identical_trajectory():
68
+ """RERUN: 完整重放,轨迹完全一致。"""
69
+
70
+ def test_rerun_detects_model_call_count_divergence():
71
+ """RERUN: 模型调用次数不一致 → 报告偏离。"""
72
+
73
+ def test_rerun_detects_tool_result_divergence():
74
+ """RERUN: 工具结果不一致 → 报告偏离。"""
75
+ ```
76
+
77
+ #### 实现
78
+
79
+ - `RerunEnvironment`:封装 `RecordingEnvironment` + `ReplayEnvironment`
80
+ - `rerun() -> RerunResult`:重放 + 比对
81
+ - `RerunResult.matches: bool` + `divergences: list[str]`
82
+ - 检测:调用次数偏离、工具名偏离、结果值偏离
83
+
84
+ ---
85
+
86
+ ## Phase B: Observability
87
+
88
+ ### B1: OpenTelemetry Sink
89
+
90
+ **目标**:Session 事件 → OTel spans,可接入 Jaeger/Zipkin/DataDog。
91
+
92
+ #### TDD 测试先行
93
+
94
+ ```python
95
+ # tests/test_otel_sink.py
96
+
97
+ def test_otel_sink_creates_span_per_tool_call():
98
+ """每个 tool.called 事件创建一个 span。"""
99
+
100
+ def test_otel_sink_creates_span_per_model_call():
101
+ """每个 model.called 事件创建一个 span。"""
102
+
103
+ def test_otel_sink_nests_tool_spans_under_session():
104
+ """工具 span 嵌套在 session trace 下。"""
105
+ ```
106
+
107
+ #### 实现
108
+
109
+ - `observability/otel_sink.py` — `OTelSink`:
110
+ - 接收 EventEmitter 事件
111
+ - Session → OTel Trace
112
+ - model.called → span (attributes: model name, tokens, cost)
113
+ - tool.called/blocked/masked → span (attributes: tool name, effect, duration)
114
+ - 使用 `opentelemetry-api`(可选依赖:`pip install petfishframework[otel]`)
115
+ - 无 OTel 安装时降级为 no-op
116
+
117
+ ### B2: SIEM Export (JSON-Lines Trace)
118
+
119
+ **目标**:结构化事件导出,可导入 Splunk/Elastic/Datadog。
120
+
121
+ #### TDD 测试先行
122
+
123
+ ```python
124
+ # tests/test_siem_export.py
125
+
126
+ def test_siem_export_produces_jsonlines():
127
+ """导出为 JSON-Lines 格式。"""
128
+
129
+ def test_siem_export_redacts_credentials():
130
+ """导出中不含 secret。"""
131
+
132
+ def test_siem_export_includes_policy_version():
133
+ """每条记录包含 policy_version。"""
134
+ ```
135
+
136
+ #### 实现
137
+
138
+ - `observability/siem_sink.py` — `SIEMSink`:
139
+ - 接收事件 → 写入 JSON-Lines 文件/流
140
+ - 每行:`{"timestamp", "session_id", "event_type", "tool_name", "effect", "executed", "policy_version", "duration_ms"}`
141
+ - 自动脱敏(复用 event_mask 逻辑)
142
+
143
+ ---
144
+
145
+ ## Phase C: Production Foundation
146
+
147
+ ### C1: Vault Adapter
148
+
149
+ **目标**:CredentialBroker 支持 HashiCorp Vault 作为 secret 后端。
150
+
151
+ #### TDD 测试先行
152
+
153
+ ```python
154
+ # tests/test_vault_adapter.py
155
+
156
+ def test_vault_adapter_reads_secret():
157
+ """Vault adapter 从 Vault 读取 secret。"""
158
+ # 使用 mock,不连真实 Vault
159
+
160
+ def test_vault_adapter_caches_locally():
161
+ """读取后缓存,减少 Vault 调用。"""
162
+ ```
163
+
164
+ #### 实现
165
+
166
+ - `credentials/vault_adapter.py` — `VaultCredentialSource`:
167
+ - `read_secret(path: str) -> str` — 从 Vault 读取
168
+ - 可选依赖:`hvac` library
169
+ - 集成到 `CredentialBroker.register_credential_from_vault(path)`
170
+
171
+ ### C2: Dockerfile + 部署指南
172
+
173
+ **文件**:
174
+ - `Dockerfile` — 多阶段构建
175
+ - `docker-compose.yml` — 开发环境
176
+ - `docs/deployment.md` — 部署指南
177
+
178
+ ### C3: SBOM + Signed Release
179
+
180
+ - `.github/workflows/publish.yml` 增强:
181
+ - 生成 SBOM (cyclonedx)
182
+ - cosign 签名
183
+ - `docs/threat-model.md` — 威胁模型
184
+
185
+ ### C4: 文档 + README 更新
186
+
187
+ - api.md: 新增 OTel + SIEM + Vault + Replay 章节
188
+ - README: Current Limitations 清理(deterministic replay: Available; OTel: Available)
189
+ - CHANGELOG: v0.4.0
190
+
191
+ ---
192
+
193
+ ## 执行顺序与依赖
194
+
195
+ ```
196
+ A1 (RESUME) → A2 (RERUN)
197
+
198
+ B1 (OTel) → B2 (SIEM)
199
+
200
+ C1 (Vault) → C2 (Docker) → C3 (SBOM) → C4 (docs)
201
+ ```
202
+
203
+ ## 验收标准
204
+
205
+ | # | 标准 | 验证方法 |
206
+ |---|---|---|
207
+ | V1 | RESUME 从检查点恢复执行 | `test_replay_resume.py` |
208
+ | V2 | RERUN 完整重放 + 偏离检测 | `test_replay_rerun.py` |
209
+ | V3 | OTel sink 生成 spans | `test_otel_sink.py` |
210
+ | V4 | SIEM 导出 JSON-Lines + 脱敏 | `test_siem_export.py` |
211
+ | V5 | Vault adapter 读取 secret(mock) | `test_vault_adapter.py` |
212
+ | V6 | Dockerfile 可构建 | `docker build .` |
213
+ | V7 | SBOM 生成 | CI workflow |
214
+ | V8 | pre_release.py exit 0 | `python scripts/pre_release.py 0.4.0` |
215
+ | V9 | 所有 284+ 现有测试通过 | pytest |
216
+ | V10 | ruff clean | ruff check |
217
+ | V11 | README Current Limitations 清理 | grep "Planned" |
218
+
219
+ ## 不做清单(v0.5.0+)
220
+
221
+ - ❌ MCP server mode
222
+ - ❌ MCP governance (allowlist, rate limit)
223
+ - ❌ Tool sandbox (subprocess isolation)
224
+ - ❌ Policy hot-reload
225
+ - ❌ Multi-tenant policy namespace
226
+
227
+ ## 新测试预估
228
+
229
+ | Phase | 测试数 |
230
+ |---|---|
231
+ | A1 RESUME | 3 |
232
+ | A2 RERUN | 3 |
233
+ | B1 OTel | 3 |
234
+ | B2 SIEM | 3 |
235
+ | C1 Vault | 2 |
236
+ | **总计** | **~14 新测试** |
@@ -2,6 +2,68 @@
2
2
 
3
3
  All notable changes to petfishFramework will be documented in this file.
4
4
 
5
+ ## [0.4.0] — 2026-07-08
6
+
7
+ ### v0.4.0 Production Foundation
8
+
9
+ #### Phase A+B: Deterministic Replay + Observability
10
+ - Deterministic replay infrastructure (`petfishframework.reliability.replay`):
11
+ - `RecordingEnvironment` captures every model response, tool call, and retrieval
12
+ - `ReplayEnvironment` replays recorded calls for deterministic audit
13
+ - `ResumableEnvironment` replays prefix then switches to live execution
14
+ - `ReplayMode.AUDIT`, `RESUME`, `RERUN` semantics
15
+ - OpenTelemetry sink (`OTelSink`) — creates spans for model/tool/session events
16
+ - SIEM export (`SIEMSink`) — structured JSON audit events for downstream SIEMs
17
+
18
+ #### Phase C: Vault Adapter + Deployment + Threat Model
19
+ - `VaultCredentialSource` — reads secrets from HashiCorp Vault with lazy `hvac` import
20
+ - `CredentialBroker.register_credential_from_vault(name, source, path)` — register
21
+ credentials fetched from Vault
22
+ - Optional dependency group `vault = ["hvac>=1.0"]`
23
+ - `Dockerfile` and `docker-compose.yml` for containerized deployment
24
+ - `docs/deployment.md` — Docker deployment guide, environment variables, volume mounts,
25
+ credential-broker integration, and security notes
26
+ - `docs/threat-model.md` — attack surface, trust boundaries, threats, mitigations,
27
+ and fail-closed defaults
28
+
29
+ #### Documentation Sync
30
+ - `docs/api.md` updated to v0.4.0
31
+ - README: deterministic replay and OTel marked available, roadmap bumped to v0.4.x
32
+ - Test count badge updated to 300
33
+
34
+ ## [0.3.2] — 2026-07-08
35
+
36
+ ### YAML Policy DSL Expansion
37
+ - 13 new condition matchers (total 20): `amount_eq/gte/lte`, generic `field_eq` prefix, `role_count_gte`, `clearance_eq`, `tenant_id_eq`, `classification_eq`, `tags_contains`, `risk_level_eq`, `capabilities_contains`, `requires_credentials`, `session_risk_gt`, `prompt_risk_gt`
38
+ - AND/OR/NOT combinators (`any`/`all`/`not`, nestable in `when:` blocks)
39
+ - Policy schema validation (`policies/validator.py`): validates version, name, rules, effect values
40
+
41
+ ### CredentialBroker Phase 2
42
+ - `BaseTool.credential_name` field — decouple tool name from credential name
43
+ - `ScopedToken.max_uses` + use counting — one-time tokens (`max_uses=1`)
44
+ - `ScopedToken.uses_remaining` property + `use()` method
45
+ - `revoke_all_for_tool(tool_name)` — revoke tokens for a specific tool
46
+ - `revoke_all()` — revoke everything (session-end cleanup)
47
+ - `active_token_count` property + `list_active_tokens()`
48
+ - Environment uses `credential_name` mapping when injecting tokens
49
+ - Session-end credential cleanup
50
+
51
+ ## [0.3.1] — 2026-07-08
52
+
53
+ ### Credential Event Safety (P0 security fix)
54
+ - Event log no longer stores `ScopedToken` objects — replaced with `{"credential_ref": ..., "tool_name": ..., "redacted": True}`
55
+ - Untrusted event sinks cannot access `.get_secret()` via event introspection
56
+
57
+ ### Agent/Session credential_broker API
58
+ - `Agent(credential_broker=broker)` — first-class parameter
59
+ - `Session` passes broker to `RuntimeEnvironment`
60
+ - `RuntimeEnvironment` field renamed `_credential_broker` → `credential_broker`
61
+
62
+ ### Documentation Sync
63
+ - `docs/api.md` version updated
64
+ - README roadmap: v0.3.x (current)
65
+ - README: added YAML Policy Engine + CredentialBroker usage examples
66
+
5
67
  ## [0.3.0] — 2026-07-08
6
68
 
7
69
  ### v0.3.0 Policy Engine + Credential Broker Integration
@@ -0,0 +1,7 @@
1
+ FROM python:3.12-slim
2
+ WORKDIR /app
3
+ COPY pyproject.toml uv.lock ./
4
+ RUN pip install uv && uv sync --no-dev
5
+ COPY src/ src/
6
+ RUN uv pip install --system .
7
+ ENTRYPOINT ["python", "-m", "petfishframework"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: petfishframework
3
- Version: 0.3.2
3
+ Version: 0.4.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
@@ -26,6 +26,11 @@ Provides-Extra: mcp
26
26
  Requires-Dist: mcp>=1.0; extra == 'mcp'
27
27
  Provides-Extra: openai
28
28
  Requires-Dist: openai>=1.0; extra == 'openai'
29
+ Provides-Extra: otel
30
+ Requires-Dist: opentelemetry-api>=1.20; extra == 'otel'
31
+ Requires-Dist: opentelemetry-sdk>=1.20; extra == 'otel'
32
+ Provides-Extra: vault
33
+ Requires-Dist: hvac>=1.0; extra == 'vault'
29
34
  Description-Content-Type: text/markdown
30
35
 
31
36
  # petfishFramework
@@ -34,7 +39,7 @@ Description-Content-Type: text/markdown
34
39
 
35
40
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
36
41
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/kylecui/petfishFramework/blob/master/LICENSE)
37
- [![Tests: 284](https://img.shields.io/badge/tests-259-brightgreen.svg)](https://github.com/kylecui/petfishFramework/tree/master/tests/)
42
+ [![Tests: 300](https://img.shields.io/badge/tests-300-brightgreen.svg)](https://github.com/kylecui/petfishFramework/tree/master/tests/)
38
43
 
39
44
  **Status: Alpha** — API may change. Core runtime works; see [Roadmap](#roadmap).
40
45
 
@@ -247,8 +252,8 @@ agent = Agent(model=model, reasoning=ReAct(), tools=tools, credential_broker=bro
247
252
  ## Roadmap
248
253
 
249
254
  - **v0.2.x**: Core runtime, permission semantics, enterprise PoC, Trusted Publishing ✅
250
- - **v0.3.x** (current): YAML Policy Engine, CredentialBroker ✅
251
- - **v0.4.x**: Production hardening, deployment guides
255
+ - **v0.3.x**: YAML Policy Engine, CredentialBroker ✅
256
+ - **v0.4.x** (current): Production hardening, deployment guides, Vault adapter, Docker, threat model ✅
252
257
 
253
258
  ## Current Limitations
254
259
 
@@ -263,8 +268,8 @@ petfishFramework is **Alpha**. API may change before v1.0.
263
268
  | PARTIAL_ALLOW | ✅ Enforced (pre-execution arg filtering) |
264
269
  | MASK | ✅ Enforced (input mask before + output mask after) |
265
270
  | DEGRADE | ✅ Enforced (fallback tool switching) |
266
- | Session replay | Audit replay available |
267
- | Deterministic rerun / resume | 📋 Planned |
271
+ | Session replay / deterministic rerun / resume | ✅ Available |
272
+ | OpenTelemetry + SIEM observability | Available |
268
273
  | MCP client stdio | ✅ Available |
269
274
  | MCP server mode | 📋 Planned |
270
275
  | Structured output / conversation memory | ✅ Available |
@@ -279,7 +284,7 @@ petfishFramework is **Alpha**. API may change before v1.0.
279
284
  git clone https://github.com/kylecui/petfishFramework.git
280
285
  cd petfishFramework
281
286
  uv sync --all-extras
282
- uv run pytest # 284 tests
287
+ uv run pytest # 300 tests
283
288
  uv run ruff check src/ tests/
284
289
  ```
285
290
 
@@ -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: 284](https://img.shields.io/badge/tests-259-brightgreen.svg)](https://github.com/kylecui/petfishFramework/tree/master/tests/)
7
+ [![Tests: 300](https://img.shields.io/badge/tests-300-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
 
@@ -217,8 +217,8 @@ agent = Agent(model=model, reasoning=ReAct(), tools=tools, credential_broker=bro
217
217
  ## Roadmap
218
218
 
219
219
  - **v0.2.x**: Core runtime, permission semantics, enterprise PoC, Trusted Publishing ✅
220
- - **v0.3.x** (current): YAML Policy Engine, CredentialBroker ✅
221
- - **v0.4.x**: Production hardening, deployment guides
220
+ - **v0.3.x**: YAML Policy Engine, CredentialBroker ✅
221
+ - **v0.4.x** (current): Production hardening, deployment guides, Vault adapter, Docker, threat model ✅
222
222
 
223
223
  ## Current Limitations
224
224
 
@@ -233,8 +233,8 @@ petfishFramework is **Alpha**. API may change before v1.0.
233
233
  | PARTIAL_ALLOW | ✅ Enforced (pre-execution arg filtering) |
234
234
  | MASK | ✅ Enforced (input mask before + output mask after) |
235
235
  | DEGRADE | ✅ Enforced (fallback tool switching) |
236
- | Session replay | Audit replay available |
237
- | Deterministic rerun / resume | 📋 Planned |
236
+ | Session replay / deterministic rerun / resume | ✅ Available |
237
+ | OpenTelemetry + SIEM observability | Available |
238
238
  | MCP client stdio | ✅ Available |
239
239
  | MCP server mode | 📋 Planned |
240
240
  | Structured output / conversation memory | ✅ Available |
@@ -249,7 +249,7 @@ petfishFramework is **Alpha**. API may change before v1.0.
249
249
  git clone https://github.com/kylecui/petfishFramework.git
250
250
  cd petfishFramework
251
251
  uv sync --all-extras
252
- uv run pytest # 284 tests
252
+ uv run pytest # 300 tests
253
253
  uv run ruff check src/ tests/
254
254
  ```
255
255
 
@@ -0,0 +1,8 @@
1
+ version: "3.9"
2
+ services:
3
+ agent:
4
+ build: .
5
+ environment:
6
+ - OPENAI_API_KEY=${OPENAI_API_KEY}
7
+ volumes:
8
+ - ./examples:/app/examples
@@ -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.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.4.0. 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
 
@@ -1294,3 +1294,178 @@ decision.policy_name # "enterprise-expense-policy"
1294
1294
  ```
1295
1295
 
1296
1296
  This lets audit logs trace each decision back to the policy file version that produced it.
1297
+
1298
+ ### AND/OR/NOT Combinators (v0.3.2)
1299
+
1300
+ `when:` blocks support `any`/`all`/`not` combinators:
1301
+
1302
+ ```yaml
1303
+ when:
1304
+ any:
1305
+ - action.tool_name: approve_payment
1306
+ - action.tool_name: transfer_funds
1307
+
1308
+ when:
1309
+ not:
1310
+ subject.role_in: [admin]
1311
+
1312
+ when:
1313
+ not:
1314
+ any:
1315
+ - subject.role_in: [admin]
1316
+ - subject.clearance_eq: secret
1317
+ ```
1318
+
1319
+ ### Full matcher list (v0.3.2)
1320
+
1321
+ | Key | Description | Example |
1322
+ |---|---|---|
1323
+ | `action.tool_name` | Exact tool name match | `approve_payment` |
1324
+ | `subject.role_in` | Any role in list matches | `[finance, admin]` |
1325
+ | `subject.role_not_in` | No role in list matches | `[intern]` |
1326
+ | `subject.role_count_gte` | Subject has ≥ N roles | `2` |
1327
+ | `subject.clearance_eq` | Clearance level match | `confidential` |
1328
+ | `subject.tenant_id_eq` | Tenant ID match | `acme-corp` |
1329
+ | `action.args.amount_gt` | `args["amount"] > value` | `5000` |
1330
+ | `action.args.amount_lt` | `args["amount"] < value` | `100` |
1331
+ | `action.args.amount_eq` | `args["amount"] == value` | `500` |
1332
+ | `action.args.amount_gte` | `args["amount"] >= value` | `1000` |
1333
+ | `action.args.amount_lte` | `args["amount"] <= value` | `200` |
1334
+ | `action.args.<field>_eq` | Generic field equality (prefix matcher) | `recipient_eq: alice@` |
1335
+ | `resource.classification_eq` | Resource classification match | `internal` |
1336
+ | `resource.tags_contains` | Resource has any of the listed tags | `[pii, sensitive]` |
1337
+ | `tool.side_effect` | Match tool metadata | `true` |
1338
+ | `tool.external_egress` | Match tool metadata | `true` |
1339
+ | `tool.requires_credentials` | Match tool metadata | `true` |
1340
+ | `tool.risk_level_eq` | Match risk level | `high` |
1341
+ | `tool.capabilities_contains` | Tool has any of listed capabilities | `[network, fs:write]` |
1342
+ | `context.session_risk_gt` | `context.session_risk > value` | `0.5` |
1343
+ | `context.prompt_risk_gt` | `context.prompt_risk > value` | `0.7` |
1344
+
1345
+ Unknown condition keys are fail-closed (return `False`).
1346
+
1347
+ ### Policy schema validation (v0.3.2)
1348
+
1349
+ ```python
1350
+ from petfishframework.policies.validator import validate_policy, validate_policy_file
1351
+
1352
+ errors = validate_policy_file("enterprise-expense.yaml")
1353
+ if errors:
1354
+ for err in errors:
1355
+ print(f" - {err}")
1356
+ ```
1357
+
1358
+ Checks: version exists, name exists, rules is a list, each rule has name + valid effect, priority is integer.
1359
+
1360
+ ---
1361
+
1362
+ ## 16. Credential Broker
1363
+
1364
+ ### CredentialBroker
1365
+
1366
+ ```python
1367
+ from petfishframework.credentials import CredentialBroker
1368
+
1369
+ broker = CredentialBroker(default_ttl_s=3600)
1370
+ broker.register_credential("github_app", os.environ["GITHUB_TOKEN"])
1371
+ broker.register_credential("db_connection", "postgres://...")
1372
+ ```
1373
+
1374
+ | Method | Description |
1375
+ |---|---|
1376
+ | `register_credential(name, secret)` | Register a real credential |
1377
+ | `issue_token(name, tool_name, ttl_s=None, max_uses=0)` | Issue a scoped, time-limited token |
1378
+ | `validate_token(token_id)` | Check if token is valid (not expired, not revoked) |
1379
+ | `revoke_token(token_id)` | Revoke a single token |
1380
+ | `revoke_all_for_tool(tool_name)` | Revoke all tokens for a specific tool (v0.3.2) |
1381
+ | `revoke_all()` | Revoke all active tokens (session-end cleanup) (v0.3.2) |
1382
+ | `cleanup_expired()` | Remove expired tokens, returns count removed |
1383
+ | `active_token_count` | Property: number of active tokens (v0.3.2) |
1384
+ | `list_active_tokens()` | Return list of active token_ids (v0.3.2) |
1385
+
1386
+ ### ScopedToken
1387
+
1388
+ ```python
1389
+ token = broker.issue_token("github_app", tool_name="github_tool", max_uses=1)
1390
+ token.token_id # unique ID
1391
+ token.tool_name # "github_tool"
1392
+ token.is_valid() # True (not expired, uses remaining)
1393
+ token.get_secret() # returns the actual secret (consumes one use if max_uses set)
1394
+ token.uses_remaining # remaining uses (v0.3.2)
1395
+ ```
1396
+
1397
+ `__repr__` and `__str__` never expose the secret:
1398
+ ```python
1399
+ repr(token) # "ScopedToken(token_id='abc...', tool_name='github_tool', ...)"
1400
+ str(token) # "ScopedToken(abc..., tool=github_tool, valid=True)"
1401
+ ```
1402
+
1403
+ ### Agent Integration
1404
+
1405
+ ```python
1406
+ agent = Agent(
1407
+ model=model,
1408
+ reasoning=ReAct(),
1409
+ tools=tools,
1410
+ credential_broker=broker,
1411
+ )
1412
+ # Tools with requires_credentials=True receive a ScopedToken in args["_credential_token"]
1413
+ ```
1414
+
1415
+ ### Tool credential_name (v0.3.2)
1416
+
1417
+ ```python
1418
+ @dataclass
1419
+ class GitHubTool(BaseTool):
1420
+ name: str = "github_create_issue"
1421
+ requires_credentials: bool = True
1422
+ credential_name: str = "github_app" # maps to broker credential name
1423
+ ```
1424
+
1425
+ ### Event Log Safety
1426
+
1427
+ ScopedToken objects are **never** stored in event data. The Environment replaces them with:
1428
+
1429
+ ```python
1430
+ {"credential_ref": "token_id", "tool_name": "github_tool", "redacted": True}
1431
+ ```
1432
+
1433
+ ### One-time Tokens (v0.3.2)
1434
+
1435
+ ```python
1436
+ token = broker.issue_token("secret_key", tool_name="signing_tool", max_uses=1)
1437
+ token.get_secret() # works, uses_remaining becomes 0
1438
+ token.get_secret() # raises ValueError("max uses exceeded")
1439
+ ```
1440
+
1441
+ ### Session-End Cleanup
1442
+
1443
+ ```python
1444
+ # In Session._finalize_run():
1445
+ if session.credential_broker:
1446
+ session.credential_broker.cleanup_expired()
1447
+ ```
1448
+
1449
+ ### Vault Integration (v0.4.0)
1450
+
1451
+ ```python
1452
+ from petfishframework.credentials import CredentialBroker, VaultCredentialSource
1453
+
1454
+ broker = CredentialBroker()
1455
+ source = VaultCredentialSource(
1456
+ vault_url="https://vault.example.com",
1457
+ token="hvs.CAES...",
1458
+ )
1459
+ broker.register_credential_from_vault("openai", source, path="secrets/openai")
1460
+ ```
1461
+
1462
+ `VaultCredentialSource` performs a lazy import of `hvac` and raises a clear
1463
+ `ImportError` if the optional dependency is missing. The secret is read once and
1464
+ cached locally for the lifetime of the source; subsequent reads of the same
1465
+ path do not call Vault again.
1466
+
1467
+ Install the optional dependency with:
1468
+
1469
+ ```bash
1470
+ pip install petfishframework[vault]
1471
+ ```