Skip to content

Aldo: a sleeping machine's thread opens with its messages and can be sent to, without waking it - #135

Merged
logancsack merged 3 commits into
aldo/cloud-platformfrom
aldo/wnwdffgwm4
Sep 30, 2026
Merged

logancsack merged 3 commits into
aldo/cloud-platformfrom
aldo/wnwdffgwm4

Conversation

@logancsack

Copy link
Copy Markdown
Owner

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

  • Messages from Aldo's copy. When T3 loads a thread whose machine isn't connected, its thread cache now also asks Aldo for the machine's last-reported detail of that thread (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.
  • Wake once when there's no copy anywhere. A thread neither Aldo nor this browser has a copy of (its machine hasn't run since machines began reporting them) wakes its machine once to show it, even with "wake opened threads" off. It goes back to sleep 90 seconds after the user leaves, like a preload. The machine then reports the thread, so it opens from the copy after that. If Aldo refuses that wake (no credits, too many agents), the user is told why.
  • Models for machines this browser never connected to. Before this, the composer said "No provider available". The directory sync now gives them the model list a new thread shows (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:

  • A sleeping machine's thread opened with its messages, and no wake request was made. The composer had models, and sending woke the machine. The live thread picked up from the copy with no duplicate messages.
  • With Aldo's copy deleted (an older thread), opening it woke the machine once and the thread loaded. aldod re-reported the copy. Leaving through in-app navigation put the machine back to sleep 90 seconds later. Reopening with an empty cache showed the copy without waking.
  • vp check, the web typecheck and src/aldo tests pass, including new threadDetails.logic tests.

🤖 Generated with Claude Code

…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>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-30T19:02:11.880621Z 13d7135 PR opened
🔒 Security Review ✅ Completed 2026-09-30T18:59:55.070231Z 13d7135 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread apps/web/src/connection/storage.ts Outdated
Comment on lines +566 to +568
Effect.flatMap((cached) =>
withAldoThreadDetail(database, environmentId, threadId, cached),
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread apps/web/src/aldo/shells.ts Outdated
Comment on lines +99 to +100
for (const environmentId of ids) modelsChecked.add(environmentId);
const configs = await aldoServerConfigsFor(ids);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

logancsack and others added 2 commits September 30, 2026 19:07
…; 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
@logancsack
logancsack merged commit 6bd9098 into aldo/cloud-platform Sep 30, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant