Suggestion for a vision of shared agent infrastructure #476

Description

@brennancheung

[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.tsderivePendingApprovals(), 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
       blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      Suggestion for a vision of shared agent infrastructure #476

      Description

      @brennancheung

      [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.tsderivePendingApprovals(), 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.

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        No labels
        No labels

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          Suggestion for a vision of shared agent infrastructure #476

          Description

          @brennancheung

          [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.tsderivePendingApprovals(), 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.

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            No labels
            No labels

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              Suggestion for a vision of shared agent infrastructure #476

              Description

              @brennancheung

              [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.tsderivePendingApprovals(), 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.

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                No labels
                No labels

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  Suggestion for a vision of shared agent infrastructure #476

                  Description

                  @brennancheung

                  [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.tsderivePendingApprovals(), 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.

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    No labels
                    No labels

                    Type

                    No type

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      Suggestion for a vision of shared agent infrastructure #476

                      Description

                      @brennancheung

                      [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.tsderivePendingApprovals(), 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.

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        No labels
                        No labels

                        Type

                        No type

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          Suggestion for a vision of shared agent infrastructure #476

                          Description

                          @brennancheung

                          [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.tsderivePendingApprovals(), 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.

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            No labels
                            No labels

                            Type

                            No type

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              Suggestion for a vision of shared agent infrastructure #476

                              Description

                              @brennancheung

                              [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.tsderivePendingApprovals(), 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.

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                No labels
                                No labels

                                Type

                                No type

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions