Files
Eduard van Valkenburg 28389df805 Python: Move SessionStore to core and persist Foundry Responses sessions (#7306)
* Python: Move session persistence into core

Move SessionStore and durable msgspec-backed storage into core, restore sessions in Foundry Responses hosting with per-user isolation, and document the serialization design.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Python: Address session persistence review feedback

Harden scoped file paths and corruption recovery, preserve session serialization compatibility, clarify dependency placement, and add reproducible benchmark evidence.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Python: Preserve session snapshot compatibility

Deep-copy in-memory session writes and retain existing Telegram session keys so stored conversations continue resolving.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Python: Simplify Foundry session isolation

Add experimental FoundrySessionStore backed by Agent Server request context, remove resolver plumbing, and centralize v2 user isolation for sessions, checkpoints, and approvals.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Python: Reduce Foundry session helper layering

Inline the single-use request user accessor while keeping separate context validation, fingerprint, and directory helpers for their distinct callers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Python: Clarify Foundry request context validation

Separate fail-fast request validation from context retrieval so Responses no longer appears to discard a returned context.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Python: Share Foundry request context helpers

Move protocol validation and user-scope derivation into a dedicated request-context module, leaving the session-store module focused on storage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Restore Foundry checkpoint storage paths

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Simplify Foundry session storage paths

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Persist Foundry sessions under hosted home

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Make hosted path test platform independent

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Address session persistence review feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Isolate Foundry session path handling

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Clarify Foundry session path terminology

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Align Foundry sessions with Responses continuity

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Finalize Foundry Responses session persistence

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Add session store feature usage telemetry

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

* Fix hosted per-call history persistence

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c

---------

Copilot-Session: 3e5c81ad-75e8-4e92-a883-8bbd676c6c8c
2026-07-30 13:04:08 +00:00

4.1 KiB

agent-framework-hosting-telegram

Telegram Bot API-shaped helpers for app-owned Agent Framework hosting.

This is an alpha, helper-only package: it converts between Telegram's Update JSON shape and Agent Framework run values in both directions. It does not provide a bot client, a hosting/channel registry, or a long-running service. Your app remains fully responsible for:

  • Fetching updates -- long polling (getUpdates) or registering a webhook -- and for verifying webhook authenticity (e.g. Telegram's secret token header, or an IP allowlist).
  • The Bot API client -- issuing the actual HTTP calls (sendMessage, sendPhoto, editMessageText, answerCallbackQuery, getFile, ...) with whatever HTTP library you prefer.
  • Rate limits and retries -- Telegram enforces per-chat and global rate limits; back off and retry on 429/5xx yourself.
  • Command dispatch -- telegram_command(...) only parses and normalizes a leading /command; your app decides what each command does.
  • Sessions/storage -- pair these helpers with agent-framework-hosting's AgentState and core's SessionStore or FileSessionStore to persist AgentSessions across turns.

Helpers

  • telegram_chat_id(update) -- the chat id an update belongs to.
  • telegram_session_id(update, bot_id=...) -- a bot-scoped AgentState session id. Private chats use telegram:<bot_id>:<user_id>; other chats use telegram:<bot_id>:<chat_id>.
  • telegram_command(update) -- a leading slash command, with /name@bot args normalized to /name args. Returns None if there is none.
  • telegram_callback_query_id(update) -- a callback query's id, so you can call answerCallbackQuery yourself.
  • telegram_media_file_id(update_or_message) -- the (file_id, mime_type) of inbound media (largest photo size, document, voice, audio, or video).
  • telegram_to_run(update, *, resolve_file_url=None, stream=False) -- convert a message, edited_message, or callback_query update into Agent.run arguments. Provide resolve_file_url (typically backed by getFile) to turn inbound media into content; without it (or when it returns None), text/caption is preserved and media is otherwise dropped. Media-only input with no resolvable URL raises ValueError.
  • telegram_from_run(result, *, chat_id, parse_mode=None) -- render a finished run as one TelegramOperation (sendPhoto when the response has an image, otherwise sendMessage, falling back to "(no response)").
  • telegram_from_streaming_run(stream, *, chat_id, message_id, initial_text=None, parse_mode=None) -- render a streaming run as editMessageText operations with the cumulative text so far, followed by any images in the final response as sendPhoto operations. Pass the app-created placeholder text as initial_text so an identical first edit is omitted. Image-only responses first emit deleteMessage for the placeholder.

TelegramOperation is a minimal TypedDict of {"method": str, "payload": dict} -- your app is responsible for actually calling the Bot API with it.

from agent_framework_hosting import AgentState
from agent_framework_hosting_telegram import (
    telegram_chat_id,
    telegram_from_run,
    telegram_session_id,
    telegram_to_run,
)

state = AgentState(agent)


async def handle_update(update: dict) -> None:
    chat_id = telegram_chat_id(update)
    if chat_id is None:
        return  # Not a chat update this bot handles.

    session_id = telegram_session_id(update, bot_id=bot.id)
    session = await state.get_or_create_session(session_id)
    run = await telegram_to_run(update, resolve_file_url=resolve_telegram_file_url)
    result = await (await state.get_target()).run(run["messages"], session=session, options=run["options"])
    await state.set_session(session_id, session)  # type: ignore[arg-type]

    operation = telegram_from_run(result, chat_id=chat_id)
    await call_bot_api(operation["method"], operation["payload"])  # Your HTTP client.

The base execution-state helpers live in agent-framework-hosting.