Find a file
Ivan Kolos 8e21cd629f
Make the errors surface in the activity report (#569)
## Summary

The generation activity report could not be relied on when something
went wrong:

- steps were swallowing the exceptions they produced, with no chance for
a user to see them
- a step whose call failed usually closed as finished, because the
services absorb their failures into a fallback;
- several requests behind one step — retries, the narrator's
empty-answer loop — showed as a single row;
- a step could stay *in progress* indefinitely while a retry loop ran
out of sight, and the narrator's own errors were swallowed and re-sent
as empty answers.

This makes the report readable in full and makes it say why each step
failed, request by request, without changing what a turn does.

## Changes

**Display**

- The timeline is uncapped, read by scrolling the story like the
reasoning block. Times are rounded to the nearest second, sit in a fixed
column, and are left blank for a step recorded after the fact without a
duration (an agent tool call, a cached selection) instead of reading a
default `0s`.
- The collapsed line wraps instead of being cut, and on a narrow card
shares its row with the story time.
- The latest turn's timeline opens and the previous one folds; a
reader's own choice sticks. Records are no longer hidden when a turn
ends, and the session keeps the last 20.
- Sending scrolls to the end of the story, and the story view no longer
jumps when the streaming card is replaced by the entry.
- The sparkle marks the model calls themselves, not the steps that
contain them.

**Failure reporting**

- A failed step carries its reason, worded once by
`describeActivityError`: status and provider message for an API error,
`Failed after N attempts, with no retries left: <last reason>` for
exhausted retries, a lost connection named as such rather than the HTTP
plugin's `error decoding response body`.
- Services that absorb a failure into a fallback — translation,
suggestions, action choices, timeline fill, Tier 3 selection, scene
analysis, the background image, the classifier's `_error` — still close
the step they serve as failed (`failStep`) and return the same fallback
as before.
- A failure is told once, on the deepest step that carries it; every
step above it is marked with a red ⚠. When the only failures beneath a
step are attempts its request got past, the ⚠ is in the text colour.
- A turn ends with an outcome (`turnOutcome`); only a halted turn says
*Failed* on the collapsed line. A halted turn's record moves to its
error entry, which is marked by `metadata.source` rather than recognised
by its text, and every other system entry gets edit, delete and
regenerate. The "Generation failed" banner above the input is removed:
it duplicated the error card.
- The error card for a turn without narration reads *The narration could
not be generated. Please try again.* — it no longer claims a number of
attempts.

**Requests and retries**

- `activityMiddleware` sits inside `retryOn429Middleware`: a request
that needs a second attempt gets a row per attempt (the first
backfilled) and a row per wait the retry middleware schedules. The SDK's
own retries show as attempts.
- The narrator's empty-answer loop reports *passes*, kept apart from the
transport attempts inside them. A single pass has no container; it is
grouped into `Pass 1` only once a second follows. An empty pass is a
failure.
- Within a pass, `Waiting for model` gives way to `Generating` as soon
as the response starts, so a slow first token and an empty answer are
both visible.

**Narrator errors**

- `streamText` reports a failed request as an `error` part rather than
by throwing. The narrator dropped it, so every narrator failure read as
an empty answer and was re-sent twice. The part is now rethrown, with a
provider's in-stream error object turned into an `Error` with its code
and message.
- A stream that fails after text has arrived keeps that text as the
narration and reports the failure without halting: the request was paid
for, and the reader can still regenerate. One that fails before any text
is passed again, like an empty answer; a request refused outright is
not.

**Phases**

- `Updating world` and `Saving translation` are steps of their own, so a
failed write shows where it happened. `Updating world` reads *partly
applied* after a salvaged classification and *fallback bookkeeping only*
after a failed one.
- Agent Mode's hand-off is a `Handed off · N images queued` step under
*Images*: the images finish after the turn, and the record does not
follow them.
- A phase switched off in settings is not reported; one switched on but
missing its profile is struck through with that reason.

**Stop**

- Once the narration is saved, Stop dims, and a tap explains with a
toast that the turn is finishing steps that cannot be interrupted.
Nothing after the narration observes the abort, so Stop used to free the
input while the turn still held the generation lease: Send was offered
and refused, the story could not be left, and the report went on
counting. "Generate a different response", which refuses Stop
throughout, now explains its disabled Stop the same way.

**Tooling**

- `scripts/fake-provider.mjs` (`npm run fake-provider`) is an
OpenAI-compatible chat and image API whose behaviour is chosen by the
requested model name: rate limits, 4xx and 5xx, hangs, empty, malformed
and partly valid answers, cut streams. Setup and the model list are in
`docs/development/testing.md`.

`docs/architecture/overview.md` describes the reporting model, the
narrator's passes and the Stop refusal.

## Testing

- New unit tests for `describeActivityError`, `turnOutcome`, the
recorder (including `groupChildren` and untimed steps), the tree
readings (`failuresShownBelow`, `failureMarks`, `stepsAboveLLMSteps`),
`trackPhase`, the retry and activity middleware, the narrator's chunk
translation, each phase's reporting, and `storyEntries`.
- `reportingDoesNotAlterTurn.test.ts` and `absorbedFailures.test.ts`
check that reporting changes nothing a turn does and that every absorbed
failure still returns its fallback.
- Every call a turn makes was run against the fake provider in the
desktop app, each in the failures it can meet; the display was also
checked on Android.
- No test renders a component; the timeline, the collapsed line and the
Stop button were checked in the running app.

## Known gaps

- The retrieval agent builds its model elsewhere and its retries are not
reported.
- Stop still cannot cancel work after the narration is saved; this only
stops it from claiming to.
- On Android a tap on Stop while the narration streams can fail to
register; that is separate from this change.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Generation activity reports now show clearer step summaries, elapsed
times, failures, recoveries, and retry attempts. More activity history
is retained, and reports are visible by default.
* Generation errors include clearer explanations. Narration already
produced is preserved if a later step fails, and failed generations can
be retried from the story.
* The story view scrolls to the latest entry when requested during
generation.
* Added a fake AI provider for testing successful responses and failure
scenarios.
* Story creation no longer waits for background-image generation to
finish.

* **Bug Fixes**
* Stop controls now reflect when generation can no longer be
interrupted.
* Generation and translation failures are more consistently shown in
activity reports.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Tony Frazier <16245649+a-frazier@users.noreply.github.com>
2026-10-06 10:28:37 -04:00
.cargo Add Incognito Keyboard option for Android (#467) 2026-08-26 20:45:30 -04:00
.github Persist the generated Kotlin in Android's Rust cache (#565) 2026-10-01 20:12:48 -04:00
docs Make the errors surface in the activity report (#569) 2026-10-06 10:28:37 -04:00
scripts Make the errors surface in the activity report (#569) 2026-10-06 10:28:37 -04:00
src Make the errors surface in the activity report (#569) 2026-10-06 10:28:37 -04:00
src-tauri Route dev/build/release scripts through aube when it installed node_modules (#552) 2026-09-28 14:34:27 -04:00
static switched font for logo 2026-01-19 14:48:22 -06:00
third-party-licenses Added harper 2026-01-02 05:08:39 -07:00
.gitattributes Fix CRLF Migrations (#162) 2026-02-22 15:46:59 -05:00
.gitignore Overhaul CI/CD: Node 24, shared release workflows, faster cached builds (#518) 2026-09-16 22:38:32 -04:00
.nvmrc Overhaul CI/CD: Node 24, shared release workflows, faster cached builds (#518) 2026-09-16 22:38:32 -04:00
.prettierrc fix: prettier, eslint, pipeline (#71) 2026-02-06 00:49:36 +01:00
CLAUDE.md Clean the Style Reviewer's input of narration markup (#560) 2026-10-01 15:01:03 -04:00
compileApk.sh Android build hygiene: minSdk 26, lint in CI, resource cleanup (#468) 2026-08-29 21:52:01 -04:00
components.json fix: prettier, eslint, pipeline (#71) 2026-02-06 00:49:36 +01:00
eslint.config.js Validated release script, extracted version arithmetic, lint config fixes (#416) 2026-08-08 19:55:23 +02:00
lefthook.yml Route dev/build/release scripts through aube when it installed node_modules (#552) 2026-09-28 14:34:27 -04:00
LICENSE.md Db rework (#140) (#141) 2026-02-13 17:37:27 +05:30
logo.png switched font for logo 2026-01-19 14:48:22 -06:00
package-lock.json Clean the Style Reviewer's input of narration markup (#560) 2026-10-01 15:01:03 -04:00
package.json Make the errors surface in the activity report (#569) 2026-10-06 10:28:37 -04:00
README.md Add iOS as a build target (#541) 2026-09-27 18:27:49 -04:00
sun4.jpg Use custom icon for android 2026-01-03 07:08:45 -07:00
sun17.jpg Use custom icon for android 2026-01-03 07:08:45 -07:00
sun2022.png Use custom icon for android 2026-01-03 07:08:45 -07:00
svelte.config.js fix: prettier, eslint, pipeline (#71) 2026-02-06 00:49:36 +01:00
tsconfig.json Initial commit 2025-12-30 16:08:22 -07:00
vite.config.js fix: prettier, eslint, pipeline (#71) 2026-02-06 00:49:36 +01:00
vitest.config.ts Validated release script, extracted version arithmetic, lint config fixes (#416) 2026-08-08 19:55:23 +02:00

Aventuras

Interactive fiction with an AI that remembers. Play a text adventure or write collaboratively, on desktop and Android, with any LLM provider you like — cloud or running on your own machine.

Aventuras keeps the story straight: chapters are summarized as you play, a lorebook feeds the right entries into the right scene, an agent tidies that lorebook behind you, and the world state — who is present, where you are, what you carry — is tracked turn by turn.

Highlights

  • Two modes — Adventure, with multiple-choice actions and world tracking; Creative Writing, freeform with AI suggestions. First/second/third person, past or present tense.
  • Any provider — OpenRouter, OpenAI, Anthropic, Google, xAI, Groq, DeepSeek, Mistral, Z.AI, NanoGPT, Chutes, Pollinations, NVIDIA NIM, plus local llama.cpp, LM Studio and Ollama. Streaming, extended thinking, and per-subsystem Agent Profiles so retrieval, classification and narration can each use a different model.
  • Memory that scales — automatic chapter summarization, and a retrieval step that goes and finds what the current scene needs from everything that came before.
  • Lorebook — characters, locations, items, factions, concepts and events, with aliases, secrets, keyword and relevance-based injection, SillyTavern import, and an autonomous agent that consolidates duplicates and fills gaps.
  • The Vault — a cross-story library of reusable characters, lorebooks and scenarios, with a tool-calling assistant that edits them and shows you a diff before anything lands.
  • Every prompt is editable — they are Liquid templates, all of them, in an in-app editor.
  • Images — inline <pic> illustrations, scene backgrounds and character portraits, across nine backends including local ComfyUI and A1111.
  • Writing tools — local grammar checking (Harper/WASM), style analysis for repetition, suggestions that match how you write.
  • Yours to keep — SQLite on your machine, full backup/restore, .avt story export, LAN sync between devices over QR pairing, and an optional translation layer over the whole app.
  • 26 themes, adjustable text size, dialogue highlighting.

Install

Pre-built binaries are on the Releases page:

Platform Download
Windows Aventuras_vX.Y.Z_x64-setup.exe
macOS Aventuras_vX.Y.Z_x64.dmg / _aarch64.dmg
Linux Aventuras_vX.Y.Z_amd64.deb / .AppImage, or _x86_64.rpm
Android Aventuras_vX.Y.Z.apk
iOS Aventuras_vX.Y.Z_ios-arm64-unsigned.ipa (sideload; unsigned)

No API keys in config files — providers are set up in the app, under Settings → API Settings.

Build from source

Requires the Node.js version in .nvmrc, the latest stable Rust, (for Android) the Android SDK, NDK r27d and JDK 21–24, and (for iOS) a Mac with Xcode.

git clone https://github.com/AventurasTeam/Aventuras.git
cd Aventuras
npm install
npx tauri dev
Script Does
npm run dev Vite dev server (frontend only)
npx tauri dev full app, with hot reload
npm run build production build
npm run check type checking (svelte-check)
npm test test suite (Vitest)
npm run lint ESLint — lint:fix to fix
npm run format Prettier
npm run release version bump, tag and push

Desktop builds are npx tauri build; Android and iOS are documented in docs/development/release.md. iOS produces an unsigned device .ipa (no Apple certificates or signing secrets — see docs/development/ios-build-target.md for the full account of how the target was added and how the unsigned CI pipeline works).

Tech stack

TypeScript (strict) · SvelteKit 2 · Svelte 5 runes · Tauri 2 (Rust) · Tailwind + shadcn-svelte · SQLite (tauri-plugin-sql / sqlx) · Vercel AI SDK · LiquidJS · CodeMirror 6 · Zod · Harper.js (WASM) · Vitest

Contributing

Start with docs/ — the architecture is documented per area, and CLAUDE.md has the conventions. CI runs lint, type-check, tests and a build on every PR against master, develop or dev.

Acknowledgments

Tauri · SvelteKit · OpenRouter · Harper · Lucide

License

AGPL-3.0