Skip to content

[Feature]: Append attributed external-agent messages without starting a provider turn #5699

Description

@m-check1B

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I am describing a concrete problem or use case, not just a vague idea.

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

  1. Linked thread (current bridge behavior): safe and supported, but the answer is not visible where it was requested.
  2. Relay through thread.turn.start: provider-neutral, but records the result as user input and triggers a new provider turn.
  3. Expose internal assistant delta/complete commands: rejected because those commands encode provider-owned streaming and turn lifecycle behavior.
  4. Prefix Markdown with an author name: renders everywhere but loses typed provenance and still stores the content as an unattributed assistant message.
  5. 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

  • I would be open to helping implement this.

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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