flowjet 2.0.1__tar.gz → 2.0.3__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 (169) hide show
  1. {flowjet-2.0.1 → flowjet-2.0.3}/CHANGELOG.md +29 -0
  2. {flowjet-2.0.1 → flowjet-2.0.3}/PKG-INFO +16 -1
  3. {flowjet-2.0.1 → flowjet-2.0.3}/README.md +14 -0
  4. {flowjet-2.0.1 → flowjet-2.0.3}/pyproject.toml +4 -1
  5. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/progress.py +34 -0
  6. flowjet-2.0.3/src/flowjet/core/backend.py +107 -0
  7. flowjet-2.0.3/src/flowjet/core/bootstrap.py +310 -0
  8. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/bridges/nano/adapter.py +7 -6
  9. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/bridges/nano/mapping.py +193 -2
  10. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/identity.py +66 -0
  11. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/modes.py +24 -7
  12. flowjet-2.0.1/src/flowjet/core/bootstrap.py +0 -183
  13. {flowjet-2.0.1 → flowjet-2.0.3}/.gitignore +0 -0
  14. {flowjet-2.0.1 → flowjet-2.0.3}/LICENSE +0 -0
  15. {flowjet-2.0.1 → flowjet-2.0.3}/config/nano.yml +0 -0
  16. {flowjet-2.0.1 → flowjet-2.0.3}/deploy/README.md +0 -0
  17. {flowjet-2.0.1 → flowjet-2.0.3}/deploy/nano.yml +0 -0
  18. {flowjet-2.0.1 → flowjet-2.0.3}/docs/completion-design.md +0 -0
  19. {flowjet-2.0.1 → flowjet-2.0.3}/docs/drafts/README.md +0 -0
  20. {flowjet-2.0.1 → flowjet-2.0.3}/docs/drafts/api-spec.md +0 -0
  21. {flowjet-2.0.1 → flowjet-2.0.3}/docs/impl/IG-001-openai-compatible-server.md +0 -0
  22. {flowjet-2.0.1 → flowjet-2.0.3}/docs/impl/IG-002-isolated-thread-pool-runtime.md +0 -0
  23. {flowjet-2.0.1 → flowjet-2.0.3}/docs/impl/IG-003-production-isolation-hardening.md +0 -0
  24. {flowjet-2.0.1 → flowjet-2.0.3}/docs/impl/README.md +0 -0
  25. {flowjet-2.0.1 → flowjet-2.0.3}/docs/impl/TEST-COVERAGE-MATRIX.md +0 -0
  26. {flowjet-2.0.1 → flowjet-2.0.3}/docs/naming.md +0 -0
  27. {flowjet-2.0.1 → flowjet-2.0.3}/docs/server.md +0 -0
  28. {flowjet-2.0.1 → flowjet-2.0.3}/docs/specs/RFC-001-openai-compatible-api.md +0 -0
  29. {flowjet-2.0.1 → flowjet-2.0.3}/docs/specs/RFC-002-isolated-thread-pool-runtime.md +0 -0
  30. {flowjet-2.0.1 → flowjet-2.0.3}/docs/specs/RFC-003-production-isolation-hardening.md +0 -0
  31. {flowjet-2.0.1 → flowjet-2.0.3}/docs/specs/RFC-004-unified-package.md +0 -0
  32. {flowjet-2.0.1 → flowjet-2.0.3}/docs/specs/rfc-history.md +0 -0
  33. {flowjet-2.0.1 → flowjet-2.0.3}/docs/specs/rfc-index.md +0 -0
  34. {flowjet-2.0.1 → flowjet-2.0.3}/docs/specs/rfc-namings.md +0 -0
  35. {flowjet-2.0.1 → flowjet-2.0.3}/docs/specs/templates/architecture-design.md +0 -0
  36. {flowjet-2.0.1 → flowjet-2.0.3}/docs/specs/templates/conceptual-design.md +0 -0
  37. {flowjet-2.0.1 → flowjet-2.0.3}/docs/specs/templates/impl-interface-design.md +0 -0
  38. {flowjet-2.0.1 → flowjet-2.0.3}/docs/specs/templates/rfc-standard.md +0 -0
  39. {flowjet-2.0.1 → flowjet-2.0.3}/docs/specs/templates/rfc-template.md +0 -0
  40. {flowjet-2.0.1 → flowjet-2.0.3}/docs/upgrade.md +0 -0
  41. {flowjet-2.0.1 → flowjet-2.0.3}/examples/README.md +0 -0
  42. {flowjet-2.0.1 → flowjet-2.0.3}/nano.yml +0 -0
  43. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/__init__.py +0 -0
  44. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/__main__.py +0 -0
  45. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/README.md +0 -0
  46. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/gws-drive/SKILL.md +0 -0
  47. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/gws-drive-upload/SKILL.md +0 -0
  48. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/logo-generator-skill/.env.example +0 -0
  49. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/logo-generator-skill/README.md +0 -0
  50. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/logo-generator-skill/SKILL.md +0 -0
  51. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/logo-generator-skill/assets/background_library.html +0 -0
  52. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/logo-generator-skill/assets/showcase_template.html +0 -0
  53. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/logo-generator-skill/references/background_styles.md +0 -0
  54. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/logo-generator-skill/references/design_patterns.md +0 -0
  55. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/logo-generator-skill/references/webgl_backgrounds.md +0 -0
  56. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/logo-generator-skill/requirements.txt +0 -0
  57. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/logo-generator-skill/scripts/generate_showcase.py +0 -0
  58. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/logo-generator-skill/scripts/svg_to_png.py +0 -0
  59. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/SKILL.md +0 -0
  60. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/brainstorming/spec-document-reviewer-prompt.md +0 -0
  61. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/implementation/coding-plan-template.md +0 -0
  62. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/implementation/impl-guide-template.md +0 -0
  63. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/review/pr-review-template.md +0 -0
  64. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/review/review-checklist.md +0 -0
  65. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/specs/rfc-template.md +0 -0
  66. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/specs/template-architecture-design.md +0 -0
  67. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/specs/template-conceptual-design.md +0 -0
  68. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/specs/template-impl-interface-design.md +0 -0
  69. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-drafts-readme.md +0 -0
  70. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-impl-readme.md +0 -0
  71. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-platonic.yml +0 -0
  72. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-history.md +0 -0
  73. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-index.md +0 -0
  74. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-namings.md +0 -0
  75. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/assets/templates/template-rfc-standard.md +0 -0
  76. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/BRAINSTORM/brainstorm.md +0 -0
  77. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-code.md +0 -0
  78. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-create-guide.md +0 -0
  79. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-full.md +0 -0
  80. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-update-guide.md +0 -0
  81. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/IMPL/impl-validate-guide.md +0 -0
  82. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-plan-modular-specs.md +0 -0
  83. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-recover-architecture.md +0 -0
  84. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-recover-conceptual.md +0 -0
  85. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-recover-impl-interface.md +0 -0
  86. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-scaffold.md +0 -0
  87. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/INIT/init-scan.md +0 -0
  88. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/REFERENCE.md +0 -0
  89. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/REVIEW/review-spec-compliance.md +0 -0
  90. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-check-compliance.md +0 -0
  91. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-check-taxonomy.md +0 -0
  92. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-generate-history.md +0 -0
  93. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-generate-index.md +0 -0
  94. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-generate-namings.md +0 -0
  95. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-refine.md +0 -0
  96. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/SPECS/specs-validate-consistency.md +0 -0
  97. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-overview.md +0 -0
  98. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-phase-1.md +0 -0
  99. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-phase-2.md +0 -0
  100. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/platonic-coding/references/WORKFLOW/workflow-phase-3.md +0 -0
  101. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/SKILL.md +0 -0
  102. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/patterns/evidence-deep.json +0 -0
  103. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/patterns/evidence-first.json +0 -0
  104. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/patterns/idea-first.json +0 -0
  105. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/patterns/loop.json +0 -0
  106. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/patterns/paper-pipeline.json +0 -0
  107. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/patterns/rapid.json +0 -0
  108. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/patterns/rigor-first.json +0 -0
  109. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/patterns/stance-first.json +0 -0
  110. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/patterns/systematic-review.json +0 -0
  111. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/patterns/verify-loop.json +0 -0
  112. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/references/backends.md +0 -0
  113. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/references/handoff-ars-omr.md +0 -0
  114. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/references/handoff-omr-ars.md +0 -0
  115. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/references/routing.md +0 -0
  116. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/scripts/csljson_to_omr.py +0 -0
  117. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/builtin_skills/research-bootstrap/scripts/papers_index_to_csljson.py +0 -0
  118. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/__init__.py +0 -0
  119. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/__main__.py +0 -0
  120. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/agent.py +0 -0
  121. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/cli.py +0 -0
  122. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/completion/__init__.py +0 -0
  123. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/completion/context.py +0 -0
  124. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/completion/engine.py +0 -0
  125. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/doctor_cmd.py +0 -0
  126. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/doctor_fix_cmd.py +0 -0
  127. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/serve_cmd.py +0 -0
  128. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/setup_cmd.py +0 -0
  129. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/shell/__init__.py +0 -0
  130. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/shell/_fj.zsh +0 -0
  131. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/shell/fj.bash +0 -0
  132. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/stream.py +0 -0
  133. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/threads.py +0 -0
  134. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/cli/tool_stream.py +0 -0
  135. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/__init__.py +0 -0
  136. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/backends/__init__.py +0 -0
  137. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/backends/direct.py +0 -0
  138. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/backends/fake.py +0 -0
  139. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/backends/isolation/__init__.py +0 -0
  140. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/backends/isolation/adapter.py +0 -0
  141. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/backends/isolation/admission.py +0 -0
  142. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/backends/isolation/backend.py +0 -0
  143. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/backends/isolation/errors.py +0 -0
  144. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/backends/isolation/mode_gate.py +0 -0
  145. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/backends/isolation/pool.py +0 -0
  146. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/backends/isolation/request.py +0 -0
  147. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/backends/isolation/workspace.py +0 -0
  148. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/bridges/nano/__init__.py +0 -0
  149. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/bridges/nano/backend.py +0 -0
  150. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/events.py +0 -0
  151. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/protocol.py +0 -0
  152. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/tool_results.py +0 -0
  153. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/core/tool_stream.py +0 -0
  154. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/__init__.py +0 -0
  155. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/__main__.py +0 -0
  156. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/acp/__init__.py +0 -0
  157. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/acp/agent.py +0 -0
  158. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/acp/routes.py +0 -0
  159. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/config.py +0 -0
  160. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/http/__init__.py +0 -0
  161. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/http/app.py +0 -0
  162. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/http/auth.py +0 -0
  163. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/openai_compat/__init__.py +0 -0
  164. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/openai_compat/errors.py +0 -0
  165. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/openai_compat/projection.py +0 -0
  166. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/openai_compat/routes.py +0 -0
  167. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/openai_compat/schemas.py +0 -0
  168. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/openai_compat/service.py +0 -0
  169. {flowjet-2.0.1 → flowjet-2.0.3}/src/flowjet/server/openai_compat/store.py +0 -0
