[Discussion] Extracting reusable primitives from t3code—toward shared agent infrastructure
Hey t3code team—I've been following your work and spent some time reading through the codebase. I'm exploring building something in the same space and wanted to share some thoughts on where I think the broader ecosystem could go. This is meant as a conversation starter, not a critique—you've built something genuinely impressive and I think there's an opportunity to make it even more impactful.
What you've built that I really admire
After reading through the repo, a few things stood out as particularly well-designed:
The Thread + Turn model. Separating the persistent conversation (Thread) from individual execution cycles (Turn) is the right abstraction. The OrchestrationLatestTurn with { turnId, state, requestedAt, startedAt, completedAt } is clean and gives you natural checkpoint boundaries without over-engineering the session concept.
The activity log abstraction.OrchestrationThreadActivity with { tone, kind, summary, payload } is a genuinely elegant design. The tone field ("info" | "tool" | "approval" | "error") gives instant semantic categorization without consumers needing to understand the underlying event taxonomy. The pure derivation functions in session-logic.ts—derivePendingApprovals(), deriveWorkLogEntries(), isLatestTurnSettled()—are exactly the right pattern: pure functions over an event log, testable without infrastructure.
The event-sourced orchestration engine. The full pipeline—OrchestrationCommand through the decider, into the append-only event store, through projectors to materialized read models, broadcast to WebSocket clients—is textbook event sourcing done well. The OrchestrationEvent schema with aggregateKind + type + payload and the sequence-numbered catch-up protocol (replayEvents(fromSequenceExclusive)) solve real problems around client reconnection and state consistency.
The provider adapter pattern. The ProviderAdapter interface with startSession, sendTurn, interruptTurn is provider-agnostic by design. And the Codex adapter (CodexAdapter.ts) is, as far as I can tell, the only open-source Codex app server client implementation in existence. That's genuinely valuable work.
The status pill system. Four states, color + pulse encoding, unread detection via simple timestamp comparison (completedAt > lastVisitedAt). Minimal and it works.
The silo problem
Here's what I keep running into as I think about this space: all of this excellent infrastructure is locked inside the application.
If someone wants to build a mobile client for agent sessions, they have to rebuild the orchestration engine, the event store, the provider adapter, and the state derivation logic from scratch. If someone wants a different attention model—say, a global inbox sorted by urgency instead of a project-scoped sidebar—they have to fork the entire app. If an observability tool wants to consume agent session events, it needs a custom integration specifically for t3code.
The hard work—the Turn model, the event normalization, the provider abstraction, the derivation functions—is trapped inside one application's dependency tree. And every team building agent tooling is independently rebuilding these same primitives.
The OSI model for agent infrastructure
The framing I keep coming back to is the OSI networking model. OSI didn't just build a networking application—it defined a stack of layers, each with a clean interface, each independently swappable. You could swap out the physical layer (copper → fiber → WiFi) without touching the transport layer. You could build a new application protocol without rebuilding TCP. The layers gave the ecosystem the freedom to innovate at any level without starting from scratch.
I think agent infrastructure needs the same thing. Something like:
- Layer 1—Provider transport: raw communication with agent runtimes (Claude Code, Codex, OpenRouter, etc.)
- Layer 2—Turn execution: normalizing provider-specific events into a common Turn/Message model
- Layer 3—Session lifecycle: state machine, event store, interrupt/resume semantics
- Layer 4—Attention/observation: inbox, triage, prioritization, notification signals
- Layer 5—Integration surface: REST/WebSocket APIs, typed clients, CLI
- Layer 6—UI primitives: headless React hooks and components that speak the layers below
The key insight from OSI is that each layer only needs to know about the layer directly below and above it. A UI component at Layer 6 doesn't need to know whether the agent running at Layer 1 is Claude Code or Codex. An observability tool at Layer 4 doesn't need to know how sessions are transported.
Right now, t3code is essentially all six layers bundled into one application. That's fine for shipping—but it means anyone who wants to innovate at Layer 6 (a different UI) has to bring Layers 1–5 with them. And anyone who wants to contribute a Layer 1 implementation (a new agent runtime) has to integrate with the whole stack.
The M × N problem and the LSP analogy
This situation reminds me of what editors and programming languages looked like before LSP. Every editor had to implement language intelligence for every language. M editors × N languages meant M×N implementations. LSP collapsed that to M+N by standardizing the protocol between them.
The same M × N problem exists in agent infrastructure today: M UIs/orchestrators × N agent runtimes. t3code has excellent Codex support, and others are building Claude Code support, OpenRouter support, and more. But there's no shared interface—each integration is rebuilt from scratch for each combination. Observability tools need custom integrations for each runtime. Orchestration frameworks can't drive sessions generically.
A shared session event protocol—something like an "Agent Session Protocol"—could collapse this the same way LSP did. One provider adapter emits standard events; any consumer (UI, CLI, observability, orchestrator) can consume them without knowing which runtime is underneath.
The natural joints in agent execution are already converging toward common abstractions across independent implementations: session initialized, text streaming, tool started/completed, turn completed, approval requested, session ended. When independent projects arrive at the same abstractions, it's a signal those abstractions are right.
What could be extracted—concrete ideas
I'm not suggesting a big redesign. More like: what if the primitives that already exist in t3code were published as standalone packages—one per layer—that the broader ecosystem could build on independently?
Layer 1–2—packages/contracts + provider adapters as published packages.
Your contracts package is already cleanly separated. What if it (or a subset) were published under an ecosystem-neutral name? The OrchestrationEvent envelope, the OrchestrationThreadActivity shape, the Turn/Thread type definitions—these are useful to anyone building agent tooling. And the ProviderAdapter interface (startSession, sendTurn, interruptTurn) is generic enough that other projects could implement against it. Your Codex adapter is the reference implementation—publish it standalone and it becomes the go-to library for anyone integrating with Codex, not just t3code users.
Layer 3—The event-sourcing primitives as their own package.
The OrchestrationEventStore, the projection pipeline pattern, the replayEvents catch-up protocol—these are infrastructure that anyone building an agent session backend would want. Right now you have to take the whole server to get them.
Layer 4—The derivation functions as a shared utility.
derivePendingApprovals(), derivePendingUserInputs(), deriveWorkLogEntries(), isLatestTurnSettled()—pure functions with no infrastructure dependency, useful to anyone building agent session UIs regardless of which backend they use. A session-logic package would be immediately valuable to the ecosystem.
Layer 6—React UI components as headless primitives.
The sidebar, status pills, chat view—these represent real design decisions that others would benefit from. Published as headless components (logic separated from styling, which you're already doing with the .logic.ts pattern), they could be styled differently by different products while sharing the interaction patterns. Think Radix UI for agent session UIs.
A question, not a proposal
I might be missing context on why things are structured the way they are—there are always tradeoffs that aren't visible from reading code. But I wanted to put the idea out there:
What if t3code's biggest impact isn't the application itself, but the infrastructure primitives it proves out?
The application is great. But the Turn model, the event normalization, the provider abstraction, the derivation patterns—those could be the foundation that an entire ecosystem builds on. Like how React's component model became bigger than Facebook's use of it, or how VS Code's LSP implementation became bigger than VS Code's language support.
I'd love to hear your thoughts on this. Are you already thinking along these lines? Are there reasons the current structure makes more sense that I'm not seeing? Would it be worth exploring what a shared event schema might look like across different implementations?
Happy to jump on a call or continue the discussion here. Either way, thanks for building in the open—the architecture decisions you've made are a valuable reference for anyone working in this space.
[Discussion] Extracting reusable primitives from t3code—toward shared agent infrastructure
Hey t3code team—I've been following your work and spent some time reading through the codebase. I'm exploring building something in the same space and wanted to share some thoughts on where I think the broader ecosystem could go. This is meant as a conversation starter, not a critique—you've built something genuinely impressive and I think there's an opportunity to make it even more impactful.
What you've built that I really admire
After reading through the repo, a few things stood out as particularly well-designed:
The Thread + Turn model. Separating the persistent conversation (Thread) from individual execution cycles (Turn) is the right abstraction. The
OrchestrationLatestTurnwith{ turnId, state, requestedAt, startedAt, completedAt }is clean and gives you natural checkpoint boundaries without over-engineering the session concept.The activity log abstraction.
OrchestrationThreadActivitywith{ tone, kind, summary, payload }is a genuinely elegant design. Thetonefield ("info" | "tool" | "approval" | "error") gives instant semantic categorization without consumers needing to understand the underlying event taxonomy. The pure derivation functions insession-logic.ts—derivePendingApprovals(),deriveWorkLogEntries(),isLatestTurnSettled()—are exactly the right pattern: pure functions over an event log, testable without infrastructure.The event-sourced orchestration engine. The full pipeline—
OrchestrationCommandthrough the decider, into the append-only event store, through projectors to materialized read models, broadcast to WebSocket clients—is textbook event sourcing done well. TheOrchestrationEventschema withaggregateKind + type + payloadand the sequence-numbered catch-up protocol (replayEvents(fromSequenceExclusive)) solve real problems around client reconnection and state consistency.The provider adapter pattern. The
ProviderAdapterinterface withstartSession,sendTurn,interruptTurnis provider-agnostic by design. And the Codex adapter (CodexAdapter.ts) is, as far as I can tell, the only open-source Codex app server client implementation in existence. That's genuinely valuable work.The status pill system. Four states, color + pulse encoding, unread detection via simple timestamp comparison (
completedAt > lastVisitedAt). Minimal and it works.The silo problem
Here's what I keep running into as I think about this space: all of this excellent infrastructure is locked inside the application.
If someone wants to build a mobile client for agent sessions, they have to rebuild the orchestration engine, the event store, the provider adapter, and the state derivation logic from scratch. If someone wants a different attention model—say, a global inbox sorted by urgency instead of a project-scoped sidebar—they have to fork the entire app. If an observability tool wants to consume agent session events, it needs a custom integration specifically for t3code.
The hard work—the Turn model, the event normalization, the provider abstraction, the derivation functions—is trapped inside one application's dependency tree. And every team building agent tooling is independently rebuilding these same primitives.
The OSI model for agent infrastructure
The framing I keep coming back to is the OSI networking model. OSI didn't just build a networking application—it defined a stack of layers, each with a clean interface, each independently swappable. You could swap out the physical layer (copper → fiber → WiFi) without touching the transport layer. You could build a new application protocol without rebuilding TCP. The layers gave the ecosystem the freedom to innovate at any level without starting from scratch.
I think agent infrastructure needs the same thing. Something like:
The key insight from OSI is that each layer only needs to know about the layer directly below and above it. A UI component at Layer 6 doesn't need to know whether the agent running at Layer 1 is Claude Code or Codex. An observability tool at Layer 4 doesn't need to know how sessions are transported.
Right now, t3code is essentially all six layers bundled into one application. That's fine for shipping—but it means anyone who wants to innovate at Layer 6 (a different UI) has to bring Layers 1–5 with them. And anyone who wants to contribute a Layer 1 implementation (a new agent runtime) has to integrate with the whole stack.
The M × N problem and the LSP analogy
This situation reminds me of what editors and programming languages looked like before LSP. Every editor had to implement language intelligence for every language. M editors × N languages meant M×N implementations. LSP collapsed that to M+N by standardizing the protocol between them.
The same M × N problem exists in agent infrastructure today: M UIs/orchestrators × N agent runtimes. t3code has excellent Codex support, and others are building Claude Code support, OpenRouter support, and more. But there's no shared interface—each integration is rebuilt from scratch for each combination. Observability tools need custom integrations for each runtime. Orchestration frameworks can't drive sessions generically.
A shared session event protocol—something like an "Agent Session Protocol"—could collapse this the same way LSP did. One provider adapter emits standard events; any consumer (UI, CLI, observability, orchestrator) can consume them without knowing which runtime is underneath.
The natural joints in agent execution are already converging toward common abstractions across independent implementations: session initialized, text streaming, tool started/completed, turn completed, approval requested, session ended. When independent projects arrive at the same abstractions, it's a signal those abstractions are right.
What could be extracted—concrete ideas
I'm not suggesting a big redesign. More like: what if the primitives that already exist in t3code were published as standalone packages—one per layer—that the broader ecosystem could build on independently?
Layer 1–2—
packages/contracts+ provider adapters as published packages.Your contracts package is already cleanly separated. What if it (or a subset) were published under an ecosystem-neutral name? The
OrchestrationEventenvelope, theOrchestrationThreadActivityshape, the Turn/Thread type definitions—these are useful to anyone building agent tooling. And theProviderAdapterinterface (startSession,sendTurn,interruptTurn) is generic enough that other projects could implement against it. Your Codex adapter is the reference implementation—publish it standalone and it becomes the go-to library for anyone integrating with Codex, not just t3code users.Layer 3—The event-sourcing primitives as their own package.
The
OrchestrationEventStore, the projection pipeline pattern, thereplayEventscatch-up protocol—these are infrastructure that anyone building an agent session backend would want. Right now you have to take the whole server to get them.Layer 4—The derivation functions as a shared utility.
derivePendingApprovals(),derivePendingUserInputs(),deriveWorkLogEntries(),isLatestTurnSettled()—pure functions with no infrastructure dependency, useful to anyone building agent session UIs regardless of which backend they use. Asession-logicpackage would be immediately valuable to the ecosystem.Layer 6—React UI components as headless primitives.
The sidebar, status pills, chat view—these represent real design decisions that others would benefit from. Published as headless components (logic separated from styling, which you're already doing with the
.logic.tspattern), they could be styled differently by different products while sharing the interaction patterns. Think Radix UI for agent session UIs.A question, not a proposal
I might be missing context on why things are structured the way they are—there are always tradeoffs that aren't visible from reading code. But I wanted to put the idea out there:
What if t3code's biggest impact isn't the application itself, but the infrastructure primitives it proves out?
The application is great. But the Turn model, the event normalization, the provider abstraction, the derivation patterns—those could be the foundation that an entire ecosystem builds on. Like how React's component model became bigger than Facebook's use of it, or how VS Code's LSP implementation became bigger than VS Code's language support.
I'd love to hear your thoughts on this. Are you already thinking along these lines? Are there reasons the current structure makes more sense that I'm not seeing? Would it be worth exploring what a shared event schema might look like across different implementations?
Happy to jump on a call or continue the discussion here. Either way, thanks for building in the open—the architecture decisions you've made are a valuable reference for anyone working in this space.