#8095: fix(sessions): include accountId in deliveryContext for outbound per-peer DM sessions
gateway
stale
## Summary
- When `ensureOutboundSessionEntry()` created a per-peer DM session (e.g. via an agent's messaging tool), it called `recordSessionMetaFromInbound()` which only populated the session's `origin` metadata — it did **not** set `deliveryContext`, `lastChannel`, `lastTo`, `lastAccountId`, or `lastThreadId`
- This caused per-peer DM sessions to be missing `accountId` in their delivery context, so subsequent deliveries routed through the default account instead of the bound one (wrong bot)
- Fix: replace `recordSessionMetaFromInbound()` with `updateLastRoute()` which properly sets the full delivery context (including `accountId`) and also handles origin metadata when `ctx` is provided
**Repro:** agent with a Telegram binding (`accountId: "page"`) + `session.dmScope: "per-peer"` → cron/tool sends to a peer → per-peer session created without `accountId` → reply delivered through wrong bot.
## Test plan
- [x] `pnpm build` passes
- [x] `pnpm check` (lint + format) passes
- [x] `pnpm vitest run src/infra/outbound/` — all 82 tests pass
- [x] `pnpm vitest run src/gateway/server-methods/` — all 32 tests pass
- [x] `pnpm vitest run src/config/sessions.test.ts` — all 26 tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR fixes missing `accountId` (and related route metadata) when creating per-peer outbound DM sessions by switching `ensureOutboundSessionEntry()` to call `updateLastRoute()` instead of `recordSessionMetaFromInbound()`. `updateLastRoute()` writes a normalized `deliveryContext` (`channel`, `to`, `accountId`, `threadId`) and also derives origin metadata from `ctx`, preventing replies from routing through the default account/bot. The gateway send mirroring test was updated accordingly to mock/expect `updateLastRoute()`.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk.
- The change is narrowly scoped (swap to `updateLastRoute` and pass the needed fields) and aligns with existing session-store behavior; the updated test reflects the new dependency and the described test plan indicates targeted Vitest suites were run.
- No files require special attention
<!-- greptile_other_comments_section -->
**Context used:**
- Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=fd949e91-5c3a-4ab5-90a1-cbe184fd6ce8))
- Context from `dashboard` - AGENTS.md ([source](https://app.greptile.com/review/custom-context?memory=0d0c8278-ef8e-4d6c-ab21-f5527e322f13))
<!-- /greptile_comment -->
Most Similar PRs
#4693: fix: keep main session displayName on outbound sends
by ManojINaik · 2026-01-30
86.2%
#8507: fix: preserve accountId for multi-account agent-to-agent messaging
by djh58 · 2026-02-04
80.4%
#9051: fix(sessions): respect dmScope config in CLI agent commands
by benleavett · 2026-02-04
79.4%
#13580: fix(telegram): skip updateLastRoute when dmScope isolates DM sessions
by lailoo · 2026-02-10
79.0%
#8357: fix(gateway): preserve accountId across gateway restarts
by alfredo-feat-volky · 2026-02-03
78.8%
#7868: Default DM sessions to per-channel scope (avoid webchat contention)
by Smile232323 · 2026-02-03
78.6%
#13477: routing: normalize account ID matching for agent bindings
by davidahmann · 2026-02-10
77.8%
#17527: fix(gateway): allow WebChat to attach to main session regardless of...
by Glucksberg · 2026-02-15
77.6%
#9437: fix: normalize accountId in binding matching for consistent routing
by dbottme · 2026-02-05
75.7%
#6007: fix(cron): auto-map agentId to accountId for Discord deliveries
by dwfinkelstein · 2026-02-01
75.7%