flowjet 2.1.1__tar.gz → 2.1.2__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 (174) hide show
  1. {flowjet-2.1.1 → flowjet-2.1.2}/CHANGELOG.md +8 -0
  2. {flowjet-2.1.1 → flowjet-2.1.2}/PKG-INFO +2 -2
  3. {flowjet-2.1.1 → flowjet-2.1.2}/docs/specs/RFC-005-arcbox-tool-sandbox.md +7 -2
  4. {flowjet-2.1.1 → flowjet-2.1.2}/pyproject.toml +2 -2
  5. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/runtime/isolation/workspace.py +109 -20
  6. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/sandbox/arbox.py +36 -3
  7. {flowjet-2.1.1 → flowjet-2.1.2}/.gitignore +0 -0
  8. {flowjet-2.1.1 → flowjet-2.1.2}/LICENSE +0 -0
  9. {flowjet-2.1.1 → flowjet-2.1.2}/README.md +0 -0
  10. {flowjet-2.1.1 → flowjet-2.1.2}/config/nano-local.yml +0 -0
  11. {flowjet-2.1.1 → flowjet-2.1.2}/config/nano-sandbox-soothe.yml +0 -0
  12. {flowjet-2.1.1 → flowjet-2.1.2}/config/nano.yml +0 -0
  13. {flowjet-2.1.1 → flowjet-2.1.2}/deploy/README.md +0 -0
  14. {flowjet-2.1.1 → flowjet-2.1.2}/docs/completion-design.md +0 -0
  15. {flowjet-2.1.1 → flowjet-2.1.2}/docs/impl/IG-001-openai-compatible-server.md +0 -0
  16. {flowjet-2.1.1 → flowjet-2.1.2}/docs/impl/IG-002-isolated-thread-pool-runtime.md +0 -0
  17. {flowjet-2.1.1 → flowjet-2.1.2}/docs/impl/IG-003-production-isolation-hardening.md +0 -0
  18. {flowjet-2.1.1 → flowjet-2.1.2}/docs/specs/RFC-001-openai-compatible-api.md +0 -0
  19. {flowjet-2.1.1 → flowjet-2.1.2}/docs/specs/RFC-002-isolated-thread-pool-runtime.md +0 -0
  20. {flowjet-2.1.1 → flowjet-2.1.2}/docs/specs/RFC-003-production-isolation-hardening.md +0 -0
  21. {flowjet-2.1.1 → flowjet-2.1.2}/docs/specs/RFC-004-unified-package.md +0 -0
  22. {flowjet-2.1.1 → flowjet-2.1.2}/docs/specs/rfc-history.md +0 -0
  23. {flowjet-2.1.1 → flowjet-2.1.2}/docs/specs/rfc-index.md +0 -0
  24. {flowjet-2.1.1 → flowjet-2.1.2}/docs/specs/rfc-namings.md +0 -0
  25. {flowjet-2.1.1 → flowjet-2.1.2}/examples/README.md +0 -0
  26. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/__init__.py +0 -0
  27. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/__main__.py +0 -0
  28. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/README.md +0 -0
  29. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/gws-drive/SKILL.md +0 -0
  30. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/gws-drive-upload/SKILL.md +0 -0
  31. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/logo-generator-skill/.env.example +0 -0
  32. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/logo-generator-skill/README.md +0 -0
  33. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/logo-generator-skill/SKILL.md +0 -0
  34. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/logo-generator-skill/assets/background_library.html +0 -0
  35. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/logo-generator-skill/assets/showcase_template.html +0 -0
  36. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/logo-generator-skill/references/background_styles.md +0 -0
  37. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/logo-generator-skill/references/design_patterns.md +0 -0
  38. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/logo-generator-skill/references/webgl_backgrounds.md +0 -0
  39. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/logo-generator-skill/requirements.txt +0 -0
  40. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/logo-generator-skill/scripts/generate_showcase.py +0 -0
  41. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/logo-generator-skill/scripts/svg_to_png.py +0 -0
  42. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/SKILL.md +0 -0
  43. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/brainstorming/spec-document-reviewer-prompt.md +0 -0
  44. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/implementation/coding-plan-template.md +0 -0
  45. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/implementation/impl-guide-template.md +0 -0
  46. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/review/pr-review-template.md +0 -0
  47. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/review/review-checklist.md +0 -0
  48. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/specs/rfc-template.md +0 -0
  49. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/specs/template-architecture-design.md +0 -0
  50. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/specs/template-conceptual-design.md +0 -0
  51. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/specs/template-impl-interface-design.md +0 -0
  52. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-drafts-readme.md +0 -0
  53. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-impl-readme.md +0 -0
  54. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-platonic.yml +0 -0
  55. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-history.md +0 -0
  56. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-index.md +0 -0
  57. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-namings.md +0 -0
  58. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-standard.md +0 -0
  59. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/BRAINSTORM/brainstorm.md +0 -0
  60. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-code.md +0 -0
  61. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-create-guide.md +0 -0
  62. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-full.md +0 -0
  63. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-update-guide.md +0 -0
  64. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-validate-guide.md +0 -0
  65. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-plan-modular-specs.md +0 -0
  66. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-recover-architecture.md +0 -0
  67. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-recover-conceptual.md +0 -0
  68. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-recover-impl-interface.md +0 -0
  69. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-scaffold.md +0 -0
  70. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-scan.md +0 -0
  71. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/REFERENCE.md +0 -0
  72. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/REVIEW/review-spec-compliance.md +0 -0
  73. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-check-compliance.md +0 -0
  74. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-check-taxonomy.md +0 -0
  75. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-generate-history.md +0 -0
  76. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-generate-index.md +0 -0
  77. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-generate-namings.md +0 -0
  78. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-refine.md +0 -0
  79. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-validate-consistency.md +0 -0
  80. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-overview.md +0 -0
  81. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-phase-1.md +0 -0
  82. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-phase-2.md +0 -0
  83. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-phase-3.md +0 -0
  84. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/SKILL.md +0 -0
  85. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/patterns/evidence-deep.json +0 -0
  86. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/patterns/evidence-first.json +0 -0
  87. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/patterns/idea-first.json +0 -0
  88. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/patterns/loop.json +0 -0
  89. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/patterns/paper-pipeline.json +0 -0
  90. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/patterns/rapid.json +0 -0
  91. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/patterns/rigor-first.json +0 -0
  92. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/patterns/stance-first.json +0 -0
  93. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/patterns/systematic-review.json +0 -0
  94. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/patterns/verify-loop.json +0 -0
  95. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/references/backends.md +0 -0
  96. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/references/handoff-ars-omr.md +0 -0
  97. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/references/handoff-omr-ars.md +0 -0
  98. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/references/routing.md +0 -0
  99. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/scripts/csljson_to_omr.py +0 -0
  100. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/builtin_skills/research-bootstrap/scripts/papers_index_to_csljson.py +0 -0
  101. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/__init__.py +0 -0
  102. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/__main__.py +0 -0
  103. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/agent.py +0 -0
  104. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/cli.py +0 -0
  105. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/completion/__init__.py +0 -0
  106. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/completion/context.py +0 -0
  107. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/completion/engine.py +0 -0
  108. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/doctor_cmd.py +0 -0
  109. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/doctor_fix_cmd.py +0 -0
  110. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/progress.py +0 -0
  111. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/serve_cmd.py +0 -0
  112. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/setup_cmd.py +0 -0
  113. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/shell/__init__.py +0 -0
  114. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/shell/_fj.zsh +0 -0
  115. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/shell/fj.bash +0 -0
  116. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/stream.py +0 -0
  117. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/threads.py +0 -0
  118. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/cli/tool_stream.py +0 -0
  119. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/__init__.py +0 -0
  120. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/_warnings.py +0 -0
  121. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/agent/__init__.py +0 -0
  122. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/agent/config.py +0 -0
  123. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/agent/factory.py +0 -0
  124. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/agent/skills.py +0 -0
  125. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/agent/wiring.py +0 -0
  126. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/agent/workspace.py +0 -0
  127. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/backend_config.py +0 -0
  128. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/bridges/base.py +0 -0
  129. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/bridges/nano/__init__.py +0 -0
  130. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/bridges/nano/adapter.py +0 -0
  131. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/bridges/nano/backend.py +0 -0
  132. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/bridges/nano/mapping.py +0 -0
  133. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/bridges/soothe/__init__.py +0 -0
  134. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/bridges/soothe/adapter.py +0 -0
  135. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/bridges/soothe/backend.py +0 -0
  136. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/events.py +0 -0
  137. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/identity.py +0 -0
  138. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/modes.py +0 -0
  139. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/protocol.py +0 -0
  140. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/runtime/__init__.py +0 -0
  141. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/runtime/direct.py +0 -0
  142. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/runtime/fake.py +0 -0
  143. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/runtime/isolation/__init__.py +0 -0
  144. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/runtime/isolation/adapter.py +0 -0
  145. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/runtime/isolation/admission.py +0 -0
  146. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/runtime/isolation/backend.py +0 -0
  147. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/runtime/isolation/errors.py +0 -0
  148. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/runtime/isolation/mode_gate.py +0 -0
  149. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/runtime/isolation/pool.py +0 -0
  150. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/runtime/isolation/request.py +0 -0
  151. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/sandbox/__init__.py +0 -0
  152. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/sandbox/resolver.py +0 -0
  153. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/sandbox/settings.py +0 -0
  154. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/streaming/__init__.py +0 -0
  155. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/streaming/presentation.py +0 -0
  156. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/streaming/tool_results.py +0 -0
  157. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/core/streaming/tool_stream.py +0 -0
  158. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/home.py +0 -0
  159. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/__init__.py +0 -0
  160. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/__main__.py +0 -0
  161. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/acp/__init__.py +0 -0
  162. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/acp/agent.py +0 -0
  163. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/acp/routes.py +0 -0
  164. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/config.py +0 -0
  165. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/http/__init__.py +0 -0
  166. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/http/app.py +0 -0
  167. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/http/auth.py +0 -0
  168. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/openai_compat/__init__.py +0 -0
  169. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/openai_compat/errors.py +0 -0
  170. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/openai_compat/projection.py +0 -0
  171. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/openai_compat/routes.py +0 -0
  172. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/openai_compat/schemas.py +0 -0
  173. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/openai_compat/service.py +0 -0
  174. {flowjet-2.1.1 → flowjet-2.1.2}/src/flowjet/server/openai_compat/store.py +0 -0
@@ -10,6 +10,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [2.1.2] — 2026-10-21
14
+
15
+ ### Changed
16
+
17
+ - **Bump `soothe` to 1.2.1.** The runtime dependency floor moves from
18
+ `>=1.2.0` to `>=1.2.1,<2.0.0` to track the upstream fix release. No flowjet
19
+ API or behavior changes; the locked dependency set is otherwise unchanged.
20
+
13
21
  ## [2.1.1] — 2026-10-21
14
22
 
15
23
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: flowjet
3
- Version: 2.1.1
3
+ Version: 2.1.2
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
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: 3.14
22
22
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
24
  Requires-Python: <3.15,>=3.11
25
- Requires-Dist: soothe<2.0.0,>=1.2.0
25
+ Requires-Dist: soothe<2.0.0,>=1.2.1
26
26
  Provides-Extra: dev
27
27
  Requires-Dist: httpx>=0.27.0; extra == 'dev'
28
28
  Requires-Dist: mypy>=1.0.0; extra == 'dev'
@@ -38,11 +38,16 @@ microVM duplicated soothe-nano's sandbox capability at the wrong layer.
38
38
  - Per-session guest isolation: the sandbox is **per workspace**. Concurrent
39
39
  sessions sharing one workspace share one guest (staged serially; a peer
40
40
  mid-run skips the host push so live guest state is never clobbered).
