Skip to content

Commit aae4426

Browse files
committed
🤖 feat: add mode to stream-start events for plan/exec breakdown
- Add mode field to StreamStartEventSchema - Propagate mode from initialMetadata in streamManager (live + replay) - Include mode in aiService context_exceeded and tool-policy-noop events - Store mode in StreamingContext and return from getActiveStreamTimingStats - Update mock scenario types to support mode - The 'Split by mode' checkbox now appears when mode data is available Change-Id: I7fc9df3551ff282708eb3d189818f27fbc05d674 Signed-off-by: Thomas Kosiewski <[email protected]>
1 parent 80b7d5a commit aae4426

File tree

6 files changed

+20
-2
lines changed

6 files changed

+20
-2
lines changed

src/browser/utils/messages/StreamingMessageAggregator.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,8 @@ export class StreamingMessageAggregator {
644644
liveTokenCount: number;
645645
/** Live tokens-per-second (trailing window) */
646646
liveTPS: number;
647+
/** Mode (plan/exec) for this stream */
648+
mode?: "plan" | "exec";
647649
} | null {
648650
// Get the first (and typically only) active stream
649651
const entries = Array.from(this.activeStreams.entries());
@@ -664,6 +666,7 @@ export class StreamingMessageAggregator {
664666
model: context.model,
665667
liveTokenCount: this.getStreamingTokenCount(messageId),
666668
liveTPS: this.getStreamingTPS(messageId),
669+
mode: context.mode,
667670
};
668671
}
669672

@@ -881,6 +884,7 @@ export class StreamingMessageAggregator {
881884
firstTokenTime: null,
882885
toolExecutionMs: 0,
883886
pendingToolStarts: new Map(),
887+
mode: data.mode,
884888
};
885889

886890
// Use messageId as key - ensures only ONE stream per message

src/common/orpc/schemas/stream.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ export const StreamStartEventSchema = z.object({
4343
startTime: z.number().meta({
4444
description: "Backend timestamp when stream started (Date.now())",
4545
}),
46+
mode: z
47+
.enum(["plan", "exec"])
48+
.optional()
49+
.meta({
50+
description: "Agent mode (plan/exec) for this stream",
51+
}),
4652
});
4753

4854
export const StreamDeltaEventSchema = z.object({

src/node/services/aiService.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,7 @@ export class AIService extends EventEmitter {
12831283
model: modelString,
12841284
historySequence,
12851285
startTime: Date.now(),
1286+
...(uiMode && { mode: uiMode }),
12861287
};
12871288
this.emit("stream-start", streamStartEvent);
12881289

@@ -1319,6 +1320,7 @@ export class AIService extends EventEmitter {
13191320
model: modelString,
13201321
historySequence,
13211322
startTime: Date.now(),
1323+
...(uiMode && { mode: uiMode }),
13221324
};
13231325
this.emit("stream-start", streamStartEvent);
13241326

src/node/services/mock/mockScenarioPlayer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ export class MockScenarioPlayer {
273273
model: event.model,
274274
historySequence,
275275
startTime: Date.now(),
276+
...(event.mode && { mode: event.mode }),
276277
};
277278
this.deps.aiService.emit("stream-start", payload);
278279
break;

src/node/services/mock/scenarioTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface MockStreamStartEvent extends MockAssistantEventBase {
2020
kind: "stream-start";
2121
messageId: string;
2222
model: string;
23+
mode?: "plan" | "exec";
2324
}
2425

2526
export interface MockStreamDeltaEvent extends MockAssistantEventBase {

src/node/services/streamManager.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -789,14 +789,16 @@ export class StreamManager extends EventEmitter {
789789
// Update state to streaming
790790
streamInfo.state = StreamState.STREAMING;
791791

792-
// Emit stream start event
792+
// Emit stream start event (include mode from initialMetadata if available)
793+
const streamStartMode = streamInfo.initialMetadata?.mode as "plan" | "exec" | undefined;
793794
this.emit("stream-start", {
794795
type: "stream-start",
795796
workspaceId: workspaceId as string,
796797
messageId: streamInfo.messageId,
797798
model: streamInfo.model,
798799
historySequence,
799800
startTime: streamInfo.startTime,
801+
...(streamStartMode && { mode: streamStartMode }),
800802
} as StreamStartEvent);
801803

802804
// Initialize token tracker for this model
@@ -1690,14 +1692,16 @@ export class StreamManager extends EventEmitter {
16901692
// Initialize token tracker for this model (required for tokenization)
16911693
await this.tokenTracker.setModel(streamInfo.model);
16921694

1693-
// Emit stream-start event
1695+
// Emit stream-start event (include mode from initialMetadata if available)
1696+
const replayMode = streamInfo.initialMetadata?.mode as "plan" | "exec" | undefined;
16941697
this.emit("stream-start", {
16951698
type: "stream-start",
16961699
workspaceId,
16971700
messageId: streamInfo.messageId,
16981701
model: streamInfo.model,
16991702
historySequence: streamInfo.historySequence,
17001703
startTime: streamInfo.startTime,
1704+
...(replayMode && { mode: replayMode }),
17011705
});
17021706

17031707
// Replay accumulated parts as events using shared emission logic

0 commit comments

Comments
 (0)