fix(outbound): reject reserved Telegram targets before directory/default fallback#94107
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 25, 2026, 11:33 AM ET / 15:33 UTC. Summary PR surface: Source +268, Tests +576, Docs +4. Total +848 across 19 files. Reproducibility: yes. Source inspection shows current main and v2026.6.10 still normalize Telegram Review metrics: 2 noteworthy metrics.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land this PR or a maintainer-approved equivalent after explicitly accepting the Do we have a high-confidence way to reproduce the issue? Yes. Source inspection shows current main and v2026.6.10 still normalize Telegram Is this the best way to solve the issue? Yes for the code shape, with a maintainer gate. A plugin-owned literal list plus core directory-first/fail-closed resolution is cleaner than hardcoding Telegram names in scattered callers, but the new API and compatibility policy still need explicit maintainer acceptance. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 82a6a57330cd. Label changesLabel justifications:
Evidence reviewedPR surface: Source +268, Tests +576, Docs +4. Total +848 across 19 files. View PR surface stats
Security concerns:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@obviyus when you have a chance, could you please take a maintainer look at this Telegram target-resolution fix? The PR preserves configured Telegram targets named like reserved literals ( |
Live Telegram proofLive proof run against PR head
Local artifact paths:
|
The sync reserved-literal check in resolveOutboundTargetWithPlugin was suppressing heartbeat routes to directory entries whose names match reserved literals (e.g., a Telegram group named "current"). Skip the check for heartbeat mode so the async resolveChannelTarget → resolveMessagingTarget path can do directory-first lookup before deciding.
…literal rejection in resolveMessagingTarget Move the reserved-literal check from before directory lookup to after directory miss, so configured Telegram groups/channels whose directory key is a reserved word (current, self, this, me) still resolve through the directory before failing closed. The reserved check now runs only after the directory returns no match and before plugin fallback resolution. Update the regression test to verify directory-first ordering: a configured directory entry named current resolves successfully, and a directory miss with a reserved literal fails with the descriptive error.
…esolver In resolveAgentDeliveryPlanWithSessionRoute, reserved-literal errors from the sync outbound target check are no longer treated as fatal. Instead, the path proceeds to resolveOutboundSessionRoute which calls resolveMessagingTarget, already fixed to do directory-first lookup before rejecting reserved literals. This preserves configured Telegram directory entries named like reserved words (current, self, this, me) through the explicit agent/gateway delivery path. Update docs to reflect directory-first ordering.
Keep reserved-target detection behavior unchanged while routing callers through a shared helper so future changes stay localized. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Landed via rebase onto main.
Thanks @zhangguiping-xydt! |

Summary
What problem does this PR solve?
current,self,this, andme.targetResolutionErroror after route canonicalization misses.Why does this matter now?
targetResolutionErrorfirst could still see the original unresolved target inresolvedTo, and isolated cron delivery could return the reserved-target error before the directory-aware resolver ran.What is the intended outcome?
currentresolves to a configured Telegram directory entry when that entry exists.telegram:currentfollows the same directory-first behavior as barecurrent.currentfails closed with a reserved-target error when there is no matching Telegram directory entry.resolvedTo, and isolated cron delivery gives configured directory entries the same reserved-literal preservation before failing closed on misses.What is intentionally out of scope?
What does success look like?
currentkeep working.What should reviewers focus on?
resolveMessagingTarget: directory matches are preserved before reserved-literal rejection, and reserved literals cannot continue to plugin/default fallback after a miss.resolveAgentDeliveryPlanWithSessionRoute: it now uses the directory-aware resolver and clearsresolvedToon target-resolution errors.Fix classification: root-cause compatibility and safety fix for Telegram outbound target resolution.
Root cause: reserved-literal rejection and session-route planning were split across sync outbound validation, the directory-aware resolver, gateway delivery planning, and isolated cron delivery. Core could not consistently distinguish a real Telegram directory entry named
currentfrom an unsafe session-reference literal, and route/error paths could keep raw reserved input after validation failed or route canonicalization missed.Why this is the root cause: the resolver must first identify whether the input is a configured destination, then apply provider-reserved literal safety before plugin/default resolution; delivery planning and cron delivery must only expose destinations that have passed the directory-aware target owner.
Related PR scan: this is a repair/update to the existing PR #94107 for #91372. I did not open a competing PR; the remaining public-contract questions are disclosed below for maintainer review.
currentcould be rejected before lookup while directory misses or route misses could still leak raw reserved input across gateway or cron delivery paths.resolveMessagingTargetis kept as the source of truth for configured destinations and reserved-literal misses. Directory lookup owns real destinations, reserved-literal rejection owns misses before plugin/default resolution, and gateway/cron delivery only keep validated targets after successful directory-aware resolution.sync-main, focused resolver/outbound validation, cron delivery validation, raw-channel-fetch boundary lint, and ClawSweeper commit review all passed on the current repair branch.resolveMessagingTarget; Telegram only declares its reserved literals/capabilities, and gateway/session-route delivery consumes the resolver result instead of duplicating destination ownership.expect.any(Function)shape assertion is confined to test fixtures, explicitreturn undefinedkeeps non-matching normalization branches clear, and fallback wording describes the old path being blocked rather than a new fallback being added.target="current"strings #91372 rather than opening a competing PR; the optional pluginreservedLiteralscontract remains explicitly disclosed for maintainer review.Linked context
Closes #91372
Related #94107
Real behavior proof (required for external PRs)
currentshould route to a real configured Telegram group/channel named or handled ascurrent, but still fail closed when no directory entry exists.{ "proof": "telegram-current-directory-vs-reserved-miss", "registry": { "pluginId": "telegram", "channelId": "telegram", "source": "proof", "hasDirectory": true, "hasSessionRoute": true, "reservedLiterals": ["current", "self", "this", "me"] }, "directoryHit": { "ok": true, "target": { "to": "telegram:@current", "kind": "group", "display": "current", "source": "directory", "resolutionSource": "directory" } }, "prefixedHit": { "ok": true, "target": { "to": "telegram:@current", "kind": "group", "display": "current", "source": "directory", "resolutionSource": "directory" } }, "reservedMiss": { "ok": false, "error": "Reserved target \"current\" for Telegram cannot be used as a literal destination. Provide an explicit id or handle. Hint: <chatId>" }, "gatewayPlan": { "resolvedChannel": "telegram", "resolvedTo": "telegram:@current", "deliveryTargetMode": "explicit", "targetResolutionError": null }, "heartbeatReservedMiss": { "channel": "none", "reason": "no-target" }, "checks": { "directoryHitOk": true, "prefixedHitOk": true, "directoryMissFailClosed": true, "gatewayResolvedFromDirectory": true, "heartbeatReservedMissFailClosed": true } }currentand prefixedtelegram:currentboth resolve through the Telegram directory when a config-backed directory entry exists; the same resolved target flows into gateway delivery planning astelegram:@current; a missing directory entry returns the reserved-target error instead of normalizing to an unintended literal destination; heartbeat delivery with missingcurrentreturnschannel: "none"/reason: "no-target"before Telegram session-route canonicalization.currentbeing normalized astelegram:@currentbefore directory/miss handling; after the resolver change, reserved literals stay on the directory-first path and fail closed only after a directory miss.Tests and validation
Which commands did you run?
Result:
What regression coverage was added or updated?
target-resolver.test.tsnow covers directory-first resolution for bothcurrentandtelegram:current, fail-closed behavior after a directory miss, and exact-only reserved-literal directory preservation somedoes not match a substring-only directory entry such asmemes.agent-delivery.test.tsnow covers clearingresolvedTowhen route preparation/target validation fails, preserving the stored target error, and preserving the directory-resolved target when session-route canonicalization itself cannot produce a route.delivery-target.test.tsnow covers isolated cron delivery resolving a reserved explicit Telegram target through a configured directory entry before returning any reserved-target error.targets.test.tsnow covers heartbeat delivery preserving directory hits for reserved literals and failing closed when a heartbeat reserved literal misses the directory.What failed before this fix, if known?
currentreturned a reserved-target error before the resolver checked configured Telegram directory entries.resolvedTovalue visible next totargetResolutionError.current.mecould match a substring-only entry such asmemesbefore the fail-closed reserved check ran.If no test was added, why not?
Test guardrail rationale:
Risk checklist
Did user-visible behavior change? (
Yes/No)Yes. A real Telegram directory entry named/handled as
current,self,this, ormecan now be reached instead of being rejected before lookup; missing entries still fail closed.Did config, environment, or migration behavior change? (
Yes/No)No.
Did security, auth, secrets, network, or tool execution behavior change? (
Yes/No)No new network/auth/tool execution behavior. The safety posture is preserved for directory misses and strengthened on validation errors by clearing
resolvedTo.What is the highest-risk area?
Telegram target routing order for provider-reserved literals, especially compatibility between real directory entries and fail-closed reserved words.
How is that risk mitigated?
The resolver and delivery paths only allow reserved literals after an exact concrete directory match on id/name/handle after prefix stripping. If the directory does not contain an exact matching Telegram group/channel, the reserved literal still fails closed before plugin/default resolution. Gateway and cron delivery paths no longer expose the original unsafe target after validation failure or route canonicalization miss.
Scope boundary and patch quality notes:
Current review state
What is the next action?
Ready for maintainer review after the PR branch/body update and fresh checks.
What is still waiting on author, maintainer, CI, or external proof?
No known author-side code or test follow-up remains. The only proof limitation is that the production-path Telegram proof does not send a live Telegram network message. Maintainers still need to accept the existing SDK contract and compatibility choice for reserved Telegram literals.
Which bot or reviewer comments were addressed?
triage: mock-only-proof: addressed with production-path proof using the real Telegram plugin, production resolver, and gateway delivery-plan path; live Telegram network send remains disclosed as not tested.target: "current": addressed with current-head production-path proof showing directory hit forcurrentand a fail-closed reserved error on directory miss.currentto Telegram route parsing.src/infra/outbound/targets.test.ts: addressed with focused heartbeat coverage for both directory-hit routing and directory-miss fail-closed behavior.heartbeatReservedMissreturnschannel: "none"andreason: "no-target"for missingcurrent.proof: sufficient, while live Telegram Desktop proof is disclosed as optional maintainer-requested transport evidence.current.menot matchingmemes.