@@ -10,6 +10,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [2.0.3] — 2026-09-18
14
+
15
+ ### Added
16
+
17
+ - **Plan-step and middleware-phase progress in the CLI.** The nano bridge now
18
+ maps the host (soothe) loop's todo list and middleware phase updates into
19
+ step/tool progress events, so the terminal shows `Step 2/3 · Create a2.txt`
20
+ instead of a bare `Working` line. Late-parsing tool args refresh the status
21
+ line once, and `write_todos` bookkeeping is suppressed as a tool call (it
22
+ already drives the step line).
23
+
24
+ ## [2.0.2] — 2026-09-18
25
+
26
+ ### Added
27
+
28
+ - **Duo backends — `soothe` or `soothe-nano`.** `FLOWJET_BACKEND` (CLI, default
29
+ `soothe`) and `FLOWJET_SERVER_BACKEND` (HTTP/ACP, default `nano`) select which
30
+ runtime builds the agent. `soothe` is a host layer over soothe-nano, so the
31
+ engine stays installed either way; the two surfaces switch independently
32
+ because the server needs a dual-mode agent (AGENT + ASK graphs per request),
33
+ which soothe does not expose — with `FLOWJET_SERVER_BACKEND=soothe` the server
34
+ builds one host agent per mode and routes on `interaction_mode`.
35
+
36
+ ### Changed
37
+
38
+ - The CLI now builds its agent through the `soothe` host runtime by default
39
+ (host middleware, `request_plan_mode` / `ask_user` tools, configurable
40
+ persona). Set `FLOWJET_BACKEND=nano` for the previous behaviour.
41
+
13
42
  ## [2.0.1] — 2026-09-17
