[eric] phase 1: MCP activation gate, context pill, friendly 429s, -api routes so API keys actually work

This commit is contained in:
ciregenz
2026-04-28 19:01:07 -04:00
parent 24dbd999ac
commit 30d581e9ec
11 changed files with 1143 additions and 52 deletions
+62
View File
@@ -74,6 +74,11 @@ export interface AgentSession {
browser_id?: string | null;
parent_session_id?: string | null;
thinking_level?: 'off' | 'low' | 'medium' | 'high' | 'auto';
active_mcps?: string[];
ctx_used_pct?: number;
cache_read_pct?: number;
cache_read_tokens?: number;
context_overflow?: { reason: string; message: string; at: string } | null;
}
export interface AgentConfig {
@@ -682,6 +687,60 @@ const agentsSlice = createSlice({
}
},
updateSessionContext(
state,
action: PayloadAction<{
sessionId: string;
inputTokens: number;
outputTokens: number;
cacheReadTokens: number;
cacheReadPct: number;
ctxUsedPct: number;
activeMcps: string[];
}>
) {
const session = state.sessions[action.payload.sessionId];
if (session) {
session.tokens = {
...(session.tokens || {}),
input: action.payload.inputTokens,
output: action.payload.outputTokens,
};
session.cache_read_tokens = action.payload.cacheReadTokens;
session.cache_read_pct = action.payload.cacheReadPct;
session.ctx_used_pct = action.payload.ctxUsedPct;
session.active_mcps = action.payload.activeMcps;
}
},
setContextOverflow(
state,
action: PayloadAction<{
sessionId: string;
reason: string;
message: string;
}>
) {
const session = state.sessions[action.payload.sessionId];
if (session) {
session.context_overflow = {
reason: action.payload.reason,
message: action.payload.message,
at: new Date().toISOString(),
};
}
},
clearContextOverflow(
state,
action: PayloadAction<{ sessionId: string }>
) {
const session = state.sessions[action.payload.sessionId];
if (session) {
session.context_overflow = null;
}
},
addBranch(state, action: PayloadAction<{ sessionId: string; branch: MessageBranch }>) {
const session = state.sessions[action.payload.sessionId];
if (session) {
@@ -1048,6 +1107,9 @@ export const {
addApprovalRequest,
removeApprovalRequest,
updateSessionCost,
updateSessionContext,
setContextOverflow,
clearContextOverflow,
addBranch,
setActiveBranch,
updateSessionProvider,
@@ -11,6 +11,8 @@ import {
removeApprovalRequest,
updateSessionStatus,
updateSessionCost,
updateSessionContext,
setContextOverflow,
addBranch,
setActiveBranch,
closeSessionFromWs,
@@ -270,6 +272,42 @@ class WebSocketManager {
}
break;
case 'agent:context_update':
if (session_id) {
store.dispatch(updateSessionContext({
sessionId: session_id,
inputTokens: data.input_tokens ?? 0,
outputTokens: data.output_tokens ?? 0,
cacheReadTokens: data.cache_read_tokens ?? 0,
cacheReadPct: data.cache_read_pct ?? 0,
ctxUsedPct: data.ctx_used_pct ?? 0,
activeMcps: Array.isArray(data.active_mcps) ? data.active_mcps : [],
}));
}
break;
case 'agent:context_overflow':
if (session_id) {
store.dispatch(setContextOverflow({
sessionId: session_id,
reason: data.reason ?? 'long_context_required',
message: data.message ?? 'Context full.',
}));
}
break;
case 'agent:auth_error':
// Re-uses the context_overflow card slot — both are "this session is
// blocked, here's what to do" cards. Reason field disambiguates.
if (session_id) {
store.dispatch(setContextOverflow({
sessionId: session_id,
reason: data.reason ?? 'auth_error',
message: data.message ?? 'Authentication failed.',
}));
}
break;
case 'agent:branch_created':
if (session_id && data.branch) {
store.dispatch(addBranch({ sessionId: session_id, branch: data.branch }));