flowjet 2.2.0__tar.gz → 2.2.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 (119) hide show
  1. {flowjet-2.2.0 → flowjet-2.2.2}/CHANGELOG.md +14 -0
  2. {flowjet-2.2.0 → flowjet-2.2.2}/PKG-INFO +3 -3
  3. {flowjet-2.2.0 → flowjet-2.2.2}/README.md +1 -1
  4. {flowjet-2.2.0 → flowjet-2.2.2}/pyproject.toml +2 -2
  5. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/dashboard.py +22 -8
  6. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/stream.py +22 -0
  7. {flowjet-2.2.0 → flowjet-2.2.2}/.gitignore +0 -0
  8. {flowjet-2.2.0 → flowjet-2.2.2}/LICENSE +0 -0
  9. {flowjet-2.2.0 → flowjet-2.2.2}/config/nano-local.yml +0 -0
  10. {flowjet-2.2.0 → flowjet-2.2.2}/config/nano-sandbox-soothe.yml +0 -0
  11. {flowjet-2.2.0 → flowjet-2.2.2}/config/nano.yml +0 -0
  12. {flowjet-2.2.0 → flowjet-2.2.2}/deploy/README.md +0 -0
  13. {flowjet-2.2.0 → flowjet-2.2.2}/docs/completion-design.md +0 -0
  14. {flowjet-2.2.0 → flowjet-2.2.2}/examples/README.md +0 -0
  15. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/__init__.py +0 -0
  16. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/__main__.py +0 -0
  17. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/LICENSE +0 -0
  18. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/SKILL.md +0 -0
  19. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/agents/openai.yaml +0 -0
  20. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/autoresearch.md +0 -0
  21. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/debug.md +0 -0
  22. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/evals.md +0 -0
  23. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/fix.md +0 -0
  24. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/improve.md +0 -0
  25. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/learn.md +0 -0
  26. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/plan.md +0 -0
  27. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/predict.md +0 -0
  28. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/probe.md +0 -0
  29. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/reason.md +0 -0
  30. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/references/orchestrator-routing.md +0 -0
  31. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/references/predict-personas.md +0 -0
  32. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/references/reason-judge-protocol.md +0 -0
  33. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/references/security-checklist.md +0 -0
  34. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/regression.md +0 -0
  35. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/scenario.md +0 -0
  36. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/scripts/orchestrate.sh +0 -0
  37. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/scripts/score-regression.sh +0 -0
  38. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/security.md +0 -0
  39. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/builtin_skills/autoresearch/ship.md +0 -0
  40. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/__init__.py +0 -0
  41. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/__main__.py +0 -0
  42. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/agent.py +0 -0
  43. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/cli.py +0 -0
  44. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/completion/__init__.py +0 -0
  45. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/completion/context.py +0 -0
  46. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/completion/engine.py +0 -0
  47. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/doctor_cmd.py +0 -0
  48. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/doctor_fix_cmd.py +0 -0
  49. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/progress.py +0 -0
  50. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/render.py +0 -0
  51. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/serve_cmd.py +0 -0
  52. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/setup_cmd.py +0 -0
  53. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/shell/__init__.py +0 -0
  54. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/shell/_fj.zsh +0 -0
  55. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/shell/fj.bash +0 -0
  56. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/threads.py +0 -0
  57. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/cli/tool_stream.py +0 -0
  58. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/__init__.py +0 -0
  59. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/_warnings.py +0 -0
  60. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/agent/__init__.py +0 -0
  61. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/agent/config.py +0 -0
  62. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/agent/factory.py +0 -0
  63. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/agent/skills.py +0 -0
  64. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/agent/wiring.py +0 -0
  65. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/agent/workspace.py +0 -0
  66. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/backend_config.py +0 -0
  67. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/bridges/base.py +0 -0
  68. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/bridges/nano/__init__.py +0 -0
  69. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/bridges/nano/adapter.py +0 -0
  70. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/bridges/nano/backend.py +0 -0
  71. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/bridges/nano/interrupts.py +0 -0
  72. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/bridges/nano/mapping.py +0 -0
  73. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/bridges/nano/token_usage.py +0 -0
  74. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/bridges/soothe/__init__.py +0 -0
  75. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/bridges/soothe/adapter.py +0 -0
  76. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/bridges/soothe/backend.py +0 -0
  77. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/events.py +0 -0
  78. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/identity.py +0 -0
  79. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/ids.py +0 -0
  80. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/modes.py +0 -0
  81. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/protocol.py +0 -0
  82. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/runtime/__init__.py +0 -0
  83. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/runtime/direct.py +0 -0
  84. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/runtime/fake.py +0 -0
  85. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/runtime/isolation/__init__.py +0 -0
  86. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/runtime/isolation/adapter.py +0 -0
  87. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/runtime/isolation/admission.py +0 -0
  88. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/runtime/isolation/backend.py +0 -0
  89. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/runtime/isolation/errors.py +0 -0
  90. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/runtime/isolation/mode_gate.py +0 -0
  91. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/runtime/isolation/pool.py +0 -0
  92. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/runtime/isolation/request.py +0 -0
  93. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/runtime/isolation/workspace.py +0 -0
  94. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/sandbox/__init__.py +0 -0
  95. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/sandbox/arbox.py +0 -0
  96. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/sandbox/resolver.py +0 -0
  97. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/sandbox/settings.py +0 -0
  98. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/streaming/__init__.py +0 -0
  99. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/streaming/presentation.py +0 -0
  100. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/streaming/tool_results.py +0 -0
  101. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/core/streaming/tool_stream.py +0 -0
  102. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/home.py +0 -0
  103. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/__init__.py +0 -0
  104. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/__main__.py +0 -0
  105. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/acp/__init__.py +0 -0
  106. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/acp/agent.py +0 -0
  107. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/acp/routes.py +0 -0
  108. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/clarification.py +0 -0
  109. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/config.py +0 -0
  110. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/http/__init__.py +0 -0
  111. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/http/app.py +0 -0
  112. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/http/auth.py +0 -0
  113. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/openai_compat/__init__.py +0 -0
  114. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/openai_compat/errors.py +0 -0
  115. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/openai_compat/projection.py +0 -0
  116. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/openai_compat/routes.py +0 -0
  117. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/openai_compat/schemas.py +0 -0
  118. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/openai_compat/service.py +0 -0
  119. {flowjet-2.2.0 → flowjet-2.2.2}/src/flowjet/server/openai_compat/store.py +0 -0
@@ -10,6 +10,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [2.2.2] — 2026-11-13
14
+
15
+ ### Changed
16
+
17
+ - **Dashboard snapshot is now disabled by default.** The live progress panel is erased when the run finishes, leaving only the final answer. Set `FJ_SNAPSHOT=1` (or `true`/`yes`) to keep the DONE / FAILED / PAUSED snapshot visible above the answer, as in previous versions.
18
+
19
+ - **`✓ Done` status footer is now suppressed by default.** The run ends with just the final answer. Pass `-v` (or set `FJ_LOG_LEVEL=debug`) to surface the `✓ Done · <duration> · <tokens> · <thread_id>` summary footer.
20
+
21
+ ## [2.2.1] — 2026-11-11
22
+
23
+ ### Changed
24
+
25
+ - **`soothe` floor bumped to `>=1.3.1`** (`uv.lock` refreshed). Picks up the latest soothe 1.3.1 runtime — Proactive Coverage Evaluation (PCE) and the TUI stream-cancel fix; no flowjet-facing API changes.
26
+
13
27
  ## [2.2.0] — 2026-11-10
14
28
 
15
29
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: flowjet
3
- Version: 2.2.0
3
+ Version: 2.2.2
4
4
  Summary: FlowJet — a fast, open agent runtime for CLI and HTTP, powered by Soothe
5
5
  Project-URL: Homepage, https://github.com/caesar0301/flowjet
6
6
  Project-URL: Repository, https://github.com/caesar0301/flowjet
@@ -23,7 +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: rich>=13.0
26
- Requires-Dist: soothe<2.0.0,>=1.3.0
26
+ Requires-Dist: soothe<2.0.0,>=1.3.1
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: httpx>=0.27.0; extra == 'dev'
29
29
  Requires-Dist: mypy>=1.0.0; extra == 'dev'
@@ -104,7 +104,7 @@ fj -l # list recent threads
104
104
 
105
105
  `-f` is scoped to your current project, so work in other checkouts never hijacks your thread.
106
106
 
107
- On an interactive terminal, `fj` shows a live dashboard: a status badge, a one-line strip (step X/N, elapsed, tokens, last updated), the current action, an activity history (tool calls plus the agent's intermediate reasoning between them) and an answer preview. Piped or redirected output stays plain text with no progress content. Display is tunable via `NO_COLOR`, `FJ_FORCE_COLOR` and `FJ_PROGRESS_WIDTH`.
107
+ On an interactive terminal, `fj` shows a live dashboard: a status badge, a one-line strip (step X/N, elapsed, tokens, last updated), the current action, an activity history (tool calls plus the agent's intermediate reasoning between them) and an answer preview. Piped or redirected output stays plain text with no progress content. Display is tunable via `NO_COLOR`, `FJ_FORCE_COLOR`, `FJ_PROGRESS_WIDTH` and `FJ_SNAPSHOT`.
108
108
 
109
109
  Shell completion predicts full queries, not just flags:
110
110
 
@@ -59,7 +59,7 @@ fj -l # list recent threads
59
59
 
60
60
  `-f` is scoped to your current project, so work in other checkouts never hijacks your thread.
61
61
 
62
- On an interactive terminal, `fj` shows a live dashboard: a status badge, a one-line strip (step X/N, elapsed, tokens, last updated), the current action, an activity history (tool calls plus the agent's intermediate reasoning between them) and an answer preview. Piped or redirected output stays plain text with no progress content. Display is tunable via `NO_COLOR`, `FJ_FORCE_COLOR` and `FJ_PROGRESS_WIDTH`.
62
+ On an interactive terminal, `fj` shows a live dashboard: a status badge, a one-line strip (step X/N, elapsed, tokens, last updated), the current action, an activity history (tool calls plus the agent's intermediate reasoning between them) and an answer preview. Piped or redirected output stays plain text with no progress content. Display is tunable via `NO_COLOR`, `FJ_FORCE_COLOR`, `FJ_PROGRESS_WIDTH` and `FJ_SNAPSHOT`.
63
63
 
64
64
  Shell completion predicts full queries, not just flags:
65
65
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "flowjet"
7
- version = "2.2.0"
7
+ version = "2.2.2"
8
8
  description = "FlowJet — a fast, open agent runtime for CLI and 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.3.0,<2.0.0",
30
+ "soothe>=1.3.1,<2.0.0",
31
31
  # TTY rendering for answers/tables; imported lazily, unused when piped.
32
32
  "rich>=13.0",
33
33
  ]
@@ -9,14 +9,16 @@ The terminal renderer is split in two:
9
9
  gate, interleaved output and the final snapshot.
10
10
 
11
11
  ``Live`` itself is the only refresh source (its ``auto_refresh`` thread replaces
12
- the old hand-rolled 0.08s asyncio spinner). ``transient=False`` leaves the last
13
- frame on screen, which is what makes the DONE / FAILED / PAUSED snapshot visible
14
- above the final answer.
12
+ the old hand-rolled 0.08s asyncio spinner). By default ``transient=True`` so the
13
+ live panel is erased when the run finishes, leaving only the final answer.
14
+ Set ``FJ_SNAPSHOT=1`` (or ``true``/``yes``) to keep the last frame on screen as
15
+ a DONE / FAILED / PAUSED snapshot visible above the final answer.
15
16
  """
16
17
 
17
18
  from __future__ import annotations
18
19
 
19
20
  import logging
21
+ import os
20
22
  import sys
21
23
  import time
22
24
  from collections.abc import Callable
@@ -40,6 +42,16 @@ _TAIL_MAX = 6
40
42
  _CHROME_ROWS = 14
41
43
  # Ceiling for a reasoning stretch before it is truncated into one panel row.
42
44
  _REASONING_MAX_CHARS = 2000
45
+ # Env var that keeps the final DONE/FAILED/PAUSED panel on screen after the run
46
+ # finishes. Disabled by default so only the answer text remains on the terminal;
47
+ # set ``FJ_SNAPSHOT=1`` (or ``true``/``yes``) to leave the snapshot visible.
48
+ _SNAPSHOT_ENV = "FJ_SNAPSHOT"
49
+
50
+
51
+ def _snapshot_enabled() -> bool:
52
+ """Whether the final dashboard snapshot stays on screen after completion."""
53
+ return os.environ.get(_SNAPSHOT_ENV, "").strip().lower() in {"1", "true", "yes"}
54
+
43
55
 
44
56
  _STATUS_BADGES: dict[str, tuple[str, str]] = {
45
57
  "LIVE": ("● LIVE", "cyan"),
@@ -272,7 +284,7 @@ def _default_live(
272
284
  console=console,
273
285
  auto_refresh=True,
274
286
  refresh_per_second=refresh_per_second,
275
- transient=False,
287
+ transient=not _snapshot_enabled(),
276
288
  redirect_stdout=True,
277
289
  redirect_stderr=True,
278
290
  vertical_overflow="visible",
@@ -622,11 +634,13 @@ class ProgressDashboard:
622
634
  duration_s: float | None = None,
623
635
  tokens: int | None = None,
624
636
  ) -> None:
625
- """Freeze the panel as a visible snapshot (idempotent).
637
+ """Freeze the panel as a snapshot, or erase it (idempotent).
626
638
 
627
- ``transient=False`` means ``Live.stop()`` leaves the last frame on
628
- screen; no erase bytes are emitted, so a PAUSED snapshot and the
629
- questions printed after it are both visible.
639
+ When ``FJ_SNAPSHOT`` is set, ``transient=False`` means ``Live.stop()``
640
+ leaves the last frame on screen — no erase bytes are emitted, so a
641
+ PAUSED snapshot and the questions printed after it are both visible.
642
+ By default (snapshot disabled) ``transient=True`` erases the panel so
643
+ only the final answer remains.
630
644
  """
631
645
  if self._finalized:
632
646
  return
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import asyncio
6
6
  import contextlib
7
7
  import logging
8
+ import os
8
9
  import re
9
10
  import sys
10
11
  import time
@@ -84,6 +85,19 @@ def _format_tokens(count: int) -> str:
84
85
  return str(count)
85
86
 
86
87
 
88
+ def _verbose_mode_active(show_tool_calls: bool) -> bool:
89
+ """True when verbose mode is on (``-v`` flag or ``FJ_LOG_LEVEL=debug``).
90
+
91
+ The ``✓ Done`` summary footer is shown **only** in verbose mode — by
92
+ default the run ends with just the final answer. In verbose mode the
93
+ footer complements the mirrored tool calls / DEBUG log records, which
94
+ surface duration, tokens, and thread id.
95
+ """
96
+ if show_tool_calls:
97
+ return True
98
+ return os.environ.get("FJ_LOG_LEVEL", "").strip().lower() == "debug"
99
+
100
+
87
101
  # Western + CJK sentence boundaries for status preview.
88
102
  _STATUS_SENTENCE_SEPS = (
89
103
  ". ",
@@ -538,6 +552,10 @@ async def stream_query(
538
552
  it, and :data:`INTERRUPTED` is returned — the run is not complete and must
539
553
  be resumed with ``fjf -t <thread> <answer>``.
540
554
 
555
+ The ``✓ Done`` summary footer (duration, tokens, thread id) is shown only
556
+ in verbose mode (``show_tool_calls=True`` or ``FJ_LOG_LEVEL=debug``); by
557
+ default the run ends with just the final answer.
558
+
541
559
  ``timeout_s`` cancels the whole run after that many seconds and raises
542
560
  :class:`AgentRunError` naming the last visible activity.
543
561
  """
@@ -747,6 +765,10 @@ async def stream_query(
747
765
  # A silent "✓ Done" teaches nothing — make the empty outcome and any
748
766
  # failed tool calls visible instead.
749
767
  _write_no_output_warning(stderr, failed_tool_calls, last_tool_error, thread_id)
768
+ if not _verbose_mode_active(show_tool_calls):
769
+ # Default mode: the ``✓ Done`` summary footer is suppressed — the
770
+ # run ends with just the final answer. Only verbose mode surfaces it.
771
+ return answer_text
750
772
  duration = _format_duration(time.monotonic() - started_at)
751
773
  token_part = f" · {_format_tokens(total_tokens)} tokens" if total_tokens else ""
752
774
  stdout.write(f"\n✓ Done · {duration}{token_part} · {thread_id}\n")
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