jeo-code 0.7.21 → 0.7.23

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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  The README mirrors the latest 5 entries — regenerate with `bun run changelog:sync`.
8
8
 
9
+ ## [0.7.23] - 2026-07-01
10
+ _Anthropic 5th-generation model catalog: adds Sonnet 5, Fable 5, and Mythos 5, promotes the 4.6 generation to 1M context / 128k output, and retires the superseded 4.5/3.5/4.1 ids. Bumps the default model to Sonnet 4.6, prices the new tiers, and fixes the thinking transport for Haiku 4.5 and every 5th-gen model._
11
+
12
+ ### Added
13
+ - **Anthropic 5th-generation models.** New catalog entries `claude-sonnet-5`, `claude-fable-5` (most capable widely-released, adaptive thinking always on), and `claude-mythos-5` (limited-availability, callable by id). `inferCatalogMetadata` now recognizes the `fable`/`mythos` families, and the Anthropic version parser accepts dateless single-major ids (e.g. `claude-sonnet-5`).
14
+ - **1M context / 128k output for the 4.6 generation onward.** `claude-opus-4-6/4-7/4-8`, `claude-sonnet-4-6`, and all 5th-gen ids now advertise 1,000,000-token context and 128,000-token max output (Anthropic docs); dated pre-4.6 ids stay at 200k/64k.
15
+ - **Pricing for the new tiers.** Opus 4.8 (`$5`/`$25` per M), Fable/Mythos (`$10`/`$50`), and Sonnet 5 (`$2`/`$10`), ordered ahead of the generic Opus/Sonnet families so the newer tiers win.
16
+
17
+ ### Fixed
18
+ - **Haiku 4.5 thinking transport.** Haiku 4.5 supports `budget_tokens` thinking but rejects `output_config.effort` ("This model does not support the effort parameter"), so it now uses the plain budget transport instead of its sonnet/opus 4.5 siblings' budget-effort mode.
19
+ - **Adaptive thinking display for 5th-gen models.** `display: "summarized"` is now enabled for every 5th-generation+ model (Sonnet 5, Fable 5, Mythos 5) in addition to Opus ≥ 4.7, so their reasoning streams instead of being silently omitted.
20
+ - **Gemini path no longer inherits a `claude-` model id.** A `claude-`-prefixed model falls back to `gemini-2.0-flash` on the Gemini transport instead of forwarding an incompatible id.
21
+
22
+ ### Changed
23
+ - **Default model bumped to `claude-sonnet-4-6`.** Runtime default, setup defaults, recommended-model set, and the `sonnet`/`opus` aliases now point at the 4.6 generation.
24
+ - **Retired superseded ids.** Removed `claude-3-5-sonnet`, `claude-opus-4-1`, `claude-opus-4-5`, and `claude-sonnet-4-5` (plus their Antigravity `-thinking` variants) from the catalog.
25
+
26
+ ### Verified
27
+ - `bun run typecheck` clean; full `bun test` suite green. Updated model-catalog, pricing, registry, and Anthropic-stream tests cover the new families, 1M/128k metadata, and the Haiku 4.5 transport.
28
+
29
+ ## [0.7.22] - 2026-06-30
30
+ _gajae-code 0.7.3–0.7.8 parity sweep: terminal-bell notifications, a gruvbox-dark TUI theme, a resilient `jeo update` runtime check, and an ultragoal artifact gate. Ports the user-facing improvements from upstream gajae-code that jeo did not yet have, re-implemented to fit jeo's config, command, theming, and skill surfaces._
31
+
32
+ ### Added
33
+ - **Terminal-bell notifications (`notify.bell` / `JEO_NOTIFY_BELL`).** gajae-code 0.7.8 (#1277) parity. Emits an ASCII BEL when an agent turn finishes so a backgrounded `jeo` session pings you. Off by default; opt in with `config.notify.bell` (per-event `onComplete`/`onAsk` refine it) or force with `JEO_NOTIFY_BELL=1`/`0`. Never fires for a cancelled turn, and a dead terminal never crashes the turn.
34
+ - **gruvbox-dark TUI theme.** New `JEO_TUI_THEME=gruvbox-dark` option alongside the existing themes.
35
+
36
+ ### Fixed
37
+ - **`jeo update` no longer reports false failures from Bun tarball-extraction errors.** gajae-code 0.7.8 (#1280) parity. When the package manager exits nonzero but the active `jeo` on PATH actually reports the requested version, the update is treated as recovered (with a clear message and `recovered: true` in `--json`) instead of failing; genuine failures still exit 1.
38
+
39
+ ### Changed
40
+ - **ultragoal artifact gate.** gajae-code 0.7.4 (#1163) parity. The completion report must cite concrete verification artifacts — exact commands and their observed results, plus changed-file paths tied to each acceptance criterion — and must explicitly mark any unverifiable criterion as unresolved rather than implying success.
41
+
9
42
  ## [0.7.21] - 2026-06-26
10
43
  _Global llm-wiki vault integration, Gemini/Antigravity thinking indicators, generous file-reading windows, and autopilot flag validation. Adds a shared global wiki root configuration with a /wiki slash command, fires reasoning start signals up front for Gemini/Antigravity models, adjusts the large-file reading discipline to use generous windows, and validates autopilot goal and integer flags._
11
44
 
package/README.ja.md CHANGED
@@ -179,7 +179,7 @@ jeo doctor && jeo
179
179
  ANTHROPIC_API_KEY=... OPENAI_API_KEY=... GEMINI_API_KEY=...
180
180
  JEO_DEFAULT_MODEL=... # 例: ollama/qwen2.5:0.5b
181
181
  OLLAMA_HOST=http://localhost:11434
182
- JEO_TUI_THEME=cosmic # cosmic/matrix/solar/red-claw/blue-crab/mono/aurora/synthwave/sakura
182
+ JEO_TUI_THEME=cosmic # cosmic/matrix/solar/red-claw/blue-crab/mono/aurora/synthwave/sakura/gruvbox-dark
183
183
  JEO_TUI_ALT_SCREEN=1 # レガシー alt-screen ターン(デフォルト: インラインスクロールバック)
184
184
  JEO_STEP_BASE=24 # 動的ステップ予算のローリングベース
185
185
  JEO_STEP_HARD_CAP=600 # 絶対終了保証
@@ -205,11 +205,11 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
205
205
  ## 変更履歴 (Changelog)
206
206
 
207
207
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
208
+ - **[0.7.23]** (2026-07-01) — Anthropic 5th-generation model catalog: adds Sonnet 5, Fable 5, and Mythos 5, promotes the 4.6 generation to 1M context / 128k output, and retires the superseded 4.5/3.5/4.1 ids. Bumps the default model to Sonnet 4.6, prices the new tiers, and fixes the thinking transport for Haiku 4.5 and every 5th-gen model.
209
+ - **[0.7.22]** (2026-06-30) — gajae-code 0.7.3–0.7.8 parity sweep: terminal-bell notifications, a gruvbox-dark TUI theme, a resilient `jeo update` runtime check, and an ultragoal artifact gate. Ports the user-facing improvements from upstream gajae-code that jeo did not yet have, re-implemented to fit jeo's config, command, theming, and skill surfaces.
208
210
  - **[0.7.21]** (2026-06-26) — Global llm-wiki vault integration, Gemini/Antigravity thinking indicators, generous file-reading windows, and autopilot flag validation. Adds a shared global wiki root configuration with a /wiki slash command, fires reasoning start signals up front for Gemini/Antigravity models, adjusts the large-file reading discipline to use generous windows, and validates autopilot goal and integer flags.
209
211
  - **[0.7.20]** (2026-06-26) — OKF concept-memory retrieval gains a hybrid reranker ported from memsearch. Injection priority no longer rides one raw keyword score — it fuses two complementary ranked channels by Reciprocal Rank Fusion (RRF): IDF-weighted lexical relevance (the sparse/BM25 channel, so rare discriminating terms steer recall) and concept-graph proximity (the local dense/semantic-neighbour channel, so a hub linked from multiple query hits surfaces even with no keyword of its own). All embedding-free and deterministic, layered atop the existing failure-first tier and pinned-invariant reserved budget.
210
212
  - **[0.7.19]** (2026-06-26) — The live model picker gains gajae-code's `/model` provider tabs, and skill invocation is consolidated onto a single `$` entrypoint. The picker now shows an `ALL` tab plus one tab per provider that `tab`/`shift+tab` cycles, and skills (including their declared aliases) are invoked only via `$` — the slash palette stays builtins-only.
211
- - **[0.7.18]** (2026-06-26) — Slash-command discovery and the `/model` flow reach gajae-code parity. The slash palette/autocomplete now fuzzy-matches command names (with a description fallback for intent-style queries), resolved skills can contribute their own `/aliases` as real dispatchable commands, and `/model` runs gjc's two-menu target → reasoning flow so a picked model can be assigned to the default or any subagent role with its own thinking budget.
212
- - **[0.7.17]** (2026-06-25) — Developer workflow parity (gjc `dev:link`/`dev:doctor`, adapted for jeo's zero-native-dep Bun runtime): the global `jeo` command can be linked to run this checkout's source hot to every edit, with a drift doctor that flags when `jeo` resolves to a compiled binary or an installed copy instead. README gains "Skill migration and bundled skill inspection" + "Development" sections. Also ships OKF concept-memory search/scoring with budget-aware injection and a round of workflow-prompt hardening (anti-punt, todo-first planning, verdict discipline) that keeps every loop escape hatch intact.
213
213
 
214
214
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
215
215
  <!-- CHANGELOG:END -->
package/README.ko.md CHANGED
@@ -179,7 +179,7 @@ jeo doctor && jeo
179
179
  ANTHROPIC_API_KEY=... OPENAI_API_KEY=... GEMINI_API_KEY=...
180
180
  JEO_DEFAULT_MODEL=... # 예: ollama/qwen2.5:0.5b
181
181
  OLLAMA_HOST=http://localhost:11434
182
- JEO_TUI_THEME=cosmic # cosmic/matrix/solar/red-claw/blue-crab/mono/aurora/synthwave/sakura
182
+ JEO_TUI_THEME=cosmic # cosmic/matrix/solar/red-claw/blue-crab/mono/aurora/synthwave/sakura/gruvbox-dark
183
183
  JEO_TUI_ALT_SCREEN=1 # 레거시 alt-screen 턴(기본: 인라인 스크롤백)
184
184
  JEO_STEP_BASE=24 # 동적 스텝 버짓의 롤링 베이스
185
185
  JEO_STEP_HARD_CAP=600 # 절대 종료 보증
@@ -205,11 +205,11 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
205
205
  ## 변경 이력 (Changelog)
206
206
 
207
207
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
208
+ - **[0.7.23]** (2026-07-01) — Anthropic 5th-generation model catalog: adds Sonnet 5, Fable 5, and Mythos 5, promotes the 4.6 generation to 1M context / 128k output, and retires the superseded 4.5/3.5/4.1 ids. Bumps the default model to Sonnet 4.6, prices the new tiers, and fixes the thinking transport for Haiku 4.5 and every 5th-gen model.
209
+ - **[0.7.22]** (2026-06-30) — gajae-code 0.7.3–0.7.8 parity sweep: terminal-bell notifications, a gruvbox-dark TUI theme, a resilient `jeo update` runtime check, and an ultragoal artifact gate. Ports the user-facing improvements from upstream gajae-code that jeo did not yet have, re-implemented to fit jeo's config, command, theming, and skill surfaces.
208
210
  - **[0.7.21]** (2026-06-26) — Global llm-wiki vault integration, Gemini/Antigravity thinking indicators, generous file-reading windows, and autopilot flag validation. Adds a shared global wiki root configuration with a /wiki slash command, fires reasoning start signals up front for Gemini/Antigravity models, adjusts the large-file reading discipline to use generous windows, and validates autopilot goal and integer flags.
209
211
  - **[0.7.20]** (2026-06-26) — OKF concept-memory retrieval gains a hybrid reranker ported from memsearch. Injection priority no longer rides one raw keyword score — it fuses two complementary ranked channels by Reciprocal Rank Fusion (RRF): IDF-weighted lexical relevance (the sparse/BM25 channel, so rare discriminating terms steer recall) and concept-graph proximity (the local dense/semantic-neighbour channel, so a hub linked from multiple query hits surfaces even with no keyword of its own). All embedding-free and deterministic, layered atop the existing failure-first tier and pinned-invariant reserved budget.
210
212
  - **[0.7.19]** (2026-06-26) — The live model picker gains gajae-code's `/model` provider tabs, and skill invocation is consolidated onto a single `$` entrypoint. The picker now shows an `ALL` tab plus one tab per provider that `tab`/`shift+tab` cycles, and skills (including their declared aliases) are invoked only via `$` — the slash palette stays builtins-only.
211
- - **[0.7.18]** (2026-06-26) — Slash-command discovery and the `/model` flow reach gajae-code parity. The slash palette/autocomplete now fuzzy-matches command names (with a description fallback for intent-style queries), resolved skills can contribute their own `/aliases` as real dispatchable commands, and `/model` runs gjc's two-menu target → reasoning flow so a picked model can be assigned to the default or any subagent role with its own thinking budget.
212
- - **[0.7.17]** (2026-06-25) — Developer workflow parity (gjc `dev:link`/`dev:doctor`, adapted for jeo's zero-native-dep Bun runtime): the global `jeo` command can be linked to run this checkout's source hot to every edit, with a drift doctor that flags when `jeo` resolves to a compiled binary or an installed copy instead. README gains "Skill migration and bundled skill inspection" + "Development" sections. Also ships OKF concept-memory search/scoring with budget-aware injection and a round of workflow-prompt hardening (anti-punt, todo-first planning, verdict discipline) that keeps every loop escape hatch intact.
213
213
 
214
214
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
215
215
  <!-- CHANGELOG:END -->
package/README.md CHANGED
@@ -180,7 +180,7 @@ jeo doctor && jeo
180
180
  ANTHROPIC_API_KEY=... OPENAI_API_KEY=... GEMINI_API_KEY=...
181
181
  JEO_DEFAULT_MODEL=... # e.g. ollama/qwen2.5:0.5b
182
182
  OLLAMA_HOST=http://localhost:11434
183
- JEO_TUI_THEME=cosmic # cosmic/matrix/solar/red-claw/blue-crab/mono/aurora/synthwave/sakura
183
+ JEO_TUI_THEME=cosmic # cosmic/matrix/solar/red-claw/blue-crab/mono/aurora/synthwave/sakura/gruvbox-dark
184
184
  JEO_TUI_ALT_SCREEN=1 # legacy alt-screen turn (default: inline scrollback)
185
185
  JEO_STEP_BASE=24 # dynamic step budget: rolling base
186
186
  JEO_STEP_HARD_CAP=600 # absolute termination guarantee
@@ -232,11 +232,11 @@ Huge thanks to [gajae-code](https://github.com/Yeachan-Heo/gajae-code) for the i
232
232
  ## Changelog
233
233
 
234
234
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
235
+ - **[0.7.23]** (2026-07-01) — Anthropic 5th-generation model catalog: adds Sonnet 5, Fable 5, and Mythos 5, promotes the 4.6 generation to 1M context / 128k output, and retires the superseded 4.5/3.5/4.1 ids. Bumps the default model to Sonnet 4.6, prices the new tiers, and fixes the thinking transport for Haiku 4.5 and every 5th-gen model.
236
+ - **[0.7.22]** (2026-06-30) — gajae-code 0.7.3–0.7.8 parity sweep: terminal-bell notifications, a gruvbox-dark TUI theme, a resilient `jeo update` runtime check, and an ultragoal artifact gate. Ports the user-facing improvements from upstream gajae-code that jeo did not yet have, re-implemented to fit jeo's config, command, theming, and skill surfaces.
235
237
  - **[0.7.21]** (2026-06-26) — Global llm-wiki vault integration, Gemini/Antigravity thinking indicators, generous file-reading windows, and autopilot flag validation. Adds a shared global wiki root configuration with a /wiki slash command, fires reasoning start signals up front for Gemini/Antigravity models, adjusts the large-file reading discipline to use generous windows, and validates autopilot goal and integer flags.
236
238
  - **[0.7.20]** (2026-06-26) — OKF concept-memory retrieval gains a hybrid reranker ported from memsearch. Injection priority no longer rides one raw keyword score — it fuses two complementary ranked channels by Reciprocal Rank Fusion (RRF): IDF-weighted lexical relevance (the sparse/BM25 channel, so rare discriminating terms steer recall) and concept-graph proximity (the local dense/semantic-neighbour channel, so a hub linked from multiple query hits surfaces even with no keyword of its own). All embedding-free and deterministic, layered atop the existing failure-first tier and pinned-invariant reserved budget.
237
239
  - **[0.7.19]** (2026-06-26) — The live model picker gains gajae-code's `/model` provider tabs, and skill invocation is consolidated onto a single `$` entrypoint. The picker now shows an `ALL` tab plus one tab per provider that `tab`/`shift+tab` cycles, and skills (including their declared aliases) are invoked only via `$` — the slash palette stays builtins-only.
238
- - **[0.7.18]** (2026-06-26) — Slash-command discovery and the `/model` flow reach gajae-code parity. The slash palette/autocomplete now fuzzy-matches command names (with a description fallback for intent-style queries), resolved skills can contribute their own `/aliases` as real dispatchable commands, and `/model` runs gjc's two-menu target → reasoning flow so a picked model can be assigned to the default or any subagent role with its own thinking budget.
239
- - **[0.7.17]** (2026-06-25) — Developer workflow parity (gjc `dev:link`/`dev:doctor`, adapted for jeo's zero-native-dep Bun runtime): the global `jeo` command can be linked to run this checkout's source hot to every edit, with a drift doctor that flags when `jeo` resolves to a compiled binary or an installed copy instead. README gains "Skill migration and bundled skill inspection" + "Development" sections. Also ships OKF concept-memory search/scoring with budget-aware injection and a round of workflow-prompt hardening (anti-punt, todo-first planning, verdict discipline) that keeps every loop escape hatch intact.
240
240
 
241
241
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
242
242
  <!-- CHANGELOG:END -->
package/README.zh.md CHANGED
@@ -179,7 +179,7 @@ jeo doctor && jeo
179
179
  ANTHROPIC_API_KEY=... OPENAI_API_KEY=... GEMINI_API_KEY=...
180
180
  JEO_DEFAULT_MODEL=... # 例: ollama/qwen2.5:0.5b
181
181
  OLLAMA_HOST=http://localhost:11434
182
- JEO_TUI_THEME=cosmic # cosmic/matrix/solar/red-claw/blue-crab/mono/aurora/synthwave/sakura
182
+ JEO_TUI_THEME=cosmic # cosmic/matrix/solar/red-claw/blue-crab/mono/aurora/synthwave/sakura/gruvbox-dark
183
183
  JEO_TUI_ALT_SCREEN=1 # 旧版 alt-screen 回合(默认: 内联滚动缓冲)
184
184
  JEO_STEP_BASE=24 # 动态步数预算的滚动基数
185
185
  JEO_STEP_HARD_CAP=600 # 绝对终止保证
@@ -205,11 +205,11 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
205
205
  ## 更新日志 (Changelog)
206
206
 
207
207
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
208
+ - **[0.7.23]** (2026-07-01) — Anthropic 5th-generation model catalog: adds Sonnet 5, Fable 5, and Mythos 5, promotes the 4.6 generation to 1M context / 128k output, and retires the superseded 4.5/3.5/4.1 ids. Bumps the default model to Sonnet 4.6, prices the new tiers, and fixes the thinking transport for Haiku 4.5 and every 5th-gen model.
209
+ - **[0.7.22]** (2026-06-30) — gajae-code 0.7.3–0.7.8 parity sweep: terminal-bell notifications, a gruvbox-dark TUI theme, a resilient `jeo update` runtime check, and an ultragoal artifact gate. Ports the user-facing improvements from upstream gajae-code that jeo did not yet have, re-implemented to fit jeo's config, command, theming, and skill surfaces.
208
210
  - **[0.7.21]** (2026-06-26) — Global llm-wiki vault integration, Gemini/Antigravity thinking indicators, generous file-reading windows, and autopilot flag validation. Adds a shared global wiki root configuration with a /wiki slash command, fires reasoning start signals up front for Gemini/Antigravity models, adjusts the large-file reading discipline to use generous windows, and validates autopilot goal and integer flags.
209
211
  - **[0.7.20]** (2026-06-26) — OKF concept-memory retrieval gains a hybrid reranker ported from memsearch. Injection priority no longer rides one raw keyword score — it fuses two complementary ranked channels by Reciprocal Rank Fusion (RRF): IDF-weighted lexical relevance (the sparse/BM25 channel, so rare discriminating terms steer recall) and concept-graph proximity (the local dense/semantic-neighbour channel, so a hub linked from multiple query hits surfaces even with no keyword of its own). All embedding-free and deterministic, layered atop the existing failure-first tier and pinned-invariant reserved budget.
210
212
  - **[0.7.19]** (2026-06-26) — The live model picker gains gajae-code's `/model` provider tabs, and skill invocation is consolidated onto a single `$` entrypoint. The picker now shows an `ALL` tab plus one tab per provider that `tab`/`shift+tab` cycles, and skills (including their declared aliases) are invoked only via `$` — the slash palette stays builtins-only.
211
- - **[0.7.18]** (2026-06-26) — Slash-command discovery and the `/model` flow reach gajae-code parity. The slash palette/autocomplete now fuzzy-matches command names (with a description fallback for intent-style queries), resolved skills can contribute their own `/aliases` as real dispatchable commands, and `/model` runs gjc's two-menu target → reasoning flow so a picked model can be assigned to the default or any subagent role with its own thinking budget.
212
- - **[0.7.17]** (2026-06-25) — Developer workflow parity (gjc `dev:link`/`dev:doctor`, adapted for jeo's zero-native-dep Bun runtime): the global `jeo` command can be linked to run this checkout's source hot to every edit, with a drift doctor that flags when `jeo` resolves to a compiled binary or an installed copy instead. README gains "Skill migration and bundled skill inspection" + "Development" sections. Also ships OKF concept-memory search/scoring with budget-aware injection and a round of workflow-prompt hardening (anti-punt, todo-first planning, verdict discipline) that keeps every loop escape hatch intact.
213
213
 
214
214
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
215
215
  <!-- CHANGELOG:END -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jeo-code",
3
- "version": "0.7.21",
3
+ "version": "0.7.23",
4
4
  "description": "Clean, highly optimized AI coding agent using spec-first loop",
5
5
  "type": "module",
6
6
  "main": "src/cli.ts",
@@ -73,6 +73,17 @@ export const ConfigSchema = z
73
73
  lmstudioBaseUrl: z.string().optional(),
74
74
  defaultModel: z.string().min(1),
75
75
  theme: z.string().optional(),
76
+ /** Terminal-bell notifications (gajae-code 0.7.8 parity): emit an ASCII BEL
77
+ * (\x07) at notable interaction points so a backgrounded session pings the
78
+ * user. Off by default; `bell` is the master toggle, per-event flags refine it.
79
+ * Env `JEO_NOTIFY_BELL=1/0` force-overrides the master toggle. */
80
+ notify: z
81
+ .object({
82
+ bell: z.boolean().optional(),
83
+ onComplete: z.boolean().optional(),
84
+ onAsk: z.boolean().optional(),
85
+ })
86
+ .optional(),
76
87
  /** Root path of the global llm-wiki vault, shared across every session
77
88
  * regardless of project/cwd. A leading `~` is expanded; env `JEO_WIKI_ROOT`
78
89
  * overrides. Consumed by `resolveWikiRoot` and injected into the prompt. */
@@ -51,6 +51,13 @@ export interface Config {
51
51
  * overrides. Resolve with `resolveWikiRoot()`. */
52
52
  wikiRoot?: string;
53
53
  theme?: string;
54
+ /** Terminal-bell notifications (gajae-code 0.7.8 parity). `bell` is the master
55
+ * toggle; per-event flags refine it. Env `JEO_NOTIFY_BELL=1/0` force-overrides. */
56
+ notify?: {
57
+ bell?: boolean;
58
+ onComplete?: boolean;
59
+ onAsk?: boolean;
60
+ };
54
61
  thinkingLevel?: "minimal" | "low" | "medium" | "high" | "xhigh";
55
62
  /** Friendly model aliases, e.g. { fast: "ollama/qwen2.5:0.5b" }. Override built-ins. */
56
63
  modelAliases?: { [alias: string]: string };
@@ -167,7 +174,7 @@ export interface WorkflowState {
167
174
  /** The built-in default model when neither disk config nor JEO_DEFAULT_MODEL provides one.
168
175
  * Shared by envDefaultConfig (runtime) and readRawGlobalConfig (persistence base) so a
169
176
  * fresh-install saveConfigPatch never bakes a DIFFERENT default than the runtime resolves. */
170
- const DEFAULT_MODEL = "claude-sonnet-4-5";
177
+ const DEFAULT_MODEL = "claude-sonnet-4-6";
171
178
 
172
179
  /**
173
180
  * Resolve the global config directory at call time (not import time) so that a
@@ -22,7 +22,7 @@ export interface ModelCatalogEntry {
22
22
  }
23
23
 
24
24
  /** Canonical ids that should be surfaced as a provider's recommended default. */
25
- const RECOMMENDED = new Set(["claude-sonnet-4-5", "gpt-4o", "gemini-2.0-flash", "antigravity/gemini-3-pro-low", "qwen2.5"]);
25
+ const RECOMMENDED = new Set(["claude-sonnet-4-6", "gpt-4o", "gemini-2.0-flash", "antigravity/gemini-3-pro-low", "qwen2.5"]);
26
26
 
27
27
  export function normalizeModelId(id: string): string {
28
28
  return (id ?? "").trim().toLowerCase();
@@ -35,14 +35,11 @@ const FULL: ThinkLevel[] = ["minimal", "low", "medium", "high", "xhigh"];
35
35
  const STD: ThinkLevel[] = ["minimal", "low", "medium", "high"];
36
36
 
37
37
  export const ANTIGRAVITY_MODELS = [
38
- "claude-opus-4-5-thinking",
39
38
  "claude-opus-4-6-thinking",
40
39
  "claude-opus-4-7",
41
40
  "claude-opus-4-7-thinking",
42
41
  "claude-opus-4-8",
43
42
  "claude-opus-4-8-thinking",
44
- "claude-sonnet-4-5",
45
- "claude-sonnet-4-5-thinking",
46
43
  "claude-sonnet-4-6",
47
44
  "claude-sonnet-4-6-thinking",
48
45
  "gemini-2.5-flash",
@@ -60,18 +57,23 @@ export const ANTIGRAVITY_MODELS = [
60
57
  /** A curated set of common public models with their documented capabilities. */
61
58
  export const MODEL_CATALOG: readonly CatalogModel[] = [
62
59
  // Anthropic
63
- { canonical: "claude-3-5-sonnet", provider: "anthropic", providerModel: "claude-3-5-sonnet-20241022", contextTokens: 200_000, maxOutputTokens: 8192, thinking: [], images: true },
64
60
  { canonical: "claude-haiku-4-5", provider: "anthropic", providerModel: "claude-haiku-4-5-20251001", contextTokens: 200_000, maxOutputTokens: 64_000, thinking: FULL, images: true },
65
- { canonical: "claude-sonnet-4-5", provider: "anthropic", providerModel: "claude-sonnet-4-5-20250929", contextTokens: 200_000, maxOutputTokens: 64_000, thinking: FULL, images: true },
66
- { canonical: "claude-opus-4-1", provider: "anthropic", providerModel: "claude-opus-4-1-20250805", contextTokens: 200_000, maxOutputTokens: 32_000, thinking: FULL, images: true },
67
- { canonical: "claude-opus-4-5", provider: "anthropic", providerModel: "claude-opus-4-5-20251101", contextTokens: 200_000, maxOutputTokens: 64_000, thinking: FULL, images: true },
68
61
  // NOTE: opus 4.6+ use Anthropic ADAPTIVE thinking (type:"adaptive" + output_config.effort).
69
62
  // opus 4.7/4.8 OMIT visible thought unless the request opts into `display: "summarized"` —
70
63
  // anthropic.ts sets that on the adaptive transport so reasoning streams again (gjc parity).
71
64
  // The nativizable path still replays signature-only thinking blocks for cross-turn continuity.
72
- { canonical: "claude-opus-4-6", provider: "anthropic", providerModel: "claude-opus-4-6", contextTokens: 200_000, maxOutputTokens: 64_000, thinking: FULL, images: true },
73
- { canonical: "claude-opus-4-7", provider: "anthropic", providerModel: "claude-opus-4-7", contextTokens: 200_000, maxOutputTokens: 64_000, thinking: FULL, images: true },
74
- { canonical: "claude-opus-4-8", provider: "anthropic", providerModel: "claude-opus-4-8", contextTokens: 200_000, maxOutputTokens: 64_000, thinking: FULL, images: true },
65
+ // The 4.6 generation onward is 1M context / 128k sync max output (Anthropic docs:
66
+ // Opus 4.8 & Sonnet 5 comparison table; Opus 4.6/4.7, Sonnet 4.6 share the gen's
67
+ // dateless-snapshot 1M/128k + 300k batch-output beta). Dated pre-4.6 ids keep 200k/64k.
68
+ { canonical: "claude-opus-4-6", provider: "anthropic", providerModel: "claude-opus-4-6", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true },
69
+ { canonical: "claude-opus-4-7", provider: "anthropic", providerModel: "claude-opus-4-7", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true },
70
+ { canonical: "claude-opus-4-8", provider: "anthropic", providerModel: "claude-opus-4-8", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true },
71
+ { canonical: "claude-sonnet-4-6", provider: "anthropic", providerModel: "claude-sonnet-4-6", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true },
72
+ { canonical: "claude-sonnet-5", provider: "anthropic", providerModel: "claude-sonnet-5", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true },
73
+ // Fable 5 = Anthropic's most capable widely-released model (adaptive thinking always on);
74
+ // Mythos 5 is limited-availability (Project Glasswing) but callable by id for approved accounts.
75
+ { canonical: "claude-fable-5", provider: "anthropic", providerModel: "claude-fable-5", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true },
76
+ { canonical: "claude-mythos-5", provider: "anthropic", providerModel: "claude-mythos-5", contextTokens: 1_000_000, maxOutputTokens: 128_000, thinking: FULL, images: true },
75
77
  // OpenAI
76
78
  { canonical: "gpt-4o", provider: "openai", providerModel: "gpt-4o", contextTokens: 128_000, maxOutputTokens: 16_384, thinking: [], images: true },
77
79
  { canonical: "gpt-4o-mini", provider: "openai", providerModel: "gpt-4o-mini", contextTokens: 128_000, maxOutputTokens: 16_384, thinking: [], images: true },
@@ -200,7 +202,7 @@ export function inferCatalogMetadata(modelId: string): CatalogModel | undefined
200
202
 
201
203
  // Anthropic Claude: opus/sonnet/haiku. Major version >= 4 ships extended
202
204
  // thinking (mirrors every catalogued claude-4-x entry); claude-3-x does not.
203
- const claude = id.match(/^claude-(opus|sonnet|haiku)-(\d+)(?:[-.](\d+))?/);
205
+ const claude = id.match(/^claude-(opus|sonnet|haiku|fable|mythos)-(\d+)(?:[-.](\d+))?/);
204
206
  if (claude) {
205
207
  const major = Number(claude[2]);
206
208
  const thinking = major >= 4 ? FULL : [];
@@ -208,8 +210,8 @@ export function inferCatalogMetadata(modelId: string): CatalogModel | undefined
208
210
  canonical: raw,
209
211
  provider: antigravity ? "antigravity" : "anthropic",
210
212
  providerModel: id,
211
- contextTokens: 200_000,
212
- maxOutputTokens: claude[1] === "haiku" ? 64_000 : 64_000,
213
+ contextTokens: major >= 5 ? 1_000_000 : 200_000,
214
+ maxOutputTokens: claude[1] === "haiku" ? 64_000 : major >= 5 ? 128_000 : 64_000,
213
215
  thinking,
214
216
  images: true,
215
217
  company: antigravity ? "Anthropic via Antigravity" : "Anthropic",
@@ -160,7 +160,7 @@ export interface ModelManager {
160
160
  resolveProvider: typeof resolveProvider;
161
161
  }
162
162
 
163
- const ALIAS_DEFAULTS = { fast: "ollama/qwen2.5:0.5b", local: "ollama/qwen2.5:0.5b", sonnet: "claude-sonnet-4-5", opus: "claude-opus-4-5", haiku: "claude-haiku-4-5", gpt: "gpt-5.5", flash: "gemini-2.5-flash", grok: "grok-4.3" };
163
+ const ALIAS_DEFAULTS = { fast: "ollama/qwen2.5:0.5b", local: "ollama/qwen2.5:0.5b", sonnet: "claude-sonnet-4-6", opus: "claude-opus-4-6", haiku: "claude-haiku-4-5", gpt: "gpt-5.5", flash: "gemini-2.5-flash", grok: "grok-4.3" };
164
164
 
165
165
  /**
166
166
  * Build retry options from a config `retry` budget (gjc parity). `requestMaxRetries`
@@ -9,8 +9,8 @@ export interface ModelAliases {
9
9
  export const BUILTIN_ALIASES: ModelAliases = {
10
10
  fast: "ollama/qwen2.5:0.5b",
11
11
  local: "ollama/qwen2.5:0.5b",
12
- sonnet: "claude-sonnet-4-5",
13
- opus: "claude-opus-4-5",
12
+ sonnet: "claude-sonnet-4-6",
13
+ opus: "claude-opus-4-6",
14
14
  haiku: "claude-haiku-4-5",
15
15
  gpt: "gpt-5.5",
16
16
  flash: "gemini-2.5-flash",
package/src/ai/pricing.ts CHANGED
@@ -25,7 +25,13 @@ export interface TokenUsage {
25
25
  */
26
26
  const PRICE_TABLE: ReadonlyArray<readonly [pattern: string, price: ModelPrice]> = [
27
27
  // Anthropic Claude
28
+ // 4.6-generation specifics (Anthropic docs pricing) — MUST precede the generic
29
+ // `claude-opus`/`claude-sonnet` families so the newer, cheaper Opus tier wins.
30
+ ["claude-opus-4-8", { inPerM: 5, outPerM: 25 }],
31
+ ["claude-fable", { inPerM: 10, outPerM: 50 }],
32
+ ["claude-mythos", { inPerM: 10, outPerM: 50 }],
28
33
  ["claude-opus", { inPerM: 15, outPerM: 75 }],
34
+ ["claude-sonnet-5", { inPerM: 2, outPerM: 10 }],
29
35
  ["claude-sonnet", { inPerM: 3, outPerM: 15 }],
30
36
  ["claude-haiku", { inPerM: 0.8, outPerM: 4 }],
31
37
  ["opus", { inPerM: 15, outPerM: 75 }],
@@ -108,34 +108,44 @@ function anthropicThinkingBudget(effort: CallOptions["reasoningEffort"], maxToke
108
108
  /** Parse an Anthropic model id's family + version for thinking-transport selection.
109
109
  * Matches the modern `claude-<family>-<major>-<minor>[...]` naming (opus/sonnet/haiku 4.x+);
110
110
  * legacy ids (claude-3-5-sonnet) and non-Anthropic-compatible names return undefined. */
111
- function parseAnthropicVersion(model: string): { kind: "opus" | "sonnet" | "haiku"; major: number; minor: number } | undefined {
112
- const m = /claude-(opus|sonnet|haiku)-(\d+)-(\d+)/.exec(model);
111
+ function parseAnthropicVersion(model: string): { kind: "opus" | "sonnet" | "haiku" | "fable" | "mythos"; major: number; minor: number } | undefined {
112
+ // `<major>-<minor>` (opus/sonnet/haiku 4.x) OR a single `<major>` (Sonnet 5, Fable 5,
113
+ // Mythos 5 — the dateless 5th-gen ids), with new families fable/mythos. Legacy ids
114
+ // (claude-3-5-sonnet: number before family) and non-Anthropic names return undefined.
115
+ const m = /claude-(opus|sonnet|haiku|fable|mythos)-(\d+)(?:-(\d+))?/.exec(model);
113
116
  if (!m) return undefined;
114
- return { kind: m[1] as "opus" | "sonnet" | "haiku", major: Number(m[2]), minor: Number(m[3]) };
117
+ return { kind: m[1] as "opus" | "sonnet" | "haiku" | "fable" | "mythos", major: Number(m[2]), minor: m[3] !== undefined ? Number(m[3]) : 0 };
115
118
  }
116
119
 
117
120
  /** Adaptive thinking `display` is supported starting with Opus 4.7. Without it, Opus 4.7/4.8
118
121
  * OMIT thinking content entirely (tokens billed, signature present, but zero visible thought —
119
122
  * the "reasoning doesn't show" bug). Older adaptive models (Opus 4.6, Sonnet 4.6+) reject the
120
- * field, so it is gated to Opus ≥ 4.7. (gjc: supportsAdaptiveThinkingDisplay) */
123
+ * field, so it is gated to Opus ≥ 4.7 and every 5th-gen+ model. (gjc: supportsAdaptiveThinkingDisplay) */
121
124
  function supportsAdaptiveThinkingDisplay(model: string): boolean {
122
125
  const v = parseAnthropicVersion(model);
123
- if (!v || v.kind !== "opus") return false;
124
- return v.major > 4 || (v.major === 4 && v.minor >= 7);
126
+ if (!v) return false;
127
+ // display:"summarized" was introduced at Opus 4.7 and carried forward, so every
128
+ // 5th-generation+ model (Sonnet 5, Fable 5, Mythos 5, …) supports it too. Only the
129
+ // first adaptive gen (Opus 4.6, Sonnet 4.6) rejects the field.
130
+ if (v.major >= 5) return true;
131
+ return v.kind === "opus" && v.major === 4 && v.minor >= 7;
125
132
  }
126
133
 
127
134
  /** Thinking transport for a model (gjc parity — inferThinkingControlMode):
128
135
  * - Anthropic ≥ 4.6 → "adaptive" (model decides depth; effort rides output_config, NO budget)
129
- * - Anthropic 4.5 → "budget-effort" (budget_tokens + output_config effort)
130
- * - otherwise → "budget" (budget_tokens only).
136
+ * - Anthropic 4.5 sonnet/opus → "budget-effort" (budget_tokens + output_config effort)
137
+ * - otherwise (incl. Haiku 4.5) → "budget" (budget_tokens only).
131
138
  * The adaptive shift is the core opus-4.7/4.8 reasoning fix: those models reject the legacy
132
- * budget transport's visible-thought contract and require type:"adaptive" + display:summarized. */
139
+ * budget transport's visible-thought contract and require type:"adaptive" + display:summarized.
140
+ * Haiku 4.5 supports budget_tokens thinking but REJECTS output_config.effort ("This model does
141
+ * not support the effort parameter"), so it stays on the plain budget transport unlike its
142
+ * sonnet/opus 4.5 siblings. */
133
143
  type AnthropicThinkingMode = "adaptive" | "budget-effort" | "budget";
134
144
  function anthropicThinkingMode(model: string): AnthropicThinkingMode {
135
145
  const v = parseAnthropicVersion(model);
136
146
  if (!v) return "budget";
137
147
  if (v.major > 4 || (v.major === 4 && v.minor >= 6)) return "adaptive";
138
- if (v.major === 4 && v.minor === 5) return "budget-effort";
148
+ if (v.major === 4 && v.minor === 5 && v.kind !== "haiku") return "budget-effort";
139
149
  return "budget";
140
150
  }
141
151
 
@@ -73,7 +73,7 @@ export function geminiNativizable(m: Message, modelKey: string, thinkingEnabled:
73
73
  export function buildGeminiPayload(messages: Message[], options: CallOptions, stripArtifacts = false): { geminiModel: string; payload: Record<string, unknown> } {
74
74
  const resolvedModel = options.model.replace(/^(google|gemini)\//, "");
75
75
  let geminiModel = resolvedModel;
76
- if (!geminiModel || geminiModel === "claude-3-5-sonnet") geminiModel = "gemini-2.0-flash";
76
+ if (!geminiModel || geminiModel.startsWith("claude-")) geminiModel = "gemini-2.0-flash";
77
77
 
78
78
  const systemPrompt = options.systemPrompt ?? messages.find(m => m.role === "system")?.content;
79
79
  const thinkingBudget = geminiThinkingBudget(geminiModel, options.reasoningEffort, options.maxTokens);
@@ -29,6 +29,7 @@ import { checkForUpdate, readUpdateCache, writeUpdateCache } from "../util/updat
29
29
  import { jeoEnv } from "../util/env";
30
30
  import { renderUpdateBox } from "../tui/components/update-box";
31
31
  import { consumeLaunchWhatsNew } from "../util/whats-new";
32
+ import { maybeBell } from "../util/notify";
32
33
  import { supportsUnicode } from "../tui/components/capability";
33
34
  import pkg from "../../package.json";
34
35
  import chalk from "chalk";
@@ -4486,6 +4487,10 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
4486
4487
  } else if (usage) {
4487
4488
  console.log(usage.trim());
4488
4489
  }
4490
+ // gajae-code 0.7.8 parity: ping the terminal bell when a turn finishes so a
4491
+ // backgrounded session notifies the user. Off unless config.notify.bell (or
4492
+ // JEO_NOTIFY_BELL=1) is set; never fires for a cancelled turn.
4493
+ if (reply !== "Cancelled.") maybeBell("complete", cfg.notify, (s) => out.write(s), process.env as Record<string, string | undefined>);
4489
4494
  } catch (err) {
4490
4495
  console.log(`! ${friendlyProviderError(err)}`);
4491
4496
  }
@@ -27,7 +27,7 @@ function reportModelChoice(r: { warning?: string; suggestions: string[] }): void
27
27
  type ProviderChoice = "anthropic" | "openai" | "gemini" | "ollama" | "lmstudio" | "openai-compatible";
28
28
 
29
29
  const DEFAULT_MODELS: Record<ProviderChoice, string> = {
30
- anthropic: "claude-sonnet-4-5",
30
+ anthropic: "claude-sonnet-4-6",
31
31
  openai: "gpt-4o",
32
32
  gemini: "gemini-2.0-flash",
33
33
  ollama: "ollama/llama3.1:8b",
@@ -235,18 +235,38 @@ export async function runUpdateCommandWith(args: string[], deps: UpdateDeps): Pr
235
235
  deps.showWhatsNew?.();
236
236
  }
237
237
  } else {
238
- if (hasJson) {
239
- console.log(JSON.stringify({
240
- current,
241
- latest,
242
- upToDate: false,
243
- installed: false,
244
- error: "Installation failed"
245
- }));
238
+ // gajae-code 0.7.8 parity (#1280): a nonzero bun/npm exit is NOT always a
239
+ // real failure — Bun tarball-extraction errors can exit nonzero AFTER the
240
+ // requested version actually landed. Before reporting failure, verify the
241
+ // active `jeo` on PATH: if it now reports >= latest, treat it as recovered.
242
+ const recovered = deps.activeVersion?.();
243
+ if (recovered && latest && compareVersions(recovered, latest) >= 0) {
244
+ if (hasJson) {
245
+ console.log(JSON.stringify({
246
+ current,
247
+ latest,
248
+ upToDate: false,
249
+ installed: true,
250
+ recovered: true
251
+ }));
252
+ } else {
253
+ console.log(`Successfully installed jeo-code@${latest} (package manager reported an error, but the runtime verified clean).`);
254
+ deps.showWhatsNew?.();
255
+ }
246
256
  } else {
247
- console.error("Failed to install update.");
257
+ if (hasJson) {
258
+ console.log(JSON.stringify({
259
+ current,
260
+ latest,
261
+ upToDate: false,
262
+ installed: false,
263
+ error: "Installation failed"
264
+ }));
265
+ } else {
266
+ console.error("Failed to install update.");
267
+ }
268
+ process.exitCode = 1;
248
269
  }
249
- process.exitCode = 1;
250
270
  }
251
271
  } catch (installErr: any) {
252
272
  const installErrMsg = installErr.message || String(installErr);
@@ -9,3 +9,15 @@ when: When tasks are implemented and you need a final, high-level verification a
9
9
  Verifies the implementation against the acceptance criteria specified in the plan.
10
10
  Runs checks, tests, or validations to ensure correctness.
11
11
  Generates a final completion report outlining the changes and verification evidence.
12
+
13
+ ## Artifact gate (gajae-code 0.7.4 parity)
14
+
15
+ The completion report must be backed by concrete artifacts, not assertions:
16
+ - Cite the exact verification commands run and their observed result (e.g. test
17
+ counts, `typecheck` clean, build exit code) — never claim "tests pass" without
18
+ the command and its output.
19
+ - Reference each changed file by path, and tie it to the acceptance criterion it
20
+ satisfies.
21
+ - If an acceptance criterion could not be verified (missing credential, service,
22
+ or product decision), say so explicitly and mark it unresolved rather than
23
+ implying success. A criterion with no supporting artifact is NOT met.
@@ -181,6 +181,25 @@ const SAKURA: EvolutionTheme = {
181
181
  userCard: { accent: "#d6336c", border: "#862e59", shadow: "#43172c", fill: "#210b16" },
182
182
  };
183
183
 
184
+ const GRUVBOX_DARK: EvolutionTheme = {
185
+ name: "gruvbox-dark",
186
+ description: "Gruvbox dark — warm retro earth tones arcing from bark to cream (canonical Gruvbox palette).",
187
+ gradients: [
188
+ { from: "#282828", to: "#d65d0e" },
189
+ { from: "#d65d0e", to: "#fe8019" },
190
+ { from: "#fe8019", to: "#fabd2f" },
191
+ { from: "#fabd2f", to: "#b8bb26" },
192
+ { from: "#b8bb26", to: "#ebdbb2" },
193
+ ],
194
+ color: true,
195
+ accent: "#fabd2f",
196
+ accentShadow: "#79740e",
197
+ diff: { add: "#b8bb26", del: "#fb4934", addBg: "#1c2611", delBg: "#2a1416", hunk: "#8ec07c" },
198
+ userCard: { accent: "#fabd2f", border: "#79740e", shadow: "#3c3836", fill: "#282828" },
199
+ muted: "#a89984",
200
+ card: { fill: "#32302f" },
201
+ };
202
+
184
203
  const MONO: EvolutionTheme = {
185
204
  name: "mono",
186
205
  description: "Colorless — plain text for NO_COLOR / minimal terminals.",
@@ -189,7 +208,7 @@ const MONO: EvolutionTheme = {
189
208
  accent: "#ffffff",
190
209
  };
191
210
 
192
- export const THEMES: readonly EvolutionTheme[] = [COSMIC, MATRIX, SOLAR, RED_CLAW, BLUE_CRAB, AURORA, SYNTHWAVE, SAKURA, MONO];
211
+ export const THEMES: readonly EvolutionTheme[] = [COSMIC, MATRIX, SOLAR, RED_CLAW, BLUE_CRAB, AURORA, SYNTHWAVE, SAKURA, GRUVBOX_DARK, MONO];
193
212
 
194
213
  /** Look up a theme by name (case-insensitive); unknown names fall back to cosmic. */
195
214
  export function getTheme(name: string | undefined): EvolutionTheme {
@@ -217,7 +236,7 @@ export function resetThemeConfigCache(): void {
217
236
 
218
237
  function getExplicitThemeFromConfig(env: EnvLike): string | undefined {
219
238
  const home = os.homedir ? os.homedir() : undefined;
220
- const dir = env.JEO_CONFIG_DIR || env.JEO_CONFIG_DIR || (home ? path.join(home, ".jeo") : undefined);
239
+ const dir = env.JEO_CONFIG_DIR || (home ? path.join(home, ".jeo") : undefined);
221
240
  if (!dir) return undefined;
222
241
  if (configThemeCache && configThemeCache.dir === dir) return configThemeCache.value;
223
242
  const filePath = path.join(dir, "config.json");
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Terminal-bell notifications (gajae-code 0.7.8 parity).
3
+ *
4
+ * Emits an ASCII BEL (`\x07`) to the controlling terminal at notable
5
+ * interaction points so a backgrounded `jeo` session pings the user when it
6
+ * finishes a turn or stops to ask for input. Off by default; opt in through
7
+ * `config.notify.bell` (or force with `JEO_NOTIFY_BELL=1`).
8
+ *
9
+ * The decision logic (`shouldBell`) is pure and env-injectable so it is unit
10
+ * testable without a real terminal; `emitBell` is the only side effect.
11
+ */
12
+
13
+ export type NotifyEvent = "complete" | "ask" | "approval";
14
+
15
+ export interface NotifyConfig {
16
+ /** Master toggle — no bell fires unless this is true (or the env override is set). */
17
+ bell?: boolean;
18
+ /** Bell when an agent turn finishes (default: on when the master toggle is on). */
19
+ onComplete?: boolean;
20
+ /** Bell when an ask / approval prompt awaits the user (default: on when master on). */
21
+ onAsk?: boolean;
22
+ }
23
+
24
+ type EnvLike = Record<string, string | undefined>;
25
+
26
+ /**
27
+ * Decide whether a terminal bell should fire for `event`.
28
+ *
29
+ * Precedence: `JEO_NOTIFY_BELL` env force-overrides the config master toggle
30
+ * (`1`/`true` forces on, `0`/`false` forces off). When the master toggle is on,
31
+ * each event is enabled unless its per-event flag is explicitly `false`.
32
+ */
33
+ export function shouldBell(
34
+ event: NotifyEvent,
35
+ config: NotifyConfig | undefined,
36
+ env: EnvLike = {},
37
+ ): boolean {
38
+ const raw = env.JEO_NOTIFY_BELL;
39
+ const envForceOn = raw === "1" || raw === "true";
40
+ const envForceOff = raw === "0" || raw === "false";
41
+ const masterOn = envForceOn || (!envForceOff && config?.bell === true);
42
+ if (!masterOn) return false;
43
+ switch (event) {
44
+ case "complete":
45
+ return config?.onComplete !== false;
46
+ case "ask":
47
+ case "approval":
48
+ return config?.onAsk !== false;
49
+ default:
50
+ return false;
51
+ }
52
+ }
53
+
54
+ /** Write a single ASCII BEL to `write` (best-effort; a dead terminal never throws). */
55
+ export function emitBell(write: (s: string) => void): void {
56
+ try {
57
+ write("\x07");
58
+ } catch {
59
+ /* terminal gone — a courtesy bell must never crash a turn */
60
+ }
61
+ }
62
+
63
+ /** Convenience: emit a bell for `event` iff `shouldBell` allows it. */
64
+ export function maybeBell(
65
+ event: NotifyEvent,
66
+ config: NotifyConfig | undefined,
67
+ write: (s: string) => void,
68
+ env: EnvLike = {},
69
+ ): boolean {
70
+ if (!shouldBell(event, config, env)) return false;
71
+ emitBell(write);
72
+ return true;
73
+ }