41
- - Guest-side deletion propagation: a host file deleted between sessions is
42
- not removed from the guest (the pull direction is authoritative at close).
43
41
  - Incremental (delta) transfers: staging is a full-tree push/pull with a
44
42
  `(size, mtime_ns)` manifest skip for unchanged files.
45
43
 
44
+ > **Revision (2026-10-21, RFC-RD-607)**: guest-side deletion propagation is no
45
+ > longer a non-goal — `pull` now removes host files that were recorded in the
46
+ > last push manifest but disappeared from the guest tree (so agent-side
47
+ > deletes flow guest → host → sync checkpoint → content tree). Host files
48
+ > created after the push are never in the manifest and can never be deleted
49
+ > by this path.
50
+
46
51
  ## 4. Configuration
47
52
 
48
53
  Enabled by a `sandbox:` section in the flowjet nano.yml. Flowjet owns the
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "flowjet"
7
- version = "2.1.1"
7
+ version = "2.1.2"
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" }
@@ -27,7 +27,7 @@ classifiers = [
27
27
  ]
28
28
  # Single-dependency CLI wheel; HTTP service deps live in the `server` extra.
29
29
  dependencies = [
30
- "soothe>=1.2.0,<2.0.0",
30
+ "soothe>=1.2.1,<2.0.0",
31
31
  ]
