Add explicit local board connections with fixed runtime access (#53400)

## Why

Local message boards were scoped to an agent tree. Independently created Codex runtimes need a way to share a board while receiving different access permissions.

## What changed

- Add `LocalBoard` and `LocalBoardConnection` so hosts can supply read-only or read-write access through `thread_extension_init`, including for isolated, ephemeral runtimes.
- Allow read-only connections to read and manage their own subscriptions; reject posting, replying, channel creation, and changes to another agent's subscriptions. Expose effective permissions in channel discovery.
- Use unique runtime paths and require matching board IDs and canonical database paths when resolving agents or delivering notifications.
- Document explicit extension installation and connection setup for new runtimes, cold resumes, and forks.

## Testing

Add a scenario and context snapshot covering an independent writer and isolated Guardian sharing a board through a symlink alias, read-only permission denials, subscription and explicit notifications, and rejection of notifications to an unconnected runtime before a post is written.

GitOrigin-RevId: f92410c3a5c6a46920c3e0e71849c85dc83d9c31
This commit is contained in:
jif
2026-10-12 14:44:22 +00:00
committed by copyberry
parent cab328f275
commit 97239fd052
13 changed files with 647 additions and 35 deletions
+21
View File
@@ -953,6 +953,27 @@ impl ThreadManager {
self.state.get_thread(thread_id).await
}
/// Board hosts may reach internal runtimes only through a matching explicit connection.
pub(crate) async fn get_thread_for_board(
&self,
thread_id: ThreadId,
connection: &codex_agent_message_board_extension::LocalBoardConnection,
) -> CodexResult<Arc<CodexThread>> {
self.state
.threads
.read()
.await
.get(&thread_id)
.filter(|thread| {
thread
.thread_extension_data()
.get::<codex_agent_message_board_extension::LocalBoardConnection>()
.is_some_and(|other| connection.same_board(&other))
})
.cloned()
.ok_or(CodexErr::ThreadNotFound(thread_id))
}
/// Updates metadata for loaded and cold threads through one entrypoint.
///
/// Loaded threads route through `CodexThread`/`LiveThread`, so metadata changes stay ordered