Skip to content

feat: daily session reset by timezone - #198

Open
americodias wants to merge 1 commit into
overwirehq:mainfrom
americodias:pr/daily-session-reset
Open

americodias wants to merge 1 commit into
overwirehq:mainfrom
americodias:pr/daily-session-reset

Conversation

@americodias

Copy link
Copy Markdown

Summary

Adds two settings — SESSION_DAILY_RESET_HOUR (0-23) and SESSION_DAILY_RESET_TIMEZONE (IANA tz string) — that force session expiry at a recurring daily boundary. A session whose last_used falls before today's reset hour (in the configured local timezone) is treated as expired in addition to the existing SESSION_TIMEOUT_HOURS rule.

Why

The existing inactivity-based timeout works well for natural breaks but doesn't enforce a fresh start each day. Some users (myself included) prefer "every morning, the bot starts a fresh session" to avoid yesterday's working set leaking into today's first message — without needing to remember /new.

Two examples:

  • Personal/journaling use: each day's first message should be a clean slate. SESSION_DAILY_RESET_HOUR=3 + SESSION_DAILY_RESET_TIMEZONE=Europe/Lisbon ensures the 9am check-in starts fresh even if the previous session was at 11pm.
  • Team/operational use: similar reasoning for daily standups, incident handoffs.

What

src/claude/session.pyClaudeSession.is_expired() gains two optional parameters (daily_reset_hour, daily_reset_tz); existing (timeout_hours) calls remain valid (defaults preserve previous behavior). New SessionManager._is_session_expired(session) helper threads both rules through one call site.

src/claude/facade.py — two existing s.is_expired(self.config.session_timeout_hours) callsites switch to self.session_manager._is_session_expired(s) so they pick up the daily rule.

src/config/settings.py — adds the two new settings:

session_daily_reset_hour: Optional[int] = Field(
    default=None,  # disabled by default
    description="Hour of day (0-23) to force session reset. None = disabled.",
    ge=0, le=23,
)
session_daily_reset_timezone: str = Field(
    default="UTC",
    description="Timezone for daily reset hour (e.g. 'Europe/Lisbon')",
)

How it works

The logic is a lazy expiry check, not a background scheduler:

# (simplified)
now_local = datetime.now(UTC).astimezone(ZoneInfo(daily_reset_tz))
today_reset = now_local.replace(hour=daily_reset_hour, minute=0, second=0)
if now_local < today_reset:
    today_reset -= timedelta(days=1)
last_used_local = _to_utc(session.last_used).astimezone(tz)
if last_used_local < today_reset:
    return True  # expired

So a session created Tuesday 11pm and accessed Wednesday 9am — with reset at 3am Lisbon — is detected as expired the moment Wednesday's is_expired() runs. No timer, no race condition, no jobs to schedule.

Compatibility

  • Disabled by default (session_daily_reset_hour = None). Pure no-op when unset.
  • All existing is_expired(timeout_hours) callsites keep working — the new params are optional with sensible defaults.

Test plan

  • With reset disabled (default), behavior unchanged — sessions only expire on inactivity timeout
  • With SESSION_DAILY_RESET_HOUR=3 and TZ=Europe/Lisbon: session created 11pm, query at 9am next day → fresh session
  • Cross-DST transitions (zoneinfo handles correctly via ZoneInfo)
  • Cross-timezone deployment: bot running in UTC, SESSION_DAILY_RESET_TIMEZONE=America/New_York → reset boundary follows NYC, not UTC

Notes

Uses zoneinfo.ZoneInfo from stdlib (3.9+) — no new dependencies. The codebase already requires Python 3.11.

Adds SESSION_DAILY_RESET_HOUR and SESSION_DAILY_RESET_TIMEZONE settings.
A session whose last_used timestamp falls before today's reset hour (in
the configured local timezone) is treated as expired, in addition to the
existing inactivity-based timeout. Lazy check on next access — no
background task or scheduler needed.

Useful for forcing a fresh session each morning so the chat doesn't
carry yesterday's working set indefinitely. Disabled by default.
@FrundlesTian

Copy link
Copy Markdown
Collaborator

Thank you for submitting this feature. I understand the use case it is intended to address: some users may want to start with a clean context the first time they use the bot each day, without having to remember to run /new manually. Before merging, however, I would like to clarify the intended behavior and scope of the current design.

At the moment, “starting a new session each day” is implemented as a session-expiration rule. This may create some unexpected behavior. For example, developers often continue working across midnight or a configured reset time. If a user is still using a session at 02:59 and sends their next message at 03:01, the bot will silently switch to a new session. The running task itself will not be interrupted, but the next message will lose the previous context, and the user may not understand what happened.

The project already provides /new, /end, and the inactivity-based SESSION_TIMEOUT_HOURS. I can see daily reset being useful for journaling, daily stand-ups, and similar workflows. However, the current behavior is global, silent, and tied to session expiration and cleanup, which may be disruptive for users who work continuously across the configured boundary.

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.

2 participants