#17538: feat(sessions): add resolvedModel field
gateway
commands
agents
size: M
Cluster:
Session Management Enhancements
## Summary
- Adds `resolvedModel` field to `SessionEntry` for tracking the actual model resolved by the provider (e.g. when using `openrouter/auto`, the resolved model is `anthropic/claude-haiku-4.5`)
- Threads `resolvedModel` through the write path: `run.ts` → `agent-runner.ts` → `session-run-accounting.ts` → `session-usage.ts` → `sessions.json`
- Displays `resolvedModel` in TUI footer and Telegram response usage when it differs from the configured model
- Read-side propagation is **automatic** — one string added to `SESSION_STATUS_FIELD_NAMES` (from PR #17530)
**Depends on:** #17530 (centralize session status fields) and pi-mono PR (for `lastAssistant.resolvedModel` source data)
## Files changed
| File | Change |
|------|--------|
| `src/config/sessions/types.ts` | Add `resolvedModel?: string` to `SessionEntry` |
| `src/config/sessions/session-status.ts` | Add `"resolvedModel"` to `SESSION_STATUS_FIELD_NAMES` — auto-propagates to ALL consumers |
| `src/agents/pi-embedded-runner/types.ts` | Add `resolvedModel?: string` to `EmbeddedPiAgentMeta` |
| `src/agents/pi-embedded-runner/run.ts` | Set `agentMeta.resolvedModel` from `lastAssistant` |
| `src/auto-reply/reply/session-usage.ts` | Add `resolvedModel` param, persist in both patch paths |
| `src/auto-reply/reply/session-run-accounting.ts` | Pass `resolvedModel` through |
| `src/auto-reply/reply/agent-runner.ts` | Pass `resolvedModel` to persistence + show in Telegram footer |
| `src/tui/tui.ts` | Show `→ resolvedModel` in TUI footer when differs from model |
| `src/agents/tools/sessions-list-tool.ts` | Add defensive extraction for `resolvedModel` |
| `src/auto-reply/reply/session-usage.test.ts` | Add 2 tests for `resolvedModel` persistence |
## Data flow
```
pi-ai output (lastAssistant.resolvedModel — from pi-mono PR)
→ run.ts: agentMeta.resolvedModel
→ agent-runner.ts: persistRunSessionUsage({ resolvedModel })
→ session-run-accounting.ts: pass-through
→ session-usage.ts: patch.resolvedModel = params.resolvedModel ?? entry.resolvedModel
→ sessions.json via mergeSessionEntry
READ side (automatic via SESSION_STATUS_FIELD_NAMES):
pickSessionStatus() extracts it → all consumers get it via spread
→ TUI: applySessionStatusFields → SessionInfo → updateFooter displays it
```
## Display format
- **TUI footer**: `openrouter/openrouter/auto → anthropic/claude-haiku-4.5`
- **Telegram**: `Usage: 10k in / 1k out · est $0.001 · openrouter/auto → anthropic/claude-haiku-4.5 · session agent:main:main`
## Test plan
- [x] New tests for `resolvedModel` persistence and preservation
- [x] `pnpm build` — clean
- [x] `pnpm check` (format + tsgo + lint) — clean
- [x] `pnpm test` — all 815 test files, 6018 tests pass
## Contribution checklist
- [x] Local validation: `pnpm build && pnpm check && pnpm test` — all passing
- [x] Focused scope: single feature (resolvedModel tracking and display)
- [x] Clear "what" + "why" in description
- [x] AI-assisted (Claude Code) — fully tested, code reviewed and understood by author
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR adds a `resolvedModel` field to track the actual model resolved by providers (e.g. when `openrouter/auto` resolves to `anthropic/claude-haiku-4.5`). The feature is well-structured:
- **Type additions**: `resolvedModel` added to `SessionEntry` and `EmbeddedPiAgentMeta`
- **Centralized status fields**: New `session-status.ts` module with `SESSION_STATUS_FIELD_NAMES`, `pickSessionStatus`, and `applySessionStatusFields` — a clean refactor that eliminates repetitive field-by-field extraction across multiple consumers
- **Write path**: Threads `resolvedModel` from pi-ai output through `run.ts` → `agent-runner.ts` → `session-run-accounting.ts` → `session-usage.ts` → `sessions.json`
- **Display**: Shows `model → resolvedModel` in TUI footer and Telegram usage line when the resolved model differs from the configured model
- **Tests**: Good coverage for the new persistence logic and session-status utilities
**Issue found:**
- In `session-usage.ts`, `resolvedModel` uses `params.resolvedModel ?? entry.resolvedModel` in both patch paths. This causes stale `resolvedModel` values to persist when switching from a resolving provider (e.g. `openrouter/auto`) to a direct model (e.g. `openai/gpt-4`), since `params.resolvedModel` will be `undefined` and the fallback preserves the old value. This differs from `model`/`modelProvider` which are always populated on every run.
<h3>Confidence Score: 3/5</h3>
- Mostly safe to merge but has a stale-data bug in resolvedModel persistence that would cause incorrect display after model switches.
- The centralized session-status refactor is clean and well-tested. The resolvedModel threading is correct end-to-end except for the persistence fallback in session-usage.ts which preserves stale resolvedModel values when switching to a non-resolving provider. The bug is user-facing (incorrect TUI footer and Telegram usage display) but not destructive.
- Pay close attention to `src/auto-reply/reply/session-usage.ts` — the `params.resolvedModel ?? entry.resolvedModel` pattern on lines 96 and 119 causes stale values to persist across model switches.
<sub>Last reviewed commit: 4f46d8a</sub>
<!-- greptile_other_comments_section -->
<sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#21791: feat(TUI): show main agent model in status footer
by chansuke · 2026-02-20
81.6%
#6673: fix: preserve allowAny flag in createModelSelectionState for custom...
by tenor0 · 2026-02-01
78.0%
#21932: fix(tui): eliminate stale model indicator lag in TUI
by graysurf · 2026-02-20
77.8%
#21615: fix(tui): preserve main session model during heartbeat model override
by lailoo · 2026-02-20
77.7%
#21847: fix(session): /new and /reset no longer carry over model overrides
by hydro13 · 2026-02-20
76.9%
#17530: refactor(sessions): centralize session status field extraction
by Facens · 2026-02-15
76.7%
#8083: fix(tui): update model status immediately after /model command
by rohanjangala · 2026-02-03
76.7%
#6750: fix(tui): show session model overrides in status bar
by ewijaya · 2026-02-02
76.6%
#19328: Fix: preserve modelOverride in agent handler (#5369)
by CodeReclaimers · 2026-02-17
76.5%
#21088: fix: sessions_sspawn model override ignored for sub-agents
by Slats24 · 2026-02-19
76.4%