Before submitting
Area
packages/contracts or packages/shared
Problem or use case
An authenticated external agent can create or continue its own T3 thread, but it cannot publish a response into an existing thread owned by another provider without starting that provider again.
I maintain a working T3–Hermes bridge: https://github.com/m-check1B/t3-hermes-bridge
Today, when a user invokes Hermes from a Codex/Claude/Cursor/Grok/OpenCode thread, the bridge must create a separate linked Hermes thread. Relaying the result back with thread.turn.start is not equivalent: it records the result as a user message and starts another provider turn, adding cost and potentially changing the response.
The current internal assistant message commands are provider-owned lifecycle commands. Making them public would let an external client impersonate the thread provider and interfere with turn/session state, so the bridge intentionally does not use them.
Related but distinct:
This request is narrower: append one explicitly external, attributed message without starting or adopting a provider session.
Proposed solution
Add a supported orchestration command for appending a completed external-agent message to an existing thread.
Suggested behavior:
- Requires an authenticated session with
orchestration:operate.
- The server derives the visible author label from authenticated client metadata rather than trusting caller-supplied attribution.
- Emits a durable, non-streaming message with an explicit external source/provenance field.
- Does not create a turn, alter
latestTurn, adopt or resume a provider session, create checkpoints, or invoke the thread's provider.
- Uses the existing
commandId idempotency behavior so a bridge retry cannot duplicate the message.
- Is available through the existing WebSocket orchestration RPC, so local, remote, relay, web, desktop, and mobile clients receive the normal event projection.
- Renders a small source label such as
Hermes bridge on web and mobile. Older messages without source metadata render unchanged.
I am deliberately not proposing that callers submit arbitrary role: "assistant" messages through the existing internal provider commands.
Why this matters
T3 is already a cockpit for several coding-agent providers. A narrow external-message primitive would let bridges, review agents, and orchestration tools return a result to the thread where the user asked for it, while preserving provenance and avoiding an unnecessary second provider turn.
It also makes the transcript honest: the response is shown as external agent output, not as if it came from Codex/Claude/etc. and not as if the user wrote it.
The bridge above provides a concrete working integration and currently documents this exact upstream limitation.
Smallest useful scope
One non-streaming text message only:
- existing, non-deleted thread;
- authenticated client label used as author;
- no attachments, tool calls, streaming, model switching, or provider-session import;
- no changes to provider adapters;
- optional provenance on the existing message/event schema;
- a compact author label on web and mobile;
- contract, decider/idempotency, projection/reducer, authorization, and rendering tests.
If maintainers prefer an activity/timeline-entry abstraction instead of extending message provenance, I am happy to implement that seam instead.
Alternatives considered
- Linked thread (current bridge behavior): safe and supported, but the answer is not visible where it was requested.
- Relay through
thread.turn.start: provider-neutral, but records the result as user input and triggers a new provider turn.
- Expose internal assistant delta/complete commands: rejected because those commands encode provider-owned streaming and turn lifecycle behavior.
- Prefix Markdown with an author name: renders everywhere but loses typed provenance and still stores the content as an unattributed assistant message.
- Write directly to T3 persistence: unsupported and unsafe for projections, remote clients, upgrades, and idempotency.
Risks or tradeoffs
- This crosses contracts, server decisions/projections, shared client state, web, and mobile even though the behavior is narrow.
- Attribution must come from authenticated session metadata; caller-controlled labels could spoof providers or users.
- External messages should not settle or reopen turns, affect checkpoints, or become resumable provider output.
- Consecutive messages and out-of-order bridge replies need deterministic timeline behavior.
- Command IDs provide duplicate suppression, but correlation IDs and hop limits remain the bridge's responsibility.
- Message provenance adds a small amount of snapshot/WebSocket data; the field should be optional and compact for backward compatibility.
Examples or references
Contribution
Per CONTRIBUTING.md, I am opening the scoped issue before sending code. If this direction fits T3, I can follow with a small focused PR and tests, or adjust to the extension seam maintainers prefer.
Before submitting
Area
packages/contracts or packages/shared
Problem or use case
An authenticated external agent can create or continue its own T3 thread, but it cannot publish a response into an existing thread owned by another provider without starting that provider again.
I maintain a working T3–Hermes bridge: https://github.com/m-check1B/t3-hermes-bridge
Today, when a user invokes Hermes from a Codex/Claude/Cursor/Grok/OpenCode thread, the bridge must create a separate linked Hermes thread. Relaying the result back with
thread.turn.startis not equivalent: it records the result as a user message and starts another provider turn, adding cost and potentially changing the response.The current internal assistant message commands are provider-owned lifecycle commands. Making them public would let an external client impersonate the thread provider and interfere with turn/session state, so the bridge intentionally does not use them.
Related but distinct:
This request is narrower: append one explicitly external, attributed message without starting or adopting a provider session.
Proposed solution
Add a supported orchestration command for appending a completed external-agent message to an existing thread.
Suggested behavior:
orchestration:operate.latestTurn, adopt or resume a provider session, create checkpoints, or invoke the thread's provider.commandIdidempotency behavior so a bridge retry cannot duplicate the message.Hermes bridgeon web and mobile. Older messages without source metadata render unchanged.I am deliberately not proposing that callers submit arbitrary
role: "assistant"messages through the existing internal provider commands.Why this matters
T3 is already a cockpit for several coding-agent providers. A narrow external-message primitive would let bridges, review agents, and orchestration tools return a result to the thread where the user asked for it, while preserving provenance and avoiding an unnecessary second provider turn.
It also makes the transcript honest: the response is shown as external agent output, not as if it came from Codex/Claude/etc. and not as if the user wrote it.
The bridge above provides a concrete working integration and currently documents this exact upstream limitation.
Smallest useful scope
One non-streaming text message only:
If maintainers prefer an activity/timeline-entry abstraction instead of extending message provenance, I am happy to implement that seam instead.
Alternatives considered
thread.turn.start: provider-neutral, but records the result as user input and triggers a new provider turn.Risks or tradeoffs
Examples or references
Contribution
Per
CONTRIBUTING.md, I am opening the scoped issue before sending code. If this direction fits T3, I can follow with a small focused PR and tests, or adjust to the extension seam maintainers prefer.