Skip to content

Expose a public external-message injection API for live TUI sessions #181

Description

@pritishd

Problem Statement

I want to steer long-running pydantic-deep TUI sessions from an external collaboration surface without switching back to the terminal.

In our current prototype, that external surface is Slack: the agent can move a conversation into a Slack thread, and replies in that thread can steer or continue the live TUI session. The same pattern seems useful beyond Slack: Jira comments, Teams/Discord threads, n8n workflows, CI/build monitors, webhook services, or any human/event channel that should be able to send a follow-up into a running agent.

pydantic-deep already has the right internal behavior via MessageQueue: steering messages can be injected before the next model request, and follow-ups can be queued while the agent is running.

What would help is a documented extension point for client-owned integrations to submit a message into the active TUI session. In our prototype, we connected an external event source to the queue through TUI/app internals.

That works well as a proof of concept, but a small public injection hook would make this pattern easier to support without depending on private implementation details. The external integration would still own transport concerns like Slack/Jira/webhook polling, authentication, deduplication, and reactions; pydantic-deep would only provide the safe session injection point.

Proposed Solution

Expose a small public bridge API on the TUI/session that lets client-owned integrations submit external messages into the active agent session.

Rough API sketch:

  await app.inject_external_message(
      text="Please also check MR 123",
      source="slack",
      mode="auto",  # auto | steer | follow_up | start_if_idle
      metadata={
          "conversation_id": "C123:1784850025.868319",
          "message_id": "1784850124.802469",
      },
  )

Possible behavior:

  • If an agent run is active:

    • mode="steer" injects before the next model request.
    • mode="follow_up" queues the message for when the current run would otherwise stop.
    • mode="auto" uses the same policy the TUI already uses internally.
  • If the agent is idle:

    • mode="start_if_idle" or auto can start/resume a run with the external message.
  • The submitted message should appear in the TUI transcript like a user message, ideally with source metadata available for logs/tracing.

An alternative shape would also work:

  bridge = app.external_message_bridge

  await bridge.submit(
      text="Please also check MR 123",
      source="jira",
      mode="auto",
      metadata={"issue": "PIPE-1234"},
  )

The key request is the stable public injection surface, not the exact naming.

Alternatives Considered

We built a Slack thread bridge outside pydantic-deep. It polls a Slack thread, reacts to user replies, and injects the reply into the running TUI session through MessageQueue.

This works well as a proof of concept, but it currently relies on private TUI details:

  • accessing the app’s queue directly,
  • detecting whether an agent run is active,
  • deciding whether to call steer() or follow_up(),
  • waking the idle TUI by reaching into screen internals.

Another option would be for pydantic-deep to implement Slack/Jira/etc. integrations directly, but I do not think that is the right layer. The client application should own the external transport and infrastructure. pydantic-deep only needs to provide the supported session injection API.

Additional Context

This is not a request for Slack support. Slack is only our current transport.

The broader use case is external human/event steering for long-running agents. For example:

  • Continue a TUI coding session from a Slack thread.
  • Let a Jira comment steer an agent working on that issue.
  • Let an n8n workflow or webhook submit a follow-up.
  • Let a CI/build monitor wake the agent when a service is ready or a test fails.
  • Allow group collaboration around one agent session from a shared chat thread.

One useful side effect we observed: the external thread becomes another source of lightweight session context. After restarting a session, the agent can inspect the thread and recover what the user was trying to do, alongside pydantic-deep memory.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

Status
In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions