Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ The chat UI manages the session id for you. The active id is shown beneath the s

The settings dialog also keeps a **Recent sessions** list (most-recent first, up to 8 per base URL and agent). Each turn adds or updates an entry, auto-titling it with your first message (renameable via **Rename**); pick one to fill the Session ID field and **Save** to resume it, or use **Remove** / **Clear recent** to prune the list. This list is a per-browser convenience stored in local storage — it is **not** synced across devices or browsers, and it does not include sessions created through the raw HTTP API from other clients.

When you resume a session — whether by pasting an id or picking one from **Recent sessions** — the chat window reloads that conversation's earlier messages from the server (via a `GET /agents/{slug}/history` endpoint) so its history is visible right away, not just carried invisibly into your next turn. The replay is capped at the 200 most recent user and assistant messages; the UI shows a notice when older messages were omitted. Intermediate tool activity is not replayed. This requires the app's blob-backed [session storage](#session-storage) to be configured; without it — or on an older runtime that predates the history endpoint — the window simply starts empty and the resumed session still continues on your next message.
When you resume a session — whether by pasting an id or picking one from **Recent sessions** — the chat window reloads that conversation's earlier messages from the server (via a `GET /agents/{slug}/history` endpoint) so its history is visible right away, not just carried invisibly into your next turn. The replay is capped at the 200 most recent user and assistant messages; the UI shows a notice when older messages were omitted. Intermediate tool activity is not replayed. The default in-language worker reads the app's blob-backed [session storage](#session-storage). The experimental ACA runtime instead reads only the owner-authorized checkpoint in its sandbox; a retained stopped or suspended sandbox may resume for this read, adding wake latency and ACA cost, without extending its retention. ACA history never falls back to Blob Storage or an external transcript copy. Without Blob storage on the default backend — or on an older runtime that predates the history endpoint — the window simply starts empty and the resumed session still continues on your next message.

### HTTP Chat API

Expand All @@ -466,6 +466,14 @@ identity, egress, lifecycle, and troubleshooting guidance; see
[architecture.md](docs/architecture.md) and
[FRD 0008](docs/frds/0008-aca-sandbox-session-runtime.md) for internal design.

ACA history is sandbox-only: it uses the validated session checkpoint, never
the default `BlobHistoryProvider`, external transcript storage, or sandbox
state-storage credentials. Missing or unowned sessions return `404`; confirmed
reclaim/loss/tombstones return `410`; and unreadable or untrusted checkpoints
return `503`, rather than an empty transcript. The customer-attached Sandbox
Group identity remains available to guest code, but the runtime neither
attaches nor strips it and does not use it for history.

When enabled, ordinary chat calls remain synchronous. Send
`Prefer: respond-async` on either built-in chat surface or a custom
`http_trigger` to receive `202 Accepted`, `Location`, `Retry-After: 2`, and:
Expand Down Expand Up @@ -573,6 +581,7 @@ By default, MCP auth follows the app-wide identity selection: `AZURE_CLIENT_ID`

## Session storage

The following storage behavior is for the default in-language worker.
Multi-turn conversations are persisted as JSON Lines, one record per message:

- **Deployed apps (recommended).** When `AzureWebJobsStorage` is configured —
Expand All @@ -595,6 +604,11 @@ Multi-turn conversations are persisted as JSON Lines, one record per message:

Session ids must match `^[A-Za-z0-9._-]{1,128}$` — anything else is rejected at the API boundary.

The experimental ACA session runtime is an explicit opt-in and does not change
this default. Its transcript remains in the session sandbox's validated
checkpoint and is available only for as long as that retained sandbox history
exists; it is not mirrored to this Blob location.

> **Single-process scope**: A per-session `asyncio.Lock` serializes concurrent turns within a single Function instance. The contract is "one active turn per session id". Multi-instance distributed locking is intentionally out of scope.

## Samples
Expand Down
44 changes: 40 additions & 4 deletions docs/aca-sandbox-session-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ normative design rationale and durable contracts, see

Configure `session_runtime.aca_sandbox` only for HTTP-triggered MAF agents.
While the capability gate is closed, enabling it fails startup rather than
silently falling back to another backend. When the gate opens, ordinary chat
remains synchronous and `Prefer: respond-async` opts into the durable
run-management URLs.
silently falling back to another backend. When the gate opens, ordinary chat remains synchronous and `Prefer: respond-async`
opts into the durable run-management URLs. A built-in chat API also exposes
`GET /agents/{slug}/history` for a selected session.

The sandbox has no public inbound port. The Functions app remains the
authenticated entry point and controller.
Expand Down Expand Up @@ -77,7 +77,8 @@ is intended.
## Identity and RBAC

Attach a dedicated, least-privileged managed identity to the customer-owned
Sandbox Group. Guest code can acquire tokens through the platform identity
Sandbox Group through customer IaC. The runtime neither attaches nor strips
that identity. Guest code can acquire tokens through the platform identity
endpoint; egress policy limits where a token is used, not whether it can be
acquired.

Expand All @@ -87,6 +88,10 @@ code genuinely needs them. Native `DefaultAzureCredential` handles Foundry,
Azure OpenAI, and authenticated MCP calls. Missing or incorrectly selected
identities fail when the outbound credential or request is used.

The group identity is not used to read history. ACA history is read by the
Functions controller through the existing authenticated ACA transport; the
sandbox receives no state-store credential for this feature.

## Egress and credentials

Every sandbox is created with `default_action="Deny"` and
Expand All @@ -108,6 +113,37 @@ Policy and credential changes are create-time-only. Drain or replace a session
to apply them. Rotate a group secret the same way; active streams do not
update in place.

## Checkpoint history

For an enabled ACA session runtime, `GET /agents/{slug}/history` reads only the
latest complete conversation checkpoint selected inside the owner-authorized
sandbox. It does not use the default in-language Blob history provider and
does not copy transcript content to Blob Storage, Tables, controller
memory/disk, logs, another sandbox, or external storage.

Reading history verifies the durable owner/session binding and live sandbox
binding before it reads the immutable checkpoint. A retained stopped or
suspended sandbox is resumed through the normal activation handshake so its
history remains available. This can add wake latency and ACA cost. A history
read does **not** extend idle retention, touch activity, or immediately stop
the sandbox; normal lifecycle policy re-suspends it when idle.

The response retains the normal presentation rules: ordered user/assistant
messages only, with at most the latest 200 after filtering. A session with no
admitted turn returns an empty `200`. The remaining outcomes are deliberately
typed:

| Condition | Response |
| --- | --- |
| Retained sandbox was resumed for this read | `200` with `x-ms-aca-history-resumed: true` |
| Caller has no matching owner/session binding | `404 session_not_found` |
| Confirmed reclaim, sandbox loss, tombstone, deletion, or deployment-epoch retirement | `410 history_gone` |
| Required/legacy checkpoint is missing, corrupt, unsafe, or temporarily unreadable; or its binding cannot be trusted | `503 history_unavailable` |

`410` is permanent for the retained row's history horizon; after normal row
pruning the same request becomes `404`. Neither outcome falls back to Blob
history or a reconstructed transcript.

## Lifecycle, recovery, and troubleshooting

The runtime applies per-sandbox lifecycle policy: it disables auto-suspend
Expand Down
Loading
Loading