feat: add provider-declared features system with Codex fast mode (#186)

Introduce a generic feature system where providers declare dynamic
features (toggles/selects) and the app renders controls automatically.
One message pair (set_agent_feature_request/response) handles all
feature mutations. Feature values persist and restore on agent resume.

First consumer: Codex fast mode (service_tier) — gated to supported
model families, with proper cleanup on model switch.
This commit is contained in:
Mohamed Boudra
2026-04-04 15:25:20 +08:00
committed by GitHub
parent 99114ddd11
commit 42cfed514c
17 changed files with 1081 additions and 8 deletions
@@ -11,6 +11,7 @@ import { z } from "zod";
import type {
AgentCapabilityFlags,
AgentClient,
AgentFeature,
AgentLaunchContext,
AgentSlashCommand,
AgentMode,
@@ -168,6 +169,7 @@ type ManagedAgentBase = {
createdAt: Date;
updatedAt: Date;
availableModes: AgentMode[];
features?: AgentFeature[];
currentModeId: string | null;
pendingPermissions: Map<string, AgentPermissionRequest>;
pendingReplacement: boolean;
@@ -940,6 +942,19 @@ export class AgentManager {
this.emitState(agent);
}
async setAgentFeature(agentId: string, featureId: string, value: unknown): Promise<void> {
const agent = this.requireAgent(agentId);
if (!agent.session.setFeature) {
throw new Error("Agent session does not support setting features");
}
await agent.session.setFeature(featureId, value);
agent.config.featureValues = { ...agent.config.featureValues, [featureId]: value };
this.touchUpdatedAt(agent);
this.emitState(agent);
}
async setTitle(agentId: string, title: string): Promise<void> {
const agent = this.requireAgent(agentId);
const normalizedTitle = title.trim();
@@ -2325,6 +2340,10 @@ export class AgentManager {
// Keep attention as an edge-triggered unread signal, not a level signal.
this.checkAndSetAttention(agent);
if (agent.session?.features) {
agent.features = agent.session.features;
}
this.dispatch({
type: "agent_state",
agent: { ...agent },