## Summary Remote HTTP goes through Tauri's HTTP plugin, and the plugin's defaults fall short of what the app's callers need. A response body that stalled after the headers hung forever. Local servers (stock Ollama, ComfyUI) rejected the webview `Origin` the plugin attaches. Responses arrived uncompressed, TLS ignored the OS trust store, and the plugin kept a cookie jar nobody used. Vault portrait downloads bypassed the plugin altogether. This branch fixes each of these and routes every plugin call through one wrapper, `httpFetch`. ## Changes ### Timeouts and aborts - **Request guard** (`utils/idleTimeout.ts`): `createRequestGuard` combines the header timer, the caller's abort signal and a per-read idle timer on the body. `withIdleTimeout` re-wraps the body so a stalled read errors with `RequestTimeoutError`. The timer runs only while a read waits on upstream, so a slow consumer never trips it. `createTimeoutFetch`, `imageFetch` and `imageGetFetch` all use the guard in place of their own timer/abort code. - **Timeout errors**: `RequestTimeoutError` (`utils/error.ts`) is deliberately not named `TimeoutError`, which the AI SDK treats as an abort. `mapError` also turns the plugin's bare-string and plain-`Error` abort rejections into an `AbortError` when the caller aborted. `imageFetch` no longer reports a caller-caused `AbortError` as a timeout. - **LLM fetch**: error responses and non-JSON (streaming) responses get the idle timer. Buffered JSON responses don't; the header timer bounds them, since `dispose()` runs after `response.text()`. - **Fake provider**: new `stream-stall` model (some text, then silence with the connection open), documented in `docs/development/testing.md`. ### One entry point into the plugin - **`httpFetch`** (`utils/http.ts`) wraps the plugin's `fetch` and sets `Origin: ''` unless the caller sets one. The plugin drops an empty `Origin` under `unsafe-headers`, so local servers no longer see the webview origin. A caller's own `Origin` and `Referer` are sent as set. - All plugin callers now use it: `createTimeoutFetch`, the image fetch helpers, ComfyUI's `fetchApi`, `pingModel`, `corsFetch` and the updater. An ESLint `no-restricted-imports` rule rejects any other import of the plugin's `fetch` outside `http.ts` and tests. ### Plugin features (`src-tauri/Cargo.toml`) - `tauri-plugin-http` default features are off and its list is spelled out, so the `cookies` jar stays out: `rustls-tls`, `http2`, `charset`, `system-proxy`, plus the three below. The plugin's cookie jar is gone, and startup deletes the `.cookies` file an older build left in the app cache dir (`lib.rs`). - **`gzip`, `brotli`, `zstd`**: requests send `Accept-Encoding: gzip, br, zstd` and responses are decoded in Rust before the WebView sees them. - **`unsafe-headers`**: needed to drop `Origin`. It lifts the plugin's whole forbidden-header filter (`Host`, `Cookie`, `Content-Length`, `Sec-*`, …), so `httpFetch` is where to filter if untrusted input ever feeds request headers. Every caller today is app code. `docs/architecture/overview.md` records this. - **`rustls-tls-native-roots`** on Windows and Linux only: the OS trust store is added to the bundled roots. Android and iOS have no store `rustls-native-certs` can read, and on macOS every request would walk the keychain, so those keep the bundled roots. The target-specific entry also sets `default-features = false`, or Cargo would merge the defaults (cookies included) back in. ### Vault portraits - Importing a card with a `sourceUrl` downloads the portrait with `fetchPublicImageBase64` (public URLs and redirects only, 15s timeout) instead of a bare WebView `fetch`. The download runs alongside `sanitize`. The result is accepted only if `sniffImageMime` recognises the bytes; otherwise the import keeps the URL as the portrait, as before. - **`sniffImageMime`** (`utils/image.ts`) identifies PNG, APNG, JPEG, GIF, WebP, AVIF, HEIC and HEIF from the leading bytes. Unlike `base64ImageMime`, it returns `null` rather than defaulting to PNG. - `fetchPublicImageBase64` accepts `timeoutMs`. ### iOS - `NSLocalNetworkUsageDescription` now also names local AI servers, since the app connects to them over the LAN. ## Changes outside the main purpose - `@tauri-apps/plugin-http` 2.8.2 → 2.8.3, with the matching `tauri-plugin-http` crate. The lockfile update also moved `tauri-plugin-fs` 2.7.0 → 2.7.1. - In `fetch.ts` and `fetchAdapter.ts`, the debug-log bodies moved into `logErrorResponse`, `logStreamResponse` and `logImageResponse`, and `normalizeHeaders` / the FormData key listing were simplified. Behaviour is unchanged. ## Testing New unit tests cover `idleTimeout`, `httpFetch`, the `fetch.ts` and `fetchAdapter.ts` guard integration, and `sniffImageMime`. The Rust and platform changes (Cargo features, native roots, `Origin`, decompression, the iOS string) have no automated coverage and need a release build to check: - Run the fake provider with the `stream-stall` model and confirm the generation errors after the timeout instead of hanging. - On Windows and Android, point the app at stock Ollama on loopback and confirm requests succeed without `OLLAMA_ORIGINS`. - On Windows or Linux, reach an HTTPS server whose CA is only in the OS store. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_019V1AUE2StsGUjVV6KzUx2G <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added safeguards for stalled AI requests, with clearer timeout errors and support for cancelling requests. * Character imports can now fetch and validate portrait images, with a fallback to the original image URL. * Improved image format detection for PNG, JPEG, GIF, WebP, AVIF, HEIC, and HEIF files. * Updated the local network permission message to include connections to local AI servers. * **Bug Fixes** * Improved handling of request headers and response timeouts across AI, discovery, and update requests. * App startup now attempts to clear cached cookies. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|---|---|---|
| .cargo | ||
| .github | ||
| docs | ||
| scripts | ||
| src | ||
| src-tauri | ||
| static | ||
| third-party-licenses | ||
| .gitattributes | ||
| .gitignore | ||
| .nvmrc | ||
| .prettierrc | ||
| CLAUDE.md | ||
| compileApk.sh | ||
| eslint.config.js | ||
| lefthook.yml | ||
| LICENSE.md | ||
| logo.png | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| sun4.jpg | ||
| sun17.jpg | ||
| sun2022.png | ||
| tsconfig.json | ||
| vite.config.js | ||
| vitest.config.ts | ||
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,
.avtstory 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 | Minimum |
|---|---|---|
| Windows | Aventuras_vX.Y.Z_x64-setup.exe |
Windows 10 |
| macOS | Aventuras_vX.Y.Z_x64.dmg / _aarch64.dmg |
macOS 13.3 |
| Linux | Aventuras_vX.Y.Z_amd64.deb / .AppImage, or _x86_64.rpm |
WebKitGTK 2.40 for the .deb / .rpm; the AppImage carries its own, needs glibc 2.35 |
| Android | Aventuras_vX.Y.Z.apk |
Android 8, with System WebView 111 or later |
| iOS | Aventuras_vX.Y.Z_ios-arm64-unsigned.ipa (sideload; unsigned) |
iOS 26 |
The minimums come from each platform's web engine, OS and runtime requirements. They are derived from documentation and have not been tested on devices at those versions; see docs/development/platform-support.md.
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 |
lint:ts (ESLint) and lint:rust (rustfmt, Clippy); :fix fixes |
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 3 · Svelte 5 runes · Tauri 2 (Rust) · Tailwind + bits-ui ·
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