perf: remove raw field from timeline items to reduce payload size

Remove the `raw` field that was duplicating provider data in timeline
items, reducing WebSocket payload sizes by 64-85%:
- session_state: ~320KB → ~48KB
- agent_stream_snapshot: ~320KB → ~114KB

Changes:
- Remove raw from AgentTimelineItem, AgentStreamEvent, AgentPermissionRequest
- Remove raw assignments from claude-agent.ts and codex-agent.ts
- Remove provider_event handling from stream.ts (only used for Codex raw)
- Update tests to reflect new behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Mohamed Boudra
2025-11-28 18:20:33 +00:00
co-authored by Claude
parent ae4678bca8
commit e80989ea78
11 changed files with 65 additions and 590 deletions
@@ -352,14 +352,13 @@ export class AgentManager {
recordUserMessage(
agentId: string,
text: string,
options?: { messageId?: string; raw?: unknown }
options?: { messageId?: string }
): void {
const agent = this.requireAgent(agentId);
const item: AgentTimelineItem = {
type: "user_message",
text,
messageId: options?.messageId,
raw: options?.raw,
};
agent.updatedAt = new Date();
agent.lastUserMessageAt = agent.updatedAt;
@@ -1,8 +1,4 @@
import type { ThreadEvent as CodexThreadEvent } from "@openai/codex-sdk";
import type {
Options as ClaudeAgentOptions,
SDKMessage as ClaudeStreamMessage,
} from "@anthropic-ai/claude-agent-sdk";
import type { Options as ClaudeAgentOptions } from "@anthropic-ai/claude-agent-sdk";
export type AgentProvider = "codex" | "claude";
@@ -54,9 +50,9 @@ export type AgentUsage = {
};
export type AgentTimelineItem =
| { type: "user_message"; text: string; messageId?: string; raw?: unknown }
| { type: "assistant_message"; text: string; raw?: unknown }
| { type: "reasoning"; text: string; raw?: unknown }
| { type: "user_message"; text: string; messageId?: string }
| { type: "assistant_message"; text: string }
| { type: "reasoning"; text: string }
| {
type: "tool_call";
server: string;
@@ -68,10 +64,9 @@ export type AgentTimelineItem =
input?: unknown;
output?: unknown;
error?: unknown;
raw?: unknown;
}
| { type: "todo"; items: { text: string; completed: boolean }[]; raw?: unknown }
| { type: "error"; message: string; raw?: unknown };
| { type: "todo"; items: { text: string; completed: boolean }[] }
| { type: "error"; message: string };
export type AgentStreamEvent =
| { type: "thread_started"; sessionId: string; provider: AgentProvider }
@@ -79,7 +74,6 @@ export type AgentStreamEvent =
| { type: "turn_completed"; provider: AgentProvider; usage?: AgentUsage }
| { type: "turn_failed"; provider: AgentProvider; error: string }
| { type: "timeline"; item: AgentTimelineItem; provider: AgentProvider }
| { type: "provider_event"; provider: AgentProvider; raw: CodexThreadEvent | ClaudeStreamMessage }
| { type: "permission_requested"; provider: AgentProvider; request: AgentPermissionRequest }
| {
type: "permission_resolved";
@@ -102,7 +96,6 @@ export type AgentPermissionRequest = {
input?: Record<string, unknown>;
suggestions?: AgentPermissionUpdate[];
metadata?: Record<string, unknown>;
raw?: unknown;
};
export type AgentPermissionResponse =
@@ -405,7 +405,6 @@ class ClaudeAgentSession implements AgentSession {
callId: pending.request.id,
displayName: "Plan approved",
kind: "plan",
raw: pending.request,
});
}
const result: PermissionResult = {
@@ -422,7 +421,6 @@ class ClaudeAgentSession implements AgentSession {
displayName: pending.request.title ?? pending.request.name,
kind: "permission",
input: pending.request.input,
raw: { request: pending.request, response },
});
} else {
const result: PermissionResult = {
@@ -439,7 +437,6 @@ class ClaudeAgentSession implements AgentSession {
displayName: pending.request.title ?? pending.request.name,
kind: "permission",
input: pending.request.input,
raw: { request: pending.request, response },
});
}
@@ -618,7 +615,7 @@ class ClaudeAgentSession implements AgentSession {
}
private translateMessageToEvents(message: SDKMessage): AgentStreamEvent[] {
const events: AgentStreamEvent[] = [{ type: "provider_event", provider: "claude", raw: message }];
const events: AgentStreamEvent[] = [];
switch (message.type) {
case "system":
@@ -722,7 +719,6 @@ class ClaudeAgentSession implements AgentSession {
input,
suggestions: options?.suggestions as AgentPermissionUpdate[] | undefined,
metadata: Object.keys(metadata).length ? metadata : undefined,
raw: { toolName, input, options },
};
this.pushToolCall({
@@ -733,7 +729,6 @@ class ClaudeAgentSession implements AgentSession {
displayName: request.title ?? toolName,
kind: "permission",
input,
raw: { toolName, input },
});
this.pushEvent({ type: "permission_requested", provider: "claude", request });
@@ -762,7 +757,6 @@ class ClaudeAgentSession implements AgentSession {
displayName: request.title ?? toolName,
kind: "permission",
input,
raw: { reason: "timeout", toolName, input },
});
this.pushEvent({
type: "permission_resolved",
@@ -808,7 +802,6 @@ class ClaudeAgentSession implements AgentSession {
this.enqueueTimeline({
type: "todo",
items: todoItems.length > 0 ? todoItems : [{ text: planText, completed: false }],
raw: input,
});
}
@@ -939,7 +932,7 @@ class ClaudeAgentSession implements AgentSession {
if (suppressAssistant) {
return [];
}
return [{ type: "assistant_message", text: content, raw: content }];
return [{ type: "assistant_message", text: content }];
}
const items: AgentTimelineItem[] = [];
@@ -952,7 +945,7 @@ class ClaudeAgentSession implements AgentSession {
this.streamedAssistantTextThisTurn = true;
}
if (!suppressAssistant) {
items.push({ type: "assistant_message", text: block.text, raw: block });
items.push({ type: "assistant_message", text: block.text });
}
}
break;
@@ -963,7 +956,7 @@ class ClaudeAgentSession implements AgentSession {
this.streamedReasoningThisTurn = true;
}
if (!suppressReasoning) {
items.push({ type: "reasoning", text: block.thinking, raw: block });
items.push({ type: "reasoning", text: block.thinking });
}
}
break;
@@ -1009,7 +1002,6 @@ class ClaudeAgentSession implements AgentSession {
displayName: this.buildToolDisplayName(entry),
kind: this.getToolKind(entry.classification),
input: entry.input ?? this.normalizeToolInput(block.input),
raw: block,
},
items
);
@@ -1020,10 +1012,6 @@ class ClaudeAgentSession implements AgentSession {
const server = entry?.server ?? block.server ?? "tool";
const tool = entry?.name ?? block.tool_name ?? "tool";
const status = block.is_error ? "failed" : "completed";
const rawPayload =
!block.is_error && entry?.classification === "file_change" && entry.files?.length
? { block, files: entry.files }
: block;
this.pushToolCall(
{
server,
@@ -1035,7 +1023,6 @@ class ClaudeAgentSession implements AgentSession {
input: entry?.input,
output: !block.is_error && entry?.files?.length ? { files: entry.files } : undefined,
error: block.is_error ? block : undefined,
raw: rawPayload,
},
items
);
@@ -1143,7 +1130,6 @@ class ClaudeAgentSession implements AgentSession {
displayName: this.buildToolDisplayName(entry),
kind: this.getToolKind(entry.classification),
input: normalized,
raw: { type: "tool_use", id: toolId, input: parsed },
});
}
@@ -1312,7 +1298,6 @@ export function convertClaudeHistoryEntry(
timeline.push({
type: "user_message",
text,
raw: message,
});
}
}
@@ -446,7 +446,6 @@ class CodexAgentSession implements AgentSession {
displayName: request.title ?? request.name,
kind: "permission",
input: request.input,
raw: { request, response },
}),
});
@@ -610,7 +609,6 @@ class CodexAgentSession implements AgentSession {
}
private *translateEvent(event: ThreadEvent): Generator<AgentStreamEvent> {
yield { type: "provider_event", provider: "codex", raw: event };
const permissionEvents = this.handlePermissionEvent(event);
if (permissionEvents) {
@@ -658,7 +656,7 @@ class CodexAgentSession implements AgentSession {
yield {
type: "timeline",
provider: "codex",
item: { type: "error", message, raw: event },
item: { type: "error", message },
};
yield { type: "turn_failed", provider: "codex", error: message };
break;
@@ -706,7 +704,6 @@ class CodexAgentSession implements AgentSession {
displayName: request.title ?? request.name,
kind: "permission",
input: request.input,
raw: request.raw,
}),
},
{ type: "permission_requested", provider: "codex", request },
@@ -763,7 +760,6 @@ class CodexAgentSession implements AgentSession {
parsedCommand,
},
metadata: sanitizeMetadata(metadata),
raw,
};
return request;
@@ -810,7 +806,6 @@ class CodexAgentSession implements AgentSession {
input,
suggestions: grantRoot ? [{ grantRoot }] : undefined,
metadata: sanitizeMetadata(metadata),
raw,
};
return request;
@@ -819,9 +814,9 @@ class CodexAgentSession implements AgentSession {
private threadItemToTimeline(item: ThreadItem): AgentTimelineItem | null {
switch (item.type) {
case "agent_message":
return { type: "assistant_message", text: item.text, raw: item };
return { type: "assistant_message", text: item.text };
case "reasoning":
return { type: "reasoning", text: item.text, raw: item };
return { type: "reasoning", text: item.text };
case "command_execution":
return createToolCallTimelineItem({
server: "command",
@@ -833,7 +828,6 @@ class CodexAgentSession implements AgentSession {
input: { command: item.command, cwd: (item as any)?.cwd },
output: (item as any)?.output,
error: (item as any)?.error,
raw: item,
});
case "file_change": {
const files = item.changes.map((change) => ({ path: change.path, kind: change.kind }));
@@ -845,7 +839,6 @@ class CodexAgentSession implements AgentSession {
displayName: buildFileChangeSummary(files),
kind: "edit",
output: { files },
raw: item,
});
}
case "mcp_tool_call":
@@ -858,7 +851,6 @@ class CodexAgentSession implements AgentSession {
kind: "tool",
input: (item as any)?.input,
output: (item as any)?.output,
raw: item,
});
case "web_search":
return createToolCallTimelineItem({
@@ -869,12 +861,11 @@ class CodexAgentSession implements AgentSession {
displayName: item.query ? `Web search: ${item.query}` : "Web search",
kind: "search",
input: { query: item.query },
raw: item,
});
case "todo_list":
return { type: "todo", items: item.items, raw: item };
return { type: "todo", items: item.items };
case "error":
return { type: "error", message: item.message, raw: item };
return { type: "error", message: item.message };
default:
return null;
}
@@ -992,12 +983,12 @@ function handleRolloutResponseItem(
const text = extractMessageText(payload.content);
if (text) {
if (payload.role === "assistant") {
events.push({ type: "timeline", provider: "codex", item: { type: "assistant_message", text, raw: payload } });
events.push({ type: "timeline", provider: "codex", item: { type: "assistant_message", text } });
} else if (payload.role === "user") {
if (isSyntheticRolloutUserMessage(text)) {
break;
}
events.push({ type: "timeline", provider: "codex", item: { type: "user_message", text, raw: payload } });
events.push({ type: "timeline", provider: "codex", item: { type: "user_message", text } });
}
}
break;
@@ -1005,7 +996,7 @@ function handleRolloutResponseItem(
case "reasoning": {
const text = extractReasoningText(payload);
if (text) {
events.push({ type: "timeline", provider: "codex", item: { type: "reasoning", text, raw: payload } });
events.push({ type: "timeline", provider: "codex", item: { type: "reasoning", text } });
}
break;
}
@@ -1031,7 +1022,7 @@ function handleRolloutEventMessage(payload: RolloutEventPayload | undefined, eve
return;
}
if (payload.type === "agent_reasoning" && typeof payload.text === "string") {
events.push({ type: "timeline", provider: "codex", item: { type: "reasoning", text: payload.text, raw: payload } });
events.push({ type: "timeline", provider: "codex", item: { type: "reasoning", text: payload.text } });
}
}
@@ -1064,7 +1055,6 @@ function handleRolloutFunctionCall(
displayName: buildCommandDisplayName(command),
kind: "execute",
input: { command, cwd },
raw: payload,
}),
});
}
@@ -1075,7 +1065,7 @@ function handleRolloutFunctionCall(
const args = safeJsonParse<{ plan?: unknown }>(payload.arguments);
const planItems = parsePlanItems(args);
if (planItems.length) {
events.push({ type: "timeline", provider: "codex", item: { type: "todo", items: planItems, raw: payload } });
events.push({ type: "timeline", provider: "codex", item: { type: "todo", items: planItems } });
}
return;
}
@@ -1091,7 +1081,6 @@ function handleRolloutFunctionCall(
displayName: `${name}`,
kind: "tool",
input: safeJsonParse(payload.arguments),
raw: payload,
}),
});
}
@@ -1123,7 +1112,6 @@ function finalizeRolloutFunctionCall(
kind: "execute",
input: { command: command.command, cwd: command.cwd },
output: result,
raw: { payload, result },
}),
});
commandCalls.delete(payload.call_id);
@@ -1143,7 +1131,6 @@ function handleRolloutCustomToolCall(payload: RolloutCustomToolCallPayload, even
displayName: buildFileChangeSummary(files),
kind: "edit",
output: { files },
raw: payload,
}),
});
}
@@ -1162,7 +1149,6 @@ function handleRolloutCustomToolCall(payload: RolloutCustomToolCallPayload, even
kind: "tool",
input: payload.input,
output: payload.output,
raw: payload,
}),
});
}
-19
View File
@@ -13,11 +13,6 @@ import type {
AgentUsage,
} from "./agent/agent-sdk-types.js";
type ProviderEventPayload = Extract<
AgentStreamEvent,
{ type: "provider_event" }
>;
export type AgentSnapshotPayload = Omit<
AgentSnapshot,
"createdAt" | "updatedAt" | "lastUserMessageAt"
@@ -118,7 +113,6 @@ export const AgentPermissionRequestPayloadSchema: z.ZodType<AgentPermissionReque
input: z.record(z.unknown()).optional(),
suggestions: z.array(AgentPermissionUpdateSchema).optional(),
metadata: z.record(z.unknown()).optional(),
raw: z.unknown().optional(),
});
export const AgentTimelineItemPayloadSchema: z.ZodType<AgentTimelineItem> =
@@ -127,17 +121,14 @@ export const AgentTimelineItemPayloadSchema: z.ZodType<AgentTimelineItem> =
type: z.literal("user_message"),
text: z.string(),
messageId: z.string().optional(),
raw: z.unknown().optional(),
}),
z.object({
type: z.literal("assistant_message"),
text: z.string(),
raw: z.unknown().optional(),
}),
z.object({
type: z.literal("reasoning"),
text: z.string(),
raw: z.unknown().optional(),
}),
z.object({
type: z.literal("tool_call"),
@@ -150,7 +141,6 @@ export const AgentTimelineItemPayloadSchema: z.ZodType<AgentTimelineItem> =
input: z.unknown().optional(),
output: z.unknown().optional(),
error: z.unknown().optional(),
raw: z.unknown().optional(),
}),
z.object({
type: z.literal("todo"),
@@ -160,21 +150,13 @@ export const AgentTimelineItemPayloadSchema: z.ZodType<AgentTimelineItem> =
completed: z.boolean(),
})
),
raw: z.unknown().optional(),
}),
z.object({
type: z.literal("error"),
message: z.string(),
raw: z.unknown().optional(),
}),
]);
const ProviderEventPayloadSchema = z.object({
type: z.literal("provider_event"),
provider: AgentProviderSchema,
raw: z.custom<ProviderEventPayload["raw"]>(),
});
export const AgentStreamEventPayloadSchema = z.discriminatedUnion("type", [
z.object({
type: z.literal("thread_started"),
@@ -200,7 +182,6 @@ export const AgentStreamEventPayloadSchema = z.discriminatedUnion("type", [
provider: AgentProviderSchema,
item: AgentTimelineItemPayloadSchema,
}),
ProviderEventPayloadSchema,
z.object({
type: z.literal("permission_requested"),
provider: AgentProviderSchema,
+1 -4
View File
@@ -2029,10 +2029,7 @@ export class Session {
this.emit({
type: "agent_stream_snapshot",
payload: {
agentId: agent.id,
events,
},
payload: { agentId: agent.id, events },
});
return timeline.length;