Skip to content

feat(kap-server): stream raw xstate inspection events over /api/v1/debug/ws - #3687

Merged
sailist merged 1 commit into
MoonshotAI:mainfrom
sailist:feat-178-09-09-ws-debug-xstate-events
Sep 9, 2026
Merged

feat(kap-server): stream raw xstate inspection events over /api/v1/debug/ws#3687
sailist merged 1 commit into
MoonshotAI:mainfrom
sailist:feat-178-09-09-ws-debug-xstate-events

Conversation

@sailist

@sailist sailist commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

Internal observability work (kimi-code-harness state-machine inspection); no linked issue.

Problem

External observers of the agent runtime (e.g. the kimi-code-harness inspection UI) can only consume the twice-projected event stream on /api/v1/ws: human-machine emitted events are first consolidated into MachineEngineEvent, then projected onto Event2 domain events, then mapped onto WS envelopes. Nested xstate states (e.g. agent idle{ready,waiting} / running{active,aborting}) are lost in projection, so a faithful, real-time state-machine view cannot be built from that stream. The existing /api/v1/debug/* RPC surface is request/response only and has no push channel.

What changed

  • Collection (agent-core-v2): new process-level xstateInspectionCollector singleton that serializes xstate inspection events into compact envelopes ({type, timestamp, actorSessionId, actorId?, logicId?, eventType?, stateValue?}) and never includes machine context. The createActor wrapper in human/xstate2.ts now publishes every root actor system's inspection events to the collector, covering the whole human stack (agent/turn/llm/tool subtree plus interaction/usage/provider-catalog/watch); AgentActorService switches its createActor import to the same wrapper so the feature actors (cron/goal/reminder/todo/notifyUserNudge/dateChange) are included too.
  • Transport (kap-server): new WebSocket endpoint /api/v1/debug/ws, registered only under the existing debugEndpoints gate (loopback bind + --debug-endpoints), sharing the upgrade pipeline's host/origin/bearer checks with /api/v1/ws. Connections use a lightweight outbound queue with high-watermark drop for this volatile stream and a 10s heartbeat — no journal, seq/epoch, or resync. Server shutdown now terminates debug clients before app.close() to avoid a deadlock where the HTTP server waits for upgraded sockets.
  • Tests: collector envelope serialization (scalar-only, no context leakage) and authorized end-to-end streaming through the gated endpoint, including rejection when the flag is off.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue (external PRs: the issue must have a maintainer's /approve).
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

@changeset-bot

changeset-bot Bot commented Sep 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: f13cec2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@sailist
sailist merged commit 6299cfd into MoonshotAI:main Sep 9, 2026
15 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f13cec2940

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +33 to +35
actorSessionId: event.actorRef.sessionId,
actorId: scalar(actorRef.id),
logicId: scalar(logic?.id),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include runtime ownership in inspection envelopes

When multiple Kimi sessions or agents are active, these fields cannot associate an inspected actor with its owning runtime: createMachineEngine creates each root actor without an application session/agent ID, while nested actors reuse IDs such as turn and tool. Because the projection also discards XState's source/parent relationship, consumers cannot reconstruct which child belongs to which root, so concurrent state-machine views will mix unrelated actors. Include an owning session/agent identifier and parent/source actor session ID in the envelope.

Useful? React with 👍 / 👎.

Comment on lines +53 to +57
private onEnvelope(envelope: XstateInspectionEnvelope): void {
if (this.closed) return;
if (this.socket.bufferedAmount > this.highWaterMarkBytes) return;
this.outbound.push(envelope);
this.scheduleFlush();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bound the queued and flushed inspection data

With a slow or non-reading debug client and a synchronous burst of inspection events, bufferedAmount may remain below the limit while an arbitrarily large outbound array accumulates; the subsequent flush then sends the entire captured batch without rechecking the high-water mark. This defeats the advertised backpressure limit and can grow both process memory and the WebSocket send buffer far beyond 1 MiB. Track queued bytes or cap the queue, and stop/batch the flush once bufferedAmount reaches the limit.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant