@pellux/goodvibes-tui 1.20.0 → 1.21.0

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 (65) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +7 -7
  3. package/docs/foundation-artifacts/operator-contract.json +106 -22
  4. package/package.json +7 -7
  5. package/src/cli/bundle-command.ts +1 -1
  6. package/src/cli/doctor.ts +7 -1
  7. package/src/cli/management.ts +1 -1
  8. package/src/cli/package-verification.ts +92 -7
  9. package/src/cli/status.ts +16 -0
  10. package/src/config/read-versioned.ts +115 -2
  11. package/src/core/active-model-identity.ts +153 -0
  12. package/src/core/conversation-event-navigation.ts +42 -0
  13. package/src/core/conversation-search-expansion.ts +67 -0
  14. package/src/core/conversation-splash-state.ts +58 -0
  15. package/src/core/conversation-user-receipts.ts +42 -0
  16. package/src/core/conversation.ts +43 -40
  17. package/src/core/model-routing-chip.ts +38 -18
  18. package/src/core/rewind-turn-anchors.ts +155 -3
  19. package/src/core/stream-event-wiring.ts +298 -17
  20. package/src/core/system-message-router.ts +22 -0
  21. package/src/core/transcript-journal.ts +145 -7
  22. package/src/daemon/handlers/inbox/cursor-store.ts +159 -3
  23. package/src/daemon/handlers/inbox/index.ts +14 -1
  24. package/src/daemon/handlers/sqlite-store.ts +160 -6
  25. package/src/input/command-registry.ts +8 -0
  26. package/src/input/commands/local-runtime.ts +6 -1
  27. package/src/input/commands/schedule-runtime.ts +15 -2
  28. package/src/input/commands/shell-core.ts +87 -38
  29. package/src/input/composer-capture.ts +47 -0
  30. package/src/input/handler-command-route.ts +4 -0
  31. package/src/input/handler-content-actions.ts +9 -0
  32. package/src/input/handler-feed-routes.ts +1 -0
  33. package/src/input/handler-modal-routes.ts +12 -1
  34. package/src/input/handler-picker-routes.ts +43 -5
  35. package/src/input/handler-shortcuts.ts +1 -1
  36. package/src/input/handler-ui-state.ts +8 -3
  37. package/src/input/model-picker-items.ts +17 -2
  38. package/src/input/model-picker.ts +19 -2
  39. package/src/input/search.ts +183 -89
  40. package/src/main.ts +31 -38
  41. package/src/{cli → providers}/provider-classification.ts +12 -0
  42. package/src/providers/reasoning-effort-surface.ts +399 -0
  43. package/src/renderer/compositor.ts +68 -18
  44. package/src/renderer/header-line.ts +146 -0
  45. package/src/renderer/markdown-inline.ts +143 -0
  46. package/src/renderer/markdown-table.ts +459 -0
  47. package/src/renderer/markdown.ts +6 -335
  48. package/src/renderer/model-workspace.ts +48 -5
  49. package/src/renderer/shell-surface.ts +36 -0
  50. package/src/renderer/ui-factory.ts +25 -78
  51. package/src/runtime/bootstrap-command-parts.ts +36 -4
  52. package/src/runtime/bootstrap-core.ts +1 -1
  53. package/src/runtime/durability-housekeeping.ts +183 -0
  54. package/src/runtime/durability-services.ts +68 -11
  55. package/src/runtime/legacy-daemon-reconcile.ts +16 -7
  56. package/src/runtime/provider-fallback.ts +16 -2
  57. package/src/runtime/recovery-decisions.ts +295 -0
  58. package/src/runtime/recovery-prompt.ts +66 -6
  59. package/src/runtime/services.ts +9 -4
  60. package/src/runtime/session-liveness-marker.ts +142 -5
  61. package/src/runtime/workstream-draft-store.ts +198 -25
  62. package/src/runtime/workstream-services.ts +17 -2
  63. package/src/shell/ui-openers.ts +1 -1
  64. package/src/version.ts +1 -1
  65. package/src/work-plans/work-plan-store.ts +334 -16
package/CHANGELOG.md CHANGED
@@ -4,6 +4,25 @@ All notable changes to GoodVibes TUI.
4
4
 
5
5
  ---
6
6
 
7
+ ## [1.21.0] - 2026-07-25
8
+
9
+ ### Changes
10
+
11
+ Fixed: the reasoning effort level you choose is no longer lowered permanently by one model. Your configured level is kept as you set it, the runtime uses whatever the current model can actually deliver, and switching back to a capable model restores your level. The display shows both values and says when a model is the reason your level is capped.
12
+ Fixed: the notice explaining that a failover snapped your reasoning level down was being deleted from the transcript by the retry path on every failover, so nobody ever saw it. The notice now stays.
13
+ Fixed: markdown tables no longer silently drop trailing columns — including headers — when the terminal is too narrow for the column count. A table that cannot fit is now stacked as "Header: value" records so every column is still readable.
14
+ Fixed: config file changes are no longer silently lost when the write lands while the settings watcher is still starting up.
15
+ Fixed: saved state now gets real housekeeping when it is recovered — stale entries are reaped, growth is bounded, contents are checked rather than assumed from the file merely existing, and anything removed is disclosed instead of vanishing. An interrupted migration no longer strands your older sessions out of reach.
16
+ Fixed: published packages no longer silently omit the bundled agent and skill content. A packaging rule change dropped those files from the tarball on newer npm versions; installs now get the complete set again.
17
+ Updated: bundled platform runtime to 1.13.1, which adds Telegram inbound messaging and the per-model reasoning effort model behind these changes.
18
+
19
+ ## [1.20.1] - 2026-07-25
20
+
21
+ ### Changes
22
+
23
+ - fd3b3037 fix: recovery offers and removals behave; active-model surfaces agree; failover is visible and temporary
24
+ - 08237438 docs: replace README screenshots with true terminal captures
25
+
7
26
  ## [1.20.0] - 2026-07-24
8
27
 
9
28
  ### Changes
@@ -469,7 +488,7 @@ Updated: @pellux/goodvibes-sdk and @pellux/goodvibes-terminal-shell to 1.11.3 (q
469
488
  ### Changes
470
489
  - 9dbd88e4 fix(daemon): attach handlers for every ws-only gateway verb — fleet.*, checkpoints.*, sessions.search, push.* were 501 on every vendored daemon build
471
490
 
472
- ## [1.13.0] — 2026-07-09
491
+ ## [1.13.1] — 2026-07-09
473
492
 
474
493
  ### Changes
475
494
  - c3a54b1b fix: clean terminal restore on exit; feat: /resume session picker
package/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  [![CI](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml/badge.svg)](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Version](https://img.shields.io/badge/version-1.20.0-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
5
+ [![Version](https://img.shields.io/badge/version-1.21.0-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
6
6
 
7
7
  GoodVibes is a terminal console for coding and operations work with an AI model. You run `goodvibes` in a project directory and get a full-screen terminal app where you talk to a model that can read and edit your files, run shell commands, search the web, and hand work off to background agents — asking your permission before anything that writes or executes. It talks to many model providers (OpenAI, Anthropic, Gemini, Bedrock, Copilot, OpenRouter and other OpenAI-compatible gateways, plus local servers like Ollama and LM Studio that it finds on startup), keeps its settings, sessions, and secrets on your own machine, and shows you the token count and running cost of every turn. Alongside the conversation, panels turn background work — running agents, git state, diffs, tokens, cost — into live control rooms.
8
8
 
9
- <img src="docs/assets/splash.png" alt="GoodVibes starting up in a terminal: a glitch-art GOODVIBES wordmark in cyan-to-magenta gradient, the version and tagline beneath it, the active model and tool count, the working directory, and a hint line reading Ctrl+P panels / ? help / F2 fleet. A status footer shows mode, token usage, context usage, and the daemon and web listener addresses." width="900">
9
+ <img src="docs/assets/splash.png" alt="GoodVibes starting up in a terminal: a glitch-art GOODVIBES wordmark in cyan-to-magenta gradient, the version and tagline beneath it, the active model and tool count, the working directory, a line offering to resume the last session, and a hint line reading Ctrl+P panels / ? help / F2 fleet. A status footer shows mode, token usage, context usage, and the daemon and web listener addresses." width="900">
10
10
 
11
11
  ---
12
12
 
@@ -45,7 +45,7 @@ Deeper install notes — the installer's environment variables, uninstall, daemo
45
45
 
46
46
  ### The conversation loop
47
47
 
48
- <img src="docs/assets/conversation.png" alt="A conversation turn in the terminal. The user asks the model to read two source files and explain the project. Below it, an assistant block lists two read tool calls with green checkmarks, followed by a collapsed group labelled tool results, 2 tools, 2 lines. A second assistant block makes one more read call. The final answer is plain prose with an identifier syntax-highlighted in yellow. The footer reads Token Usage: Input 41.3k, Output 337, Cache Read 0, Cache Write 0, Total 41.6k, approximately 0.129 dollars, above a green context usage bar at 5 percent." width="900">
48
+ <img src="docs/assets/conversation.png" alt="Two conversation turns in the terminal. The tail of an earlier answer describes the project as a minimal ISS pass tracker whose calculation is still a stub. Below it the user asks the model to grep for nextPass and list every file that references it; an assistant block shows one find tool call with a green checkmark, and its result is expanded as syntax-highlighted JSON with line numbers listing the two matching file paths and a count of 2. The final answer names src/passes.ts and src/index.ts as a bulleted list. The footer reads Token Usage: Input em dash, Output 264, Cache Read 0, Cache Write 0, Total 264, approximately 0.0040 dollars, above a green context usage bar at 4 percent." width="900">
49
49
 
50
50
  Tool calls stream inline as the model makes them, then their results fold into a collapsible group so a long working session stays readable — expand any group to see what actually came back. Assistant messages render markdown, syntax-highlighted code, and inline diffs, and any block can be collapsed, bookmarked, copied, or saved to a file.
51
51
 
@@ -57,13 +57,13 @@ The footer keeps an honest running total: fresh input tokens separated from cach
57
57
 
58
58
  The first time you open a directory, GoodVibes asks how much it is allowed to do there. Restricted is read-only: the model can look around, but writes and commands are refused until you trust the workspace.
59
59
 
60
- <img src="docs/assets/permission-prompt.png" alt="An execution approval prompt. A header reads EXECUTE, Shell Execution Approval. Fields list the requesting session, the tool exec, the full command, the working directory, a Risk line reading HIGH write in red, the surface and radius, a summary, the effects: process execution, filesystem mutation, possible network access, and a reviewer checklist. Below, four numbered remember options scope an approval to this exact command, to every mkdir command, to every exec call in this project, or to the rest of the session in memory only. The action line reads: Y Allow once, 1-4 Allow and remember, N Deny, or type a reason to deny." width="900">
60
+ <img src="docs/assets/permission-prompt.png" alt="An execution approval prompt. A header reads EXECUTE, Shell Execution Approval, one more waiting. Fields list the requesting session, the tool exec, the full command mkdir -p build, the working directory, a Risk line reading HIGH write in red, the surface and radius, a summary, the decision class, the effects: process execution, filesystem mutation, possible network access, a reviewer checklist, and the raw tool arguments. Below, four numbered remember options scope an approval to this exact command, to every mkdir command, to every exec call in this project, or to the rest of the session in memory only. The action line reads: Y Allow once, 1-4 Allow and remember, N Deny, or type a reason to deny." width="900">
61
61
 
62
62
  After that, the default permission mode is `prompt`: writes, edits, shell commands, network fetches, agent spawns, and MCP calls each stop and ask. The prompt shows what will run, in which directory, its assessed risk, and what it can affect. You can allow it once, deny it (optionally typing a reason the model sees), or remember the decision at whichever scope fits — this exact command, this command shape, this tool for the whole project, or just for the rest of the session. Four other modes are available when prompting is not what you want: `accept-edits` auto-approves file writes and edits while exec and the other risky classes still ask, `plan` allows read-only tools and refuses every mutating or exec call, `allow-all` approves everything, and `custom` takes per-tool `allow` / `prompt` / `deny` overrides. `Shift+Tab` cycles the four session postures — normal, accept-edits, plan, auto — and `/plan` toggles plan mode directly.
63
63
 
64
64
  ### Models and providers
65
65
 
66
- <img src="docs/assets/model-picker.png" alt="A fullscreen model workspace. A left column lists routing targets: Main Chat set, Helper Model off, Tool LLM off, TTS LLM inherit, and Embeddings set. The right pane shows the selected target with its current model and a filter row for search, price, capability, grouping, and availability. Below, a table of models lists model key, display name, provider, context window, tier, and capability flags across 2017 catalogued entries, with a footer of list shortcuts." width="900">
66
+ <img src="docs/assets/model-picker.png" alt="A fullscreen model workspace. A left column lists routing targets: Main Chat set, Helper Model off, Tool LLM off, TTS LLM inherit, and Embeddings set. The right pane shows the selected target with its current model and a filter row for search, price, capability, grouping, and availability. Below, a table of models lists model key, display name, provider, context window, tier, and capability flags across 2030 catalogued entries, with a footer of list shortcuts." width="900">
67
67
 
68
68
  Models come from a live catalog, so the picker lists far more than a hardcoded set, filterable by search, price, capability, and availability. Separate roles route independently: your main chat model, a cheaper helper model for grunt work, a tool model, a TTS model, and an embeddings model can each point somewhere different.
69
69
 
@@ -71,13 +71,13 @@ The `synthetic` provider groups the same model across every backend that serves
71
71
 
72
72
  ### The Fleet control room
73
73
 
74
- <img src="docs/assets/fleet-panel.png" alt="The Fleet panel open beside the conversation. It lists background work as rows with a kind, a name, elapsed time, token count, and cost — a CI watch poller and a runtime health watcher at 59 seconds each, and a code indexer. The detail region below shows the selected watcher's id, state idle, elapsed time, model, tokens, cost, a headline reading CI watch poller, healthy, an activity phase, and an approvals line. The action hints read j/k navigate, Enter attach, K kill, A archive finish." width="900">
74
+ <img src="docs/assets/fleet-panel.png" alt="The Fleet panel open beside the conversation. It lists background work as rows with a kind, a name, elapsed time, token count, and cost — a CI watch poller and a runtime heartbeat watcher at 4 minutes 23 seconds each, and an idle code indexer. The detail region below shows the selected watcher's id, state idle, elapsed time, model, tokens, cost, a headline reading CI watch poller, healthy, an activity phase, and an approvals line. The action hints read j/k navigate, Enter attach, K kill, A archive finished, v archived, n host agent." width="900">
75
75
 
76
76
  Work that is not conversation goes into panels instead of scrolling past in the transcript. Fleet — reached with `F2` — is the live control room for agents, workstreams, watchers, and scheduled jobs: what is running, for how long, at what token cost, and what it is waiting on. You can attach to a running agent to watch or steer it, detach again without killing it, pause and resume, and archive finished work. Git, diff, cost, token, and local-auth panels sit alongside it; heavier operator surfaces open as review modals.
77
77
 
78
78
  ### Everything else is a command
79
79
 
80
- <img src="docs/assets/help-overlay.png" alt="A searchable help overlay titled Help, Commands, with a search field and results grouped by category. Under Provider Accounts is the /accounts command with review, panel, show, routes, and repair subcommands and a one-line description. Under Fleet is /agents, described as hosting third-party coding agents as fleet rows over ACP. A line reads 146 below, and the hints read Up/Down Navigate, Enter Run, / Search, Esc Close." width="820">
80
+ <img src="docs/assets/help-overlay.png" alt="A searchable help overlay titled Help, Commands, with a search field and results grouped by category. Under Provider Accounts is the /accounts command with review, panel, show, routes, and repair subcommands and a one-line description. Under Fleet is /agents, described as hosting third-party coding agents as fleet rows over ACP. Under Experience is /approval, for reviewing action-specific approval classes. A line reads 147 below, and the hints read Up/Down Navigate, Enter Run, / Search, Esc Close." width="820">
81
81
 
82
82
  Press `?` for a searchable, categorized list of every slash command with its arguments and description. The same list is generated into [docs/commands-reference.md](docs/commands-reference.md).
83
83
 
@@ -3,7 +3,7 @@
3
3
  "product": {
4
4
  "id": "goodvibes",
5
5
  "surface": "operator",
6
- "version": "1.12.0"
6
+ "version": "1.13.1"
7
7
  },
8
8
  "auth": {
9
9
  "modes": [
@@ -4714,10 +4714,14 @@
4714
4714
  "reasoningEffort": {
4715
4715
  "type": "string",
4716
4716
  "enum": [
4717
+ "none",
4717
4718
  "instant",
4719
+ "minimal",
4718
4720
  "low",
4719
4721
  "medium",
4720
- "high"
4722
+ "high",
4723
+ "xhigh",
4724
+ "max"
4721
4725
  ]
4722
4726
  },
4723
4727
  "thinking": {
@@ -6344,10 +6348,14 @@
6344
6348
  "reasoningEffort": {
6345
6349
  "type": "string",
6346
6350
  "enum": [
6351
+ "none",
6347
6352
  "instant",
6353
+ "minimal",
6348
6354
  "low",
6349
6355
  "medium",
6350
- "high"
6356
+ "high",
6357
+ "xhigh",
6358
+ "max"
6351
6359
  ]
6352
6360
  },
6353
6361
  "thinking": {
@@ -7332,10 +7340,14 @@
7332
7340
  "reasoningEffort": {
7333
7341
  "type": "string",
7334
7342
  "enum": [
7343
+ "none",
7335
7344
  "instant",
7345
+ "minimal",
7336
7346
  "low",
7337
7347
  "medium",
7338
- "high"
7348
+ "high",
7349
+ "xhigh",
7350
+ "max"
7339
7351
  ]
7340
7352
  },
7341
7353
  "thinking": {
@@ -8712,10 +8724,14 @@
8712
8724
  "reasoningEffort": {
8713
8725
  "type": "string",
8714
8726
  "enum": [
8727
+ "none",
8715
8728
  "instant",
8729
+ "minimal",
8716
8730
  "low",
8717
8731
  "medium",
8718
- "high"
8732
+ "high",
8733
+ "xhigh",
8734
+ "max"
8719
8735
  ]
8720
8736
  },
8721
8737
  "thinking": {
@@ -9637,10 +9653,14 @@
9637
9653
  "reasoningEffort": {
9638
9654
  "type": "string",
9639
9655
  "enum": [
9656
+ "none",
9640
9657
  "instant",
9658
+ "minimal",
9641
9659
  "low",
9642
9660
  "medium",
9643
- "high"
9661
+ "high",
9662
+ "xhigh",
9663
+ "max"
9644
9664
  ]
9645
9665
  },
9646
9666
  "thinking": {
@@ -10648,10 +10668,14 @@
10648
10668
  "reasoningEffort": {
10649
10669
  "type": "string",
10650
10670
  "enum": [
10671
+ "none",
10651
10672
  "instant",
10673
+ "minimal",
10652
10674
  "low",
10653
10675
  "medium",
10654
- "high"
10676
+ "high",
10677
+ "xhigh",
10678
+ "max"
10655
10679
  ]
10656
10680
  },
10657
10681
  "thinking": {
@@ -11763,10 +11787,14 @@
11763
11787
  "reasoningEffort": {
11764
11788
  "type": "string",
11765
11789
  "enum": [
11790
+ "none",
11766
11791
  "instant",
11792
+ "minimal",
11767
11793
  "low",
11768
11794
  "medium",
11769
- "high"
11795
+ "high",
11796
+ "xhigh",
11797
+ "max"
11770
11798
  ]
11771
11799
  },
11772
11800
  "thinking": {
@@ -12775,10 +12803,14 @@
12775
12803
  "reasoningEffort": {
12776
12804
  "type": "string",
12777
12805
  "enum": [
12806
+ "none",
12778
12807
  "instant",
12808
+ "minimal",
12779
12809
  "low",
12780
12810
  "medium",
12781
- "high"
12811
+ "high",
12812
+ "xhigh",
12813
+ "max"
12782
12814
  ]
12783
12815
  },
12784
12816
  "thinking": {
@@ -14036,10 +14068,14 @@
14036
14068
  "reasoningEffort": {
14037
14069
  "type": "string",
14038
14070
  "enum": [
14071
+ "none",
14039
14072
  "instant",
14073
+ "minimal",
14040
14074
  "low",
14041
14075
  "medium",
14042
- "high"
14076
+ "high",
14077
+ "xhigh",
14078
+ "max"
14043
14079
  ]
14044
14080
  },
14045
14081
  "thinking": {
@@ -15005,10 +15041,14 @@
15005
15041
  "reasoningEffort": {
15006
15042
  "type": "string",
15007
15043
  "enum": [
15044
+ "none",
15008
15045
  "instant",
15046
+ "minimal",
15009
15047
  "low",
15010
15048
  "medium",
15011
- "high"
15049
+ "high",
15050
+ "xhigh",
15051
+ "max"
15012
15052
  ]
15013
15053
  },
15014
15054
  "thinking": {
@@ -15898,10 +15938,14 @@
15898
15938
  "reasoningEffort": {
15899
15939
  "type": "string",
15900
15940
  "enum": [
15941
+ "none",
15901
15942
  "instant",
15943
+ "minimal",
15902
15944
  "low",
15903
15945
  "medium",
15904
- "high"
15946
+ "high",
15947
+ "xhigh",
15948
+ "max"
15905
15949
  ]
15906
15950
  },
15907
15951
  "thinking": {
@@ -80001,10 +80045,14 @@
80001
80045
  "reasoningEffort": {
80002
80046
  "type": "string",
80003
80047
  "enum": [
80048
+ "none",
80004
80049
  "instant",
80050
+ "minimal",
80005
80051
  "low",
80006
80052
  "medium",
80007
- "high"
80053
+ "high",
80054
+ "xhigh",
80055
+ "max"
80008
80056
  ]
80009
80057
  }
80010
80058
  },
@@ -80449,10 +80497,14 @@
80449
80497
  "reasoningEffort": {
80450
80498
  "type": "string",
80451
80499
  "enum": [
80500
+ "none",
80452
80501
  "instant",
80502
+ "minimal",
80453
80503
  "low",
80454
80504
  "medium",
80455
- "high"
80505
+ "high",
80506
+ "xhigh",
80507
+ "max"
80456
80508
  ]
80457
80509
  }
80458
80510
  },
@@ -81015,10 +81067,14 @@
81015
81067
  "reasoningEffort": {
81016
81068
  "type": "string",
81017
81069
  "enum": [
81070
+ "none",
81018
81071
  "instant",
81072
+ "minimal",
81019
81073
  "low",
81020
81074
  "medium",
81021
- "high"
81075
+ "high",
81076
+ "xhigh",
81077
+ "max"
81022
81078
  ]
81023
81079
  }
81024
81080
  },
@@ -81281,10 +81337,14 @@
81281
81337
  "reasoningEffort": {
81282
81338
  "type": "string",
81283
81339
  "enum": [
81340
+ "none",
81284
81341
  "instant",
81342
+ "minimal",
81285
81343
  "low",
81286
81344
  "medium",
81287
- "high"
81345
+ "high",
81346
+ "xhigh",
81347
+ "max"
81288
81348
  ]
81289
81349
  }
81290
81350
  },
@@ -81699,10 +81759,14 @@
81699
81759
  "reasoningEffort": {
81700
81760
  "type": "string",
81701
81761
  "enum": [
81762
+ "none",
81702
81763
  "instant",
81764
+ "minimal",
81703
81765
  "low",
81704
81766
  "medium",
81705
- "high"
81767
+ "high",
81768
+ "xhigh",
81769
+ "max"
81706
81770
  ]
81707
81771
  }
81708
81772
  },
@@ -82147,10 +82211,14 @@
82147
82211
  "reasoningEffort": {
82148
82212
  "type": "string",
82149
82213
  "enum": [
82214
+ "none",
82150
82215
  "instant",
82216
+ "minimal",
82151
82217
  "low",
82152
82218
  "medium",
82153
- "high"
82219
+ "high",
82220
+ "xhigh",
82221
+ "max"
82154
82222
  ]
82155
82223
  }
82156
82224
  },
@@ -82617,10 +82685,14 @@
82617
82685
  "reasoningEffort": {
82618
82686
  "type": "string",
82619
82687
  "enum": [
82688
+ "none",
82620
82689
  "instant",
82690
+ "minimal",
82621
82691
  "low",
82622
82692
  "medium",
82623
- "high"
82693
+ "high",
82694
+ "xhigh",
82695
+ "max"
82624
82696
  ]
82625
82697
  }
82626
82698
  },
@@ -84091,10 +84163,14 @@
84091
84163
  "reasoningEffort": {
84092
84164
  "type": "string",
84093
84165
  "enum": [
84166
+ "none",
84094
84167
  "instant",
84168
+ "minimal",
84095
84169
  "low",
84096
84170
  "medium",
84097
- "high"
84171
+ "high",
84172
+ "xhigh",
84173
+ "max"
84098
84174
  ]
84099
84175
  }
84100
84176
  },
@@ -84542,10 +84618,14 @@
84542
84618
  "reasoningEffort": {
84543
84619
  "type": "string",
84544
84620
  "enum": [
84621
+ "none",
84545
84622
  "instant",
84623
+ "minimal",
84546
84624
  "low",
84547
84625
  "medium",
84548
- "high"
84626
+ "high",
84627
+ "xhigh",
84628
+ "max"
84549
84629
  ]
84550
84630
  }
84551
84631
  },
@@ -85977,10 +86057,14 @@
85977
86057
  "reasoningEffort": {
85978
86058
  "type": "string",
85979
86059
  "enum": [
86060
+ "none",
85980
86061
  "instant",
86062
+ "minimal",
85981
86063
  "low",
85982
86064
  "medium",
85983
- "high"
86065
+ "high",
86066
+ "xhigh",
86067
+ "max"
85984
86068
  ]
85985
86069
  }
85986
86070
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-tui",
3
- "version": "1.20.0",
3
+ "version": "1.21.0",
4
4
  "description": "Terminal-native GoodVibes product for coding, operations, automation, knowledge, channels, and daemon-backed control-plane workflows.",
5
5
  "type": "module",
6
6
  "main": "src/main.ts",
@@ -104,8 +104,8 @@
104
104
  "@anthropic-ai/vertex-sdk": "^0.16.0",
105
105
  "@ast-grep/napi": "^0.42.0",
106
106
  "@aws/bedrock-token-generator": "^1.1.0",
107
- "@pellux/goodvibes-sdk": "1.12.0",
108
- "@pellux/goodvibes-terminal-shell": "1.12.0",
107
+ "@pellux/goodvibes-sdk": "1.13.1",
108
+ "@pellux/goodvibes-terminal-shell": "1.13.1",
109
109
  "bash-language-server": "^5.6.0",
110
110
  "fuse.js": "^7.1.0",
111
111
  "graphql": "^16.13.2",
@@ -131,10 +131,10 @@
131
131
  "typescript": "^5.9.3"
132
132
  },
133
133
  "optionalDependencies": {
134
- "@pellux/goodvibes-tui-linux-x64": "1.20.0",
135
- "@pellux/goodvibes-tui-linux-arm64": "1.20.0",
136
- "@pellux/goodvibes-tui-darwin-x64": "1.20.0",
137
- "@pellux/goodvibes-tui-darwin-arm64": "1.20.0"
134
+ "@pellux/goodvibes-tui-linux-x64": "1.21.0",
135
+ "@pellux/goodvibes-tui-linux-arm64": "1.21.0",
136
+ "@pellux/goodvibes-tui-darwin-x64": "1.21.0",
137
+ "@pellux/goodvibes-tui-darwin-arm64": "1.21.0"
138
138
  },
139
139
  "trustedDependencies": [],
140
140
  "overrides": {
@@ -12,7 +12,7 @@ import type { ConfigKey } from '../config/index.ts';
12
12
  import type { CliCommandRuntime } from './types.ts';
13
13
  import type { CliCommandOutput } from './types.ts';
14
14
  import { getPackageVersion } from './help.ts';
15
- import { classifyProviderSetup } from './provider-classification.ts';
15
+ import { classifyProviderSetup } from '../providers/provider-classification.ts';
16
16
  import { buildCliServicePosture } from './service-posture.ts';
17
17
  import { REDACTED_VALUE, collectSensitiveConfigValues, isRedactedValue, redactConfig, redactSerializedSecrets } from './redaction.ts';
18
18
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
package/src/cli/doctor.ts CHANGED
@@ -3,6 +3,7 @@ import { PermissionManager, createPermissionConfigReader } from '@pellux/goodvib
3
3
  import type { PermissionCategory, PermissionCheckResult } from '@pellux/goodvibes-sdk/platform/permissions';
4
4
  import { PolicyRuntimeState, createFeatureFlagManager, deriveFeatureStates } from '@/runtime/index.ts';
5
5
  import { getProviderIdFromModel } from '../config/provider-model.ts';
6
+ import { describeConfiguredEffort } from '../providers/reasoning-effort-surface.ts';
6
7
  import type { CliCommandOutput } from './types.ts';
7
8
  import type { GoodVibesCliOutputFormat } from './types.ts';
8
9
  import { buildHooksValidation } from './hooks-report.ts';
@@ -263,7 +264,12 @@ function buildRoutingRoles(config: ConfigManager): RoutingRole[] {
263
264
  provider: mainProvider,
264
265
  model: mainModel || '(unset)',
265
266
  keys: ['provider.model', 'provider.reasoningEffort'],
266
- note: `reasoningEffort=${String(config.get('provider.reasoningEffort') ?? '')}`,
267
+ // Both halves: the level that is configured, and what that level
268
+ // actually becomes on this model. They differ whenever the model does
269
+ // not offer the configured level, and a doctor report that printed only
270
+ // the first would hide exactly the mismatch it exists to find.
271
+ note: `reasoningEffort=${String(config.get('provider.reasoningEffort') ?? '')}`
272
+ + ` -> ${describeConfiguredEffort(mainModel, String(config.get('provider.reasoningEffort') ?? ''))}`,
267
273
  },
268
274
  {
269
275
  role: 'Embeddings',
@@ -13,7 +13,7 @@ import { beginOpenAICodexLogin, exchangeOpenAICodexCode } from '@pellux/goodvibe
13
13
  import { inspectProviderAuth } from '@/runtime/index.ts';
14
14
  import type { CliCommandRuntime, GoodVibesCliParseResult } from './types.ts';
15
15
  import { formatProviderAuthRoute, summarizeProviderAuthRoutes } from './provider-auth-routes.ts';
16
- import { classifyProviderSetup } from './provider-classification.ts';
16
+ import { classifyProviderSetup } from '../providers/provider-classification.ts';
17
17
  import { resolveRuntimeEndpointBinding } from './endpoints.ts';
18
18
  import { applyRuntimeEndpointFlagOverrides } from './config-overrides.ts';
19
19
  import type { RuntimeEndpointId } from './endpoints.ts';
@@ -68,18 +68,103 @@ function verifyBin(root: string, command: typeof REQUIRED_BIN_COMMANDS[number],
68
68
  };
69
69
  }
70
70
 
71
- function npmPackDryRun(root: string): { readonly files: readonly string[]; readonly entryCount: number; readonly unpackedSize: number } {
71
+ export interface NpmPackDryRunResult {
72
+ readonly files: readonly string[];
73
+ readonly entryCount: number;
74
+ readonly unpackedSize: number;
75
+ }
76
+
77
+ interface NpmPackJsonEntry {
78
+ readonly files?: ReadonlyArray<{ readonly path?: string } | null>;
79
+ readonly entryCount?: number;
80
+ readonly unpackedSize?: number;
81
+ }
82
+
83
+ function describeNpmPackOutput(raw: string): string {
84
+ const trimmed = raw.trim();
85
+ if (trimmed.length === 0) return 'no output at all';
86
+ const preview = trimmed.length > 400 ? `${trimmed.slice(0, 400)}...` : trimmed;
87
+ return `${trimmed.length} characters of output beginning: ${preview}`;
88
+ }
89
+
90
+ // npm wrappers (version-manager shims, "npm notice" lines) sometimes print plain text
91
+ // on stdout alongside the JSON document. Take the first balanced JSON value and ignore
92
+ // whatever surrounds it, tracking string literals so braces inside paths do not confuse
93
+ // the depth count.
94
+ function extractJsonDocument(raw: string): string | undefined {
95
+ const start = raw.search(/[[{]/);
96
+ if (start < 0) return undefined;
97
+ let depth = 0;
98
+ let inString = false;
99
+ let escaped = false;
100
+ for (let index = start; index < raw.length; index += 1) {
101
+ const char = raw[index];
102
+ if (inString) {
103
+ if (escaped) escaped = false;
104
+ else if (char === '\\') escaped = true;
105
+ else if (char === '"') inString = false;
106
+ continue;
107
+ }
108
+ if (char === '"') {
109
+ inString = true;
110
+ continue;
111
+ }
112
+ if (char === '[' || char === '{') depth += 1;
113
+ else if (char === ']' || char === '}') {
114
+ depth -= 1;
115
+ if (depth === 0) return raw.slice(start, index + 1);
116
+ }
117
+ }
118
+ return undefined;
119
+ }
120
+
121
+ function looksLikePackEntry(value: unknown): value is NpmPackJsonEntry {
122
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
123
+ const candidate = value as Record<string, unknown>;
124
+ return Array.isArray(candidate.files) || typeof candidate.entryCount === 'number' || typeof candidate.unpackedSize === 'number';
125
+ }
126
+
127
+ function selectPackEntry(parsed: unknown): NpmPackJsonEntry | undefined {
128
+ // npm 10/11 emit `[{ files, entryCount, unpackedSize }]`.
129
+ if (Array.isArray(parsed)) return parsed.find(looksLikePackEntry);
130
+ // npm 12 emits `{ "<package-name>": { files, entryCount, unpackedSize } }`.
131
+ if (looksLikePackEntry(parsed)) return parsed;
132
+ if (parsed && typeof parsed === 'object') return Object.values(parsed as Record<string, unknown>).find(looksLikePackEntry);
133
+ return undefined;
134
+ }
135
+
136
+ export function parseNpmPackJson(raw: string): NpmPackDryRunResult {
137
+ const document = extractJsonDocument(raw);
138
+ if (document === undefined) {
139
+ throw new Error(`npm pack --json --dry-run printed no JSON document; npm emitted ${describeNpmPackOutput(raw)}`);
140
+ }
141
+ let parsed: unknown;
142
+ try {
143
+ parsed = JSON.parse(document);
144
+ } catch (error) {
145
+ const reason = error instanceof Error ? error.message : String(error);
146
+ throw new Error(`npm pack --json --dry-run printed JSON that could not be parsed (${reason}); npm emitted ${describeNpmPackOutput(raw)}`);
147
+ }
148
+ const entry = selectPackEntry(parsed);
149
+ if (!entry) {
150
+ throw new Error(
151
+ `npm pack --json --dry-run returned an unrecognized JSON shape; expected an array of pack results or an object keyed by package name, but npm emitted ${describeNpmPackOutput(raw)}`,
152
+ );
153
+ }
154
+ return {
155
+ files: Array.isArray(entry.files) ? entry.files.map((file) => String(file?.path ?? '')) : [],
156
+ entryCount: Number(entry.entryCount ?? 0),
157
+ unpackedSize: Number(entry.unpackedSize ?? 0),
158
+ };
159
+ }
160
+
161
+ function npmPackDryRun(root: string): NpmPackDryRunResult {
72
162
  const raw = execSync('npm pack --json --dry-run', {
73
163
  cwd: root,
74
164
  encoding: 'utf-8',
75
165
  stdio: ['ignore', 'pipe', 'inherit'],
76
166
  });
77
- const [packResult] = JSON.parse(raw) as Array<{ files?: Array<{ path?: string }>; entryCount?: number; unpackedSize?: number }>;
78
- return {
79
- files: Array.isArray(packResult?.files) ? packResult.files.map((entry) => String(entry.path ?? '')) : [],
80
- entryCount: Number(packResult?.entryCount ?? 0),
81
- unpackedSize: Number(packResult?.unpackedSize ?? 0),
82
- };
167
+ return parseNpmPackJson(raw);
83
168
  }
84
169
 
85
170
  export function verifyPackageCliInstall(root: string): PackageCliVerificationReport {