# MaaS Runtime Session Authority

> Canonical path: `specs/maas/runtime-session-authority.md`
> Status: living-spec
> Scope: AgentTeam-owned runtime session identity and MaaS capability readiness for
> direct hosted MaaS execution.
> Related specs: [`specs/maas/agentteam-maas-integration.md`](./agentteam-maas-integration.md);
> [`specs/daemon/daemon.md`](../daemon/daemon.md);
> [`specs/storage/storage.md`](../storage/storage.md);
> [`specs/product/overview.md`](../product/overview.md)

AgentTeam issues short-lived MaaS runtime capabilities for first-party daemon
runtimes that use the direct `agentteam-maas` bridge. Those capabilities must bind
to a AgentTeam-owned live runtime authority, not to provider-specific thread or
session identifiers.

This spec defines that authority identity and the reconnect-safe lifecycle for
publishing it.

## 1. Problem

Direct MaaS execution has three timing-sensitive components:

- the daemon WebSocket connection that publishes current runtime state;
- the local loopback bridge that obtains MaaS capabilities from AgentTeam;
- the provider runtime process that may emit its own session or thread id only
  after initialization or after the first model-visible event.

AgentTeam server process restarts clear in-memory daemon connection state. After a
restart, MaaS capability issue can only succeed if the daemon replays the live
runtime session identity before, or while, the loopback bridge requests the
capability.

Readiness must therefore be based on a daemon-owned runtime session identity
that exists before provider model traffic starts and can be replayed on every
daemon reconnect. It must not depend on a provider event arriving first.

## 2. Definitions

| Term | Owner | Meaning |
| --- | --- | --- |
| Agent | AgentTeam server | Workspace actor and durable configuration owned by AgentTeam. |
| Runtime process | Daemon | Local provider process for one agent run. |
| AgentTeam runtime session | Daemon | Opaque live runtime authority id for one local agent runtime process. This is the `runtimeSessionID` used by MaaS capability claims and daemon `agent:session` telemetry. |
| Provider session | Provider runtime | Provider-owned thread, conversation, resume handle, or session id. It is used for provider resume and recovery only. |
| LLM proxy session | AgentTeam server | Short-lived MaaS capability response created by `POST /api/v1/workspaces/:workspaceId/agents/:agentId/runtime/llm-proxy-session`. |
| Authority projection | AgentTeam server | AgentTeam authority facts pushed to `agentteam-maas` before capability signing. |

The AgentTeam runtime session and provider session are distinct identities. A
provider session must never replace the AgentTeam runtime session in MaaS authority
projection, daemon `ready.runningAgentSessions`, or `agent:session`.

## 3. Runtime Session Identity Contract

A daemon that supports direct MaaS capability authority must generate a AgentTeam
runtime session id before the provider runtime can send hosted model traffic.

The id:

- is opaque, high entropy, and unique per live runtime start;
- is scoped by the authenticated machine, workspace, and agent binding;
- is created by the daemon, not by the provider process;
- is stable for the lifetime of the local runtime process;
- survives ordinary daemon WebSocket reconnects while the runtime process
  remains live;
- may survive daemon process restart only when the daemon can safely reattach to
  the same live runtime process and local recovery state;
- must not be written to TiDB as live runtime truth.

Provider session ids are tracked separately. They may be stored in daemon-local
recovery state, prompt compiler inputs, diagnostics, or provider-specific resume
metadata, but they do not authorize MaaS requests.

## 4. Daemon Protocol Lifecycle

### Runtime Launch

When the server sends `agent:start` for a direct MaaS agent:

1. The daemon allocates a new AgentTeam runtime session id before starting the local
   loopback bridge or provider process.
2. The daemon records that id in the local interactive session state used by
   runtime snapshots.
3. The daemon publishes `agent:session` with the AgentTeam runtime session id:

   ```json
   {
     "type": "agent:session",
     "agentId": "agt_example",
     "sessionId": "ars_example"
   }
   ```

4. The daemon starts the local MaaS loopback bridge.
5. The daemon starts the provider runtime with the loopback base URL and local
   loopback credential.
6. The daemon continues publishing `agent:status`, `agent:activity`, and
   provider runtime events independently.

The existing `agent:session.sessionId` wire field remains the runtime authority
field. Its final-state value is the AgentTeam runtime session id, not the provider
session id.

### Reconnect And Ready Replay

On every daemon WebSocket connect or reconnect, the daemon must include live
AgentTeam runtime sessions in `ready.runningAgentSessions` when it reports
`ready.runningAgents`:

```json
{
  "type": "ready",
  "runningAgents": ["agt_example"],
  "runningAgentSessions": {
    "agt_example": "ars_example"
  }
}
```

The daemon should also emit the normal runtime telemetry snapshot after `ready`,
including `agent:session` for every live runtime with a known AgentTeam runtime
session id.

The server may treat `ready.runningAgentSessions` as an atomic replacement for
the connection's runtime-session map, filtered to agents that also appear in
`ready.runningAgents`. A running agent without a runtime session id is not ready
for MaaS capability issue.

### Provider Session Discovery

When the provider later emits its own session or thread id, the daemon stores it
as provider resume metadata. If browser or diagnostic surfaces need that value,
they must use a separate provider-session field. They must not reuse
`agent:session.sessionId` or `ready.runningAgentSessions`.

### Stop, Sleep, Reset, And Replacement

When a runtime process stops, is replaced, or is reset:

- the daemon must clear the current AgentTeam runtime session for that agent on the
  server connection, either by sending `agent:session` with an empty session id
  or by a protocol event that has the same clearing semantics;
- the next fresh runtime start must allocate a new AgentTeam runtime session id;
- stale MaaS capabilities for the previous runtime session must not be
  refreshed into a new authority session;
- reset must clear daemon-local resume metadata according to the daemon spec,
  without using TiDB live state as a runtime source of truth.

## 5. MaaS Capability Issue

AgentTeam owns runtime capability issue through:

- `POST /api/v1/workspaces/:workspaceId/agents/:agentId/runtime/llm-proxy-session`;
- `POST /api/v1/workspaces/:workspaceId/agents/:agentId/runtime/llm-proxy-session/refresh`.

Capability issue must validate durable authority and current live runtime
authority as separate checks:

1. Authenticate the agent request with existing AgentTeam runtime authority.
2. Load durable scope facts: organization, workspace, agent, member, machine,
   model, hosted mode, status, and authority generation.
3. Resolve the current AgentTeam runtime session id from the authenticated daemon
   connection or an equivalent TTL ephemeral runtime-state store.
4. Validate hosted model binding.
5. Push the authority projection to `agentteam-maas`.
6. Sign and return the MaaS capability.

The signed MaaS capability and authority projection must include the AgentTeam
runtime session id. They must not include or depend on provider session ids.

### Missing Runtime Session

When durable authority is valid but the current runtime session id is missing,
the server must not fall back to `agents.session_id`, `agent_instances`, machine
presence mirror columns, or any other TiDB live-state mirror.

If the authenticated machine is connected and the server currently believes the
agent is running or starting, the issue path should wait for a short bounded
interval for `ready.runningAgentSessions` or `agent:session` replay. The wait is
only a race absorber; it is not a substitute for daemon publication.

If no runtime session arrives before the deadline, the server returns a stable
conflict. The response should expose a machine-readable reason such as
`agent_runtime_session_not_ready`, while preserving existing user-facing error
compatibility where needed.

If the machine is offline or the agent is not running, the server should fail
without waiting. The failure must make clear that MaaS capability issue requires
a live daemon runtime session.

## 6. Desktop Loopback Bridge Behavior

The local loopback bridge keeps MaaS capabilities inside the daemon process and
exposes only a local loopback credential to the provider runtime.

When the bridge needs a MaaS capability:

- it should publish the current AgentTeam runtime session snapshot before the first
  capability issue attempt when the session is known locally;
- if the server returns the stable runtime-session-not-ready conflict, it may
  publish the current snapshot again and retry with a short bounded backoff;
- it must not blindly retry when the daemon does not know a AgentTeam runtime session
  id for the local process;
- retry budgets must be small enough that provider requests fail promptly when
  the runtime is genuinely not ready.

This publish-and-retry behavior is useful only because it actively restores the
server's missing ephemeral mapping. A retry that does not publish runtime
session state is not a correctness mechanism.

## 7. Storage And Ephemeral State Boundary

The source of truth for live runtime sessions is:

- daemon-local runtime state;
- current server daemon WebSocket connection memory;
- or, for multi-server deployments, a TTL ephemeral store such as Redis.

TiDB is not the source of truth for live runtime sessions. In particular:

- `agents.session_id` remains only a legacy compatibility mirror for daemons
  that do not advertise local session resume support;
- `agents.session_id` must not be used for MaaS/runtime readiness;
- `agents.status`, `agents.activity`, `agents.activity_detail`,
  `agents.current_instance_id`, `agent_instances`, and machine
  connected/disconnected mirror columns must not be used to reconstruct live
  MaaS authority.

An ephemeral runtime-state store, when introduced, must use bounded TTL,
machine/agent scoping, and reconnect replacement semantics. It must expire live
runtime authority when the daemon stops refreshing the state.

## 8. Capability Gates And Rollout

Final-state direct MaaS authority requires a daemon capability that means:

- the daemon allocates AgentTeam-owned runtime session ids before provider model
  traffic can start;
- `agent:session.sessionId` and `ready.runningAgentSessions` carry those
  AgentTeam-owned ids;
- provider session ids are stored separately.

The capability name should be stable and explicit, for example
`agent:runtime-session-authority`.

`agent:maas-direct` remains the capability for starting the local MaaS loopback
bridge. `agent:local-session-resume` remains the capability for daemon-local
resume decisions. These capabilities are related but not interchangeable.

During rollout, the server may keep compatibility behavior for older
`agent:maas-direct` daemons, but new direct MaaS projections should require the
runtime-session-authority capability once the supported daemon floor has moved.

## 9. Security Properties

The design preserves these security properties:

- Provider runtimes cannot choose the MaaS authority session id.
- Provider runtimes do not receive AgentTeam `agentAuthToken` or the MaaS capability.
- The local loopback credential is process-local and separate from the MaaS
  capability.
- MaaS capabilities bind to organization, workspace, agent, member, machine,
  AgentTeam runtime session id, and authority generation.
- A stopped or replaced runtime cannot silently refresh a previous capability
  into a new runtime authority session.
- Server restarts do not authorize stale sessions; daemons must replay current
  runtime authority.

## 10. Implementation Ownership

Current AgentTeam implementation surfaces that enforce this contract include:

- daemon runtime session storage and snapshots in `desktop/internal/dispatcher`;
- runtime process identity in `desktop/internal/agent`;
- daemon ready and telemetry replay in `desktop/internal/channel/agentteam`;
- local MaaS bridge capability issue in
  `desktop/internal/agent/agentteam_llm_bridge.go`;
- internal capability client in
  `desktop/internal/channel/agentteam/llm_proxy_sessions.go`;
- daemon connection state in `internal/api/daemon`;
- MaaS capability issue in `internal/api/agent`;
- durable authority scope loading in `internal/store/agent`;
- capability signing and projection in `internal/maas/capability`.

Implementation may move across packages, but the ownership boundaries remain:
daemon owns live local runtime identity; AgentTeam server owns durable authority
validation and capability signing; `agentteam-maas` owns hosted runtime data-plane
execution and hosted MaaS persistence.

## 11. Validation Requirements

Changes to this area should cover these cases:

- Runtime launch publishes a AgentTeam runtime session before the first hosted model
  request can require a MaaS capability.
- Provider session/thread ids do not overwrite the AgentTeam runtime session id used
  by `agent:session`, `ready.runningAgentSessions`, MaaS claims, or authority
  projection.
- Server restart followed by daemon reconnect restores MaaS readiness from
  `ready.runningAgentSessions` or `agent:session` without reading TiDB live
  mirrors.
- Capability issue waits only a bounded interval for replay and then returns a
  stable conflict when the runtime session remains absent.
- Desktop publish-and-retry retries only after publishing a locally known AgentTeam
  runtime session id.
- Stop, replacement, and reset clear old runtime authority and prevent refresh
  from carrying old MaaS capabilities into a new runtime session.
- Multi-server readiness, when enabled, uses TTL ephemeral state instead of
  TiDB.
