Skip to content

feat(save-mode): add Save Mode cost controls — FE + local dev wiring#2888

Open
ricardo-leiva wants to merge 2 commits into
PostHog:mainfrom
ricardo-leiva:feat/save-mode-cost-controls
Open

feat(save-mode): add Save Mode cost controls — FE + local dev wiring#2888
ricardo-leiva wants to merge 2 commits into
PostHog:mainfrom
ricardo-leiva:feat/save-mode-cost-controls

Conversation

@ricardo-leiva

@ricardo-leiva ricardo-leiva commented Jun 24, 2026

Copy link
Copy Markdown

Problem

Heavy AI coding sessions cost real money. A user running Opus at max effort for 10+ turns is spending ~$0.80 per session — and the agent is doing routine file reads and edits where Opus quality is overkill. There's no way to dial this back without leaving the app.

How it works

                         COST PER TURN
 ┌──────────────────────────────────────────────────────────────┐
 │  [  model price  ] × [  input tokens  +  output tokens  ]    │
 │        ▲                    ▲                   ▲            │
 │  Lever 1: downshift   Lever 3: cache      Lever 2: effort    │
 │  Opus → Sonnet (~3×)  TTL 1h (gateway)   cap + terse prompt  │
 └──────────────────────────────────────────────────────────────┘

  OFF  ────────────────────────────────────▶  Full power, no changes.

  BALANCED  ──────────────────────────────▶  Keep model. Cap effort → high
                                             (removes xhigh/max think budgets).
                                             Add terse system reminder.
                                             Est. savings: 20–40%.

  MAX SAVINGS  ───────────────────────────▶  Downshift Opus → Sonnet.
                                             Cap effort → medium.
                                             Add terse system reminder.
                                             Est. savings: 50–70%.

The toggle lives next to the reasoning-level selector in both the task-input bar and the live session bar. Setting is persisted in settingsStore and applied immediately to the active session when changed.

Request flow

image

Changes

New files:

  • packages/core/src/save-mode/saveMode.ts — pure resolveSaveMode() resolver + tests
  • packages/core/src/save-mode/budget.tsevaluateBudget() helper + tests
  • packages/ui/src/features/sessions/components/SaveModeToggle.tsx — toggle UI component
  • packages/ui/src/features/sessions/hooks/useApplySaveMode.ts — wraps one service call (AGENTS.md rule 5)
  • scripts/dev-cost-controls.sh — local dev helper

Core wiring (task start):

  • shared/task-creation-domain.tssystemPromptOverride field in TaskCreationInput
  • core/task-detail/taskInput.ts → forwards it from PrepareTaskInputOptions
  • core/task-detail/taskCreationSaga.ts → passes to ConnectParams
  • core/sessions/sessionService.tsConnectParams.systemPromptOverride, createNewLocalSession, _saveModeBaselines, applySaveMode()

Agent wiring:

  • agent/adapters/claude/session/options.tssaveModeHeaders in Options
  • agent/adapters/claude/claude-agent.ts → passes saveModeHeaders through
  • agent/adapters/claude/types.tsNewSessionMeta.saveModeHeaders
  • agent/utils/gateway.tsLLM_GATEWAY_BASE_URL env override
  • workspace-server/services/agent/agent.tsSessionConfig.saveModeHeaders, wired into newSession/resumeSession _meta
  • workspace-server/services/agent/schemas.ts → Zod schema for saveModeHeaders

UI:

  • ui/features/sessions/components/SessionView.tsxSaveModeToggle in reasoningSelector
  • ui/features/task-detail/components/TaskInput.tsxSaveModeToggle in reasoningSelector
  • ui/features/task-detail/hooks/useTaskCreation.ts → computes saveModeResult, passes systemPromptOverride, stamps save_mode on TASK_CREATED event
  • ui/features/settings/settingsStore.tssaveMode: SaveMode + setSaveMode + persistence
  • ui/features/settings/sections/GeneralSettings.tsx → save mode in Settings panel
  • ui/features/home/hooks/useRunWorkstreamAction.ts → save mode for workstream tasks

Also fixes: missing BROWSER_TAB_OPENED / LINK_CLICKED_IN_CHAT analytics event types in @posthog/shared (were used in the browser feature but never registered in the schema).

