Skip to main content

v0.7.0

A chat is no longer tied to the provider it started on. Pick a row on another endpoint in /model and the chat moves there — at once before the first reply, after you agree once a reply exists — and iota resume <id> -M now moves a session for good. To make that possible, a session records the providers: entry it runs on by name, and sessions written by earlier versions are no longer read until you convert them once (below). The Anthropic dialect asks for up to 32000 output tokens instead of 4096, a new max_tokens: key sets that ceiling, and an output the ceiling cut off is shown as an error instead of being passed off as the answer.

Upgrading: convert your saved sessions​

Sessions saved by v0.6.1 and earlier (session format v1) record a provider type and a URL; v0.7.0 records the provider's name and does not read v1. Opening an old session is an error that names the script that converts it: scripts/session-v1-to-v2.py in the iota repository. Run it once, with the config your sessions ran under:

python3 session-v1-to-v2.py # report what it would change; writes nothing
python3 session-v1-to-v2.py --write # apply it

It rewrites each bundle's meta.json and nothing else — the transcript and the attachments stay as they are — and skips a bundle already converted, so a second run changes nothing. It picks the provider by the endpoint the session recorded: the one entry of that type whose url: is that URL, or the type's built-in provider when the session ran on the default endpoint. When that does not settle it — no entry matches, two do, or an entry of another type has taken the built-in's name — it reports the bundle and leaves it alone; decide with --map TYPE=NAME (repeatable). --sessions points it at another sessions root than ~/.iota/sessions, --config passes -c to the iota it asks for the providers, and it exits 1 while any bundle is left unconverted.

Added​

  • A chat can move to another provider from /model. A row on another endpoint used to be refused with "start a new session there". Before the first reply, picking it now moves the chat. After a reply, iota first saves the chat, then tells you how many earlier replies carry provider-specific payload (reasoning, provider-side tool state) the target will not replay, that iota resume <id> continues on the new model afterwards, and that picking the old row moves it back — and asks Move this chat to <provider>:<model>? before anything changes. Your hand-set settings come along as they do on a -M resume. A chat that is not saved is told to /save first, a model of another kind (an image model for a chat) is still refused, and a bot never moves: its config does.
  • max_tokens: sets the output ceiling of an Anthropic-dialect model. It goes on a models: entry or an agents: entry, the agent's winning, as for effort, temperature and top_p, and must be 1 or more (config max_tokens 0: want 1 or more). A session records it, and iota resume restores what was recorded whatever the config says today; a bot takes it from its config on each start. Thinking counts toward it. There is no /model tab or flag for it, and on any other provider type the key does nothing and says so with a warning.

Changed​

  • Sessions record their provider by name, and resume there. A session now stores the providers: name it runs on (session format v2) instead of a provider type and a URL, and iota resume — and /session — opens that provider from the config, as if -M <provider>:<model> had been typed. A name the config no longer defines is an error that says how to resume it elsewhere (-M <provider>:<model>), never a quiet fall back to another endpoint; and a missing key on the agent's own endpoint no longer stops a session that runs on another. Older sessions need the one-time conversion above.
  • iota resume <id> -M moves the session for good. -M on a resume used to change only that run; the session now records the new provider and model, so the next plain iota resume continues there. What you set by hand — effort, temperature, top_p, the output ceiling — comes along unless the config there sets it; what the old endpoint's config supplied stays behind.
  • The Anthropic dialect asks for up to 32000 output tokens, not 4096. A thinking model could spend all of 4096 thinking and be cut off before it wrote a word. The ceiling is a bound, not a charge — tokens are billed as they are produced. An endpoint that caps lower answers a too-large value with an error; set a smaller max_tokens: for that model.
  • An output the ceiling cut off is an error, never the answer. On the Anthropic dialect and the Responses dialect, the chat shows a red Output truncated block: what cut it, the tool call it was composing if any (dropped by name — it does not run), and what to do — raise max_tokens: on Anthropic, lower the effort in /model or ask for a shorter reply on Responses. Text the model did write is kept, marked incomplete; with nothing to keep, the turn is rolled back. A cut is not retried. With -m it fails the run (exit 1, Error: output truncated — …, nothing saved); a cut-off compaction summary fails instead of replacing the history with a fragment, and a cut-off title leaves the placeholder.
  • /model rows read the same wherever the chat is. A row is provider:model, or just the model when every choice is on one provider, followed in brackets by the models: entries that name it (relay:vendor/fast [fast]). What picking it does is an annotation beside it, like (current): (moves the chat), or (needs a new session) in a bot.
  • Anthropic calls without tools stream too. A -m run with no tools, the compaction summary and the title used a non-streaming request, which sends nothing until the whole output exists and could outlast the 120-second wait for a response. They now stream like the chat, so an Anthropic-compatible endpoint that cannot stream fails there instead of answering.

Fixed​

  • An Anthropic stream that stops early is an error, not an answer. A response that ended before the message did — no message_stop, no stop reason — was taken as complete, so -m and a compaction could succeed on a fragment.
  • A failing request without tools is sent 11 times, not 121. A turn that offered the model no tools retried inside a retry, so one failing request (a 5xx, a dropped connection) could go out up to 121 times with minutes of backoff. Every model call now has one set of ten retries.
  • Typing a choice's name in /model picks that choice. Typing fast, the name of a models: entry, sent fast to the provider as a model id; the field now reads typed text as -M does, and provider:* picks nothing.