Skip to content

Commit 32ede41

Browse files
feat(gateway): expose session activity
1 parent d364142 commit 32ede41

35 files changed

Lines changed: 1723 additions & 36 deletions
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
c5220c8ccb7c2c57fb62f689bba49fa34a062546bcac909f7d38bba61e99a585 plugin-sdk-api-baseline.json
2-
41ed9c28192c9f9c47de551b854a880225212c441f92e527681462b7fc3a9bab plugin-sdk-api-baseline.jsonl
1+
63c7f46b6e9fad7c6e862b5411afb8f43a30e31b51241ac0b8dbf44a9bd76496 plugin-sdk-api-baseline.json
2+
103607db80471c884b05dceb8c6ea9d36017ee97650fe3a3a097a3c531bef170 plugin-sdk-api-baseline.jsonl

docs/gateway/protocol.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ enumeration of `src/gateway/server-methods/*.ts`.
445445

446446
<Accordion title="Session control">
447447
- `sessions.list` returns the current session index, including per-row `agentRuntime` metadata when an agent runtime backend is configured.
448+
- `sessions.activity` returns active detached tasks and live tool executions for a session. Descendant session activity is included by default so parent-session views can show subagent work without polling every child session separately.
448449
- `sessions.subscribe` and `sessions.unsubscribe` toggle session change event subscriptions for the current WS client.
449450
- `sessions.messages.subscribe` and `sessions.messages.unsubscribe` toggle transcript/message event subscriptions for one session.
450451
- `sessions.preview` returns bounded transcript previews for specific session keys.
@@ -511,9 +512,9 @@ enumeration of `src/gateway/server-methods/*.ts`.
511512
events. In protocol v4, delta payloads carry `deltaText`; `message` remains
512513
the cumulative assistant snapshot. Non-prefix replacements set `replace=true`
513514
and use `deltaText` as the replacement text.
514-
- `session.message`, `session.operation`, and `session.tool`: transcript,
515-
in-flight session operation, and event-stream updates for a subscribed
516-
session.
515+
- `session.message`, `session.operation`, `session.tool`, and
516+
`session.activity`: transcript, in-flight session operation, live tool
517+
stream, and active task/tool activity updates for subscribed sessions.
517518
- `sessions.changed`: session index or metadata changed.
518519
- `presence`: system presence snapshot updates.
519520
- `tick`: periodic keepalive / liveness event.

docs/plugins/sdk-overview.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,8 @@ cover CLI and Gateway-backed install or update paths.
444444
- `message_sending`: returning `{ cancel: false }` is treated as no decision (same as omitting `cancel`), not as an override.
445445
- `message_received`: use the typed `threadId` field when you need inbound thread/topic routing. Keep `metadata` for channel-specific extras.
446446
- `message_sending`: use typed `replyToId` / `threadId` routing fields before falling back to channel-specific `metadata`.
447+
- `task_created`, `task_updated`, `task_finished`, and `task_deleted`: observe metadata-only Gateway task lifecycle changes without raw task payloads or transcript contents.
448+
- `tool_started`, `tool_updated`, and `tool_finished`: observe metadata-only live tool activity without tool arguments or results.
447449
- `gateway_start`: use `ctx.config`, `ctx.workspaceDir`, and `ctx.getCron?.()` for gateway-owned startup state instead of relying on internal `gateway:startup` hooks.
448450
- `cron_changed`: observe gateway-owned cron lifecycle changes. Use `event.job?.state?.nextRunAtMs` and `ctx.getCron?.()` when syncing external wake schedulers, and keep OpenClaw as the source of truth for due checks and execution.
449451

docs/web/control-ui.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ Imported themes are stored only in the current browser profile. They are not wri
134134
- Chat history refreshes request a bounded recent window with per-message text caps so large sessions do not force the browser to render a full transcript payload before the chat becomes usable.
135135
- Talk through browser realtime sessions. OpenAI uses direct WebRTC, Google Live uses a constrained one-use browser token over WebSocket, and backend-only realtime voice plugins use the Gateway relay transport. Client-owned provider sessions start with `talk.client.create`; Gateway relay sessions start with `talk.session.create`. The relay keeps provider credentials on the Gateway while the browser streams microphone PCM through `talk.session.appendAudio`, forwards `openclaw_agent_consult` provider tool calls through `talk.client.toolCall` for Gateway policy and the larger configured OpenClaw model, and routes active-run voice steering through `talk.client.steer` or `talk.session.steer`.
136136
- Stream tool calls + live tool output cards in Chat (agent events).
137-
- Activity tab with browser-local, redaction-first summaries of live tool activity from existing `session.tool` / tool event delivery.
137+
- Chat running-work strip for active background tasks and live tools, including descendant-session work (`sessions.activity`, `session.activity`, and `session.tool`).
138138

139139
</Accordion>
140140
<Accordion title="Channels, instances, sessions, dreams">
@@ -201,11 +201,11 @@ Typical workflow:
201201

202202
The page redacts credential-bearing URL-like values before rendering and quotes server names in command snippets so copied commands still work with spaces or shell metacharacters. The full CLI and config reference lives in [MCP](/cli/mcp).
203203

204-
## Activity tab
204+
## Activity tab and chat activity strip
205205

206-
The Activity tab is an ephemeral browser-local observer for live tool activity. It is derived from the same Gateway `session.tool` / tool event stream that powers Chat tool cards; it does not add another Gateway event family, endpoint, durable activity store, metrics feed, or external observer stream.
206+
The Chat view shows a running-work strip for active session work. It loads the current snapshot with `sessions.activity`, refreshes from `session.activity` events, and still uses the same `session.tool` / tool event stream that powers Chat tool cards for detailed live tool rendering.
207207

208-
Activity entries keep only sanitized summaries and redacted, truncated output previews. Tool argument values are not stored in Activity state; the UI shows that arguments are hidden and records only the argument field count. The in-memory list follows the current browser tab, survives navigation within the Control UI, and resets on page reload, session switch, or **Clear**.
208+
The Activity tab remains a redaction-first tool activity log. Activity entries keep only sanitized summaries and redacted, truncated output previews. Tool argument values are not stored in Activity state; the UI shows that arguments are hidden and records only the argument field count. Running detached tasks and live tools are session-scoped in the Chat strip, include descendant sessions by default, and clear from the list when the Gateway reports completion.
209209

210210
## Operator terminal
211211

packages/gateway-protocol/src/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,10 @@ import {
352352
SecretsResolveResultSchema,
353353
type SessionsAbortParams,
354354
SessionsAbortParamsSchema,
355+
type SessionsActivityParams,
356+
SessionsActivityParamsSchema,
357+
type SessionsActivityResult,
358+
SessionsActivityResultSchema,
355359
type SessionsCompactParams,
356360
SessionsCompactParamsSchema,
357361
type SessionsCleanupParams,
@@ -682,6 +686,9 @@ export const validateSessionsListParams = lazyCompile<SessionsListParams>(Sessio
682686
export const validateSessionsCleanupParams = lazyCompile<SessionsCleanupParams>(
683687
SessionsCleanupParamsSchema,
684688
);
689+
export const validateSessionsActivityParams = lazyCompile<SessionsActivityParams>(
690+
SessionsActivityParamsSchema,
691+
);
685692
export const validateSessionsPreviewParams = lazyCompile<SessionsPreviewParams>(
686693
SessionsPreviewParamsSchema,
687694
);
@@ -1088,6 +1095,8 @@ export {
10881095
NodePendingEnqueueResultSchema,
10891096
SessionsListParamsSchema,
10901097
SessionsCleanupParamsSchema,
1098+
SessionsActivityParamsSchema,
1099+
SessionsActivityResultSchema,
10911100
SessionsPreviewParamsSchema,
10921101
SessionsDescribeParamsSchema,
10931102
SessionsResolveParamsSchema,
@@ -1441,6 +1450,8 @@ export type {
14411450
NodePendingEnqueueResult,
14421451
SessionsListParams,
14431452
SessionsCleanupParams,
1453+
SessionsActivityParams,
1454+
SessionsActivityResult,
14441455
SessionsPreviewParams,
14451456
SessionsDescribeParams,
14461457
SessionsResolveParams,

packages/gateway-protocol/src/schema/protocol-schemas.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ import {
255255
} from "./secrets.js";
256256
import {
257257
SessionsAbortParamsSchema,
258+
SessionsActivityParamsSchema,
259+
SessionsActivityResultSchema,
258260
SessionsCompactParamsSchema,
259261
SessionsCompactionBranchParamsSchema,
260262
SessionsCompactionBranchResultSchema,
@@ -394,6 +396,8 @@ export const ProtocolSchemas = {
394396
// Session lifecycle, message routing, compaction, and usage accounting.
395397
SessionsListParams: SessionsListParamsSchema,
396398
SessionsCleanupParams: SessionsCleanupParamsSchema,
399+
SessionsActivityParams: SessionsActivityParamsSchema,
400+
SessionsActivityResult: SessionsActivityResultSchema,
397401
SessionsPreviewParams: SessionsPreviewParamsSchema,
398402
SessionsDescribeParams: SessionsDescribeParamsSchema,
399403
SessionsResolveParams: SessionsResolveParamsSchema,

packages/gateway-protocol/src/schema/sessions.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,73 @@ export const SessionsCleanupParamsSchema = Type.Object(
204204
{ additionalProperties: false },
205205
);
206206

207+
const SessionActivityTaskStatusSchema = Type.Union([
208+
Type.Literal("queued"),
209+
Type.Literal("running"),
210+
]);
211+
212+
export const SessionActivityTaskSchema = Type.Object(
213+
{
214+
id: NonEmptyString,
215+
sessionKey: NonEmptyString,
216+
runtime: Type.Union([
217+
Type.Literal("subagent"),
218+
Type.Literal("acp"),
219+
Type.Literal("cli"),
220+
Type.Literal("cron"),
221+
]),
222+
title: Type.String(),
223+
status: SessionActivityTaskStatusSchema,
224+
createdAt: Type.Integer({ minimum: 0 }),
225+
startedAt: Type.Optional(Type.Integer({ minimum: 0 })),
226+
lastEventAt: Type.Optional(Type.Integer({ minimum: 0 })),
227+
childSessionKey: Type.Optional(NonEmptyString),
228+
runId: Type.Optional(NonEmptyString),
229+
label: Type.Optional(Type.String()),
230+
progressSummary: Type.Optional(Type.String()),
231+
},
232+
{ additionalProperties: false },
233+
);
234+
235+
export const SessionActivityToolSchema = Type.Object(
236+
{
237+
id: NonEmptyString,
238+
sessionKey: NonEmptyString,
239+
runId: NonEmptyString,
240+
toolCallId: NonEmptyString,
241+
name: NonEmptyString,
242+
title: NonEmptyString,
243+
status: Type.Literal("running"),
244+
startedAt: Type.Integer({ minimum: 0 }),
245+
updatedAt: Type.Integer({ minimum: 0 }),
246+
},
247+
{ additionalProperties: false },
248+
);
249+
250+
export const SessionsActivityParamsSchema = Type.Object(
251+
{
252+
key: NonEmptyString,
253+
/** Include work running in recursively spawned child sessions. Defaults to true. */
254+
includeDescendants: Type.Optional(Type.Boolean()),
255+
},
256+
{ additionalProperties: false },
257+
);
258+
259+
export const SessionsActivityResultSchema = Type.Object(
260+
{
261+
key: NonEmptyString,
262+
/** Monotonic best-effort version for this session activity view. */
263+
revision: Type.Integer({ minimum: 0 }),
264+
/** The root session and any child sessions represented in this snapshot. */
265+
includedSessionKeys: Type.Array(NonEmptyString),
266+
/** True when descendant traversal reached the implementation safety limit. */
267+
truncated: Type.Boolean(),
268+
tasks: Type.Array(SessionActivityTaskSchema),
269+
tools: Type.Array(SessionActivityToolSchema),
270+
},
271+
{ additionalProperties: false },
272+
);
273+
207274
/** Reads short previews for selected session keys. */
208275
export const SessionsPreviewParamsSchema = Type.Object(
209276
{

packages/gateway-protocol/src/schema/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ export type PushTestResult = SchemaType<"PushTestResult">;
7070
/** Session lifecycle, message routing, compaction, patch, and usage payloads. */
7171
export type SessionsListParams = SchemaType<"SessionsListParams">;
7272
export type SessionsCleanupParams = SchemaType<"SessionsCleanupParams">;
73+
export type SessionsActivityParams = SchemaType<"SessionsActivityParams">;
74+
export type SessionsActivityResult = SchemaType<"SessionsActivityResult">;
7375
export type SessionsPreviewParams = SchemaType<"SessionsPreviewParams">;
7476
export type SessionsDescribeParams = SchemaType<"SessionsDescribeParams">;
7577
export type SessionsResolveParams = SchemaType<"SessionsResolveParams">;

src/gateway/methods/core-descriptors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export const CORE_GATEWAY_METHOD_SPECS: readonly CoreGatewayMethodSpec[] = [
140140
{ name: "voicewake.routing.get", scope: "operator.read" },
141141
{ name: "voicewake.routing.set", scope: "operator.write" },
142142
{ name: "sessions.list", scope: "operator.read", startup: true },
143+
{ name: "sessions.activity", scope: "operator.read" },
143144
{ name: "sessions.subscribe", scope: "operator.read" },
144145
{ name: "sessions.unsubscribe", scope: "operator.read" },
145146
{ name: "sessions.messages.subscribe", scope: "operator.read" },

src/gateway/server-broadcast.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const EVENT_SCOPE_GUARDS: Record<string, string[]> = {
4848
"session.message": [READ_SCOPE],
4949
"session.operation": [READ_SCOPE],
5050
"session.tool": [READ_SCOPE],
51+
"session.activity": [READ_SCOPE],
5152
// Operator terminal byte/exit streams. Admin-gated to match the terminal.*
5253
// methods; also targeted to the owning connection at broadcast time.
5354
"terminal.data": [ADMIN_SCOPE],

0 commit comments

Comments
 (0)