Screenshot 2026-06-23 at 5 15 59 PM Screenshot 2026-06-23 at 5 15 23 PM Screenshot 2026-06-23 at 5 16 09 PM

How did you test this?

Automated tests run and passing:

  • packages/core/src/save-mode/saveMode.test.ts — 5 cases: off pass-through, balanced caps effort, max_save downshifts model, never raises low effort, perTaskFullPower bypass
  • packages/core/src/save-mode/budget.test.ts — 5 cases: disabled/no cap, ok, warn@70%, engage@85%, blocked@100%
  • Full pnpm typecheck passes across all packages (including apps/web and apps/code)
  • pnpm --filter @posthog/core test run — 1610 tests, all pass

Manual testing by human:

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Built with Claude Code (claude-sonnet-4-6) across two sessions. The implementation covers:

  • Pure resolveSaveMode function (testable, host-neutral) computing effective model/effort/reminder
  • Gateway wiring via saveModeHeaders (x-posthog-property-* headers) fed to the LLM Gateway in a companion BE PR
  • Architecture review finding fixes: moved multi-store hook logic to SessionService.applySaveMode() (AGENTS.md rule 5), replaced dead SETTING_CHANGED event with typed SAVE_MODE_CHANGED, fixed claude-fable downshift target to claude-sonnet-4-6
  • Branch was rebased off main (not feat/skills-improvements) to avoid contamination

@greptile-apps

greptile-apps Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "feat(save-mode): add Save Mode cost cont..." | Re-trigger Greptile

Comment thread packages/ui/src/features/task-detail/hooks/useTaskCreation.ts
Comment thread packages/ui/src/features/home/hooks/useRunWorkstreamAction.ts
Comment thread packages/core/src/sessions/sessionService.ts
Comment thread packages/core/src/save-mode/saveMode.test.ts
ricardo-leiva and others added 2 commits June 24, 2026 18:31
Introduces Save Mode: a three-tier user setting (off / balanced / max_save)
that reduces per-turn LLM cost without changing the workflow.

Levers: model downshift (Opus→Sonnet-4-6 at max_save), effort cap (high/medium),
terse system reminder (fewer output tokens), and x-posthog-property-* headers that
stamp baseline vs effective model/effort on every $ai_generation event.

Wiring (task start): resolveSaveMode → systemPromptOverride →
taskCreationSaga → ConnectParams → agent.start → buildSystemPrompt

Wiring (live session): SessionService.applySaveMode() stores per-task baselines
and calls setSessionConfigOptionByCategory for model + thought_level;
useApplySaveMode wraps the single service call in the UI (AGENTS.md rule 5).

UI: SaveModeToggle appears next to ReasoningLevelSelector in both the task-input
bar and the session prompt bar; saveMode setting persisted in settingsStore.

Also fixes missing BROWSER_TAB_OPENED / LINK_CLICKED_IN_CHAT analytics event
types that were referenced in the browser feature but never added to the schema.

Local dev: COST_CONTROLS_ENABLED=true enables the alpha gate without a PostHog
flag (see scripts/dev-cost-controls.sh). The gateway wiring (cache TTL upgrade,
budget guard) lives in a separate BE PR.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHrRwvm6QmmEM39ez3AjD3
P1 fixes:
- useTaskCreation: pass saveModeResult.model/effort to prepareTaskInput — only
  systemReminder was forwarded before, silently dropping the two biggest cost levers
- useRunWorkstreamAction: forward saveModeResult.effort as reasoningLevel to
  TaskCreationInput — model was updated but effort cap was discarded

P2 fixes:
- sessionService: evict _saveModeBaselines on teardownSession to prevent slow
  leak when tasks complete/error with save mode still active
- saveMode.test.ts + budget.test.ts: convert to it.each parameterised table shape
  per AGENTS.md testing conventions; adds balanced/low-effort edge case

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WHrRwvm6QmmEM39ez3AjD3
@ricardo-leiva ricardo-leiva force-pushed the feat/save-mode-cost-controls branch from c92d176 to 1447ea3 Compare June 25, 2026 00:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant