feat: add URL-driven workspace state and streamed Codex research
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
# Home-agent research
|
||||
|
||||
Historical design: the WebSocket transport and global active selection described
|
||||
here were replaced by the [AI SDK research runtime](../research-runtime.md).
|
||||
|
||||
## Product boundary
|
||||
|
||||
The user starts a research task from the deck workspace. A home agent chooses
|
||||
|
||||
+19
-12
@@ -24,15 +24,15 @@ list, user-profile, and conversation pages are not provided.
|
||||
unique column IDs within each deck. Each column contains `id`, `title`,
|
||||
`connectionId`, and a platform-specific `source`.
|
||||
|
||||
| Platform | Source kind | Conditions |
|
||||
| --- | --- | --- |
|
||||
| Twitter | `search` | Native `query`, `product` (`Top`/`Latest`), `following` |
|
||||
| Twitter | `user` | `target`: handle or X/Twitter profile URL |
|
||||
| Twitter | `list` | `target`: numeric list ID or X/Twitter list URL |
|
||||
| Mastodon | `search` | `query`; results depend on the instance's search configuration |
|
||||
| Mastodon | `user` | `target`: account handle or the connected instance's numeric account ID |
|
||||
| Mastodon | `list` | `target`: numeric list ID for the connected account |
|
||||
| Mastodon | `hashtag` | `target`: tag without `#`, containing letters, numbers, or underscores |
|
||||
| Platform | Source kind | Conditions |
|
||||
| -------- | ----------- | ----------------------------------------------------------------------- |
|
||||
| Twitter | `search` | Native `query`, `product` (`Top`/`Latest`), `following` |
|
||||
| Twitter | `user` | `target`: handle or X/Twitter profile URL |
|
||||
| Twitter | `list` | `target`: numeric list ID or X/Twitter list URL |
|
||||
| Mastodon | `search` | `query`; results depend on the instance's search configuration |
|
||||
| Mastodon | `user` | `target`: account handle or the connected instance's numeric account ID |
|
||||
| Mastodon | `list` | `target`: numeric list ID for the connected account |
|
||||
| Mastodon | `hashtag` | `target`: tag without `#`, containing letters, numbers, or underscores |
|
||||
|
||||
`connectionId` is the account binding, distinct from a deck's name. Twitter
|
||||
connections resolve to profiles discovered from the configured relay.
|
||||
@@ -76,9 +76,9 @@ in a transaction. A stale revision is rejected. Save failures remain visible
|
||||
and do not report unsaved edits as persisted.
|
||||
|
||||
Saved decks refresh on focus and every five seconds while visible. Refresh is
|
||||
held while an editor is open. Active selection is a local preference under
|
||||
`twitter-lite-active-deck`; selecting a deck does not switch another device's
|
||||
view. Deleting the active deck selects a remaining one. When none remain, the
|
||||
held while an editor is open. The `deck` URL parameter selects the active view;
|
||||
selecting a deck does not switch another device's view. Browser history restores
|
||||
selection, and saved decks can be opened from a copied URL. Deleting the active deck selects a remaining one. When none remain, the
|
||||
app opens an empty temporary view.
|
||||
|
||||
WebMCP-created views are temporary by default. Temporary copies of saved decks
|
||||
@@ -139,3 +139,10 @@ uses an isolated SQLite database and mock relay through real server functions
|
||||
to exercise shared decks across browser contexts, editing, persistence,
|
||||
pagination, and native WebMCP. Deterministic automated tests do not require
|
||||
live SNS credentials.
|
||||
|
||||
## Conversation execution
|
||||
|
||||
The [research runtime](research-runtime.md) uses AI SDK and the Codex app-server
|
||||
provider. Conversations are selected by URL, with per-conversation background
|
||||
execution and reconnectable SSE. Opening New chat or another saved conversation
|
||||
does not stop an existing turn or change another browser's selection.
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# Research runtime
|
||||
|
||||
Research uses Vercel AI SDK 7 `streamText` with
|
||||
`ai-sdk-provider-codex-cli` 2.3's app-server provider. The provider owns its local
|
||||
stdio child, thread resumption, protocol decoding, and abort handling. The app
|
||||
owns accepted requests, account-scoped tools, snapshots, and report validation.
|
||||
The separate WebSocket launcher is removed.
|
||||
|
||||
## Tools
|
||||
|
||||
The provider does not register AI SDK `tools` as Codex dynamic tools. Existing
|
||||
research definitions and executors are exposed with `createSdkMcpServer` instead.
|
||||
Validation, connected-account scope, request budgets, evidence tracking, and
|
||||
read-only SNS behavior remain in `agent-tools.server.ts`. Provider execution is
|
||||
one agent turn; there is no second AI SDK tool loop replaying those calls.
|
||||
Resumed threads that already registered dynamic tools route their calls through
|
||||
`onDynamicToolCall` to the same validated executor.
|
||||
|
||||
## Lifecycle and selection
|
||||
|
||||
The background runner is keyed by conversation ID. Acceptance saves the user
|
||||
message before starting the model. A request ID identifies a new conversation
|
||||
and deduplicates retries within the process. Overlapping turns of the same
|
||||
conversation are rejected, while different conversations can run independently.
|
||||
|
||||
The URL selects a conversation. GET status and SSE subscribe to that ID;
|
||||
selecting history never starts or cancels a turn. Reconnection sends the latest
|
||||
complete snapshot. Closing a browser or changing its URL only detaches a viewer.
|
||||
Only Stop, the execution timeout, or a server-side failure aborts generation.
|
||||
Authentication expiry or logout closes the viewer's stream.
|
||||
|
||||
SQLite retains messages, thread IDs, generated decks, and citations. It no
|
||||
longer stores a global active conversation pointer. Browser views cannot change
|
||||
another browser's selection. After a backend restart, unfinished conversations
|
||||
are marked interrupted and require an explicit follow-up. No turn is replayed
|
||||
automatically. Reports remain files under the configured report root.
|
||||
|
||||
## Streaming presentation
|
||||
|
||||
The model stream uses `smoothStream` with 15 ms pacing and the supplied chunking
|
||||
pattern `/[\u3040-\u309F\u30A0-\u30FF]|\S+\s+/`. The trailing buffer flushes at the
|
||||
text-end event. The existing snapshot SSE remains the UI's reconnectable data
|
||||
channel; an HTTP request's lifetime never owns the model execution.
|
||||
|
||||
Streamdown displays partial Markdown and animates only the latest live answer.
|
||||
Completed messages render immediately. Reduced-motion preferences disable the
|
||||
animation. HTML is skipped, unsafe link protocols are rejected, images remain
|
||||
links, and retrieved post citations keep their deck navigation behavior.
|
||||
`@shadcn/helpers/ai-sdk` provides deterministic streaming fixtures for UI tests.
|
||||
|
||||
References: [Codex provider](https://github.com/ben-vargas/ai-sdk-provider-codex-cli),
|
||||
[AI SDK smoothing](https://ai-sdk.dev/docs/reference/ai-sdk-core/smooth-stream),
|
||||
[Streamdown animation](https://streamdown.ai/docs/animation),
|
||||
[shadcn AI SDK helpers](https://ui.shadcn.com/docs/helpers/ai-sdk).
|
||||
@@ -16,3 +16,7 @@ as the anatomy illustration source and clipped in CSS. Source media:
|
||||
https://pbs.twimg.com/media/HTE4JWFWUAAnPBt.jpg
|
||||
The rest of the interface is HTML/CSS with SVG data charts. Reference-specific
|
||||
colors are scoped to `--vital-*` tokens in `vitals.css`.
|
||||
|
||||
Sections, tabs, history, chart filters, and detail dialogs are URL state and
|
||||
restore on reload or browser Back/Forward. Simulated climate control values
|
||||
remain local; opening a URL never issues a device command.
|
||||
|
||||
@@ -13,15 +13,15 @@ operations use the same access controls and persistence rules as the UI.
|
||||
|
||||
## Workspace tools
|
||||
|
||||
| Tool | Input | Behavior |
|
||||
| --- | --- | --- |
|
||||
| `list_connections` | `{}` | Return connection IDs, platforms, origins, account IDs, display names, and states; never credentials |
|
||||
| `list_decks` | `{}` | Return saved decks and this tab's temporary views, `activeDeckId`, and `storageError` |
|
||||
| `get_deck` | Optional `deckId` | Read the named or active view, including `persisted` and saved `revision` |
|
||||
| `set_deck` | Optional `deckId` and `expectedRevision`, required `title` and `columns` | Without an ID, create a temporary view; with an ID, replace and activate that existing view |
|
||||
| `save_deck` | `deckId` | Explicitly persist a temporary view; an already saved deck is unchanged |
|
||||
| `select_deck` | `deckId` | Activate a view; selection remains device-local |
|
||||
| `delete_deck` | `deckId`, optional `expectedRevision` | Discard a temporary view or delete a saved deck for all devices |
|
||||
| Tool | Input | Behavior |
|
||||
| ------------------ | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
|
||||
| `list_connections` | `{}` | Return connection IDs, platforms, origins, account IDs, display names, and states; never credentials |
|
||||
| `list_decks` | `{}` | Return saved decks and this tab's temporary views, `activeDeckId`, and `storageError` |
|
||||
| `get_deck` | Optional `deckId` | Read the named or active view, including `persisted` and saved `revision` |
|
||||
| `set_deck` | Optional `deckId` and `expectedRevision`, required `title` and `columns` | Without an ID, create a temporary view; with an ID, replace and activate that existing view |
|
||||
| `save_deck` | `deckId` | Explicitly persist a temporary view; an already saved deck is unchanged |
|
||||
| `select_deck` | `deckId` | Activate a view and update the URL; other tabs remain unchanged |
|
||||
| `delete_deck` | `deckId`, optional `expectedRevision` | Discard a temporary view or delete a saved deck for all devices |
|
||||
|
||||
Read tools return the workspace's current client snapshot, not a fresh server
|
||||
request. Saved definitions refresh on focus and while visible, except during
|
||||
|
||||
Reference in New Issue
Block a user