Repository navigation
Aldo: a sleeping machine's thread opens with its messages and can be sent to, without waking it - #135
Conversation
…sent to, without waking it A device that never had a thread open while its machine ran showed it as "Loading messages..." with sending turned off, and nothing on it could wake the machine. Now: - T3's thread cache reads Aldo's copy of the thread (its latest turns, as the machine last reported them) when the machine isn't connected and the copy is newer than the cache (connection/storage.ts, aldo/threadDetails.ts). - A thread neither Aldo nor this browser has a copy of wakes its machine once to show it, whatever the preload setting; the machine then reports it. A refused wake says why. - A machine this browser never connected to gets the models a new thread shows, so its threads can be sent to (sending wakes it) (aldo/shells.ts). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 13d7135787
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Effect.flatMap((cached) => | ||
| withAldoThreadDetail(database, environmentId, threadId, cached), | ||
| ), |
There was a problem hiding this comment.
Run the Aldo fallback after stale cache decode errors
When a browser still has an older thread-cache record (for example, the documented v2 entry after the schema-v3 bump), decodeStoredThreadSnapshot fails before this new flatMap executes. The caller converts that failure to an empty thread, but newerDetail is never queried and therefore never marks the thread as missing; with “wake opened threads” disabled, the route remains stuck on “Loading messages…” even if Aldo has a valid copy or could trigger the one-time wake. Recover decode failures as Option.none() before invoking the Aldo fallback.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in f2c40d5: a cached record that fails to decode now still asks Aldo for its copy (so it can open from it, or be marked missing for the one-time wake). If Aldo has none, the original persistence error is reported as before.
| for (const environmentId of ids) modelsChecked.add(environmentId); | ||
| const configs = await aldoServerConfigsFor(ids); |
There was a problem hiding this comment.
Retry model seeding after an unsuccessful attempt
On a new tab/device, these environments are marked checked before /api/models and the IndexedDB transaction succeed. If the first request transiently fails, returns no template before any agent has reported one, or the write aborts, every later directory poll skips those environments for the rest of the tab; even after another agent reports models, their sleeping threads can still show “No provider available” and cannot be sent to without reloading the page. Only mark an environment after its config was written or an existing valid cached config was confirmed.
AGENTS.md reference: AGENTS.md:L74-L74
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in f2c40d5: a machine is only marked done once its config is cached (written now, or one was already there). Ones that got nothing (no template yet, Aldo out of reach, a failed write) are tried again on the next directory fetch. The accounts are only fetched when there's a template, so those retries stay cheap.
| onNone: () => null, | ||
| onSome: (snapshot) => snapshot.snapshotSequence, | ||
| }); | ||
| return Effect.promise(() => aldoThreadDetail(environmentId, threadId, cachedSequence)).pipe( |
There was a problem hiding this comment.
Render a valid cached thread before waiting on Aldo
When a sleeping thread already has a valid local snapshot, this makes loadThread await the Aldo detail request before returning that snapshot. If the detail endpoint is slow or unreachable, opening a previously cached conversation now shows “Loading messages…” for the full five-second timeout even though all renderable data was already available locally. Return the cached snapshot immediately and perform the freshness request in the background, or only block when the cache is empty.
AGENTS.md reference: AGENTS.md:L17-L17
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Partly taken, in f2c40d5. Returning the cache at once would show stale messages in the common Aldo case: the agent worked (a reminder, a PR follow-up) while this device was away, so Aldo's copy is newer than the cache. Refreshing the thread state from the storage layer afterwards would race its finalizer's persist. So the check stays in the load, but with a cache in hand it waits at most 1.5 s (5 s only when there's nothing to show). The endpoint answers in about 70 ms locally, and it's one indexed row read.
…; models are tried again until they're in; a cached thread waits less for Aldo - A cached thread that fails to decode still asks Aldo for its copy (and reports the failure as before if Aldo has none). - A machine is only done with models once its config is cached; one that didn't get any (no template yet, Aldo out of reach) is tried again on the next directory fetch. The accounts are only read when there's a template. - A thread with an older copy in this browser waits at most 1.5 seconds for Aldo's (5 with nothing cached). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nwdffgwm4 # Conflicts: # apps/web/src/aldo/preload.ts
A device that never had a thread open while its machine ran showed it as "Loading messages…" with sending turned off ("Messages loading"). Since sending is what wakes a machine, those threads were stuck: no data, and no way to act on them. This mostly hit older threads, and any thread opened on a new device.
What changes
GET /api/environments/:id/detail?t3ThreadId=&after=, served by logancsack/aldo's companion PR). It uses Aldo's copy when that's newer than the local cache, and caches it (connection/storage.ts,aldo/threadDetails.ts). The copy is T3's own snapshot, decoded with T3's schema, so once the machine connects the live thread resumes from its sequence. Nothing is woken to show a thread.aldoServerConfigsFor), and never overwrites one the machine reported itself. So their threads can be sent to, and sending wakes the machine.The client assumes no server feature: against an Aldo without the detail route, the request 404s and nothing changes, including no forced wake.
Tested against a local Aldo (with the companion PR) and real sandboxes, with "wake opened threads" off and an emptied IndexedDB:
vp check, the web typecheck andsrc/aldotests pass, including newthreadDetails.logictests.🤖 Generated with Claude Code