32
32
 
33
33
  [project.optional-dependencies]
@@ -7,6 +7,7 @@ import hashlib
7
7
  import logging
8
8
  import re
9
9
  import shutil
10
+ from dataclasses import dataclass
10
11
  from pathlib import Path
11
12
  from typing import Any
12
13
 
@@ -110,6 +111,29 @@ class WorkspaceResolver:
110
111
  return path == self._home
111
112
 
112
113
 
114
+ @dataclass
115
+ class _OpenWorkspaceEntry:
116
+ """One open workspace — shared by all concurrent turns of the same run_id.
117
+
118
+ ``active_runs`` counts turns currently mid-run on this workspace; the
119
+ lifecycle (``manager.close``) belongs to the last run out. ``manifest``
120
+ caches the recovered engine manifest so the per-turn refresh can protect
121
+ agent artifacts from deletion reconcile.
122
+ """
123
+
124
+ ws: Any
125
+ active_runs: int = 0
126
+ manifest: Any | None = None
127
+ restored: bool = False
128
+
129
+
130
+ def _manifest_artifact_paths(manifest: Any | None) -> list[str]:
131
+ """Relative paths of the manifest's artifacts (reconcile protection set)."""
132
+ if manifest is None:
133
+ return []
134
+ return [entry.path for entry in (getattr(manifest, "artifacts", None) or [])]
135
+
136
+
113
137
  class SyncAwareWorkspaceResolver(WorkspaceResolver):
