Skip to content

vis: rewrite for new agent-core protocol#34

Open
RealKai42 wants to merge 23 commits into
mainfrom
kaiyi/richmond-v1
Open

vis: rewrite for new agent-core protocol#34
RealKai42 wants to merge 23 commits into
mainfrom
kaiyi/richmond-v1

Conversation

@RealKai42
Copy link
Copy Markdown
Collaborator

Summary

Rewrite the apps/vis debug visualization tool to consume the current AgentRecord wire protocol. The old code targeted a now-defunct protocol with record types like turn_begin / step_begin / content_part / tool_call / tool_result / notification / subagent_*; none of those types exist anymore. No backward compatibility is preserved.

What changed

  • Single-source types. vis now imports AgentRecord, ContextMessage, PromptOrigin, LoopRecordedEvent, TokenUsage, etc. directly from @moonshot-ai/agent-core and @moonshot-ai/kosong. The 700-LOC local type mirror is gone — drift that caused this collapse cannot recur.

  • New storage layout. Session lister reads ~/.kimi-code/sessions/<wd_key>/<session_id>/state.json (camelCase, ISO timestamps) and agents/<id>/wire.jsonl per agent. Sessions with state.custom.imported_from_kimi_cli === true are filtered out.

  • New record union. All 24 new record types (metadata, config.update, turn.{prompt,steer,cancel}, context.*, tools.*, permission.*, plan_mode.*, full_compaction.*, usage.record, background.stop) are exhaustively rendered with badges, headlines, and detail views. Loop-level granularity (step.begin / step.end / content.part / tool.call / tool.result) is preserved by expanding context.append_loop_event records on demand.

  • Per-agent timelines. Each agent has its own wire.jsonl; the Wire and Context tabs include an agent dropdown (defaults to main). The Subagents tab builds its tree from state.json.agents instead of synthesized spawn/complete events.

  • Context projection. A new context-projector derives the conversation timeline, token usage totals (by scope and by model), config snapshot, permission mode, and plan-mode state from raw wire records.

  • API redesigned. All response payloads are camelCase with epoch-ms timestamps. Endpoints: GET /api/sessions, GET /api/sessions/:id, GET /api/sessions/:id/wire?agent=<id>, GET /api/sessions/:id/context?agent=<id>, GET /api/sessions/:id/agents, DELETE /api/sessions/:id. The legacy /tool-results/* route is gone — tool outputs are inline in context.append_message records.

  • agent-core export surface. Added wire-record type re-exports from @moonshot-ai/agent-core root so in-monorepo consumers don't need deep imports.

Verification

  • pnpm --filter @moonshot-ai/vis-server build — passes
  • pnpm --filter @moonshot-ai/vis-server test — 13/13 across 4 test files (session-store, wire-reader, context-projector, agent-tree)
  • pnpm --filter @moonshot-ai/vis-web build — passes
  • End-to-end smoke against real session data: list, detail, wire, context, agent-tree endpoints all return correct shapes
  • Multi-agent tree validated against a session with main + agent-0 + agent-1

Known issue

pnpm --filter @moonshot-ai/vis-web typecheck reports 22 errors from agent-core's *.md raw imports and one pre-existing kosong Uint8Array typing. These are upstream packaging issues unrelated to this PR — vite build succeeds. Tracking separately.

Test plan

  • Open vis at the list page, confirm native sessions render with correct timestamps and agent counts
  • Click into a session with subagents, verify the Wire tab's agent dropdown switches timelines
  • In Context tab, verify the 4-segment token bar, message bubbles by role, and tool-call attribution
  • In Subagents tab, navigate the agent tree to a child agent and confirm its wire/context loads
  • In State tab, confirm raw state.json is rendered alongside highlight cards

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 25, 2026

⚠️ No Changeset found

Latest commit: ad04343

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

Copy link
Copy Markdown

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

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: ad04343aac

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +53 to +56
<div className="flex flex-1 items-center justify-center p-6 font-mono text-[12px] text-fg-3">
<div className="text-center">
<div>session detail UI rewrite in progress</div>
<div className="mt-2 text-fg-3">
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 Restore session detail UI instead of placeholder message

This replaces the /sessions/:sessionId body with a static “rewrite in progress” placeholder, so users can no longer access Wire/Context/Subagents/State from the main detail route at all. That breaks the primary inspection flow for every session and makes the new protocol data effectively unreachable unless users manually guess deep links.

Useful? React with 👍 / 👎.

Comment on lines +17 to +20
export function WireTab({ sessionId, initialAgentId = 'main' }: WireTabProps) {
const [agentId, setAgentId] = useState<string>(initialAgentId);
const { data: detail } = useSession(sessionId);
const { data: wire, isLoading, error } = useWire(sessionId, agentId);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Sync WireTab agent state with route-driven prop changes

The selected agent is initialized from initialAgentId only once and then stored in local state, so navigating between /sessions/:sessionId/agents/:agentId URLs while the component remains mounted can leave the dropdown/data on the previous agent. In that case the URL and rendered wire timeline disagree, which can lead users to inspect the wrong agent data.

Useful? React with 👍 / 👎.

Comment on lines +77 to +81
return {
sessionId,
sessionDir,
workDir: '', // filled in by caller via session_index lookup
title: state.title ?? null,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Populate workDir instead of returning empty workspace for all sessions

Session summaries hardcode workDir to an empty string, and the list route returns these values directly. As a result, the UI cannot show or filter by actual workspace and every session appears as “(no workspace)”, which is a functional regression for identifying sessions across projects.

Useful? React with 👍 / 👎.

Comment on lines 110 to 112
{active === 'context' ? (
contextQ.isLoading ? (
<Centered>loading context…</Centered>
) : contextQ.error ? (
<ErrorView msg={(contextQ.error).message} />
) : contextQ.data ? (
<ContextTab
sessionId={sessionId}
messages={contextQ.data.annotated_messages}
projectedState={contextQ.data.projected_state}
/>
) : null
<Centered>context tab pending</Centered>
) : null}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Implement subagent context tab instead of pending placeholder

The subagent detail page still exposes a Context tab, but selecting it only renders a “context tab pending” stub and never fetches or displays context data. This is a direct feature regression from the previous behavior and blocks core debugging workflows for subagent runs.

Useful? React with 👍 / 👎.

Comment on lines +69 to +70
const info = await scanWire(mainWirePath);
mainCount = info.count;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Handle wire read failures per session during listing

Session listing now calls scanWire without guarding read-stream failures, so a single unreadable/corrupt wire.jsonl can reject listSessions and fail the entire /api/sessions response instead of marking just that session unhealthy. The previous code path tolerated per-session wire read failures, so this reduces robustness when any session data is partially broken.

Useful? React with 👍 / 👎.

files_read: [archivePath],
health: result.health,
warnings: [...result.warnings],
const result = await readAgentWire(join(agent.homedir, 'wire.jsonl'));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Read wire from canonical agent path instead of state.homedir

This route verifies wire existence using the canonical session path during inventory, but then reads from join(agent.homedir, 'wire.jsonl') taken from mutable state.json metadata. If homedir is stale or inconsistent, requests can fail with READ_ERROR (or read the wrong file) even though wireExists was true, causing incorrect or unavailable wire/context views.

Useful? React with 👍 / 👎.

Comment on lines +106 to +107
for (const [id, meta] of Object.entries(state.agents ?? {})) {
const wirePath = join(sessionDir, 'agents', id, 'wire.jsonl');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fallback to main agent when state.agents is absent

Agent inventory is built only from state.agents, so sessions with a valid agents/main/wire.jsonl but missing/partial state.agents produce an empty agent list. In that case /api/sessions/:id/wire and /context return agent-not-found even though the main wire file exists, which regresses debuggability for partially written or migrated state files.

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