flowjet 2.1.3__tar.gz → 2.1.4__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 (176) hide show
  1. {flowjet-2.1.3 → flowjet-2.1.4}/CHANGELOG.md +22 -0
  2. {flowjet-2.1.3 → flowjet-2.1.4}/PKG-INFO +1 -1
  3. {flowjet-2.1.3 → flowjet-2.1.4}/pyproject.toml +14 -1
  4. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/agent.py +1 -3
  5. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/cli.py +14 -31
  6. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/doctor_cmd.py +3 -1
  7. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/progress.py +1 -1
  8. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/setup_cmd.py +1 -1
  9. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/agent/factory.py +56 -12
  10. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/agent/wiring.py +12 -3
  11. flowjet-2.1.4/src/flowjet/core/agent/workspace.py +157 -0
  12. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/bridges/base.py +90 -6
  13. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/bridges/nano/backend.py +2 -0
  14. flowjet-2.1.4/src/flowjet/core/bridges/nano/interrupts.py +74 -0
  15. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/bridges/nano/mapping.py +22 -14
  16. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/bridges/soothe/backend.py +2 -0
  17. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/runtime/isolation/backend.py +1 -0
  18. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/runtime/isolation/pool.py +1 -0
  19. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/runtime/isolation/request.py +5 -0
  20. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/runtime/isolation/workspace.py +7 -5
  21. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/sandbox/resolver.py +1 -1
  22. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/streaming/tool_stream.py +3 -3
  23. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/acp/agent.py +21 -17
  24. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/acp/routes.py +2 -2
  25. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/config.py +8 -0
  26. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/http/app.py +1 -0
  27. flowjet-2.1.3/src/flowjet/core/agent/workspace.py +0 -89
  28. {flowjet-2.1.3 → flowjet-2.1.4}/.gitignore +0 -0
  29. {flowjet-2.1.3 → flowjet-2.1.4}/LICENSE +0 -0
  30. {flowjet-2.1.3 → flowjet-2.1.4}/README.md +0 -0
  31. {flowjet-2.1.3 → flowjet-2.1.4}/config/nano-local.yml +0 -0
  32. {flowjet-2.1.3 → flowjet-2.1.4}/config/nano-sandbox-soothe.yml +0 -0
  33. {flowjet-2.1.3 → flowjet-2.1.4}/config/nano.yml +0 -0
  34. {flowjet-2.1.3 → flowjet-2.1.4}/deploy/README.md +0 -0
  35. {flowjet-2.1.3 → flowjet-2.1.4}/docs/completion-design.md +0 -0
  36. {flowjet-2.1.3 → flowjet-2.1.4}/docs/impl/IG-001-openai-compatible-server.md +0 -0
  37. {flowjet-2.1.3 → flowjet-2.1.4}/docs/impl/IG-002-isolated-thread-pool-runtime.md +0 -0
  38. {flowjet-2.1.3 → flowjet-2.1.4}/docs/impl/IG-003-production-isolation-hardening.md +0 -0
  39. {flowjet-2.1.3 → flowjet-2.1.4}/docs/specs/RFC-001-openai-compatible-api.md +0 -0
  40. {flowjet-2.1.3 → flowjet-2.1.4}/docs/specs/RFC-002-isolated-thread-pool-runtime.md +0 -0
  41. {flowjet-2.1.3 → flowjet-2.1.4}/docs/specs/RFC-003-production-isolation-hardening.md +0 -0
  42. {flowjet-2.1.3 → flowjet-2.1.4}/docs/specs/RFC-004-unified-package.md +0 -0
  43. {flowjet-2.1.3 → flowjet-2.1.4}/docs/specs/RFC-005-arcbox-tool-sandbox.md +0 -0
  44. {flowjet-2.1.3 → flowjet-2.1.4}/docs/specs/rfc-history.md +0 -0
  45. {flowjet-2.1.3 → flowjet-2.1.4}/docs/specs/rfc-index.md +0 -0
  46. {flowjet-2.1.3 → flowjet-2.1.4}/docs/specs/rfc-namings.md +0 -0
  47. {flowjet-2.1.3 → flowjet-2.1.4}/examples/README.md +0 -0
  48. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/__init__.py +0 -0
  49. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/__main__.py +0 -0
  50. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/README.md +0 -0
  51. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/gws-drive/SKILL.md +0 -0
  52. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/gws-drive-upload/SKILL.md +0 -0
  53. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/logo-generator-skill/.env.example +0 -0
  54. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/logo-generator-skill/README.md +0 -0
  55. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/logo-generator-skill/SKILL.md +0 -0
  56. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/logo-generator-skill/assets/background_library.html +0 -0
  57. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/logo-generator-skill/assets/showcase_template.html +0 -0
  58. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/logo-generator-skill/references/background_styles.md +0 -0
  59. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/logo-generator-skill/references/design_patterns.md +0 -0
  60. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/logo-generator-skill/references/webgl_backgrounds.md +0 -0
  61. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/logo-generator-skill/requirements.txt +0 -0
  62. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/logo-generator-skill/scripts/generate_showcase.py +0 -0
  63. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/logo-generator-skill/scripts/svg_to_png.py +0 -0
  64. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/SKILL.md +0 -0
  65. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/brainstorming/spec-document-reviewer-prompt.md +0 -0
  66. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/implementation/coding-plan-template.md +0 -0
  67. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/implementation/impl-guide-template.md +0 -0
  68. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/review/pr-review-template.md +0 -0
  69. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/review/review-checklist.md +0 -0
  70. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/specs/rfc-template.md +0 -0
  71. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/specs/template-architecture-design.md +0 -0
  72. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/specs/template-conceptual-design.md +0 -0
  73. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/specs/template-impl-interface-design.md +0 -0
  74. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-drafts-readme.md +0 -0
  75. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-impl-readme.md +0 -0
  76. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-platonic.yml +0 -0
  77. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-history.md +0 -0
  78. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-index.md +0 -0
  79. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-namings.md +0 -0
  80. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-standard.md +0 -0
  81. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/BRAINSTORM/brainstorm.md +0 -0
  82. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-code.md +0 -0
  83. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-create-guide.md +0 -0
  84. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-full.md +0 -0
  85. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-update-guide.md +0 -0
  86. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-validate-guide.md +0 -0
  87. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-plan-modular-specs.md +0 -0
  88. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-recover-architecture.md +0 -0
  89. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-recover-conceptual.md +0 -0
  90. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-recover-impl-interface.md +0 -0
  91. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-scaffold.md +0 -0
  92. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-scan.md +0 -0
  93. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/REFERENCE.md +0 -0
  94. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/REVIEW/review-spec-compliance.md +0 -0
  95. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-check-compliance.md +0 -0
  96. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-check-taxonomy.md +0 -0
  97. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-generate-history.md +0 -0
  98. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-generate-index.md +0 -0
  99. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-generate-namings.md +0 -0
  100. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-refine.md +0 -0
  101. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-validate-consistency.md +0 -0
  102. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-overview.md +0 -0
  103. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-phase-1.md +0 -0
  104. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-phase-2.md +0 -0
  105. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-phase-3.md +0 -0
  106. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/SKILL.md +0 -0
  107. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/evidence-deep.json +0 -0
  108. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/evidence-first.json +0 -0
  109. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/idea-first.json +0 -0
  110. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/loop.json +0 -0
  111. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/paper-pipeline.json +0 -0
  112. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/rapid.json +0 -0
  113. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/rigor-first.json +0 -0
  114. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/stance-first.json +0 -0
  115. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/systematic-review.json +0 -0
  116. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/patterns/verify-loop.json +0 -0
  117. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/references/backends.md +0 -0
  118. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/references/handoff-ars-omr.md +0 -0
  119. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/references/handoff-omr-ars.md +0 -0
  120. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/references/routing.md +0 -0
  121. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/scripts/csljson_to_omr.py +0 -0
  122. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/builtin_skills/research-bootstrap/scripts/papers_index_to_csljson.py +0 -0
  123. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/__init__.py +0 -0
  124. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/__main__.py +0 -0
  125. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/completion/__init__.py +0 -0
  126. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/completion/context.py +0 -0
  127. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/completion/engine.py +0 -0
  128. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/doctor_fix_cmd.py +0 -0
  129. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/serve_cmd.py +0 -0
  130. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/shell/__init__.py +0 -0
  131. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/shell/_fj.zsh +0 -0
  132. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/shell/fj.bash +0 -0
  133. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/stream.py +0 -0
  134. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/threads.py +0 -0
  135. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/cli/tool_stream.py +0 -0
  136. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/__init__.py +0 -0
  137. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/_warnings.py +0 -0
  138. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/agent/__init__.py +0 -0
  139. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/agent/config.py +0 -0
  140. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/agent/skills.py +0 -0
  141. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/backend_config.py +0 -0
  142. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/bridges/nano/__init__.py +0 -0
  143. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/bridges/nano/adapter.py +0 -0
  144. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/bridges/soothe/__init__.py +0 -0
  145. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/bridges/soothe/adapter.py +0 -0
  146. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/events.py +0 -0
  147. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/identity.py +0 -0
  148. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/modes.py +0 -0
  149. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/protocol.py +0 -0
  150. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/runtime/__init__.py +0 -0
  151. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/runtime/direct.py +0 -0
  152. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/runtime/fake.py +0 -0
  153. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/runtime/isolation/__init__.py +0 -0
  154. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/runtime/isolation/adapter.py +0 -0
  155. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/runtime/isolation/admission.py +0 -0
  156. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/runtime/isolation/errors.py +0 -0
  157. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/runtime/isolation/mode_gate.py +0 -0
  158. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/sandbox/__init__.py +0 -0
  159. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/sandbox/arbox.py +0 -0
  160. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/sandbox/settings.py +0 -0
  161. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/streaming/__init__.py +0 -0
  162. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/streaming/presentation.py +0 -0
  163. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/core/streaming/tool_results.py +0 -0
  164. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/home.py +0 -0
  165. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/__init__.py +0 -0
  166. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/__main__.py +0 -0
  167. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/acp/__init__.py +0 -0
  168. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/http/__init__.py +0 -0
  169. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/http/auth.py +0 -0
  170. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/openai_compat/__init__.py +0 -0
  171. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/openai_compat/errors.py +0 -0
  172. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/openai_compat/projection.py +0 -0
  173. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/openai_compat/routes.py +0 -0
  174. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/openai_compat/schemas.py +0 -0
  175. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/openai_compat/service.py +0 -0
  176. {flowjet-2.1.3 → flowjet-2.1.4}/src/flowjet/server/openai_compat/store.py +0 -0
@@ -10,6 +10,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [2.1.4] — 2026-10-23
14
+
15
+ ### Added
16
+
17
+ - **Resumable `ask_user` pauses via shared thread-state checkpointer.** The
18
+ server now persists agent thread state (LangGraph checkpoint) to a shared
19
+ SQLite database (WAL mode + busy_timeout) so that `ask_user` pauses survive
20
+ pool-worker switches and server restarts. A second turn on the same session
21
+ resumes the paused interrupt and delivers the user's input text as the
22
+ answer — no more re-asking the same question after a worker rotation. The
23
+ `server_checkpointer` setting (`"auto"` by default, `"off"` for legacy
24
+ stateless behavior) controls the policy end-to-end: Settings →
25
+ `ServerRuntimeBuilder` → `build_isolating_nano_backend` →
26
+ `AgentAdapterBase`, which opens the checkpointer once per worker, attaches
27
+ it at build time, and survives taint rebuilds.
28
+
29
+ ### Changed
30
+
31
+ - **CI now enforces mypy type compliance.** A `make typecheck` (mypy) gate
32
+ was added to the CI pipeline, and type annotations were fixed across the
33
+ CLI, core, and server packages to achieve full compliance.
34
+
13
35
  ## [2.1.3] — 2026-10-23
14
36
 