14
43
 
15
44
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: flowjet
3
- Version: 2.0.1
3
+ Version: 2.0.3
4
4
  Summary: FlowJet — agent CLI (fj) and server (ACP + OpenAI-compatible HTTP) powered by soothe-nano
5
5
  Project-URL: Homepage, https://github.com/caesar0301/flowjet
6
6
  Project-URL: Repository, https://github.com/caesar0301/flowjet
@@ -23,6 +23,7 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
24
  Requires-Python: <3.15,>=3.11
25
25
  Requires-Dist: soothe-nano<1.3.0,>=1.2.28
26
+ Requires-Dist: soothe<2.0.0,>=1.0.12
26
27
  Provides-Extra: dev
27
28
  Requires-Dist: httpx>=0.27.0; extra == 'dev'
28
29
  Requires-Dist: mypy>=1.0.0; extra == 'dev'
@@ -78,6 +79,20 @@ fj summarize README.md
78
79
 
79
80
  Not sure your machine is ready? `fj doctor` (add `--deep`, `--live-llm`).
80
81
 
82
+ ### Backends
83
+
84
+ Two runtimes can build the agent; they are switched independently per surface:
85
+
86
+ | Env var | Surface | Default | Values |
87
+ |---------|---------|---------|--------|
88
+ | `FLOWJET_BACKEND` | CLI (`fj`) | `soothe` | `soothe`, `nano` |
89
+ | `FLOWJET_SERVER_BACKEND` | `flowjet-server` / `fj serve` | `nano` | `soothe`, `nano` |
90
+
91
+ `soothe` is the host runtime (extra middleware and tools over the same
92
+ soothe-nano engine). The server defaults to `nano` because it needs a dual-mode
93
+ agent — AGENT and ASK graphs selected per request — which `soothe` does not
94
+ expose yet; on `soothe` the server builds one host agent per mode instead.
95
+
81
96
  ---
82
97
 
83
98
  ## CLI
@@ -35,6 +35,20 @@ fj summarize README.md
35
35
 
36
36
  Not sure your machine is ready? `fj doctor` (add `--deep`, `--live-llm`).
37
37
 
38
+ ### Backends
39
+
40
+ Two runtimes can build the agent; they are switched independently per surface:
41
+
42
+ | Env var | Surface | Default | Values |
43
+ |---------|---------|---------|--------|
44
+ | `FLOWJET_BACKEND` | CLI (`fj`) | `soothe` | `soothe`, `nano` |
45
+ | `FLOWJET_SERVER_BACKEND` | `flowjet-server` / `fj serve` | `nano` | `soothe`, `nano` |
46
+
47
+ `soothe` is the host runtime (extra middleware and tools over the same
48
+ soothe-nano engine). The server defaults to `nano` because it needs a dual-mode
49
+ agent — AGENT and ASK graphs selected per request — which `soothe` does not
50
+ expose yet; on `soothe` the server builds one host agent per mode instead.
51
+
38
52
  ---
39
53
 
40
54
  ## CLI
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "flowjet"
7
- version = "2.0.1"
7
+ version = "2.0.3"
8
8
  description = "FlowJet — agent CLI (fj) and server (ACP + OpenAI-compatible HTTP) powered by soothe-nano"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -29,6 +29,9 @@ classifiers = [
29
29
  # lives in the `server` extra, and `flowjet.cli` never imports it at module
30
30
  # scope (see tests/unit/test_cli_no_server_deps.py).
31
31
  dependencies = [
32
+ # Default CLI runtime (FLOWJET_BACKEND=soothe). soothe is a host layer over
33
+ # soothe-nano, so nano stays pinned explicitly: soothe only caps it at <2.
34
+ "soothe>=1.0.12,<2.0.0",
32
35
  "soothe-nano>=1.2.28,<1.3.0",
33
36
  ]
34
37
 
@@ -67,6 +67,14 @@ _ACTION_LABELS: dict[str, str] = {
67
67
  "queued": "Queued",
68
68
  }
69
69
 
70
+ # Host (soothe) loop phases surfaced as graph updates by the nano bridge.
71
+ _STEP_PHASE_LABELS: dict[str, str] = {
72
+ "planned": "Planning steps",
73
+ "decomposed": "Decomposing task",
74
+ "evaluated": "Evaluating step",
75
+ "fanned_out": "Coordinating agents",
76
+ }
77
+
70
78
  # Verb + color for well-known tools (fallback: Running / yellow)
71
79
  _TOOL_VERBS: dict[str, tuple[str, str]] = {
72
80
  "read_file": ("Reading", "yellow"),
@@ -693,6 +701,32 @@ def friendly_progress(data: dict[str, Any]) -> tuple[str, str] | None:
693
701
  label = f"{label} · {detail}"
694
702
  return _fit(label), color
695
703
 
704
+ if domain == "step":
705
+ # Plan steps come from the agent's todo list (one event per change).
706
+ if "steps" in data or "todo" in data:
707
+ step = data.get("step")
708
+ steps = data.get("steps")
709
+ todo = _compact(data.get("todo"))
710
+ head = f"Step {step}/{steps}" if step and steps else "Step"
711
+ if todo:
712
+ return _fit(f"{head} · {todo}"), "blue"
713
+ if data.get("status") == "completed":
714
+ return _fit(f"Steps complete · {step}/{steps}"), "green"
715
+ return _fit(head), "blue"
716
+ label = _STEP_PHASE_LABELS.get(action) or verb
717
+ return _fit(label), "blue"
718
+
719
+ if event_type == "soothe.ask.requested":
720
+ question = _compact(data.get("question") or data.get("message"))
721
+ if question:
722
+ q_limit = _detail_budget(prefix="Preparing question · ", fraction=0.55, floor=16)
723
+ return _fit(f"Preparing question · {_truncate_middle(question, q_limit)}"), "magenta"
724
+ return _fit("Preparing question"), "magenta"
725
+
726
+ if event_type == "soothe.intake.routed":
727
+ name = _compact(data.get("subagent") or data.get("subagent_type") or "specialist")
728
+ return _fit(f"Routing · {name}"), "magenta"
729
+
696
730
  if domain == "skill" or "skill" in short:
697
731
  color = "blue"
698
732
  name = data.get("skill") or data.get("name") or "skill"
@@ -0,0 +1,107 @@
1
+ """Backend selection — which runtime builds FlowJet's agent.
2
+
3
+ FlowJet can build its agent two ways:
4
+
5
+ ``soothe``
6
+ The host runtime (``soothe.coreagent.create_soothe_agent``). ``soothe`` is a
7
+ layer **on top of** soothe-nano, not a replacement for it: it injects host
8
+ middleware (goal/step guard, decompose, eval, ask-user), host tools
9
+ (``request_plan_mode``, ``ask_user``) and a configurable persona over the
10
+ same nano agent graph.
11
+
12
+ ``nano``
13
+ soothe-nano directly (``create_nano_agent`` for the CLI,
14
+ ``create_dual_mode_nano_agent`` for the server).
15
+
16
+ The two surfaces switch **independently**, because they do not share the same
17
+ constraints: the CLI builds one agent per invocation, while the server needs a
18
+ dual-mode agent (AGENT + ASK graphs selected per request) — an API soothe does
19
+ not expose today. Hence the defaults differ:
20
+
21
+ * ``FLOWJET_BACKEND`` (CLI) — default ``soothe``.
22
+ * ``FLOWJET_SERVER_BACKEND`` (HTTP/ACP) — default ``nano``.
23
+
24
+ Both accept ``soothe`` or ``nano`` (case-insensitive); anything else is a hard
25
+ error rather than a silent fallback, so a typo can never quietly run the wrong
26
+ runtime.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import os
32
+ from enum import StrEnum
33
+
34
+ from flowjet.core.modes import Surface
35
+
36
+ CLI_BACKEND_ENV = "FLOWJET_BACKEND"
37
+ SERVER_BACKEND_ENV = "FLOWJET_SERVER_BACKEND"
38
+
39
+
40
+ class Backend(StrEnum):
41
+ """Runtime that builds the agent."""
42
+
43
+ SOOTHE = "soothe"
44
+ NANO = "nano"
45
+
46
+
47
+ #: CLI builds one agent per invocation; the host runtime is the default there.
48
+ DEFAULT_CLI_BACKEND = Backend.SOOTHE
49
+
50
+ #: The server needs per-request mode switching (dual-mode agent), which soothe
51
+ #: does not expose; it stays on nano until host dual-mode exists.
52
+ DEFAULT_SERVER_BACKEND = Backend.NANO
53
+
54
+ _BACKENDS: dict[str, Backend] = {b.value: b for b in Backend}
55
+
56
+
57
+ def parse_backend(value: str | None, *, default: Backend, env_name: str) -> Backend:
58
+ """Resolve one backend value (env string → :class:`Backend`).
59
+
60
+ ``None`` and empty/whitespace values fall back to ``default``; an unknown
61
+ value raises ``ValueError`` naming the offending variable.
62
+ """
63
+ if value is None:
64
+ return default
65
+ token = value.strip().lower()
66
+ if not token:
67
+ return default
68
+ try:
69
+ return _BACKENDS[token]
70
+ except KeyError:
71
+ raise ValueError(
72
+ f"{env_name}={value!r} is not a valid backend "
73
+ f"(expected one of: {', '.join(sorted(_BACKENDS))})"
74
+ ) from None
75
+
76
+
77
+ def resolve_backend(surface: Surface | str) -> Backend:
78
+ """Return the backend configured for ``surface`` (read from the env)."""
79
+ resolved = Surface(str(surface))
80
+ if resolved is Surface.SERVER:
81
+ return parse_backend(
82
+ os.environ.get(SERVER_BACKEND_ENV),
83
+ default=DEFAULT_SERVER_BACKEND,
84
+ env_name=SERVER_BACKEND_ENV,
85
+ )
86
+ return parse_backend(
87
+ os.environ.get(CLI_BACKEND_ENV),
88
+ default=DEFAULT_CLI_BACKEND,
89
+ env_name=CLI_BACKEND_ENV,
90
+ )
91
+
92
+
93
+ def backend_for_profile(profile: object) -> Backend:
94
+ """Return the backend for a :class:`~flowjet.core.modes.SurfaceProfile`."""
95
+ return resolve_backend(getattr(profile, "surface", Surface.CLI))
96
+
97
+
98
+ __all__ = [
99
+ "CLI_BACKEND_ENV",
100
+ "DEFAULT_CLI_BACKEND",
101
+ "DEFAULT_SERVER_BACKEND",
102
+ "SERVER_BACKEND_ENV",
103
+ "Backend",
104
+ "backend_for_profile",
105
+ "parse_backend",
106
+ "resolve_backend",
107
+ ]
@@ -0,0 +1,310 @@
1
+ """Single bootstrap shared by the CLI and the server.
2
+
3
+ Both surfaces used to build their agent separately (``fj_ai.agent.build_agent``
4
+ and ``flowjet_server.bridges.nano.adapter.create_nano_agent_instance``), with
5
+ different constructors and contradictory security defaults. This module is the
6
+ one place where a ``SootheConfig`` is loaded, profiled and turned into an agent.
7
+
8
+ Which runtime builds that agent is decided by :mod:`flowjet.core.backend`
9
+ (``soothe`` host runtime or ``soothe-nano``), per surface.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ import os
16
+ from collections.abc import AsyncIterator
17
+ from contextlib import asynccontextmanager
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+ from soothe_nano.config import SOOTHE_HOME, SootheConfig
22
+ from soothe_nano.resolve import resolve_checkpointer
23
+
24
+ from flowjet.core.backend import Backend, resolve_backend
25
+ from flowjet.core.modes import (
26
+ CLI_PROFILE,
27
+ InteractionMode,
28
+ Surface,
29
+ SurfaceProfile,
30
+ require_mode,
31
+ )
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+ # ---------------------------------------------------------------------------
36
+ # Builtin skills
37
+ # ---------------------------------------------------------------------------
38
+
39
+ _REGISTERED = False
40
+
41
+ # Skills belong to the distribution, not to either surface: both the CLI and
42
+ # the server expose the same FlowJet skill set.
43
+ BUILTIN_SKILLS_DIR = Path(__file__).resolve().parents[1] / "builtin_skills"
44
+
45
+ # FlowJet additions on top of soothe-nano's DEFAULT_CORE_SKILL_NAMES.
46
+ FJ_CORE_SKILL_NAMES: tuple[str, ...] = ()
47
+
48
+
49
+ def default_core_skill_names() -> list[str]:
50
+ """Nano default core skills plus FlowJet workflow skills."""
51
+ from soothe_nano.skills.registry import DEFAULT_CORE_SKILL_NAMES
52
+
53
+ return sorted(DEFAULT_CORE_SKILL_NAMES | {n.lower() for n in FJ_CORE_SKILL_NAMES})
54
+
55
+
56
+ def register_flowjet_builtin_skills() -> None:
57
+ """Register ``flowjet/builtin_skills`` as a soothe-nano builtin skill root.
58
+
59
+ Idempotent — safe to call from either surface and from pool worker threads.
60
+ """
61
+ global _REGISTERED
62
+ if _REGISTERED:
63
+ return
64
+ if not BUILTIN_SKILLS_DIR.is_dir():
65
+ return
66
+ from soothe_nano.skills import register_builtin_skill_root
67
+
68
+ register_builtin_skill_root(BUILTIN_SKILLS_DIR, source="builtin")
69
+ _REGISTERED = True
70
+
71
+
72
+ # ---------------------------------------------------------------------------
73
+ # Config
74
+ # ---------------------------------------------------------------------------
75
+
76
+
77
+ def default_config_path() -> Path:
78
+ """Return ``~/.soothe/config/nano.yml`` (respects ``SOOTHE_HOME``)."""
79
+ return SOOTHE_HOME / "config" / "nano.yml"
80
+
81
+
82
+ def load_config(
83
+ config_path: str | Path | None = None,
84
+ *,
85
+ backend: Backend | None = None,
86
+ ) -> Any:
87
+ """Load a ``SootheConfig`` for ``backend``, or bootstrap from env when missing.
88
+
89
+ The config class follows the backend: ``soothe`` loads the *host*
90
+ ``soothe.config.SootheConfig`` (a superset of nano's — the host agent reads
91
+ host-owned fields such as ``agent.loop`` / ``agent.clarification`` that nano's
92
+ config does not define), ``nano`` loads ``soothe_nano.config.SootheConfig``.
93
+
94
+ Resolution order:
95
+ 1. Explicit ``config_path``
96
+ 2. ``SOOTHE_HOME / config / nano.yml`` (default ``~/.soothe/config/nano.yml``)
97
+ 3. Zero-config ``SootheConfig()`` from ``OPENAI_API_KEY`` / ``ANTHROPIC_API_KEY``
98
+
99
+ On the ``soothe`` backend, a sibling ``soothe.yml`` (host-owned keys) is
100
+ composed over ``nano.yml`` when present.
101
+ """
102
+ backend = backend or resolve_backend(Surface.CLI)
103
+ path = Path(config_path).expanduser() if config_path else default_config_path()
104
+ if path.is_file():
105
+ return _load_config_file(path, backend)
106
+ if config_path is not None:
107
+ raise FileNotFoundError(f"Config not found: {path}")
108
+ return default_config(backend)
109
+
110
+
111
+ def default_config(backend: Backend) -> Any:
112
+ """Zero-config ``SootheConfig`` for ``backend`` (env-derived providers)."""
113
+ if backend is Backend.SOOTHE:
114
+ from soothe.config import SootheConfig as HostSootheConfig
115
+
116
+ return HostSootheConfig()
117
+ return SootheConfig()
118
+
119
+
120
+ def _load_config_file(path: Path, backend: Backend) -> Any:
121
+ """Parse ``path`` into the config class owned by ``backend``."""
122
+ if backend is Backend.SOOTHE:
123
+ from soothe.config import SootheConfig as HostSootheConfig
124
+
125
+ host_path = path.with_name("soothe.yml")
126
+ if host_path.is_file():
127
+ return HostSootheConfig.from_split_yaml_files(
128
+ nano_path=str(path), soothe_path=str(host_path)
129
+ )
130
+ return HostSootheConfig.from_yaml_file(str(path))
131
+ return SootheConfig.from_yaml_file(str(path))
132
+
133
+
134
+ # ---------------------------------------------------------------------------
135
+ # Workspace / checkpointer
136
+ # ---------------------------------------------------------------------------
137
+
138
+
139
+ def ensure_workspace(workspace: Path | None = None) -> Path:
140
+ """Use ``workspace`` or cwd as ``SOOTHE_WORKSPACE`` for file/shell tools."""
141
+ root = (workspace or Path.cwd()).resolve()
142
+ os.environ.setdefault("SOOTHE_WORKSPACE", str(root))
143
+ try:
144
+ from soothe_nano.workspace import FrameworkFilesystem
145
+
146
+ FrameworkFilesystem.set_current_workspace(root)
147
+ except Exception: # pragma: no cover - optional API surface
148
+ logger.debug("Could not set FrameworkFilesystem workspace", exc_info=True)
149
+ return root
150
+
151
+
152
+ @asynccontextmanager
153
+ async def open_sqlite_checkpointer(config: Any) -> AsyncIterator[Any | None]:
154
+ """Yield an ``AsyncSqliteSaver`` using nano's default sqlite data path.
155
+
156
+ Path: ``$SOOTHE_DATA_DIR/soothe_checkpoints.db`` (default ``~/.soothe/data/``).
157
+ """
158
+ sqlite_cfg = config.model_copy(
159
+ update={
160
+ "agent": config.agent.model_copy(
161
+ update={
162
+ "protocols": config.agent.protocols.model_copy(
163
+ update={
164
+ "durability": config.agent.protocols.durability.model_copy(
165
+ update={"backend": "sqlite", "checkpointer": "sqlite"}
166
+ )
167
+ }
168
+ )
169
+ }
170
+ )
171
+ }
172
+ )
173
+ result = resolve_checkpointer(sqlite_cfg)
174
+ db_path: str | None = None
175
+ if isinstance(result, tuple):
176
+ _placeholder, pool_or_path = result
177
+ if isinstance(pool_or_path, str):
178
+ db_path = pool_or_path
179
+
180
+ if not db_path:
181
+ logger.warning("SQLite checkpointer path unresolved; running without persistence")
182
+ yield None
183
+ return
184
+
185
+ import aiosqlite
186
+ from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
187
+ from soothe_sdk.utils.serde import create_soothe_serde
188
+
189
+ Path(db_path).parent.mkdir(parents=True, exist_ok=True)
190
+ conn = await aiosqlite.connect(db_path)
191
+ checkpointer = AsyncSqliteSaver(conn, serde=create_soothe_serde())
192
+ await checkpointer.setup()
193
+ logger.debug("SQLite checkpointer ready at %s", db_path)
194
+ try:
195
+ yield checkpointer
196
+ finally:
197
+ await conn.close()
198
+
199
+
200
+ # ---------------------------------------------------------------------------
201
+ # Agent construction
202
+ # ---------------------------------------------------------------------------
203
+
204
+
205
+ def create_agent(
206
+ config: Any,
207
+ profile: SurfaceProfile = CLI_PROFILE,
208
+ *,
209
+ interaction_mode: InteractionMode | str | None = None,
210
+ backend: Backend | None = None,
211
+ ) -> Any:
212
+ """Build an agent for ``profile``'s surface on the selected backend.
213
+
214
+ The backend comes from ``FLOWJET_BACKEND`` (CLI) / ``FLOWJET_SERVER_BACKEND``
215
+ (server) unless ``backend`` is passed explicitly — see
216
+ :mod:`flowjet.core.backend`.
217
+
218
+ The server uses a dual-mode agent and supplies the interaction mode per
219
+ request through the LangGraph ``configurable`` dict; the CLI resolves the
220
+ mode up front and passes it to the single-mode constructor.
221
+ """
222
+ from flowjet.core.modes import apply_profile
223
+
224
+ mode = require_mode(interaction_mode, profile)
225
+ resolved = backend or resolve_backend(profile.surface)
226
+ cfg = apply_profile(config, profile, backend=resolved)
227
+
228
+ if resolved is Backend.SOOTHE:
229
+ return create_soothe_agent(cfg, profile, mode)
230
+
231
+ if profile.surface is Surface.SERVER:
232
+ from soothe_nano import create_dual_mode_nano_agent
233
+
234
+ return create_dual_mode_nano_agent(cfg)
235
+
236
+ from soothe_nano import create_nano_agent
237
+
238
+ return create_nano_agent(cfg, interaction_mode=mode)
239
+
240
+
241
+ def soothe_agent_factory() -> Any:
242
+ """Return ``soothe.coreagent.create_soothe_agent`` (imported lazily).
243
+
244
+ Kept as a seam so the host runtime is imported only when it is actually
245
+ selected — the ``nano`` backend never pays for it, and tests patch this
246
+ instead of building a real host agent.
247
+ """
248
+ from soothe.coreagent import create_soothe_agent
249
+
250
+ return create_soothe_agent
251
+
252
+
253
+ def create_soothe_agent(
254
+ config: Any,
255
+ profile: SurfaceProfile = CLI_PROFILE,
256
+ mode: str | None = None,
257
+ ) -> Any:
258
+ """Build a ``soothe`` (host runtime) agent for ``profile``'s surface.
259
+
260
+ ``interrupt_on`` is pinned to ``{}``: the host builder otherwise injects
261
+ HITL interrupts for mutating tools (``edit_file`` / ``write_file`` /
262
+ ``delete`` / ``run_command``), and FlowJet has no resume path — it renders
263
+ ``InterruptWaiting`` and stops, so any write would stall the run.
264
+ """
265
+ factory = soothe_agent_factory()
266
+ if profile.surface is Surface.SERVER:
267
+ return ModeRoutedSootheAgent(config, factory)
268
+ return factory(config, interaction_mode=mode, interrupt_on={})
269
+
270
+
271
+ class ModeRoutedSootheAgent:
272
+ """Route one server request to the host agent built for its mode.
273
+
274
+ soothe has no dual-mode API (nano's ``create_dual_mode_nano_agent`` has no
275
+ host equivalent), so the server surface keeps one host agent per allowed
276
+ mode and picks it from ``configurable["interaction_mode"]`` — an ``ask``
277
+ request must never land on the ``agent`` graph. Agents are built lazily, so
278
+ a worker that only serves ``ask`` never builds the ``agent`` graph.
279
+
280
+ Only ``astream`` is proxied: that is the whole surface the nano bridge
281
+ (``flowjet.core.bridges.nano.mapping``) drives.
282
+ """
283
+
284
+ def __init__(
285
+ self,
286
+ config: Any,
287
+ factory: Any,
288
+ modes: tuple[str, ...] = ("agent", "ask"),
289
+ ) -> None:
290
+ self._config = config
291
+ self._factory = factory
292
+ self._modes = modes
293
+ self._agents: dict[str, Any] = {}
294
+
295
+ def _agent_for(self, mode: str) -> Any:
296
+ resolved = mode if mode in self._modes else "agent"
297
+ agent = self._agents.get(resolved)
298
+ if agent is None:
299
+ agent = self._factory(self._config, interaction_mode=resolved, interrupt_on={})
300
+ self._agents[resolved] = agent
301
+ return agent
302
+
303
+ async def astream(self, payload: Any, **kwargs: Any) -> Any:
304
+ """Proxy ``astream`` to the agent built for the request's mode."""
305
+ config = kwargs.get("config") or {}
306
+ configurable = config.get("configurable") or {} if isinstance(config, dict) else {}
307
+ mode = str(configurable.get("interaction_mode") or "agent")
308
+ agent = self._agent_for(mode)
309
+ async for chunk in agent.astream(payload, **kwargs):
310
+ yield chunk
@@ -22,15 +22,16 @@ def create_nano_agent_instance(config_path: str | Path | None = None) -> Any:
22
22
  :data:`flowjet.core.modes.SERVER_PROFILE`; they are applied by the shared
23
23
  bootstrap rather than re-implemented here.
24
24
  """
25
- from flowjet.core.bootstrap import create_agent, load_config
25
+ from flowjet.core.backend import resolve_backend
26
+ from flowjet.core.bootstrap import create_agent, default_config, load_config
27
+ from flowjet.core.modes import Surface
26
28
 
29
+ backend = resolve_backend(Surface.SERVER)
27
30
  try:
28
- config = load_config(config_path)
31
+ config = load_config(config_path, backend=backend)
29
32
  except FileNotFoundError:
30
- from soothe_nano.config import SootheConfig
31
-
32
- config = SootheConfig()
33
- return create_agent(config, SERVER_PROFILE)
33
+ config = default_config(backend)
34
+ return create_agent(config, SERVER_PROFILE, backend=backend)
34
35
 
35
36
 
36
37
  class NanoAgentAdapter: