Skip to content

Commit 139ada2

Browse files
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7wpfleger96
andcommitted
feat(desktop): add config bridge E2E screenshots and UI fixes
Add mock handler for get_agent_config_surface in e2eBridge.ts with realistic fixtures per runtime (goose, claude-code, codex, pre-spawn). Replace lock icon with info icon + tooltip on read-only fields. Add override/strikethrough rendering for superseded config values. Fix mock config file paths to match real discovery.rs values. Add Playwright screenshot spec covering 7 scenarios for PR review. Co-authored-by: Will Pfleger <wpfleger@squareup.com> Signed-off-by: Will Pfleger <wpfleger@squareup.com>
1 parent 9939a10 commit 139ada2

4 files changed

Lines changed: 518 additions & 3 deletions

File tree

desktop/playwright.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export default defineConfig({
3131
"**/channel-star-screenshots.spec.ts",
3232
"**/channel-controls-screenshots.spec.ts",
3333
"**/team-management-screenshots.spec.ts",
34+
"**/config-bridge-screenshots.spec.ts",
3435
"**/file-attachment.spec.ts",
3536
"**/mentions.spec.ts",
3637
"**/relay-reconnect.spec.ts",

desktop/src/features/agents/ui/AgentConfigPanel.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from "react";
2-
import { ChevronDown, ChevronRight, Lock } from "lucide-react";
2+
import { ChevronDown, ChevronRight, Info } from "lucide-react";
33

44
import { useAgentConfigSurface } from "../hooks";
55
import { cn } from "@/shared/lib/cn";
@@ -126,8 +126,22 @@ function NormalizedRow({
126126

127127
<OriginBadge origin={field.origin} configFilePath={configFilePath} />
128128

129+
{field.overriddenValue && (
130+
<span className="flex items-center gap-1 text-xs text-muted-foreground/60">
131+
<span className="line-through">{field.overriddenValue}</span>
132+
{field.overriddenOrigin && (
133+
<OriginBadge
134+
origin={field.overriddenOrigin}
135+
configFilePath={configFilePath}
136+
/>
137+
)}
138+
</span>
139+
)}
140+
129141
{!field.isWritable && (
130-
<Lock className="h-3 w-3 shrink-0 text-muted-foreground/50" />
142+
<span title="Read-only — edit this field directly in the config file">
143+
<Info className="h-3 w-3 shrink-0 text-muted-foreground/50" />
144+
</span>
131145
)}
132146
</div>
133147
);
@@ -154,7 +168,9 @@ function AdvancedRow({
154168
</span>
155169
<OriginBadge origin={field.origin} configFilePath={configFilePath} />
156170
{!field.isWritable && (
157-
<Lock className="h-3 w-3 shrink-0 text-muted-foreground/50" />
171+
<span title="Read-only — edit this field directly in the config file">
172+
<Info className="h-3 w-3 shrink-0 text-muted-foreground/50" />
173+
</span>
158174
)}
159175
</div>
160176
);

desktop/src/testing/e2eBridge.ts

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,304 @@ function resetMockRelayMembers(config: E2eConfig | undefined) {
922922
];
923923
}
924924

925+
function buildMockConfigSurface(pubkey: string): {
926+
runtimeId: string | null;
927+
runtimeLabel: string | null;
928+
isPreSpawn: boolean;
929+
normalized: Record<string, unknown>;
930+
advanced: unknown[];
931+
sources: Record<string, unknown>;
932+
} {
933+
// Goose running — mixed origins, override on model
934+
const gooseSurface = {
935+
runtimeId: "goose",
936+
runtimeLabel: "Goose",
937+
isPreSpawn: false,
938+
normalized: {
939+
model: {
940+
value: "gpt-4o",
941+
origin: "buzzExplicit",
942+
isWritable: true,
943+
writeVia: { type: "acpSetSessionModel" },
944+
overriddenValue: "gpt-4o-mini",
945+
overriddenOrigin: "configFile",
946+
},
947+
provider: {
948+
value: "openai",
949+
origin: "configFile",
950+
isWritable: false,
951+
writeVia: { type: "readOnly" },
952+
overriddenValue: null,
953+
overriddenOrigin: null,
954+
},
955+
mode: {
956+
value: "auto",
957+
origin: "envVar",
958+
isWritable: true,
959+
writeVia: { type: "respawnWithEnvVar", envKey: "GOOSE_MODE" },
960+
overriddenValue: null,
961+
overriddenOrigin: null,
962+
},
963+
thinkingEffort: {
964+
value: "medium",
965+
origin: "configFile",
966+
isWritable: true,
967+
writeVia: {
968+
type: "gooseNativeConfigWrite",
969+
configKey: "GOOSE_THINKING_EFFORT",
970+
},
971+
overriddenValue: null,
972+
overriddenOrigin: null,
973+
},
974+
maxOutputTokens: null,
975+
contextLimit: null,
976+
systemPrompt: null,
977+
},
978+
advanced: [
979+
{
980+
key: "extensions.developer",
981+
label: "Extension: developer",
982+
value: "enabled",
983+
origin: "configFile",
984+
schemaType: { type: "enum", options: ["enabled", "disabled"] },
985+
isWritable: false,
986+
writeVia: { type: "readOnly" },
987+
},
988+
{
989+
key: "extensions.web_search",
990+
label: "Extension: web_search",
991+
value: "enabled",
992+
origin: "configFile",
993+
schemaType: { type: "enum", options: ["enabled", "disabled"] },
994+
isWritable: false,
995+
writeVia: { type: "readOnly" },
996+
},
997+
{
998+
key: "extensions.memory",
999+
label: "Extension: memory",
1000+
value: "disabled",
1001+
origin: "configFile",
1002+
schemaType: { type: "enum", options: ["enabled", "disabled"] },
1003+
isWritable: false,
1004+
writeVia: { type: "readOnly" },
1005+
},
1006+
],
1007+
sources: {
1008+
acpNative: "available",
1009+
acpConfigOptions: "available",
1010+
envVars: "available",
1011+
configFile: "available",
1012+
configFilePath: "~/.config/goose/config.yaml",
1013+
},
1014+
};
1015+
1016+
// Claude Code — mostly ACP-sourced
1017+
const claudeSurface = {
1018+
runtimeId: "claude-code",
1019+
runtimeLabel: "Claude Code",
1020+
isPreSpawn: false,
1021+
normalized: {
1022+
model: {
1023+
value: "claude-sonnet-4-20250514",
1024+
origin: "acpConfigOption",
1025+
isWritable: true,
1026+
writeVia: { type: "acpSetConfigOption", configId: "model" },
1027+
overriddenValue: null,
1028+
overriddenOrigin: null,
1029+
},
1030+
provider: {
1031+
value: "anthropic",
1032+
origin: "acpConfigOption",
1033+
isWritable: false,
1034+
writeVia: { type: "readOnly" },
1035+
overriddenValue: null,
1036+
overriddenOrigin: null,
1037+
},
1038+
mode: {
1039+
value: "code",
1040+
origin: "acpConfigOption",
1041+
isWritable: true,
1042+
writeVia: { type: "acpSetConfigOption", configId: "mode" },
1043+
overriddenValue: null,
1044+
overriddenOrigin: null,
1045+
},
1046+
thinkingEffort: {
1047+
value: "high",
1048+
origin: "acpConfigOption",
1049+
isWritable: true,
1050+
writeVia: {
1051+
type: "acpSetConfigOption",
1052+
configId: "thinking_effort",
1053+
},
1054+
overriddenValue: null,
1055+
overriddenOrigin: null,
1056+
},
1057+
maxOutputTokens: {
1058+
value: "16384",
1059+
origin: "acpConfigOption",
1060+
isWritable: true,
1061+
writeVia: {
1062+
type: "acpSetConfigOption",
1063+
configId: "max_output_tokens",
1064+
},
1065+
overriddenValue: null,
1066+
overriddenOrigin: null,
1067+
},
1068+
contextLimit: null,
1069+
systemPrompt: null,
1070+
},
1071+
advanced: [],
1072+
sources: {
1073+
acpNative: "available",
1074+
acpConfigOptions: "available",
1075+
envVars: "notApplicable",
1076+
configFile: "available",
1077+
configFilePath: "~/.claude/settings.json",
1078+
},
1079+
};
1080+
1081+
// Pre-spawn — model from config file, ACP fields pending
1082+
const preSpawnSurface = {
1083+
runtimeId: "goose",
1084+
runtimeLabel: "Goose",
1085+
isPreSpawn: true,
1086+
normalized: {
1087+
model: {
1088+
value: "gpt-4o-mini",
1089+
origin: "configFile",
1090+
isWritable: false,
1091+
writeVia: { type: "readOnly" },
1092+
overriddenValue: null,
1093+
overriddenOrigin: null,
1094+
},
1095+
provider: {
1096+
value: "openai",
1097+
origin: "configFile",
1098+
isWritable: false,
1099+
writeVia: { type: "readOnly" },
1100+
overriddenValue: null,
1101+
overriddenOrigin: null,
1102+
},
1103+
mode: {
1104+
value: null,
1105+
origin: "acpNativeRead",
1106+
isWritable: false,
1107+
writeVia: { type: "readOnly" },
1108+
overriddenValue: null,
1109+
overriddenOrigin: null,
1110+
},
1111+
thinkingEffort: {
1112+
value: null,
1113+
origin: "acpNativeRead",
1114+
isWritable: false,
1115+
writeVia: { type: "readOnly" },
1116+
overriddenValue: null,
1117+
overriddenOrigin: null,
1118+
},
1119+
maxOutputTokens: null,
1120+
contextLimit: null,
1121+
systemPrompt: null,
1122+
},
1123+
advanced: [],
1124+
sources: {
1125+
acpNative: "pending",
1126+
acpConfigOptions: "pending",
1127+
envVars: "available",
1128+
configFile: "available",
1129+
configFilePath: "~/.config/goose/config.yaml",
1130+
},
1131+
};
1132+
1133+
// Codex — dual-axis mode
1134+
const codexSurface = {
1135+
runtimeId: "codex",
1136+
runtimeLabel: "Codex",
1137+
isPreSpawn: false,
1138+
normalized: {
1139+
model: {
1140+
value: "codex-mini",
1141+
origin: "configFile",
1142+
isWritable: true,
1143+
writeVia: { type: "respawnWithEnvVar", envKey: "CODEX_MODEL" },
1144+
overriddenValue: null,
1145+
overriddenOrigin: null,
1146+
},
1147+
provider: {
1148+
value: "openai",
1149+
origin: "configFile",
1150+
isWritable: false,
1151+
writeVia: { type: "readOnly" },
1152+
overriddenValue: null,
1153+
overriddenOrigin: null,
1154+
},
1155+
mode: {
1156+
value: "suggest / auto-edit",
1157+
origin: "configFile",
1158+
isWritable: true,
1159+
writeVia: { type: "respawnWithEnvVar", envKey: "CODEX_MODE" },
1160+
overriddenValue: null,
1161+
overriddenOrigin: null,
1162+
},
1163+
thinkingEffort: null,
1164+
maxOutputTokens: null,
1165+
contextLimit: null,
1166+
systemPrompt: null,
1167+
},
1168+
advanced: [
1169+
{
1170+
key: "approval_policy",
1171+
label: "Approval Policy",
1172+
value: "unless-allow-listed",
1173+
origin: "configFile",
1174+
schemaType: {
1175+
type: "enum",
1176+
options: ["suggest", "auto-edit", "full-auto", "unless-allow-listed"],
1177+
},
1178+
isWritable: false,
1179+
writeVia: { type: "readOnly" },
1180+
},
1181+
{
1182+
key: "sandbox_mode",
1183+
label: "Sandbox Mode",
1184+
value: "container",
1185+
origin: "envVar",
1186+
schemaType: {
1187+
type: "enum",
1188+
options: ["container", "host", "none"],
1189+
},
1190+
isWritable: false,
1191+
writeVia: { type: "readOnly" },
1192+
},
1193+
],
1194+
sources: {
1195+
acpNative: "notApplicable",
1196+
acpConfigOptions: "notApplicable",
1197+
envVars: "available",
1198+
configFile: "available",
1199+
configFilePath: "~/.codex/config.toml",
1200+
},
1201+
};
1202+
1203+
// Map well-known test pubkeys to specific fixtures
1204+
const PUBKEY_CLAUDE =
1205+
"953d3363262e86b770419834c53d2446409db6d918a57f8f339d495d54ab001f";
1206+
const PUBKEY_PRESPAWN =
1207+
"bb22a5299220cad76ffd46190ccbeede8ab5dc260faa28b6e5a2cb31b9aff260";
1208+
const PUBKEY_CODEX =
1209+
"554cef57437abac34522ac2c9f0490d685b72c80478cf9f7ed6f9570ee8624ea";
1210+
1211+
switch (pubkey) {
1212+
case PUBKEY_CLAUDE:
1213+
return claudeSurface;
1214+
case PUBKEY_PRESPAWN:
1215+
return preSpawnSurface;
1216+
case PUBKEY_CODEX:
1217+
return codexSurface;
1218+
default:
1219+
return gooseSurface;
1220+
}
1221+
}
1222+
9251223
function buildSeededManagedAgent(seed: MockManagedAgentSeed): MockManagedAgent {
9261224
const now = new Date().toISOString();
9271225
const status = seed.status ?? "stopped";
@@ -6131,6 +6429,10 @@ export function maybeInstallE2eTauriMocks() {
61316429
selectedModel: null,
61326430
supportsSwitching: false,
61336431
};
6432+
case "get_agent_config_surface": {
6433+
const configArgs = payload as { pubkey: string };
6434+
return buildMockConfigSurface(configArgs.pubkey);
6435+
}
61346436
case "update_managed_agent":
61356437
return handleUpdateManagedAgent(
61366438
payload as Parameters<typeof handleUpdateManagedAgent>[0],

0 commit comments

Comments
 (0)