114
138
  """Workspace resolver backed by soothe's fsspec workspace sync.
115
139
 
@@ -150,7 +174,7 @@ class SyncAwareWorkspaceResolver(WorkspaceResolver):
150
174
  self._manager: Any | None = None
151
175
  self._sync_backend: Any | None = None
152
176
  self._restore_cas: Any | None = None
153
- self._open_workspaces: dict[str, Any] = {}
177
+ self._open_workspaces: dict[str, _OpenWorkspaceEntry] = {}
154
178
 
155
179
  @property
156
180
  def is_sync_enabled(self) -> bool:
@@ -263,7 +287,17 @@ class SyncAwareWorkspaceResolver(WorkspaceResolver):
263
287
  return path
264
288
 
265
289
  async def open_session(self, session: str, workspace: Path) -> None:
266
- """Materialize workspace from object store + start dirty tracking."""
290
+ """Refresh the workspace from the object store + start dirty tracking.
291
+
292
+ RFC-RD-607 §4.3: the sync boundary is the agent turn (one pair of
293
+ open/close hooks per prompt). Every turn unconditionally materializes
294
+ the content tree — including the reuse path where a peer session's
295
+ turn already holds the workspace open (sha-skip keeps repeat turns
296
+ cheap). Deletion reconcile runs only while this turn is the sole
297
+ active run on the workspace: deleting files under a peer's feet is
298
+ worse than a delayed delete, so peer-occupied turns skip it (logged).
299
+ Prior agent artifacts are restored once, on the opening turn.
300
+ """
267
301
  manager = self._ensure_manager()
268
302
  if manager is None:
269
303
  return
@@ -273,18 +307,39 @@ class SyncAwareWorkspaceResolver(WorkspaceResolver):
273
307
  # for perfect consistency across resolve/open/close.
274
308
  run_id = workspace.name
275
309
 
276
- # If a workspace is already open for this session (reuse), skip.
277
- if run_id in self._open_workspaces:
278
- return
310
+ entry = self._open_workspaces.get(run_id)
311
+ if entry is None:
312
+ ws = await manager.open(run_id)
313
+ entry = _OpenWorkspaceEntry(ws=ws)
314
+ self._open_workspaces[run_id] = entry
315
+ else:
316
+ ws = entry.ws
317
+ entry.active_runs += 1
318
+
319
+ if entry.manifest is None:
320
+ try:
321
+ entry.manifest = await self._recover_manifest(ws)
322
+ except Exception:
323
+ logger.warning("Manifest recovery failed for run %s", run_id, exc_info=True)
279
324
 
280
- ws = await manager.open(run_id)
281
325
  # Dual-source sync, read path: materialize the content root (e.g.
282
326
  # triarch workspace materials) into the workspace BEFORE replaying
283
327
  # checkpoint state, so the agent sees both its prior outputs and the
284
- # latest backend-uploaded files.
328
+ # latest backend-uploaded files. The dirty tracker only watches
329
+ # input/working/output + root files, so content-tree writes never
330
+ # pollute the dirty set.
285
331
  if hasattr(ws, "materialize_content"):
286
332
  try:
287
- await ws.materialize_content()
333
+ if self._supports_content_reconcile(ws):
334
+ await ws.materialize_content(
335
+ protected=_manifest_artifact_paths(entry.manifest),
336
+ reconcile=entry.active_runs == 1,
337
+ )
338
+ else:
339
+ # soothe < 2.0: no protected/reconcile kwargs — degrade to
340
+ # a plain materialize (no reconcile; deletes stay until
341
+ # the soothe 2.0.0 release train lands).
342
+ await ws.materialize_content()
288
343
  except Exception:
289
344
  logger.warning(
290
345
  "Content materialization failed for run %s; continuing with "
@@ -292,19 +347,43 @@ class SyncAwareWorkspaceResolver(WorkspaceResolver):
292
347
  run_id,
293
348
  exc_info=True,
294
349
  )
295
- await self._restore_prior_state(ws)
296
- self._open_workspaces[run_id] = ws
350
+
351
+ if entry.manifest is not None and not entry.restored:
352
+ await self._restore_prior_state(ws, entry.manifest)
353
+ entry.restored = True
297
354
 
298
355
  # Start dirty tracking + background uploader for incremental persistence.
299
356
  await ws.start_tracking()
300
357
  logger.info("Opened sync workspace for session %s at %s", session, workspace)
301
358
 
302
- async def _restore_prior_state(self, ws: Any) -> None:
303
- """Recover the prior manifest and materialize its files locally.
359
+ @staticmethod
360
+ def _supports_content_reconcile(ws: Any) -> bool:
361
+ """True when the workspace's ``materialize_content`` takes RFC-RD-607 kwargs.
362
+
363
+ soothe < 2.0 lacks ``protected``/``reconcile``; probing the signature
364
+ keeps flowjet runnable against both release trains.
365
+ """
366
+ import inspect
367
+
368
+ try:
369
+ params = inspect.signature(ws.materialize_content).parameters
370
+ except (TypeError, ValueError):
371
+ return False
372
+ return "protected" in params and "reconcile" in params
373
+
374
+ async def _recover_manifest(self, ws: Any) -> Any | None:
375
+ """Recover the prior engine manifest snapshot (single call per open).
304
376
 
305
377
  Round-trip half of workspace sync: ``close_session`` checkpoints and
306
- uploads dirty files; this restores them so a fresh server process (or
307
- an evicted session workspace) sees the agent's prior outputs.
378
+ uploads dirty files; this reads back the manifest so the per-turn
379
+ materialize can protect agent artifacts and the opening turn can
380
+ restore them.
381
+ """
382
+ payload = await ws.recover() # reads the latest checkpoint payload
383
+ return payload.manifest_snapshot if payload is not None else None
384
+
385
+ async def _restore_prior_state(self, ws: Any, manifest: Any | None) -> None:
386
+ """Materialize the recovered manifest's files locally.
308
387
 
309
388
  - Agent-written files live in ``manifest.artifacts`` (path → sha256,
310
389
  blobs under ``<root>/blobs/``) and are restored in place at their
@@ -312,8 +391,6 @@ class SyncAwareWorkspaceResolver(WorkspaceResolver):
312
391
  - Declared input resources (``manifest.resources``) are materialized
313
392
  under ``<root>/input/`` via the soothe Materializer.
314
393
  """
315
- payload = await ws.recover() # restores checkpoint state, returns snapshot payload
316
- manifest = payload.manifest_snapshot if payload is not None else None
317
394
  if manifest is None:
