feat: harden workflow gates and expose token usage

This commit is contained in:
2026-07-21 12:14:26 +02:00
parent 8aa1676811
commit 2ff63d371f
20 changed files with 604 additions and 53 deletions
+14 -1
View File
@@ -1,5 +1,5 @@
import { create } from "zustand";
import type { ActivityEntry, StreamEvent, WidgetDescriptor } from "../api/types";
import type { ActivityEntry, StreamEvent, TokenUsage, WidgetDescriptor } from "../api/types";
interface Entry {
role: "assistant";
@@ -21,6 +21,7 @@ interface SessionState {
// system event — the only end-of-turn signal on the final workflow step, where no
// follow-up gate arrives to release the spinner.
agentActive: boolean;
tokenUsage: TokenUsage | null;
applyEvent: (e: StreamEvent) => void;
clearPending: () => void;
resetSession: () => void;
@@ -65,6 +66,7 @@ const empty = {
phaseError: null as string | null,
seenGateIds: new Set<string>(),
agentActive: false,
tokenUsage: null as TokenUsage | null,
};
export const useSessionStore = create<SessionState>((set) => ({
@@ -145,6 +147,17 @@ export const useSessionStore = create<SessionState>((set) => ({
}
return { activityLog, agentActive: true };
}
if (e.type === "usage") {
return {
tokenUsage: {
input: (st.tokenUsage?.input ?? 0) + e.usage.input,
cacheRead: (st.tokenUsage?.cacheRead ?? 0) + e.usage.cacheRead,
output: (st.tokenUsage?.output ?? 0) + e.usage.output,
totalTokens: e.usage.totalTokens,
contextWindow: e.usage.contextWindow,
},
};
}
if (e.type === "info") {
const level = e.level ?? "info";
const stepMessages = [...st.stepMessages, { level, text: e.text }];