Skip to content

Dashboard webchat turns can enter message-tool-only mode without a send target #81092

@brandonlipman

Description

@brandonlipman

Summary

Dashboard/webchat-originated sessions can enter sourceReplyDeliveryMode: message_tool_only even when there is no external source conversation target for the message tool to infer.

When that happens, the prompt tells the model that visible replies must use message(action="send") and that the target defaults to the current source conversation. In a dashboard-only turn, the dynamic message tool does not have a usable currentChannelId, so message.send fails with:

Action send requires a target.

The assistant can then still produce a normal final answer such as Sent., which is related to the broader fail-closed issue in #49876, but the target-default mismatch here is narrower.

Observed example

In a dashboard session, the user asked the agent to describe its tools. The agent generated a good message(action="send") payload but omitted target, following the prompt guidance. The tool result was Action send requires a target., then the assistant finaled with Sent..

Root cause

The source reply delivery policy can choose message_tool_only for direct sessions from config or harness defaults without excluding internal dashboard/webchat turns.

For external providers, this is fine because the runtime can pass a source target such as currentChannelId / OriginatingTo. For dashboard-only webchat turns, Provider, Surface, and OriginatingChannel are internal webchat, and there is no external OriginatingTo. The low-level target validator is correct to reject the send; the policy should not have selected message-tool-only delivery for that source in the first place.

Proposed fix

Keep automatic final delivery for internal dashboard/webchat-only turns, even if config or a harness default prefers message-tool delivery. Preserve message_tool_only when webchat is merely operating on behalf of an external origin or explicit deliver route.

Concretely:

  • Detect internal-only webchat sources in resolveSourceReplyDeliveryMode.
  • If the mode would be message_tool_only, coerce it to automatic when there is no external origin/deliver route.
  • Leave message.send target validation unchanged.
  • Add regression coverage for:
    • direct dashboard/webchat turns with global or harness message_tool defaults falling back to automatic
    • webchat routing an external source remaining message_tool_only
    • full dispatchReplyFromConfig dashboard/webchat turn queueing a normal final reply instead of suppressing it

Local validation

I tested this locally with a small patch following the above approach:

pnpm test src/auto-reply/reply/source-reply-delivery-mode.test.ts src/auto-reply/reply/dispatch-from-config.test.ts
# 2 Vitest shards passed: 19 policy tests, 107 auto-reply dispatch tests

pnpm exec oxfmt --check --threads=1 src/auto-reply/reply/source-reply-delivery-mode.ts src/auto-reply/reply/source-reply-delivery-mode.test.ts src/auto-reply/reply/dispatch-from-config.test.ts
# All matched files use the correct format.

pnpm tsgo:core:test
# passed

Repo-wide pnpm format:check still reports unrelated pre-existing formatting issues in other files; the three changed files pass oxfmt --check directly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions