diff --git a/src/common/types/message.ts b/src/common/types/message.ts index 1b2853849..7d9c1661c 100644 --- a/src/common/types/message.ts +++ b/src/common/types/message.ts @@ -124,7 +124,7 @@ export interface MuxMetadata { // Readers should use helper: isCompacted = compacted !== undefined && compacted !== false compacted?: "user" | "idle" | boolean; toolPolicy?: ToolPolicy; // Tool policy active when this message was sent (user messages only) - mode?: string; // The mode (plan/exec/etc) active when this message was sent (assistant messages only) + mode?: string; // The mode active when this message was sent (assistant messages only) - plan/exec today, custom modes in future cmuxMetadata?: MuxFrontendMetadata; // Frontend-defined metadata, backend treats as black-box muxMetadata?: MuxFrontendMetadata; // Frontend-defined metadata, backend treats as black-box } diff --git a/src/node/services/sessionTimingService.ts b/src/node/services/sessionTimingService.ts index a456f2e83..6ebcdf510 100644 --- a/src/node/services/sessionTimingService.ts +++ b/src/node/services/sessionTimingService.ts @@ -482,11 +482,15 @@ export class SessionTimingService { const model = normalizeGatewayModel(data.model); + // Validate mode: stats schema only accepts "plan" | "exec" for now. + // Custom modes will need schema updates when supported. + const mode = data.mode === "plan" || data.mode === "exec" ? data.mode : undefined; + const state: ActiveStreamState = { workspaceId: data.workspaceId, messageId: data.messageId, model, - mode: data.mode, + mode, startTimeMs: data.startTime, firstTokenTimeMs: null, completedToolExecutionMs: 0, diff --git a/src/node/services/streamManager.ts b/src/node/services/streamManager.ts index fbe1966da..7e13b07ff 100644 --- a/src/node/services/streamManager.ts +++ b/src/node/services/streamManager.ts @@ -865,7 +865,9 @@ export class StreamManager extends EventEmitter { streamInfo.state = StreamState.STREAMING; // Emit stream start event (include mode from initialMetadata if available) - const streamStartMode = streamInfo.initialMetadata?.mode as "plan" | "exec" | undefined; + // Validate mode - stats schema only accepts "plan" | "exec" for now + const rawMode = streamInfo.initialMetadata?.mode; + const streamStartMode = rawMode === "plan" || rawMode === "exec" ? rawMode : undefined; this.emit("stream-start", { type: "stream-start", workspaceId: workspaceId as string, @@ -1776,7 +1778,10 @@ export class StreamManager extends EventEmitter { await this.tokenTracker.setModel(streamInfo.model); // Emit stream-start event (include mode from initialMetadata if available) - const replayMode = streamInfo.initialMetadata?.mode as "plan" | "exec" | undefined; + // Validate mode - stats schema only accepts "plan" | "exec" for now + const rawReplayMode = streamInfo.initialMetadata?.mode; + const replayMode = + rawReplayMode === "plan" || rawReplayMode === "exec" ? rawReplayMode : undefined; this.emit("stream-start", { type: "stream-start", workspaceId,