318
395
  return
319
396
 
@@ -363,20 +440,32 @@ class SyncAwareWorkspaceResolver(WorkspaceResolver):
363
440
  return self._restore_cas
364
441
 
365
442
  async def close_session(self, session: str, workspace: Path) -> None:
366
- """Checkpoint dirty files + upload to object store + close workspace."""
443
+ """Checkpoint dirty files + upload to object store; close when last run ends.
444
+
445
+ Concurrent turns of the same workspace share one open workspace:
446
+ a mid-run peer's close checkpoints the shared state but keeps the
447
+ workspace open — the lifecycle (``manager.close``) belongs to the
448
+ last run out (RFC-RD-607 §4.3).
449
+ """
367
450
  if self._manager is None:
368
451
  return
369
452
 
370
453
  run_id = workspace.name
371
454
 
372
- ws = self._open_workspaces.pop(run_id, None)
373
- if ws is None:
455
+ entry = self._open_workspaces.get(run_id)
456
+ if entry is None:
374
457
  return
375
458
 
459
+ if entry.active_runs > 0:
460
+ entry.active_runs -= 1
376
461
  try:
377
- await ws.checkpoint()
462
+ await entry.ws.checkpoint()
378
463
  except Exception:
379
464
  logger.warning("Checkpoint failed for session %s", session, exc_info=True)
465
+ if entry.active_runs > 0:
466
+ return
467
+
468
+ self._open_workspaces.pop(run_id, None)
380
469
  try:
381
470
  await self._manager.close(run_id)
382
471
  except Exception:
@@ -116,12 +116,18 @@ class WorkspaceStager:
116
116
  ) -> dict[str, tuple[int, int]]:
117
117
  """Download the guest workspace tree back to the host (skip identical).
118
118
 
119
- Returns the manifest for the next push.
119
+ Deletion propagation (RFC-RD-607 §4.4): files the agent deleted inside
120
+ the guest are absent from its tree and are removed from the host too.
121
+ Only paths recorded in the push manifest are eligible — host files
122
+ created after the push are never in it, so they can never be deleted
123
+ by this path. Returns the manifest for the next push.
120
124
  """
121
125
  root = Path(workspace)
122
126
  guest_root = PurePosixPath(self._settings.workspace_root)
123
127
  pulled = 0
128
+ deleted = 0
124
129
  next_manifest: dict[str, tuple[int, int]] = {}
130
+ guest_files: set[str] = set()
125
131
 
126
132
  async def walk(guest_dir: PurePosixPath, rel_dir: PurePosixPath) -> None:
127
133
  nonlocal pulled
@@ -142,6 +148,7 @@ class WorkspaceStager:
142
148
  continue
143
149
  if entry.kind != "file":
144
150
  continue
151
+ guest_files.add(rel_entry.as_posix())
145
152
  data = await sandbox.files.read_bytes(guest_path)
146
153
  dest = root / Path(*rel_entry.parts)
147
154
  if dest.is_file():
@@ -157,8 +164,34 @@ class WorkspaceStager:
157
164
  pulled += 1
158
165
 
159
166
  await walk(guest_root, PurePosixPath("."))
160
- if pulled:
161
- logger.info("Sandbox pull for %s: %d files", workspace, pulled)
167
+ for rel in manifest:
168
+ if rel in guest_files:
169
+ continue
170
+ dest = root / Path(*PurePosixPath(rel).parts)
171
+ if not dest.is_file() or dest.is_symlink():
172
+ continue
173
+ try:
174
+ await asyncio.to_thread(dest.unlink)
175
+ except OSError:
176
+ logger.warning("Sandbox pull: host delete failed for %s in %s", rel, workspace)
177
+ continue
178
+ deleted += 1
179
+ parent = dest.parent
180
+ while parent != root and parent.is_relative_to(root):
181
+ if any(parent.iterdir()):
182
+ break
183
+ try:
184
+ parent.rmdir()
185
+ except OSError:
186
+ break
187
+ parent = parent.parent
188
+ if pulled or deleted:
189
+ logger.info(
190
+ "Sandbox pull for %s: %d files pulled, %d guest deletions propagated",
191
+ workspace,
192
+ pulled,
193
+ deleted,
194
+ )
162
195
  return next_manifest
163
196
 
164
197
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes