Events
Every observable fact about a session arrives as an event on one ordered log.
This page is the catalog: all 23 types, the exact data payload of each, the
moment each is appended, and what is deliberately absent from the stream. For
the live follow loop that reads the log, see
Follow a run live.
The envelope
Section titled “The envelope”Every event returned by the endpoint has the same eight-field envelope:
{ "id": "6f1c1d80-6c2a-4f0e-9d5f-0c2a2f0f4b11", "type": "wamp.run.completed", "createdAt": "2026-08-11T09:04:18.220Z", "sequence": 42, "subject": { "sessionId": "3f7d4f4c-2b6a-4a2e-9c1a-1f2b3c4d5e6f", "runId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d" }, "data": { "status": "completed", "stopReason": "completed", "costUsd": 0.0123, "costComplete": true, "turns": 3 }}| Field | Type | Notes |
|---|---|---|
id |
UUID | Stable across replays. Use it as your deduplication key. |
type |
string | One of the 23 values below. Unknown future values must be ignored, not rejected. |
createdAt |
ISO 8601 | When the event was appended, not necessarily when the fact occurred — timeline events carry their own occurredAt. |
sequence |
integer ≥ 1 | The total order within the session, and the only cursor. |
subject.sessionId |
UUID | Always present. |
subject.runId |
UUID | Present on run and interaction events; absent on publication and merge events. Timeline events carry it in practice but do not guarantee it — treat it as optional there. |
data |
object | Type-specific, documented per type below. May carry properties beyond the ones listed. |
Four properties of the log are worth designing against, and each is enforced
rather than best-effort: sequence is total-ordered and gap-free within a
session, so you may assert sequence === previous + 1; events are immutable
and append-only; the log is exactly-once at rest, because the runtimes
that produce events retry at-least-once and the store deduplicates before
insert; and after is exclusive, so replaying a cursor never repeats an item.
Follow a run live covers what those mean for a loop.
GET /v1/sessions/{sessionId}/events/stream replays from the exclusive
Last-Event-ID header (or after query, default 0), then delivers each
append as id: <sequence> and data: <SessionEvent JSON>. It carries this
same envelope and the same visibility as the JSON page. Keep the last
sequence your handler committed; reconnect with it after a stream ends.
Which subject each event carries
Section titled “Which subject each event carries”| Events | subject.runId |
|---|---|
| Run, interaction, and timeline events | always present |
wamp.artifact.created for a run result or a presented file |
present |
wamp.artifact.created for a pull request record |
absent — data.publicationId identifies it instead |
wamp.publication.* |
absent — publications are session-scoped commands |
Match on subject.runId when you follow one specific run. A session accumulates
runs, and a terminal event from an earlier run will otherwise end your loop
early.
Run events
Section titled “Run events”Six types: the start, four terminal outcomes, and a progress fact that may repeat. Exactly one of the four terminal types closes any run.
wamp.run.started
Section titled “wamp.run.started”Appended when the run begins executing on a sandbox — after the turn was admitted and a workspace was provisioned.
{ "status": "running" }wamp.environment.apply
Section titled “wamp.environment.apply”started and a terminal succeeded or failed event describe the pinned
environment revision applied to a sandbox lease. A lease and revision get one
started and one terminal event, however many Turns join the apply; each names
the Run whose call wrote it, so the two may name different Runs, or none for a
sandbox brought up without one. A
terminal event may include a redacted setup tail and a content-free cache skip
reason; secret values and signed storage URLs are never event data.
When the apply was offered a cache restore and discarded it whole, so setup
ran without the cache, a terminal event carries cacheDiscarded:
{ "reason": "the archive does not match its sha256" }. The reason is a
redacted sentence of at most 1,024 characters: the download failed, the bytes
did not match their sha256, or the archive was malformed or could not be
installed. cache then reports the save, or miss when nothing was saved.
When that revision enables GitHub access, a terminal event also reports github:
{ "status": "granted", "owners": ["acme"] } or
{ "status": "unavailable", "reason": "no_grant" }. The other unavailable
reasons are authority (the owner lost access), temporary (retried before the
next message), and runtime_outdated (the engine cannot hold access). owners
contains 1–16 GitHub account logins, never tokens. The field is absent on
started and when the revision did not enable GitHub access.
When the revision lists repositories, a terminal event reports what happened
to each one as { "repository", "path", "state" }. In a Session with a launch
repository every entry is skipped. Otherwise the entries follow the list
order, each cloned or kept (something was already at its path, for example
restored from a checkpoint), up to and including the first failed clone; that
failure is errorCode: "environment_setup_failed" and git’s redacted output is
the outputTail. The field is absent on started.
When the revision lists extensions, a succeeded event reports each one in list
order as { "ref", "id"?, "version"?, "sha256"?, "state", "reason"? }: ref is
the catalog slug or the archive’s sha256:<hex>, state is active or
failed, sha256 is the digest of the catalog package installed, and a
failed entry carries a redacted reason. A failed extension does not fail the
apply. The field is absent on started and on failed.
{ "leaseId": "...", "environmentId": "...", "name": "backend", "revision": 3, "status": "succeeded", "cache": "skipped_secret", "cacheSkip": { "reason": "secret", "path": "~/.cache/npm/token" }, "setupExitCode": 0, "durationMs": 1200, "outputTail": "setup complete", "github": { "status": "granted", "owners": ["acme"] }, "repositories": [ { "repository": "acme/backend", "path": "backend", "state": "cloned" }, { "repository": "acme/web", "path": "site", "state": "kept" } ], "extensions": [ { "ref": "wamp-mcp-postgres", "id": "wamp-mcp-postgres", "version": "1.4.0", "sha256": "…", "state": "active" }, { "ref": "sha256:…", "state": "failed", "reason": "Could not download the archive: the download answered HTTP 404." } ]}wamp.run.progress
Section titled “wamp.run.progress”A bounded tool-lifecycle fact, appended while the run is still working. It may appear many times.
{ "activityId": "a3f1…", "category": "tool", "name": "bash", "status": "started", "occurredAt": "2026-08-11T09:02:38.410Z"}| Field | Type | Always present |
|---|---|---|
activityId |
string | yes — stable per tool call, so started, its outcome and the call’s wamp.activity.completed share it |
category |
"tool" |
yes — the only value |
name |
string, ≤ 128 chars | yes |
status |
started | succeeded | failed |
yes |
delegationId |
string, ≤ 255 chars | no — set when an agent the session delegated to made the call; the same id as that lane’s wamp.delegation.completed |
occurredAt |
ISO 8601 | yes |
It carries no arguments, no output, no command line, and no path. Use it to
show that something is happening; use wamp.activity.completed for the
retained transcript.
wamp.run.completed
Section titled “wamp.run.completed”The agent finished the turn on its own terms.
{ "status": "completed", "stopReason": "completed", "costUsd": 0.0123, "costComplete": true, "turns": 3 }| Field | Type | Always present |
|---|---|---|
status |
"completed" |
yes |
stopReason |
see below | no |
errorCode |
stable remediation code | no; failed/crashed only |
costUsd |
number | no |
costComplete |
boolean | no; false means costUsd is only a known subtotal |
turns |
integer | no |
stopReason is one of completed, aborted, max_turns, max_response,
error, cost_budget, cost_unknown, turn_budget, time_budget,
repeated_tool_failure, engine_unreachable, execution_deadline. It tells
you why the agent stopped, which is not the same as whether it succeeded: a
completed run with stopReason: "max_turns" ran out of budget mid-task.
repeated_tool_failure means the agent sent the same tool call five times in a
row and it failed with the same error each time. The last two are only ever
produced by the platform, and only on wamp.run.crashed.
wamp.run.failed
Section titled “wamp.run.failed”The run ended in an error the platform recognized as terminal. Same payload
shape as wamp.run.completed, with status: "failed". When the platform can
classify the failure safely, errorCode carries a bounded machine code such as
authority_revoked_dispatch or resume_unavailable; it never carries an
exception message. Use the same field on wamp.run.crashed.
wamp.run.cancelled
Section titled “wamp.run.cancelled”The run stopped because cancellation was requested through
POST …/runs/{runId}/cancel. Same payload shape, with status: "cancelled".
Cancellation is a request, not an immediate state: the accepted request appears
on the run resource as cancellationRequested: true, and this event arrives
when the run actually stops.
wamp.run.crashed
Section titled “wamp.run.crashed”The run ended without a clean result — the runtime or the sandbox was lost. Same
payload shape, with status: "crashed". The platform may retry the run on its
own; the run resource exposes attempts so you can see that it did.
When the platform itself declared the run dead, stopReason says which way, and
errorCode is turn_execution_lost:
stopReason |
What happened | What to do |
|---|---|---|
engine_unreachable |
The sandbox stopped answering. The runner was lost, not the work. | Retry. Read continuation first if the session must resume prior state. |
execution_deadline |
The run hit the sandbox lifetime ceiling — see Limits. | Split the work, or continue in a follow-up turn on a fresh lease. |
{ "status": "crashed", "stopReason": "execution_deadline", "errorCode": "turn_execution_lost" }A crash with no stopReason is one the platform could not classify. Do not
branch on the absence of the field.
Run error codes
Section titled “Run error codes”errorCode appears on wamp.run.failed and wamp.run.crashed, and on the run
resource as run.lastError.code. It is a bounded, non-sensitive remediation
code — never an exception message. Event payloads carry only the modelled
values below; the run resource can carry a few more, documented after the
table.
errorCode |
What happened | What to do |
|---|---|---|
authorization_unavailable |
The identity service could not be reached to authorize the run | Transient. Retry with backoff |
authorization_rejected |
The identity service refused WAMP’s own workload credential | Not yours to fix. Retrying cannot help; report it to the operator |
authority_revoked_dispatch |
The caller’s authority was revoked before the run could start | Re-consent the installation, then submit a new turn |
authority_revoked_cancel |
The caller’s authority was revoked while a cancellation was in flight | As above |
dispatch_failed |
Handing the turn to a sandbox failed | Retry by submitting a new turn |
dispatch_attempts_exhausted |
Dispatch failed its bounded attempt budget | The run is terminal. Submit a new turn; if it recurs, escalate |
runtime_account_unavailable |
No vendor runtime account could be leased | Check availability in discovery; ask an administrator to connect an account |
runtime_outdated |
The runtime in the sandbox cannot serve this session as configured | Create a new session |
environment_setup_failed |
The pinned environment setup failed or timed out | Inspect the redacted wamp.environment.apply tail, fix the environment, then submit a new Turn |
environment_forbidden |
The stored dispatch authority can no longer apply the environment | Restore the authority, then submit a new turn |
compute_outdated |
The organization’s machine is reachable but holds an engine image older than this release | Not retryable. An administrator updates that machine’s images, and sees which machine needs it under Runners in WAMP Cloud Settings. No /v1 endpoint reports it |
turn_execution_lost |
The execution backing the turn died with its sandbox | Submit a new turn. Read continuation first if prior state matters |
interaction_conflict |
The reply did not match the open interaction | Re-read GET …/interactions and answer the one that is actually open |
agent_locked |
The execution path refused the requested agent change | Inspect the run error. Select an agent on a new Turn; Session PATCH cannot change it after a Turn exists |
resume_unavailable |
The session’s previous state could not be restored | Start a new session |
session_not_found |
The session disappeared beneath the run | Terminal. Do not retry |
turn_not_found |
The turn disappeared beneath the run | Terminal. Do not retry |
Engine failures use the following codes. The engine chooses them from a closed
list; provider messages and arbitrary gateway codes are never copied into an
event. These codes also appear on run.lastError.code.
errorCode |
What happened | What to do |
|---|---|---|
upstream_429 |
The model provider rate-limited the request | Submit a new turn after the limit clears |
upstream_5xx |
The model provider or gateway returned a server error | Submit a new turn; escalate if it persists |
upstream_4xx |
The provider rejected a request outside the specific gateway refusals below | Check the model and request before retrying |
upstream_timeout |
The provider did not respond before a transport deadline | Submit a new turn |
upstream_network |
A connection, DNS lookup or socket failed | Submit a new turn after connectivity recovers |
stream_incomplete |
The model stream ended without completion | Submit a new turn |
refused_invalid_request |
The gateway rejected the request | Check the selected model and request settings |
refused_unsupported_parameter |
The gateway does not support a request parameter | Choose another model or setting |
refused_unsupported_tool_type |
The gateway does not support a tool type the agent sent | Choose a model that supports the agent’s tools |
refused_invalid_tool_arguments |
The gateway rejected tool arguments in the request | Submit a new turn; report it if it recurs |
refused_model_not_found |
The gateway could not find the selected model | Select an available model |
refused_end_user_id_conflict |
The gateway found a conflicting end-user identity | Report an identity configuration error |
refused_invalid_end_user_id |
The gateway rejected the end-user identity | Report an identity configuration error |
refused_too_many_concurrent |
The gateway concurrency limit was reached | Submit a new turn after other work settles |
refused_service_unavailable |
The gateway was temporarily unavailable | Submit a new turn |
refused_missing_api_key |
The gateway has no API key for this inference request | Restore the account credential |
refused_invalid_api_key |
The gateway refused the API key | Reconnect the account credential |
refused_malformed_request_body |
The gateway rejected the request format | Report a client defect if it recurs |
refused_request_entity_too_large |
The gateway rejected the request size | Shorten the input or attachments |
context_overflow |
Compaction could not fit the conversation in the model context | Start a smaller session or shorten the input |
model_no_tools |
The selected model cannot call the agent’s tools | Select a tool-capable model |
tool_loop_limit |
The engine reached its loop step limit | Review the partial work before submitting another turn |
repeated_tool_failure |
The agent sent the same tool call five times in a row and it failed the same way each time | Read the repeated error and change the request, the environment or its credentials before submitting another turn |
response_limit |
The response passed the engine’s single-turn size limit | Ask for a shorter answer |
budget_exhausted |
The engine reached a cost, turn or time budget | Review the budget before retrying |
runtime_error |
A foreign agent runtime failed or could not be resolved | Check runtime availability and retry |
unknown |
The engine could not classify the failure | Review the session and report repeated failures |
Seven further codes appear on the run resource only, and never on an event, for
one reason in two shapes: the run is still alive when they are written. An
unobserved cancellation leaves the run running, and a dispatch
still retrying leaves it queued. Neither has produced a terminal event for a
code to ride on, so read these from run.lastError.code.
Two of them are written while cancellation is pending or revoked:
errorCode |
What happened | What to do |
|---|---|---|
cancel_not_observed |
Cancellation was acknowledged but no stop was observed before the worker’s claim ended; the platform re-asks on its retry curve | Keep watching for cancelled |
cancel_authority_revoked |
The caller’s authority was revoked mid-cancellation; the request was abandoned | Re-consent the installation, then cancel again |
The other five are the placement and lifecycle conditions a dispatch attempt
hits and then retries. Seeing one means the run has not started yet, not that it
failed — the run is still queued and the platform is still trying:
errorCode |
What happened | What to do |
|---|---|---|
workspace_unavailable |
A sandbox for this session could not be started on this attempt | Nothing. The platform retries; watch the run’s status |
workspace_expired |
The workspace this turn wanted to continue from is gone | Restore the workspace, or accept a fresh one — see Sandboxes |
compute_not_configured |
The organization has no compute it may be placed on | An administrator must configure compute; retrying will not help |
conversation_busy |
Another turn on this session is still holding the conversation | Wait for the active run to end, then submit again |
repository_grant_required |
The repository authority this turn needs is missing or no longer valid | Re-grant repository access, then submit again |
The tables above are the whole vocabulary. A run failure on a condition
that has no code of its own is recorded as dispatch_failed — the platform
does not mint a code by stripping the cloud_ prefix off an HTTP error, which
is what once put values on this field that appeared in no table, no enum and no
SDK. The cause of such a failure is in the platform’s own logs, not on the
resource.
Do not render an errorCode as prose. Map the values you handle to your own
copy, and treat an unrecognized one as a generic failure — the set is additive.
isKnownCloudRunErrorCode in @wamp/app-sdk tests membership of the table
above for you. Publication and merge failures use different families,
catalogued in
Errors.
Interaction events
Section titled “Interaction events”Three types cover a suspended question or a live approval.
wamp.interaction.opened
Section titled “wamp.interaction.opened”The agent needs input. A question or plan moves its Run to awaiting; an
approval leaves its Run running while it waits for a decision.
{ "interactionId": "ask_user:01H8…", "kind": "ask_user", "request": { "questions": [ { "question": "Which package should the test live in?", "options": ["core", "cli"], "header": "Placement", "recommendedIndex": 0, "multiSelect": false, "allowCustom": true } ] }}| Field | Type | Always present |
|---|---|---|
interactionId |
string, ≤ 255 chars | yes |
kind |
ask_user | exit_plan_mode | approval |
yes |
request.questions[] |
array | only for ask_user |
request.prompt |
string | optional for ask_user |
request.plan |
string, ≤ 64 KiB | only for exit_plan_mode, and only when the agent wrote a plan |
toolName, title, actionKind, deadline |
tool metadata and Unix milliseconds | approval metadata, when present |
The opened approval event never carries action arguments. Read
GET /v1/sessions/{sessionId}/interactions with wamp.cloud.sessions:read and
show request.approval.input, the complete proposed action. The request also
contains its requestId, deadline, options, and allowed memory scopes. Answer
through PUT …/interactions/{interactionId}/decision with
wamp.cloud.approvals:respond and live Session access. Send requestId, a
Cloud choice (allow_once, allow_turn, allow_chat, or deny), and a
caller-owned idempotencyKey; reuse the key on retry. The answer resumes the
same Run without creating a reply Turn. A vendor allow_always or
reject_always option is never a Cloud choice.
Within a question, question, options, multiSelect, and allowCustom are
always present; header and recommendedIndex are optional. An
empty options array is a free-text or numeric field. ACP form questions also
carry fieldId, valueType, typed optionValues, defaults and constraints;
customFieldId is the optional “Other” text field. Answer a structured form
with a JSON object in the Turn’s message: use fieldId keys and the typed
wire values, use customFieldId instead for “Other”, and omit optional fields.
Plain text also answers it: one question takes its answer as the whole
message, several take one Label: answer line each, labelled by header or
question. Text naming an option’s label or value selects that option; other
text becomes the “Other” answer. A reply that fits none of these cancels the
question, and the agent may ask again. Questions without fieldId keep the
ordinary human-readable answer string.
An
exit_plan_mode request carries plan instead: the plan markdown to show
before you ask someone to approve leaving planning and starting to edit. It is
truncated at 64 KiB, and request is absent entirely when the agent produced no
plan file to read. Either way the answer is a normal turn.
Answer a question or plan by submitting a turn with replyTo: { interactionId }. Answering the
wrong or an already-answered interaction is 409 cloud_interaction_conflict;
submitting a plain turn while one is open is 409 cloud_conversation_busy,
which never clears on its own — see
Errors.
wamp.interaction.resolved
Section titled “wamp.interaction.resolved”The answer was accepted. A question has resolvedByTurnId; an approval has
resolution, optional vendor optionId, the winning choice, and
answeredBy (HUMAN or APP_INSTALLATION with its id).
{ "interactionId": "ask_user:01H8…", "resolvedByTurnId": "…" }resolvedByTurnId is the turn you submitted with replyTo. One turn resolves
at most one interaction.
wamp.interaction.expired
Section titled “wamp.interaction.expired”The question can no longer be answered.
{ "interactionId": "ask_user:01H8…", "reason": "workspace_lost" }reason is one of workspace_lost, session_archived, run_cancelled,
run_ended, timed_out, cancelled, request_gone, or authority_revoked.
After this event, a replyTo or decision naming
that interaction is a conflict. If your process restarted and you do not know
what is outstanding, GET /v1/sessions/{sessionId}/interactions returns only
the still-open ones.
Artifact events
Section titled “Artifact events”Two types: one per artifact recorded, one honest report of what was not kept.
wamp.artifact.created
Section titled “wamp.artifact.created”A durable output is now readable. data has three shapes, discriminated by
kind, which is always present.
kind |
When | Fields |
|---|---|---|
result.summary |
at the end of a run — the agent’s written summary of what it did | artifactId, role: "output", contentType: "text/markdown; charset=utf-8", size, sha256, state: "available", truncated |
presented.file |
a file the agent chose to hand back | artifactId, role: "output", contentType, size, sha256, state: "available", truncated: false |
github.pull_request |
a publication opened or updated a pull request | artifactId, publicationId, role: "output", contentType: "application/json", size, sha256, state: "available", truncated: false |
{ "artifactId": "b1c2d3e4-…", "kind": "result.summary", "role": "output", "contentType": "text/markdown; charset=utf-8", "size": 1284, "sha256": "9f2b…", "state": "available", "truncated": false}truncated: true on a result.summary means the summary hit its size cap and
was cut. The event carries the manifest only; fetch the bytes from
GET …/artifacts/{artifactId}/content. sha256 is also the ETag that
endpoint returns, so you can skip a download you already have.
wamp.artifact.omitted
Section titled “wamp.artifact.omitted”The run produced files that were not retained, and this is the platform saying so rather than under-reporting.
{ "omittedArtifacts": 3, "reason": "not_retained" }reason is always not_retained today. The usual cause is a size cap — a
single file over 32 MiB, or a run whose presented files exceed 64 MiB in total
(see Limits). subject.runId is always present on this
event, and the count is always at least 1.
Publication events
Section titled “Publication events”Four types covering reviewed delivery and pull-request merging. None of them carries
a subject.runId.
wamp.publication.created
Section titled “wamp.publication.created”The exact reviewed revision was delivered. This event is only emitted on
success, so its status is the literal succeeded.
{ "publicationId": "…", "status": "succeeded", "kind": "github.pull_request", "delivery": "pull_request", "branch": "wamp/publications-v1/cloud-3f7d4f4c-2b6a-4a2e-9c1a-1f2b3c-6b031ca6", "commitSha": "6f1c1d8047b2c3d4e5f60718293a4b5c6d7e8f90", "pullRequest": { "number": 412, "url": "https://github.com/…/pull/412", "draft": true }}branch is the head branch with refs/heads/ stripped, commitSha is a 40-hex
git object id, and draft reflects what you asked for (publications default to
draft). A github.pull_request artifact event is appended alongside this one.
An explicitly authorized direct delivery uses the same event type but a discriminated payload and does not create a pull-request artifact:
{ "publicationId": "…", "status": "succeeded", "kind": "github.branch", "delivery": "direct_branch", "branch": "main", "commitSha": "6f1c1d8047b2c3d4e5f60718293a4b5c6d7e8f90"}wamp.publication.failed
Section titled “wamp.publication.failed”The publish attempt ended terminally.
{ "publicationId": "…", "status": "failed", "errorCode": "branch_conflict" }errorCode is a repository-level machine code, not one of the HTTP error codes
in Errors. Treat it as a string to log and surface, not as
a closed set to branch on exhaustively.
wamp.publication.merge_succeeded
Section titled “wamp.publication.merge_succeeded”The exact reviewed head was merged.
{ "publicationId": "…", "mergeId": "…", "status": "succeeded", "strategy": "squash", "pullRequestNumber": 412, "expectedHeadSha": "6f1c1d80…", "mergedCommitSha": "0a1b2c3d…"}strategy is merge, squash, or rebase. expectedHeadSha is the head the
merge was authorized against and mergedCommitSha is the 40-hex commit that
resulted — a merge only ever integrates the head that was reviewed.
wamp.publication.merge_failed
Section titled “wamp.publication.merge_failed”{ "publicationId": "…", "mergeId": "…", "status": "failed", "errorCode": "pull_request_changed" }As with wamp.publication.failed, errorCode is a repository-level code. A
merge that is merely not ready yet (waiting on checks or branch protection) does
not fail — it stays in waiting with lastError.code: merge_not_ready, and
polling it is a 200 carrying that status. The HTTP code
409 cloud_publication_merge_not_ready belongs to the PUT that opens a merge,
and means the publication is not a succeeded pull request yet.
Timeline events
Section titled “Timeline events”Six types. Together they are the transcript: what the agent said, what it ran, and what it dropped from its own context. They are the events a chat UI is built from.
These six are the retained transcript. wamp.message.created arrives while the
run works, as each message completes; the other five are committed at the
terminal boundary, in occurrence order, immediately before the terminal
wamp.run.* event, which is always last for that run. The live view of a call
is wamp.run.progress, which arrives during the run and is
a recovery signal rather than a record.
wamp.message.created
Section titled “wamp.message.created”Assistant text.
{ "messageId": "3b8f…", "role": "assistant", "text": "I added a regression test for the date parser and ran the suite.", "truncated": false, "occurredAt": "2026-08-11T09:03:02.104Z"}role is always the literal assistant — there is no user-message event,
because your messages are turns and you already have them. text is bounded and
truncated: true says it was cut. It always has something to read: text that
is only whitespace is not published as a message.
Each message is appended once, shortly after the agent writes it. WAMP’s own
agent writes one after each model response, as the calls it made start; an
agent runtime such as Claude Code or Codex writes at its segment boundaries,
usually the end of its turn. A message the run had not delivered by its end is
appended at the terminal boundary instead, with the same messageId.
For WAMP’s own agent, sequence is the order the agent worked in: a message
comes before the wamp.run.progress and presented files of the calls it
announced. If reading a message from the sandbox takes longer than three
seconds, the call’s progress is published without waiting and that message
follows it. An agent runtime writes its prose at segment boundaries, so its
calls’ progress usually comes before the message that framed them.
occurredAt is when the agent wrote the message. The run’s answer is its
result.summary artifact — see
the summary artifact and the last message.
wamp.activity.completed
Section titled “wamp.activity.completed”One tool call or runtime step finished.
{ "activityId": "7c1a…", "category": "tool", "name": "bash", "status": "success", "occurredAt": "2026-08-11T09:02:41.900Z"}category is tool or runtime. name is the tool name (or the runtime id
for runtime), bounded to 128 characters. status is success, error, or
unknown. You learn that a tool ran and whether it worked — never its
arguments or its output.
A call the run also reported as wamp.run.progress keeps
that event’s activityId and name, so draw the two as one row and let this
event settle it. An agent runtime’s own tools are named by action class, such
as runtime__execute. Events written before 2026-09-15 carry an activityId of
their own, and name an agent runtime’s call by the bare action class
(execute, other).
delegationId is present only when an agent the session delegated to made this
call, and names the wamp.delegation.completed it
belongs to. Group by it to show a subagent’s work as its own; treat its absence
as the session’s own work, which is what every event written before delegated
lanes were attributed carries.
wamp.context.compacted
Section titled “wamp.context.compacted”The agent compacted its own conversation to stay inside its context window.
{ "factId": "5d2e…", "droppedMessages": 42, "summaryRetained": true, "occurredAt": "…" }summaryRetained: true means a summary of the dropped messages was kept.
This is why a later message can reference work you no longer see in full.
wamp.delegation.completed
Section titled “wamp.delegation.completed”The session handed work to another agent.
{ "delegationId": "0f2b…", "name": "explorer", "status": "success", "occurredAt": "2026-09-06T09:02:41.900Z"}delegationId identifies the lane, an opaque id of at most 255 characters;
every wamp.activity.completed and
wamp.run.progress that agent produced carries the same id. name is the agent as the delegating side named it, bounded to 128
characters. status is success or error, and is absent when the lane never
reported an outcome.
Like every activity event, this says what happened and not what was asked: the task the delegating agent wrote is never published.
wamp.conversation.cleared
Section titled “wamp.conversation.cleared”The conversation was reset; earlier context is gone from the agent’s view. Your event log still holds everything.
{ "factId": "5d2e…", "occurredAt": "…" }wamp.timeline.truncated
Section titled “wamp.timeline.truncated”The run produced more timeline facts than were retained.
{ "omittedFacts": 118 }subject.runId is always present, and omittedFacts is always at least 1. Like
wamp.artifact.omitted, this exists so that a gap in the transcript is
explicit rather than silent.
Child session events
Section titled “Child session events”Two types. An agent can start a child session — a separate Session, with its own sandbox, its own turns and its own events, that outlives the turn which started it. These two events are the parent’s record of that: which conversations it started, and what each one reported back to it.
They are not committed at the terminal boundary as most timeline types are. A
started event is written when the child is created, and a completed event
when the control plane hands that child’s outcome to the parent’s agent — which
may be at the start of a later turn, or when the agent asks for it.
Both are delivered through the Event stream, like the timeline types. subject.runId is the parent’s
Run in both; the child’s own id is in the payload, and its own events are read
from its own Session.
wamp.child_session.started
Section titled “wamp.child_session.started”The session’s agent started a conversation of its own.
{ "childSessionId": "1f0c…", "title": "Reproduce the checkout timeout", "occurredAt": "2026-09-14T09:02:41.900Z"}childSessionId is a Session id you can read with
GET /v1/sessions/{sessionId} and whose parent names this Session and this
Run. title is the agent’s own short name for the work, bounded to 160
characters. Written once per child, so a retried spawn adds no second event.
Like every other event here, this says what happened and not what was asked: the task the agent wrote for its child is never published.
wamp.child_session.completed
Section titled “wamp.child_session.completed”A child session finished, and this Session’s agent has been told.
{ "childSessionId": "1f0c…", "title": "Reproduce the checkout timeout", "state": "idle", "finalMessage": "The timeout reproduces on any cart with more than 40 lines…", "occurredAt": "2026-09-14T09:31:08.120Z"}state is idle (the child ended its turn normally), stopped (it was
aborted) or failed. finalMessage is the child’s last assistant message,
bounded to 4 000 characters, and is present when the hand-over included that
text. A delta carries at most five terminal outcomes at once, each with its
message when one exists; further outcomes reach later turns.
Written once per child run, after the parent Run accepting a delta is confirmed
or after a session_wait or one-id session_status reply is sent.
subject.runId names that parent Run. An attempted parent Run that never
started leaves no completion event; the next accepted Run can deliver it.
What this means for access. A reader of this Session’s events can read those
final messages without being able to open the child Session itself, whose access
is granted separately. That is deliberate and it is the same boundary
wamp.message.created draws: a Session’s events are the record of what that
Session’s agent was told and said, and this text was told to it. The child’s own
transcript stays behind the child’s own access.
Change notices
Section titled “Change notices”An App installation that follows many Sessions holds one
GET /v1/sessions/stream connection instead of one Event stream per Session.
That stream carries no events. Each message’s data is a notice that one of the
installation’s Sessions moved:
{ "type": "wamp.session.events_appended", "timestamp": "2026-08-12T09:21:07.000Z", "data": { "sessionId": "9f2b7c14-59d3-4f7a-b8e1-2a6c05d4e731", "lastEventSequence": 12 }}| Field | Type | Notes |
|---|---|---|
type |
string | wamp.session.events_appended. It is not one of the 23 event types and never appears in a Session’s log. Ignore any other value. |
timestamp |
ISO 8601 | When Cloud observed the append. |
data.sessionId |
UUID | A Session this installation created, including child Sessions and forks of its Sessions. |
data.lastEventSequence |
integer ≥ 1 | The Session’s log reached at least this sequence. |
A notice means “read this Session’s log from your cursor” with
GET /v1/sessions/{sessionId}/events?after=<cursor>. Cloud sends at least one
notice per append. One notice may stand for several appends and carries the
newest sequence. Notices may repeat and arrive out of order across Sessions, so
a notice at or below your cursor needs no work. The watermark that resumes the
stream is described in
Follow many Sessions.
What the stream deliberately does not contain
Section titled “What the stream deliberately does not contain”The timeline is a security-reviewed projection, not a debug log. It carries
bounded assistant output and activity metadata; raw tool inputs, raw tool
outputs, and model reasoning are intentionally not part of the public contract.
The /v1 stream is the only public event projection; raw traces are not a
selectable access level and are never exposed by this resource.
Two more absences worth knowing: there is no event for a turn you submitted (the
turn resource is your record of that), and there is no event for sandbox
lifecycle. Read session.workspace for the sandbox and
Sandboxes for what its states mean.
Forward compatibility
Section titled “Forward compatibility”The catalog is closed today and grows additively. Write your handler so that a new type costs you nothing:
switch (event.type) { case 'wamp.message.created': /* … */ break; case 'wamp.run.completed': /* … */ break; default: break; // unknown types are ignored, by contract}Do not treat an unrecognized type as an error, do not reject an unrecognized
property inside data, and do not assume data fields marked optional above
will be present. Deduplicate on event.id, which is stable across replays.
Related
Section titled “Related”- Follow a run live — the stream, cursors, resuming after a disconnect, and following many Sessions at once.
- Events operations — the generated endpoint reference for reading the log.
- the API reference — the
SessionEventandEventPageobject definitions. - Errors — the HTTP failures you meet while reading it.