Commit Graph

15 Commits

Author SHA1 Message Date
Peter Steinberger
3e2bc28e51 fix: forward chat images to acp dispatch 2026-04-23 19:06:19 +01:00
Omar Shahine
0362f21784 fix: sendPolicy deny should suppress delivery, not inbound processing (#53328) (#65461)
* fix: sendPolicy deny suppresses delivery, not inbound processing (#53328)

Previously, sendPolicy "deny" returned early before the agent dispatch,
preventing the agent from ever seeing the message. This broke the use
case of an agent listening on WhatsApp groups with sendPolicy: deny to
read messages without replying — the agent couldn't read them at all.

Move the deny gate from before the agent dispatch to after it. The agent
now processes inbound messages normally (context, memory, tool calls),
but all outbound delivery paths are suppressed: final replies, tool
results, block replies, working status, plan updates, typing indicators,
and TTS payloads.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: propagate sendPolicy to ACP tail dispatch instead of hardcoded allow

The ACP tail dispatch path (ctx.AcpDispatchTailAfterReset) was passing
sendPolicy: "allow" unconditionally, which would bypass delivery
suppression in a /reset <tail> turn when the session has sendPolicy deny.

Pass through the resolved sendPolicy so the tail dispatch respects it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: guard before_dispatch hook and ACP tail dispatch under sendPolicy deny

before_dispatch handled replies were leaking through sendFinalPayload
before the suppressDelivery guard was checked. ACP tail dispatch (from
/new <tail>) was being rejected by acp-runtime.ts deny checks instead
of proceeding with delivery suppression handled downstream.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* auto-reply: propagate deny suppression to reply_dispatch

* fix(acp): suppress onReplyStart when user delivery is denied

When sendPolicy resolves to "deny", ACP tail dispatch still invoked
onReplyStart via startReplyLifecycle before the suppressUserDelivery
check. Channels wire onReplyStart to typing indicators, so deny-scoped
sessions could still emit outbound typing events on /reset <tail>
flows and command bypass paths.

Gate startReplyLifecycleOnce on suppressUserDelivery so the lifecycle
is marked started but the callback is skipped. Payload delivery was
already suppressed; this closes the typing-indicator leak flagged by
Codex review (PR #65461 P1/P2).

* fix(acp): route non-tail deny turns through ACP when suppression is wired

tryDispatchAcpReplyHook was returning early for non-tail, non-command ACP
turns under sendPolicy: "deny", causing ACP-bound sessions to fall back
to the embedded reply path instead of flowing through acpManager.runTurn.
That diverged ACP session state, tool calls, and memory whenever
delivery suppression was active.

Now the early-return only fires when sendPolicy is "deny" AND the event
lacks suppressUserDelivery — i.e., when downstream delivery suppression
is not wired up. When suppressUserDelivery is set, dispatch-acp-delivery
already drops outbound sends (see onReplyStart / deliver guards), so ACP
can safely run the turn with state consistency preserved.

Existing behavior preserved:
- Command bypass still overrides deny
- Tail dispatch still overrides deny
- Plain-text deny turns without suppression still short-circuit

Addresses Codex bot P1 feedback on #65461.

* fix: gate empty-body typing indicator behind suppressTyping (#53328)

* fix: guard plugin-binding + fast-abort outbound paths under sendPolicy deny

The original PR computed suppressDelivery inside the try block, which was
after two outbound paths:

1. The plugin-owned binding block (sendBindingNotice calls for
   unavailable/declined/error outcomes, plus the plugin's own "handled"
   outcome) ran before the suppressDelivery flag existed, so plugin
   notices still leaked under deny.
2. The fast-abort path dispatched "Agent was aborted." via
   routeReplyToOriginating / sendFinalReply before the flag existed.

Move resolveSendPolicy() above the plugin-binding block so suppressDelivery
covers every outbound path downstream, matching the PR description's claim
that "all outbound paths are guarded by the flag."

Plugin-bound inbound handling under deny: plugin handlers can emit
outbound replies we cannot rewind, so skip the claim hook entirely under
deny and fall through to normal (suppressed) agent processing.
touchConversationBindingRecord still runs so binding activity stays
tracked.

Fast-abort under deny: still run the abort and record the completed
state, just don't emit the abort reply.

Tests:
- suppresses the fast-abort reply under sendPolicy deny
- delivers the fast-abort reply normally when sendPolicy is allow
  (regression guard)
- skips plugin-bound claim hook under deny and falls through to
  suppressed agent dispatch

Addresses Codex review findings on PR #65461.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Lobster <lobster@shahine.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:42:25 -07:00
Peter Steinberger
02c08b3929 refactor: dedupe shared normalizer readers 2026-04-07 08:40:35 +01:00
Peter Steinberger
bf269e7b67 test(plugin-sdk): tighten ACP command dispatch guards 2026-04-06 02:43:14 +01:00
Peter Steinberger
adbcfbe2bb perf: skip acp runtime work for no-media and no-command turns 2026-04-05 17:58:38 +01:00
Peter Steinberger
82ce30b789 feat(plugins): add reply dispatch hook 2026-04-05 16:11:31 +01:00
Peter Steinberger
fb61986767 refactor(acpx): embed ACP runtime in plugin 2026-04-05 15:40:11 +01:00
Peter Steinberger
3d55b28853 style: wrap long runtime and test lines 2026-04-04 15:24:50 +09:00
Vincent Koc
32dd0aa7e7 fix(plugin-sdk): lazy acp runtime testing merge 2026-04-04 14:43:53 +09:00
Peter Steinberger
8861cdbb6f refactor(plugin-sdk): untangle extension test seams 2026-03-29 23:43:53 +01:00
Peter Steinberger
fcabecc9a4 fix: remove duplicate plugin sdk exports 2026-03-20 16:52:10 +00:00
Peter Steinberger
cb89325cd8 fix: restore latest main gate 2026-03-20 16:46:34 +00:00
Vincent Koc
aa78a0c00e refactor(plugin-sdk): formalize runtime contract barrels 2026-03-20 09:30:34 -07:00
Vincent Koc
fbd88e2c8f Main recovery: restore formatter and contract checks (#49570)
* Extensions: fix oxfmt drift on main

* Plugins: restore runtime barrel exports on main

* Config: restore web search compatibility types

* Telegram: align test harness with reply runtime

* Plugin SDK: fix channel config accessor generics

* CLI: remove redundant search provider casts

* Tests: restore main typecheck coverage

* Lobster: fix test import formatting

* Extensions: route bundled seams through plugin-sdk

* Tests: use extension env helper for xai

* Image generation: fix main oxfmt drift

* Config: restore latest main compatibility checks

* Plugin SDK: align guardrail tests with lint

* Telegram: type native command skill mock
2026-03-18 00:30:01 -07:00
Peter Steinberger
9ebe38b6e3 refactor: untangle remaining plugin sdk boundaries 2026-03-16 21:16:32 -07:00