Sessions
The wippy/session module owns persistent chat sessions and the policy for
accepting user input. A session blocks new messages during an active turn by
default. Steering lets an enabled session accept input during generation and
apply it before the next model step in the same turn.
Availability
Session steering is available in wippy/session 0.6.3. The built-in chat and
chat web components support it in Web Host 1.0.59, with public npm packages at
0.0.59 and facade 0.6.41 pointing to that Host release. No frontend feature flag
is required.
A new client connected to an older Session server keeps the legacy status-based
sending behavior when interaction is absent. An older client connected to a
new Session server can keep using normal chat and Stop, but it does not gain
steering controls or pending-message presentation.
Setup
Add the module to the project:
wippy add wippy/session@0.6.3
wippy install
Input policy
A session may store one persistent override:
config:
input_policy:
while_running: steer
while_running accepts block or steer. The effective policy is resolved
in this order:
- Temporary override for the active turn.
- Persistent session override in
config.input_policy. - The active agent's default.
block.
The wippy.session.traits:steering trait sets the active agent default to
steer:
traits:
- wippy.session.traits:steering
Steering remains opt-in. Agents without this trait and sessions without an override keep the blocking behavior.
Agent management tool
The wippy.session.traits:input_control trait grants the
set_session_input_policy tool:
traits:
- wippy.session.traits:input_control
This trait grants control but does not enable steering by itself. The tool has two arguments:
| Argument | Values | Default | Meaning |
|---|---|---|---|
mode |
block, steer, inherit |
Required | Set or remove the override at the selected scope. |
scope |
turn, session |
turn |
Apply the change to this turn or persist it on the session. |
The tool accepts no session ID. It is bound to the calling session and checks that the calling agent still owns that session. The session validates and persists the change before the tool reports success. A failed write does not change the in-memory policy.
Temporary turn overrides clear on completion, Stop, failure, recovery, and
agent handoff. A persistent session override remains until it is changed or
removed with inherit at session scope.
Public interaction state
REST session responses and WebSocket session updates may include:
interaction?: {
can_send: boolean
revision: number
}
can_send is the committed effective sending state. revision increases only
when committed can_send changes. Clients should keep explicit false values
and ignore lower revisions.
A full REST response without interaction means the server uses the older
contract. The client should clear its cached interaction state and use the
legacy session-status behavior. A partial WebSocket update that omits
interaction leaves the current value unchanged. A supplied malformed object
also leaves the last valid state unchanged.
Stop is separate from can_send. Its availability comes from the existing
session status.
Steering lifecycle
The server assigns the canonical message ID and persists a steering message before acknowledging it. Steering messages alone receive input metadata:
input?: {
state: "pending" | "applied"
after_message_id?: string
}
Normal idle messages keep their existing metadata. Pending steering is ordered
by persisted date, then by server message_id. After the current response
and its running tool batch finish, the session builds the next prompt and runs
its pre-step lifecycle work while every steering row is still pending. At the
model dispatch boundary, it changes the complete batch to applied in one
transaction and places those messages after the completed response and tool
results. A failed batch update leaves every row pending and publishes no
applied event.
If the recorded prompt anchor was pruned or is missing, the session inserts the steering message at the first valid boundary after the checkpoint context. It does not omit the message. Malformed steering metadata leaves the durable row in place and fails the apply operation visibly.
If prompt construction, agent loading, or lifecycle work fails before model dispatch, steering stays pending and the session does not continue by itself. If the provider fails after dispatch, the steering stays applied because it already entered the prompt. Later prompt history keeps it.
An event publication failure after a database commit does not roll back the committed state. REST reads and reconnect refreshes reconstruct that state.
Stop, recovery, and handoff
Stop is committed before it is acknowledged. If persistence fails, the active turn continues and the command returns an error. Repeated Stop commands are idempotent.
If a steering message commits before Stop, it remains pending. If Stop commits first, a later send is rejected. The current provider operation or a tool batch that is already running may finish. The session suppresses new tools and any further continuation at the next operation boundary.
The existing Stop supervisor also bounds a stuck operation. If the session has not stopped after 10 seconds, it requests cancellation of the session process. After another 10 seconds, it requests termination if the process is still running. These are process-level safeguards, not a guarantee that an external provider request or a tool's side effects are cancelled immediately.
Unused steering remains pending after Stop, recovery, or handoff. Recovery and handoff clear the temporary policy, retain the persistent policy and pending input, and recompute the active agent default. They do not start a turn. The pending messages are included when the user starts the next turn.
Delivery confirmation
The client uses the existing WebSocket request_id correlation mechanism.
After persisting a message, the server publishes its existing received event
on session:<session_id>:message:<message_id> with the original request_id,
server message ID, text, attachments, and optional input metadata. Only a
valid receipt matching the send request confirms delivery and lets the sender
clear its draft and attachments. Receipts without that request ID update
history without clearing the draft.
Stop returns a session update with the original request_id after committing
its state. Rejections use the existing error event with the request ID and
error details. Opening or recovering a session while handling a send must not
confirm that send before the message is persisted.
When steering enters the model prompt, the server publishes type: "update"
on the message topic with message_id and the changed input metadata.
Clients merge this patch into the message and preserve applied state when a
delayed pending receipt arrives. A patch received before its message must not
create an empty chat row.
Older fire and forget transports may retain a random message ID. That legacy ID has no retry or deduplication meaning.
Admission, validation, and definite database failures return a request-matched error and write no message. If the database commit succeeds but the acknowledgement is lost, the row remains durable and appears after refresh. The client keeps the draft and reports that delivery was not confirmed.
There is no automatic retry, client message ID, fingerprint, or deduplication scan. Manual resend after uncertain delivery may create a duplicate. Immediate cancellation of an in-flight provider request is also outside this contract.
Related pages
- Agents describes agent definitions and general trait behavior.
- Chat Web Components describes composer behavior, acknowledgements, pending messages, and reconnects.