15
37
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: flowjet
3
- Version: 2.1.3
3
+ Version: 2.1.4
4
4
  Summary: FlowJet — agent CLI (fj) and server (ACP + OpenAI-compatible HTTP) powered by Soothe
5
5
  Project-URL: Homepage, https://github.com/caesar0301/flowjet
6
6
  Project-URL: Repository, https://github.com/caesar0301/flowjet
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "flowjet"
7
- version = "2.1.3"
7
+ version = "2.1.4"
8
8
  description = "FlowJet — agent CLI (fj) and server (ACP + OpenAI-compatible HTTP) powered by Soothe"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -102,6 +102,8 @@ python_version = "3.12"
102
102
  warn_return_any = true
103
103
  warn_unused_configs = true
104
104
  disallow_untyped_defs = true
105
+ # Skill-local scripts ship their own (looser) tooling; excluded from package lint.
106
+ exclude = "src/flowjet/builtin_skills"
105
107
 
106
108
  # Any-heavy moved code; loosened until annotated.
107
109
  [[tool.mypy.overrides]]
@@ -109,6 +111,17 @@ module = ["flowjet.server.*", "flowjet.core.bridges.*"]
109
111
  disallow_untyped_defs = false
110
112
  warn_return_any = false
111
113
 
114
+ # Local runtimes and stub-less third-party packages consumed at runtime;
115
+ # typed via runtime contract, not via inline types.
116
+ [[tool.mypy.overrides]]
117
+ module = [
118
+ "soothe_nano.*",
119
+ "soothe.*",
120
+ "fsspec",
121
+ "yaml",
122
+ ]
123
+ ignore_missing_imports = true
124
+
112
125
  [tool.pytest.ini_options]
113
126
  asyncio_mode = "auto"
114
127
  asyncio_default_fixture_loop_scope = "function"
@@ -108,11 +108,9 @@ async def build_agent(
108
108
  mode = "bypass"
109
109
  configure_cli_logging(verbose=verbose)
110
110
  ensure_workspace(workspace)
111
- agent = create_agent(config, CLI_PROFILE, interaction_mode=mode)
111
+ agent = create_agent(config, CLI_PROFILE, interaction_mode=mode, checkpointer=checkpointer)
112
112
  # Plugin imports (e.g. browser_use) may still attach root console handlers.
113
113
  silence_after_plugins(verbose=verbose)
114
- if checkpointer is not None:
115
- agent.graph.checkpointer = checkpointer
116
114
  return agent
117
115
 
118
116
 
@@ -16,7 +16,7 @@ import traceback
16
16
  from collections.abc import Coroutine
17
17
  from logging.handlers import RotatingFileHandler
18
18
  from pathlib import Path
19
- from typing import Any
19
+ from typing import Any, cast
20
20
 
21
21
  from flowjet.cli import __version__
22
22
 
@@ -441,7 +441,8 @@ def _build_parser(prog: str | None = None) -> argparse.ArgumentParser:
441
441
  "-c",
442
442
  "--config",
443
443
  metavar="PATH",
444
- help=_LazyHelp(_default_config_help),
444
+ # Lazy help keeps the heavy default-config import off -V / normal runs.
445
+ help=cast("str", _LazyHelp(_default_config_help)),
445
446
  )
446
447
  paths.add_argument(
447
448
  "-w",
@@ -468,7 +469,8 @@ def _build_setup_parser(prog: str | None = None) -> argparse.ArgumentParser:
468
469
  "-c",
469
470
  "--config",
470
471
  metavar="PATH",
471
- help=_LazyHelp(_default_config_help),
472
+ # Lazy help keeps the heavy default-config import off -V / normal runs.
473
+ help=cast("str", _LazyHelp(_default_config_help)),
472
474
  )
473
475
  return parser
474
476
 
@@ -570,35 +572,16 @@ def run_pin_thread(thread_id: str) -> int:
570
572
  async def _detect_pending_interrupt(agent: Any, thread_id: str) -> Any:
571
573
  """Return the pending ``ask_user`` interrupt value, or ``None``.
572
574
 
573
- Checks the LangGraph snapshot for the thread. When an ``ask_user``
574
- interrupt is pending, its ``value`` carries the question specs; the CLI
575
- can resume the run by feeding the user's answer to ``Command(resume=...)``.
575
+ Thin wrapper over the shared bridge helper
576
+ (:func:`flowjet.core.bridges.nano.interrupts.detect_pending_ask_user`) so
577
+ the CLI and the server surfaces apply the exact same probe. When an
578
+ ``ask_user`` interrupt is pending, its ``value`` carries the question
579
+ specs; the run resumes by feeding the user's answer to
580
+ ``Command(resume=...)``.
576
581
  """
577
- get_state = getattr(agent, "aget_state", None)
578
- if not callable(get_state):
579
- return None
580
- try:
581
- snapshot = await get_state({"configurable": {"thread_id": thread_id}})
582
- except Exception as exc:
583
- # Not fatal — but explain why a pending question may be missed and a
584
- # new turn started instead of resuming (visible with -v / FJ_LOG_FILE).
585
- _logger.warning("thread snapshot check failed; starting a new turn: %s", exc)
586
- return None
587
- if snapshot is None:
588
- return None
589
- interrupts = getattr(snapshot, "interrupts", None) or ()
590
- if not interrupts:
591
- # Some snapshots nest interrupts under per-task state.
592
- tasks = getattr(snapshot, "tasks", None) or ()
593
- for task in tasks:
594
- task_interrupts = getattr(task, "interrupts", None) or ()
595
- interrupts = task_interrupts
596
- break
597
- for entry in interrupts:
598
- value = getattr(entry, "value", None)
599
- if isinstance(value, dict) and value.get("type") == "ask_user":
600
- return value
601
- return None
582
+ from flowjet.core.bridges.nano.interrupts import detect_pending_ask_user
583
+
584
+ return await detect_pending_ask_user(agent, {"configurable": {"thread_id": thread_id}})
602
585
 
603
586
 
604
587
  async def run_async(args: argparse.Namespace) -> int:
@@ -153,7 +153,8 @@ async def _run_diagnose(
153
153
  "soothe-nano diagnose API unavailable; upgrade soothe-nano "
154
154
  "(fj doctor requires soothe-nano>=1.0.8)"
155
155
  ) from exc
156
- return await diagnose(config, deep=deep, live_llm=live_llm)
156
+ result: list[dict[str, Any]] = await diagnose(config, deep=deep, live_llm=live_llm)
157
+ return result
157
158
 
158
159
 
159
160
  def _bin_version(bin_path: str) -> str | None:
@@ -174,6 +175,7 @@ def _bin_version(bin_path: str) -> str | None:
174
175
 
175
176
  def _find_chrome_executable() -> str | None:
176
177
  """Locate a Chrome/Chromium-family browser executable, or None."""
178
+ candidates: list[str]
177
179
  if sys.platform == "darwin":
178
180
  candidates = [
179
181
  "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
@@ -310,7 +310,7 @@ def _abbrev_path(path: str) -> str:
310
310
  try:
311
311
  from soothe_nano.utils.formatting import convert_and_abbreviate_path
312
312
 
313
- return convert_and_abbreviate_path(path)
313
+ return str(convert_and_abbreviate_path(path))
314
314
  except Exception:
315
315
  home = os.path.expanduser("~")
316
316
  if path.startswith(home + os.sep):
@@ -241,7 +241,7 @@ def choose_provider_interactive(providers: list[dict[str, Any]]) -> dict[str, An
241
241
 
242
242
  def resolve_config_value(value: str, *, field: str) -> str:
243
243
  """Expand ``${ENV_VAR}`` placeholders; raise if any remain unresolved."""
244
- resolved = _resolve_env(value)
244
+ resolved = str(_resolve_env(value))
245
245
  match = _ENV_VAR_RE.search(resolved)
246
246
  if match:
247
247
  env_name = match.group(1)
@@ -41,6 +41,7 @@ class AgentFactory:
41
41
  interaction_mode: InteractionMode | str | None = None,
42
42
  backend: Backend | None = None,
43
43
  sandbox: Any | None = None,
44
+ checkpointer: Any | None = None,
44
45
  ) -> Any:
45
46
  from flowjet.core.modes import apply_profile
46
47
 
@@ -49,16 +50,18 @@ class AgentFactory:
49
50
  cfg = apply_profile(config, profile, backend=resolved)
50
51
 
51
52
  if resolved is Backend.SOOTHE:
52
- return cls.create_soothe(cfg, profile, mode, sandbox=sandbox)
53
+ return cls.create_soothe(cfg, profile, mode, sandbox=sandbox, checkpointer=checkpointer)
53
54
 
54
55
  if profile.surface is Surface.SERVER:
55
56
  from soothe_nano import create_dual_mode_nano_agent
56
57
 
57
- return create_dual_mode_nano_agent(cfg, backend=sandbox)
58
+ return create_dual_mode_nano_agent(cfg, backend=sandbox, checkpointer=checkpointer)
58
59
 
59
60
  from soothe_nano import create_nano_agent
60
61
 
61
- return create_nano_agent(cfg, interaction_mode=mode, backend=sandbox)
62
+ return create_nano_agent(
63
+ cfg, interaction_mode=mode, backend=sandbox, checkpointer=checkpointer
64
+ )
62
65
 
63
66
  @staticmethod
64
67
  def soothe_builder() -> Any:
@@ -80,18 +83,33 @@ class AgentFactory:
80
83
  mode: str | None = None,
81
84
  *,
82
85
  sandbox: Any | None = None,
86
+ checkpointer: Any | None = None,
83
87
  ) -> Any:
84
88
  """Build a ``soothe`` (host runtime) agent for ``profile``'s surface.
85
89
 
86
90
  ``interrupt_on`` is pinned to ``{}``: the host builder otherwise injects
87
91
  HITL interrupts for mutating tools (``edit_file`` / ``write_file`` /
88
- ``delete`` / ``run_command``), and FlowJet has no resume path — it
89
- renders ``InterruptWaiting`` and stops, so any write would stall the run.
92
+ ``delete`` / ``run_command``), and FlowJet does not surface those
93
+ approval requests as resumable pauses — only ``ask_user`` clarifications
94
+ are resumed (via the checkpointer + ``Command(resume=...)``), so a
95
+ mutating-tool interrupt would stall the run.
96
+
97
+ ``checkpointer`` is attached at graph-compile time so both mode graphs
98
+ of a server-surface agent share it (a post-hoc ``graph.checkpointer``
99
+ assignment would only reach the default mode's lazily compiled graph).
90
100
  """
91
101
  factory = cls.soothe_builder()
92
102
  if profile.surface is Surface.SERVER:
93
- return ModeRoutedSootheAgent(config, factory, sandbox=sandbox)
94
- return factory(config, interaction_mode=mode, interrupt_on={}, backend=sandbox)
103
+ return ModeRoutedSootheAgent(
104
+ config, factory, sandbox=sandbox, checkpointer=checkpointer
105
+ )
106
+ return factory(
107
+ config,
108
+ interaction_mode=mode,
109
+ interrupt_on={},
110
+ backend=sandbox,
111
+ checkpointer=checkpointer,
112
+ )
95
113
 
96
114
 
97
115
  class ModeRoutedSootheAgent:
@@ -103,8 +121,10 @@ class ModeRoutedSootheAgent:
103
121
  request must never land on the ``agent`` graph. Agents are built lazily, so
104
122
  a worker that only serves ``ask`` never builds the ``agent`` graph.
105
123
 
106
- Only ``astream`` is proxied: that is the whole surface the nano bridge
107
- (``flowjet.core.bridges.nano.mapping``) drives.
124
+ Only ``astream`` and ``aget_state`` are proxied: that is the whole
125
+ surface the nano bridge (``flowjet.core.bridges.nano.mapping``) drives
126
+ — streaming a turn, and probing the thread snapshot for a pending
127
+ ``ask_user`` interrupt before it.
108
128
  """
109
129
 
110
130
  def __init__(
@@ -114,11 +134,13 @@ class ModeRoutedSootheAgent:
114
134
  modes: tuple[str, ...] = ("agent", "ask"),
115
135
  *,
116
136
  sandbox: Any | None = None,
137
+ checkpointer: Any | None = None,
117
138
  ) -> None:
118
139
  self._config = config
119
140
  self._factory = factory
120
141
  self._modes = modes
121
142
  self._sandbox = sandbox
143
+ self._checkpointer = checkpointer
122
144
  self._agents: dict[str, Any] = {}
123
145
 
124
146
  def _agent_for(self, mode: str) -> Any:
@@ -130,18 +152,40 @@ class ModeRoutedSootheAgent:
130
152
  interaction_mode=resolved,
131
153
  interrupt_on={},
132
154
  backend=self._sandbox,
155
+ checkpointer=self._checkpointer,
133
156
  )
134
157
  self._agents[resolved] = agent
135
158
  return agent
136
159
 
160
+ def _agent_for_config(self, config: Any) -> Any:
161
+ """Pick the mode agent for a runnable config (mirrors ``astream``)."""
162
+ configurable: dict[str, Any] = {}
163
+ if isinstance(config, dict):
164
+ raw = config.get("configurable") or {}
165
+ if isinstance(raw, dict):
166
+ configurable = raw
167
+ mode = str(configurable.get("interaction_mode") or "agent")
168
+ return self._agent_for(mode)
169
+
137
170
  async def astream(self, payload: Any, **kwargs: Any) -> Any:
138
171
  """Proxy ``astream`` to the agent built for the request's mode."""
139
172
  config = kwargs.get("config") or {}
140
- configurable = config.get("configurable") or {} if isinstance(config, dict) else {}
141
- mode = str(configurable.get("interaction_mode") or "agent")
142
- agent = self._agent_for(mode)
173
+ agent = self._agent_for_config(config)
143
174
  async for chunk in agent.astream(payload, **kwargs):
144
175
  yield chunk
145
176
 
177
+ async def aget_state(self, config: Any = None) -> Any:
178
+ """Proxy ``aget_state`` to the agent built for the config's mode.
179
+
180
+ Soft-fails to ``None`` when the mode agent cannot read graph state
181
+ (no checkpointer), matching ``SootheNanoAgent.aget_state`` semantics
182
+ so pending-interrupt probes degrade instead of raising.
183
+ """
184
+ agent = self._agent_for_config(config)
185
+ get_state = getattr(agent, "aget_state", None)
186
+ if not callable(get_state):
187
+ return None
188
+ return await get_state(config=config)
189
+
146
190
 
147
191
  __all__ = ["AgentFactory", "ModeRoutedSootheAgent"]
@@ -8,7 +8,7 @@ delegates here. core remains server-agnostic — no imports from
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
- from typing import TYPE_CHECKING, Any
11
+ from typing import TYPE_CHECKING, Any, cast
12
12
 
13
13
  from flowjet.core.backend_config import Backend, resolve_backend
14
14
  from flowjet.core.bridges.nano import build_isolating_nano_backend
@@ -42,6 +42,13 @@ class ServerRuntimeBuilder:
42
42
  workspace host↔guest and the adapters bind soothe-nano's sandbox backend
43
43
  at agent build time. The AgentRuntime isolation is unchanged (in-process
44
44
  thread pool).
45
+
46
+ ``checkpointer_policy`` ("auto" by default) makes each worker's adapter
47
+ open the shared thread-state checkpointer resolved from the config's
48
+ durability settings — sqlite lands on
49
+ ``$SOOTHE_DATA_DIR/databases/checkpoints.db``. This is what keeps one
50
+ ACP session's conversation continuous across turns, pool workers and
51
+ restarts, and what makes ``ask_user`` pauses resumable.
45
52
  """
46
53
 
47
54
  @classmethod
@@ -57,6 +64,7 @@ class ServerRuntimeBuilder:
57
64
  content_source: str | None,
58
65
  allow_external_workspace: bool,
59
66
  pool_settings: Any,
67
+ checkpointer_policy: str | None = "auto",
60
68
  ) -> RuntimeBackend:
61
69
  backend = resolve_backend(Surface.SERVER)
62
70
  sandbox_manager = cls.sandbox_manager(config_path)
@@ -77,10 +85,11 @@ class ServerRuntimeBuilder:
77
85
  allow_external_workspace=allow_external_workspace,
78
86
  workspace_resolver=workspace_resolver,
79
87
  sandbox_manager=sandbox_manager,
88
+ checkpointer_policy=checkpointer_policy,
80
89
  )
81
90
  if backend is Backend.SOOTHE:
82
- return build_isolating_soothe_backend(**common)
83
- return build_isolating_nano_backend(**common)
91
+ return cast("RuntimeBackend", build_isolating_soothe_backend(**common))
92
+ return cast("RuntimeBackend", build_isolating_nano_backend(**common))
84
93
 
85
94
  @classmethod
86
95
  def sandbox_manager(cls, config_path: str | None) -> ArcBoxToolSandboxManager | None:
@@ -0,0 +1,157 @@
1
+ """Workspace env binding and sqlite checkpointer opening."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import os
7
+ from collections.abc import AsyncIterator
8
+ from contextlib import asynccontextmanager
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ from soothe_nano.utils.resolver import resolve_checkpointer
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ # Treatments of the checkpointer policy that mean "do not persist".
17
+ _OFF_VALUES = frozenset({"", "off", "disabled", "none", "false", "0"})
18
+
19
+
20
+ def checkpointer_policy_enabled(policy: str | None) -> bool:
21
+ """Whether a checkpointer policy string enables persistence."""
22
+ return str(policy or "").strip().lower() not in _OFF_VALUES
23
+
24
+
25
+ class Workspaces:
26
+ """Bind the workspace root used by file/shell tools."""
27
+
28
+ @staticmethod
29
+ def ensure(workspace: Path | None = None) -> Path:
30
+ """Use ``workspace`` or cwd as ``SOOTHE_WORKSPACE`` for file/shell tools."""
31
+ root = (workspace or Path.cwd()).resolve()
32
+ os.environ.setdefault("SOOTHE_WORKSPACE", str(root))
33
+ try:
34
+ from soothe_nano.workspace import FrameworkFilesystem
35
+
36
+ FrameworkFilesystem.set_current_workspace(root)
37
+ except Exception: # pragma: no cover - optional API surface
38
+ logger.debug("Could not set FrameworkFilesystem workspace", exc_info=True)
39
+ return root
40
+
41
+
42
+ class Checkpointers:
43
+ """Side persistence: an ``AsyncSqliteSaver`` on nano's sqlite data path."""
44
+
45
+ @staticmethod
46
+ @asynccontextmanager
47
+ async def open_sqlite(config: Any) -> AsyncIterator[Any | None]:
48
+ """Yield an ``AsyncSqliteSaver`` using nano's default sqlite data path.
49
+
50
+ Path: ``$SOOTHE_DATA_DIR/databases/checkpoints.db`` (default
51
+ ``~/.soothe/data/databases/checkpoints.db``). The config copy forces
52
+ the sqlite backend so this works regardless of the durability settings
53
+ (the CLI always runs sqlite).
54
+ """
55
+ sqlite_cfg = config.model_copy(
56
+ update={
57
+ "agent": config.agent.model_copy(
58
+ update={
59
+ "protocols": config.agent.protocols.model_copy(
60
+ update={
61
+ "durability": config.agent.protocols.durability.model_copy(
62
+ update={
63
+ "backend": "sqlite",
64
+ "checkpointer": "sqlite",
65
+ }
66
+ )
67
+ }
68
+ )
69
+ }
70
+ )
71
+ }
72
+ )
73
+ result = resolve_checkpointer(sqlite_cfg)
74
+ db_path = _sqlite_db_path(result)
75
+ if not db_path:
76
+ logger.warning("SQLite checkpointer path unresolved; running without persistence")
77
+ yield None
78
+ return
79
+
80
+ saver = await _connect_sqlite(db_path)
81
+ try:
82
+ yield saver
83
+ finally:
84
+ await saver.conn.close()
85
+
86
+ @staticmethod
87
+ @asynccontextmanager
88
+ async def open(config: Any) -> AsyncIterator[Any | None]:
89
+ """Yield a checkpointer for the config's resolved durability backend.
90
+
91
+ Server-surface counterpart of :meth:`open_sqlite`: honors
92
+ ``agent.protocols.durability.checkpointer`` (which inherits
93
+ ``persistence.default_backend``), so a deployment that asks for sqlite
94
+ gets thread state that survives across turns, pool workers and
95
+ restarts. ``postgresql`` is not wired for the server surface yet — it
96
+ warns and yields ``None`` (no persistence) rather than failing
97
+ startup.
98
+ """
99
+ try:
100
+ backend = str(config.resolve_checkpointer_backend()).strip().lower()
101
+ except Exception: # noqa: BLE001 - unknown config shape → sqlite default
102
+ backend = "sqlite"
103
+ if backend != "sqlite":
104
+ logger.warning(
105
+ "Checkpointer backend %r is not wired for the flowjet server "
106
+ "surface yet; thread state will not persist across turns",
107
+ backend,
108
+ )
109
+ yield None
110
+ return
111
+
112
+ result = resolve_checkpointer(config)
113
+ db_path = _sqlite_db_path(result)
114
+ if not db_path:
115
+ logger.warning("SQLite checkpointer path unresolved; running without persistence")
116
+ yield None
117
+ return
118
+
119
+ saver = await _connect_sqlite(db_path)
120
+ try:
121
+ yield saver
122
+ finally:
123
+ await saver.conn.close()
124
+
125
+
126
+ def _sqlite_db_path(result: Any) -> str | None:
127
+ """Extract the sqlite db path from a ``resolve_checkpointer`` result."""
128
+ if isinstance(result, tuple):
129
+ _placeholder, pool_or_path = result
130
+ if isinstance(pool_or_path, str):
131
+ return pool_or_path
132
+ return None
133
+
134
+
135
+ async def _connect_sqlite(db_path: str) -> Any:
136
+ """Open one ``AsyncSqliteSaver`` connection on ``db_path``.
137
+
138
+ WAL + busy timeout are load-bearing for the server: its thread pool opens
139
+ one connection per worker onto the same file, and the default rollback
140
+ journal would block readers behind writers and surface as
141
+ ``database is locked``.
142
+ """
143
+ import aiosqlite
144
+ from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
145
+ from soothe_nano.utils.serde import create_soothe_serde
146
+
147
+ Path(db_path).parent.mkdir(parents=True, exist_ok=True)
148
+ conn = await aiosqlite.connect(db_path)
149
+ await conn.execute("PRAGMA journal_mode=WAL")
150
+ await conn.execute("PRAGMA busy_timeout=5000")
151
+ checkpointer = AsyncSqliteSaver(conn, serde=create_soothe_serde())
152
+ await checkpointer.setup()
153
+ logger.debug("SQLite checkpointer ready at %s", db_path)
154
+ return checkpointer
155
+
156
+
157
+ __all__ = ["Checkpointers", "Workspaces", "checkpointer_policy_enabled"]