llm.rb 13.1.0 → 14.0.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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +320 -0
  3. data/README.md +340 -31
  4. data/bin/llm.rb +36 -12
  5. data/data/anthropic.json +206 -263
  6. data/data/bedrock.json +2138 -1860
  7. data/data/deepinfra.json +1003 -624
  8. data/data/deepseek.json +38 -34
  9. data/data/google.json +1079 -371
  10. data/data/mistral.json +448 -368
  11. data/data/moonshot.json +384 -0
  12. data/data/openai.json +974 -1343
  13. data/data/xai.json +154 -126
  14. data/data/zai.json +191 -191
  15. data/lib/llm/agent.rb +47 -14
  16. data/lib/llm/context.rb +71 -88
  17. data/lib/llm/cost.rb +23 -17
  18. data/lib/llm/error.rb +0 -8
  19. data/lib/llm/function/async/task.rb +2 -0
  20. data/lib/llm/function/fiber/task.rb +2 -0
  21. data/lib/llm/function/fork/task.rb +2 -0
  22. data/lib/llm/function/ractor/task.rb +2 -0
  23. data/lib/llm/function/sequential/group.rb +4 -1
  24. data/lib/llm/function/sequential/task.rb +1 -1
  25. data/lib/llm/function/task.rb +4 -0
  26. data/lib/llm/function/thread/task.rb +2 -0
  27. data/lib/llm/function.rb +32 -4
  28. data/lib/llm/guard/loop.rb +89 -0
  29. data/lib/llm/guard/null.rb +19 -0
  30. data/lib/llm/guard.rb +61 -0
  31. data/lib/llm/provider.rb +36 -0
  32. data/lib/llm/providers/anthropic/stream_parser.rb +1 -0
  33. data/lib/llm/providers/anthropic.rb +1 -8
  34. data/lib/llm/providers/bedrock/stream_parser.rb +1 -0
  35. data/lib/llm/providers/bedrock.rb +1 -8
  36. data/lib/llm/providers/google/stream_parser.rb +1 -0
  37. data/lib/llm/providers/google.rb +1 -8
  38. data/lib/llm/providers/moonshot.rb +76 -0
  39. data/lib/llm/providers/ollama.rb +1 -8
  40. data/lib/llm/providers/openai/responses/stream_parser.rb +1 -0
  41. data/lib/llm/providers/openai/responses.rb +6 -8
  42. data/lib/llm/providers/openai/stream_parser.rb +1 -0
  43. data/lib/llm/providers/openai.rb +3 -10
  44. data/lib/llm/repl/bar.rb +4 -3
  45. data/lib/llm/repl/buffer.rb +42 -15
  46. data/lib/llm/repl/color.rb +78 -0
  47. data/lib/llm/repl/input/char.rb +46 -0
  48. data/lib/llm/repl/input/row.rb +39 -0
  49. data/lib/llm/repl/input.rb +251 -66
  50. data/lib/llm/repl/markdown/table.rb +6 -2
  51. data/lib/llm/repl/markdown.rb +31 -5
  52. data/lib/llm/repl/status.rb +38 -3
  53. data/lib/llm/repl/stream.rb +16 -4
  54. data/lib/llm/repl/walker.rb +3 -2
  55. data/lib/llm/repl/window.rb +25 -5
  56. data/lib/llm/repl.rb +29 -13
  57. data/lib/llm/stream.rb +8 -7
  58. data/lib/llm/tool.rb +29 -0
  59. data/lib/llm/transformer/null.rb +21 -0
  60. data/lib/llm/transformer.rb +55 -0
  61. data/lib/llm/version.rb +1 -1
  62. data/lib/llm.rb +12 -2
  63. data/llm.gemspec +1 -0
  64. data/resources/deepdive/advanced/cancellation.md +74 -0
  65. data/resources/deepdive/advanced/compaction.md +83 -0
  66. data/resources/deepdive/advanced/context.md +267 -0
  67. data/resources/deepdive/advanced/guard.md +371 -0
  68. data/resources/deepdive/advanced/tracer.md +180 -0
  69. data/resources/deepdive/advanced/transformer.md +67 -0
  70. data/resources/deepdive/advanced/transports.md +45 -0
  71. data/resources/deepdive/everything_else/audio.md +122 -0
  72. data/resources/deepdive/everything_else/cost.md +99 -0
  73. data/resources/deepdive/everything_else/images.md +89 -0
  74. data/resources/deepdive/everything_else/object.md +108 -0
  75. data/resources/deepdive/everything_else/ocr.md +48 -0
  76. data/resources/deepdive/fundamentals/agents.md +202 -0
  77. data/resources/deepdive/fundamentals/builtin_tools.md +191 -0
  78. data/resources/deepdive/fundamentals/concurrency.md +104 -0
  79. data/resources/deepdive/fundamentals/database.md +449 -0
  80. data/resources/deepdive/fundamentals/embeddings.md +157 -0
  81. data/resources/deepdive/fundamentals/repl.md +87 -0
  82. data/resources/deepdive/fundamentals/schema.md +61 -0
  83. data/resources/deepdive/fundamentals/skills.md +106 -0
  84. data/resources/deepdive/fundamentals/stream.md +110 -0
  85. data/resources/deepdive/fundamentals/tools.md +265 -0
  86. data/resources/deepdive/protocols/a2a.md +106 -0
  87. data/resources/deepdive/protocols/mcp.md +111 -0
  88. data/resources/deepdive.md +7 -1
  89. metadata +36 -3
  90. data/lib/llm/loop_guard.rb +0 -107
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb5c6136080e0136309987a4e042e6f646c26de39e38d6422bb3904fc665bf5f
4
- data.tar.gz: d832b9c19f4173ba6e5631ffdc57f495eb4a9a8fb0b657801de185707bc4085e
3
+ metadata.gz: 80d9ecf944df7561e9a0ac70bb37225dac11f18b13fba94d689b672da3b27e6f
4
+ data.tar.gz: d3986e29989bcd415c40cd26fd4361ac8b3f92461cf81f2f5abf5a9d291cd068
5
5
  SHA512:
6
- metadata.gz: 7ba4417a8c5b48a28b2baf04a2960374eae28c44dc2ec19e8f79da9a95f75fdf8cb5f1c749e6bfa1b1f6d1c790723e2005cb576f94f118997bb5e8b0f5d53a63
7
- data.tar.gz: 0105af6bcaaec50930546e3a5a7207b8cf65e30bbea33359784061159748a3e2f3afb6e2b599eaad904e0c477d9827bb444c698315e56d43572be6ba86fe98b8
6
+ metadata.gz: 702cd38c23fa817b068dd9b7c8942f3641915c874e1ed9bef6526127767092f138fd09c320cd54fafc265e2b33131d0cca8b7376f2c7b5e9e3203e66b1f4a513
7
+ data.tar.gz: f5eebb31abe9349e451bb1410b01a35353dd466fc66b6df230c5cb6be469856be234dea3351f885eb9c616375c0b84bf18e72dc268b88454997919f475a89774
data/CHANGELOG.md CHANGED
@@ -17,6 +17,326 @@
17
17
 
18
18
  *No unreleased changes yet. Check back after the next release.*
19
19
 
20
+ ## v14.0.0
21
+
22
+ Changes since `v13.1.0`.
23
+
24
+ This release replaces the `transformer=` setter with the new
25
+ `LLM::Transformer` class hierarchy, refactors guards into the
26
+ `LLM::Guard` superclass with per-tool-call interception, and replaces
27
+ the agent `tool_attempts` parameter with the `tool_budget` class DSL.
28
+ It also adds the Moonshot (Kimi) provider, the `LLM::Tool.set`
29
+ bulk-assignment DSL, a `LLM::Function#return` shorthand, and a wide
30
+ range of REPL improvements.
31
+
32
+ ### Breaking
33
+
34
+ #### Migration
35
+
36
+ | Old | New |
37
+ |-----|-----|
38
+ | `ctx.transformer = MyTransformer` | `LLM::Context.new(transformer: MyTransformer)` |
39
+ | `transformer.call(ctx, prompt, params)` | `transformer.call(message:, **opts)` |
40
+ | `~/.llm.rb/session.json` (shared across providers) | `~/.llm.rb/<provider>/<uuid>.json` (scoped per provider and directory) |
41
+ | `agent.talk(tool_attempts: 25)` | `set :tool_budget => 50` (disabled by default) |
42
+ | `LLM::LoopGuard` | `LLM::Guard::Loop` |
43
+ | `guard: true` / `ctx.guard = MyGuard` | `guard: MyGuard, guard_options: {}` |
44
+ | `guard.call(ctx)` (warning string) | `guard.call(function:)` (`LLM::Function::Return` or nil) |
45
+ | `LLM::GuardError` | `"guard_error"` |
46
+
47
+ * **replace the transformer setter with `LLM::Transformer`** <br>
48
+ The previous `transformer=` setter and 3-argument
49
+ `call(ctx, prompt, params)` interface on `LLM::Context` have been
50
+ replaced by the new
51
+ [`LLM::Transformer`](https://r.uby.dev/api-docs/llm.rb/LLM/Transformer.html)
52
+ class interface. Configure a transformer class through `transformer:`
53
+ and options through `transformer_options:` instead.
54
+
55
+ * **cli: scope session persistence per provider and directory** <br>
56
+ `bin/llm.rb` no longer shares a single session file between providers.
57
+ Each provider now has a `~/.llm.rb/<provider>.json` file that maps the
58
+ current working directory to a UUID-scoped session file under
59
+ `~/.llm.rb/<provider>/<uuid>.json`, so sessions are scoped to both the
60
+ provider and the directory they were started in.
61
+
62
+ * **cli: harden the executable against bad inputs** <br>
63
+ `bin/llm.rb` now prints an error message followed by the help menu and
64
+ exits with status 1 when the `-p` switch is given without an argument or
65
+ when an unknown option is passed. Previously unknown options produced a
66
+ warning but the run continued. The session-file lookup also no longer
67
+ rewrites `~/.llm.rb/<provider>.json` when it already exists.
68
+
69
+ * **agent: replace `tool_attempts` with the `tool_budget` class DSL** <br>
70
+ [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#tool_budget-class_method)
71
+ replaces the `tool_attempts` parameter with a `tool_budget` class DSL
72
+ (`tool_budget { 50 }`) that caps the number of tool calls allowed in a
73
+ single turn. Once the budget is spent, the agent sends an in-band
74
+ advisory message back through the model telling it to solve the problem
75
+ with fewer tool calls.
76
+ <br><br>
77
+ The feature is now disabled by default; the old `tool_attempts`
78
+ parameter defaulted to 25, which long-horizon agents could easily
79
+ exhaust in a single turn.
80
+
81
+ * **guard: replace `LLM::LoopGuard` with the `LLM::Guard` class hierarchy** <br>
82
+ [`LLM::Guard`](https://r.uby.dev/api-docs/llm.rb/LLM/Guard.html)
83
+ is a new superclass for context-level supervisors, with
84
+ [`LLM::Guard::Loop`](https://r.uby.dev/api-docs/llm.rb/LLM/Guard/Loop.html)
85
+ (replacing `LLM::LoopGuard`) and
86
+ [`LLM::Guard::Null`](https://r.uby.dev/api-docs/llm.rb/LLM/Guard/Null.html)
87
+ as the built-in implementations.
88
+ [`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html)
89
+ now accepts `guard:` (a guard class defaulting to `LLM::Guard::Null`)
90
+ and `guard_options:` (a hash forwarded to the guard's `call` method),
91
+ matching the transformer and compactor interfaces. The old boolean and
92
+ hash forms of `guard` and the `guard=` setter are removed. `LLM::Agent`
93
+ enables `LLM::Guard::Loop` by default.
94
+
95
+ * **guard: block individual tool calls instead of the whole batch** <br>
96
+ [`LLM::Guard#call`](https://r.uby.dev/api-docs/llm.rb/LLM/Guard.html#call-instance_method)
97
+ now receives the pending `function:` and returns an
98
+ [`LLM::Function::Return`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Return.html)
99
+ (or nil) instead of a warning string for the entire batch, so a guard
100
+ can block a single tool call while the rest of the batch still
101
+ executes. Custom guards that implemented the old `call(ctx)`
102
+ warning-string interface must be updated to return a
103
+ `LLM::Function::Return` instead.
104
+
105
+ * **errors: drop `LLM::GuardError`** <br>
106
+ Remove `LLM::GuardError`. The constant was never raised as an
107
+ exception; it only named the in-band error type for guarded tool
108
+ returns. Guarded tool returns now use the string `"guard_error"` as
109
+ their error type.
110
+
111
+ ### Core
112
+
113
+ * **add `LLM::Provider#build_messages` for assembling outgoing messages** <br>
114
+ [`LLM::Provider#build_messages`](https://r.uby.dev/api-docs/llm.rb/LLM/Provider.html#build_messages-instance_method)
115
+ normalizes a prompt into `LLM::Message` objects and prepends the existing
116
+ history, replacing the per-provider `build_complete_messages`
117
+ implementation. The method is idempotent: prompts that are already
118
+ [`LLM::Message`](https://r.uby.dev/api-docs/llm.rb/LLM/Message.html)
119
+ instances or arrays of messages are returned as-is.
120
+
121
+ * **copy the `params` hash in `LLM::Context` and `LLM::Agent`** <br>
122
+ [`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html) and
123
+ [`LLM::Agent`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html) now copy
124
+ the `params` hash in their constructors before mutating it, leaving the
125
+ caller's hash untouched. Previously the constructors deleted keys from
126
+ the caller's hash in place.
127
+
128
+ * **gemspec: ship the deepdive sub-files in the gem** <br>
129
+ The gemspec now includes `resources/deepdive/*/*.md` in the gem
130
+ package, so the full deepdive guide (fundamentals, advanced,
131
+ protocols, and everything-else chapters) is available after
132
+ installation.
133
+
134
+ * **add short aliases to `LLM::Cost`** <br>
135
+ [`LLM::Cost`](https://r.uby.dev/api-docs/llm.rb/LLM/Cost.html) now
136
+ offers short aliases for its cost accessors: `input`, `output`,
137
+ `input_audio`, `output_audio`, `input_image`, `cache_read`,
138
+ `cache_write`, and `reasoning`. Each alias matches the key used by
139
+ `#to_h`, so `cost.input` reads the same value as `cost.input_costs`.
140
+
141
+ ### Provider
142
+
143
+ * **add `LLM::Moonshot` for the Moonshot AI provider** <br>
144
+ [`LLM::Moonshot`](https://r.uby.dev/api-docs/llm.rb/LLM/Moonshot.html)
145
+ is a new provider that talks to
146
+ [Moonshot AI](https://platform.moonshot.ai) through its
147
+ OpenAI-compatible Kimi API. Create an instance with
148
+ [`LLM.moonshot`](https://r.uby.dev/api-docs/llm.rb/LLM.html#moonshot-class_method),
149
+ which accepts the same `key:`, `host:`, and `base_path:` options as the
150
+ OpenAI provider. The provider defaults to the `kimi-k3` model and
151
+ supports chat completions, streaming, tool calls, and structured output
152
+ through the shared OpenAI-compatible path; image, audio, moderation,
153
+ responses, and vector store endpoints raise `NotImplementedError`.
154
+ Model metadata ships in `data/moonshot.json` for the registry.
155
+
156
+ ### Transformer
157
+
158
+ * **add `LLM::Transformer` for rewriting messages before they reach the provider** <br>
159
+ [`LLM::Transformer`](https://r.uby.dev/api-docs/llm.rb/LLM/Transformer.html)
160
+ is a new superclass for message transformers. A transformer is bound to a
161
+ context and rewrites a single message before it is sent to the provider,
162
+ which makes it possible to redact personal information or rewrite any
163
+ message before it goes out over the wire. Each subclass implements
164
+ `call(message:, **opts)` and returns the message to send, either by
165
+ mutating it in place or returning a new one.
166
+ [`LLM::Transformer::Null`](https://r.uby.dev/api-docs/llm.rb/LLM/Transformer/Null.html)
167
+ is a no-op transformer used as the default.
168
+
169
+ * **hook the transformer API into `LLM::Context`** <br>
170
+ [`LLM::Context`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html) now
171
+ accepts `transformer:` (a transformer class defaulting to
172
+ `LLM::Transformer::Null`) and `transformer_options:` (a hash forwarded to
173
+ the transformer's `call` method). The transformer runs on the most recent
174
+ message in both chat and responses turns.
175
+ [`LLM::Stream#on_transform`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html#on_transform-instance_method)
176
+ and
177
+ [`LLM::Stream#on_transform_finish`](https://r.uby.dev/api-docs/llm.rb/LLM/Stream.html#on_transform_finish-instance_method)
178
+ now receive the transformer instance as their single argument.
179
+
180
+ ### Tool
181
+
182
+ * **add `LLM::Tool.set` for bulk-assigning tool properties** <br>
183
+ [`LLM::Tool.set`](https://r.uby.dev/api-docs/llm.rb/LLM/Tool.html#set-class_method)
184
+ accepts a hash of `name`, `description`, `parameters`, `required`, and
185
+ `defaults` to configure a tool subclass in a single call. Parameters are
186
+ defined as tuples of `[name, type, description, options]`, matching the
187
+ same interface as the existing `parameter` DSL. Unknown keys raise
188
+ `KeyError`.
189
+
190
+ ### Function
191
+
192
+ * **add `LLM::Function#return` for building tool returns** <br>
193
+ [`LLM::Function#return`](https://r.uby.dev/api-docs/llm.rb/LLM/Function.html#return-instance_method)
194
+ returns an
195
+ [`LLM::Function::Return`](https://r.uby.dev/api-docs/llm.rb/LLM/Function/Return.html)
196
+ built from the function's own id and name, using the given hash as its
197
+ value. It is a shorthand mainly useful inside a
198
+ [`LLM::Guard`](https://r.uby.dev/api-docs/llm.rb/LLM/Guard.html)
199
+ subclass and is defined via `define_method` because `return` is a Ruby
200
+ keyword.
201
+
202
+ ### Guard
203
+
204
+ * **run the guard for streamed tool calls** <br>
205
+ Fix a gap where the guard was not consulted when a tool call was queued
206
+ while a response was still streaming. The guard is now stamped onto the
207
+ functions a context binds, so it runs wherever a task is spawned,
208
+ including tool calls queued from a stream. A blocked call yields its
209
+ `guard_error` return without executing.
210
+
211
+ ### Agent
212
+
213
+ * **add `LLM::Agent#compacted?`** <br>
214
+ [`LLM::Agent#compacted?`](https://r.uby.dev/api-docs/llm.rb/LLM/Agent.html#compacted%3F-instance_method)
215
+ delegates to the wrapped
216
+ [`LLM::Context#compacted?`](https://r.uby.dev/api-docs/llm.rb/LLM/Context.html#compacted%3F-instance_method)
217
+ and reports whether the conversation has been compacted, so callers
218
+ can detect when history was trimmed.
219
+
220
+ ### Change
221
+
222
+ * **openai: default to `gpt-5.6-luna`** <br>
223
+ The default OpenAI chat model has changed from `gpt-5.4-mini` to
224
+ `gpt-5.6-luna`. The new model is OpenAI's fastest and most affordable
225
+ option, matching the kind of default llm.rb aims for.
226
+
227
+ ### Repl
228
+
229
+ * **show an unknown context state after `/compact`** <br>
230
+ After running `/compact`, the REPL status line now renders `Context
231
+ compacted` and the context-usage bar shows `???` instead of a percentage,
232
+ because the used context is unknown until the next response.
233
+
234
+ * **land on a blank line after Ctrl+N at the end of history** <br>
235
+ When recalling history with Ctrl+P and Ctrl+N, Ctrl+N at the last item
236
+ now advances to a blank line so you can start typing new input, instead
237
+ of staying stuck on the last item in history (the previous behavior).
238
+ Recalling with Ctrl+P or Ctrl+N also no longer overwrites the input when
239
+ there is no history to show.
240
+
241
+ * **restore history wrap for Ctrl+P and Ctrl+N** <br>
242
+ Fix a regression where Ctrl+P and Ctrl+N recalled history text without
243
+ reflowing it into rows, so recalled lines wider than the terminal were
244
+ clipped. Recalled text now flows through the same word-wrap path as
245
+ typed input and wraps at the terminal width.
246
+
247
+ * **restore Ctrl+D deletion across rows** <br>
248
+ Fix a bug where Ctrl+D at the end of an input row was a no-op, so
249
+ multiline input could not be joined by deleting a row break. Deleting
250
+ at the end of a row now consumes the break and pulls the next row up,
251
+ restoring the split space so merged words do not run together.
252
+
253
+ * **center the buffer with 20% gutters** <br>
254
+ The curses-based REPL now centers
255
+ [`LLM::Repl::Buffer`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl/Buffer.html)
256
+ in a content area that is 60% of the terminal width, with an unused 20%
257
+ gutter on each side. The drawing area is based on the available rows and
258
+ columns instead of a fixed 80-column width, and `Buffer#wrap` now
259
+ hard-breaks words that overflow the width onto the next row, fixing a
260
+ bug where a word could be cut off between rows.
261
+
262
+ * **apply markdown to previous messages** <br>
263
+ The curses-based REPL now renders every message in the buffer with
264
+ markdown styling, including messages that were already present when
265
+ the session started or restored from disk. Previously only newly
266
+ streamed responses were styled; older messages fell back to plain
267
+ text.
268
+
269
+ * **add `LLM::Repl#sender` for the user label** <br>
270
+ [`LLM::Repl#sender`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl.html#sender-instance_method)
271
+ returns the label used for user messages in the curses-based REPL. It
272
+ defaults to `"You"` (previously `"user"`), and the buffer layout now
273
+ places each label on its own line followed by the message content and a
274
+ blank line.
275
+
276
+ * **add `LLM::Repl::Color` for coloring the curses UI** <br>
277
+ Add
278
+ [`LLM::Repl::Color`](https://r.uby.dev/api-docs/llm.rb/LLM/Repl/Color.html)
279
+ as a new module that returns Curses color bitmasks. `Color.enable`
280
+ initializes 8 color pairs, and methods like `Color.blue` return the
281
+ corresponding `Curses.color_pair(X)` bitmask, which can be bitwise
282
+ OR'ed with other attributes such as `Curses::A_BOLD`. User labels in
283
+ the REPL are now rendered in blue instead of plain bold text.
284
+
285
+ * **split on words rather than characters** <br>
286
+ `LLM::Repl::Buffer#wrap` now breaks text on word boundaries instead of
287
+ wrapping one character at a time. A word that does not fit on the
288
+ current row moves to the next, and only a single word longer than the
289
+ whole width is hard-broken, so text is never clipped by the window.
290
+
291
+ * **render kramdown typographic symbols and smart quotes** <br>
292
+ Fix a bug where certain character sequences such as `...` were not
293
+ rendered at all in the curses-based REPL. Kramdown parses them into
294
+ `:typographic_sym` and `:smart_quote` nodes, which previously fell
295
+ through to the children clause and were dropped. The markdown renderer
296
+ now maps them to their unicode equivalents: ellipsis, en and em
297
+ dashes, guillemets, and single and double quotation marks.
298
+
299
+ * **apply colors to the markdown renderer** <br>
300
+ The curses-based REPL now renders markdown with the `LLM::Repl::Color`
301
+ palette: headers and strong text in white, code spans and code blocks
302
+ in green, and links in underlined green, on the black background.
303
+ Previously markdown styling used bold, underline, and reverse video
304
+ attributes only.
305
+
306
+ * **wrap the input line at word boundaries** <br>
307
+ The curses-based REPL input line now wraps words whole onto the next
308
+ row at the terminal width instead of cutting them in half. A word that
309
+ does not fit on the current row moves to the next row, and only a
310
+ single word longer than the whole width is hard-broken, so typed text
311
+ is never clipped by the window.
312
+
313
+ * **distinguish the connecting and thinking status bar phases** <br>
314
+ The curses-based REPL status bar now shows `Connecting • Esc to
315
+ cancel` while the model is establishing a connection, then switches
316
+ to `Thinking • Esc to cancel` once a tool call or text fragment
317
+ arrives on the stream. Active tool calls appear in the status bar
318
+ with a lambda indicator.
319
+
320
+ * **add emoji to the status bar phases** <br>
321
+ The curses-based REPL status bar now uses emoji to identify each
322
+ phase at a glance: a globe (`🌐`) while the model is connecting, and
323
+ a brain (`🧠`) while it is thinking. The text after the emoji still
324
+ reads `Connecting • Esc to cancel` and `Thinking • Esc to cancel`
325
+ respectively.
326
+
327
+ * **render the status bar with color and attributes** <br>
328
+ The curses-based REPL status bar now supports colored and attributed
329
+ status text, so the lambda indicator for active tool calls is drawn
330
+ in bold red.
331
+
332
+ ### Registry
333
+
334
+ * **refresh model metadata across providers** <br>
335
+ Update `data/*.json` files with current provider model listings and
336
+ pricing. Remove the deprecated Claude Opus 4.1 entries from the
337
+ Anthropic registry, add `Qwen/Qwen3.8-Max` to DeepInfra, and add a
338
+ `low` reasoning-effort option to DeepSeek.
339
+
20
340
  ## v13.1.0
21
341
 
22
342
  Changes since `v13.0.0`.