pi-codemcp 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/modal.ts CHANGED
@@ -13,13 +13,7 @@ import {
13
13
  } from "@earendil-works/pi-tui";
14
14
  import type { ChainScope, SavedChainView } from "./chains.js";
15
15
  import { summarizeError } from "./errors.js";
16
- import {
17
- type CodeMcpSettings,
18
- type EditableSettingKey,
19
- type EditableSettingValue,
20
- setEditableSetting,
21
- setToolEnabled,
22
- } from "./settings.js";
16
+ import type { CodeMcpSettings, EditableSettingKey, EditableSettingValue } from "./settings.js";
23
17
 
24
18
  export interface ToolModalState {
25
19
  name: string;
@@ -78,6 +72,7 @@ export interface StatsModalState {
78
72
  p95Ms: number;
79
73
  maxMs: number;
80
74
  }>;
75
+ outcomes: Array<{ name: string; count: number }>;
81
76
  failures: Array<{ stage: string; count: number }>;
82
77
  upstreamOutputBytes: number;
83
78
  cacheHits: number;
@@ -104,26 +99,6 @@ interface Keybindings {
104
99
  matches(data: string, id: "tui.select.up" | "tui.select.down" | "tui.select.cancel"): boolean;
105
100
  }
106
101
 
107
- export interface ServerEnabledChange {
108
- name: string;
109
- previousEnabled: boolean;
110
- enabled: boolean;
111
- }
112
-
113
- export interface ChainEnabledChange {
114
- name: string;
115
- scope: ChainScope;
116
- previousEnabled: boolean;
117
- enabled: boolean;
118
- }
119
-
120
- interface ManagerSaveResult {
121
- settings: CodeMcpSettings;
122
- servers: ServerModalState[];
123
- chains: ChainModalState[];
124
- }
125
-
126
- type UnsavedAction = "save" | "discard" | "cancel";
127
102
  export type ServerManagerResult = "report-problem" | undefined;
128
103
 
129
104
  interface ServerManagerOptions {
@@ -131,13 +106,15 @@ interface ServerManagerOptions {
131
106
  chains: ChainModalState[];
132
107
  settings: CodeMcpSettings;
133
108
  stats: StatsModalState;
109
+ onSetServerEnabled(server: ServerModalState, enabled: boolean): Promise<ServerModalState>;
134
110
  onDiscover(server: ServerModalState): Promise<ServerModalState>;
135
- onSaveChanges(
136
- settings: CodeMcpSettings,
137
- serverChanges: ServerEnabledChange[],
138
- chainChanges: ChainEnabledChange[],
139
- ): Promise<ManagerSaveResult>;
140
- onResolveUnsaved(): Promise<UnsavedAction>;
111
+ onSetToolEnabled(
112
+ server: ServerModalState,
113
+ tool: ToolModalState,
114
+ enabled: boolean,
115
+ ): Promise<ServerModalState>;
116
+ onSetSetting(key: EditableSettingKey, value: EditableSettingValue): Promise<CodeMcpSettings>;
117
+ onSetChainEnabled(chain: ChainModalState, enabled: boolean): Promise<ChainModalState[]>;
141
118
  onRevalidateChain(chain: ChainModalState): Promise<ChainModalState[]>;
142
119
  onDeleteChain(chain: ChainModalState): Promise<ChainModalState[]>;
143
120
  }
@@ -282,6 +259,10 @@ export function statsStateFromSnapshot(snapshot: Record<string, unknown>): Stats
282
259
  },
283
260
  ];
284
261
  });
262
+ const rawOutcomes = isRecord(snapshot.outcomes) ? snapshot.outcomes : {};
263
+ const outcomes = Object.entries(rawOutcomes)
264
+ .flatMap(([name, count]) => (typeof count === "number" && count >= 0 ? [{ name, count }] : []))
265
+ .sort((left, right) => right.count - left.count || left.name.localeCompare(right.name));
285
266
  const rawFailures = isRecord(snapshot.failures) ? snapshot.failures : {};
286
267
  const failures = Object.entries(rawFailures)
287
268
  .flatMap(([stage, count]) =>
@@ -302,6 +283,7 @@ export function statsStateFromSnapshot(snapshot: Record<string, unknown>): Stats
302
283
  recent,
303
284
  operations,
304
285
  phases,
286
+ outcomes,
305
287
  failures,
306
288
  upstreamOutputBytes,
307
289
  cacheHits: numberField(cache, "hits"),
@@ -345,12 +327,6 @@ class ServerManagerModal implements Component, Focusable {
345
327
  private selectedToolIndex = 0;
346
328
  private selectedChainIndex = 0;
347
329
  private selectedSettingIndex = 0;
348
- private savedSettings: CodeMcpSettings;
349
- private draftSettings: CodeMcpSettings;
350
- private savedServerEnabled: Map<string, boolean>;
351
- private savedChainEnabled: Map<string, boolean>;
352
- private settingsBusy = false;
353
- private closePromptBusy = false;
354
330
  private settingsError: string | undefined;
355
331
  private _focused = false;
356
332
 
@@ -360,13 +336,7 @@ class ServerManagerModal implements Component, Focusable {
360
336
  private readonly keybindings: Keybindings,
361
337
  private readonly close: (result?: ServerManagerResult) => void,
362
338
  private readonly requestRender: () => void,
363
- ) {
364
- this.savedSettings = cloneSettings(options.settings);
365
- this.draftSettings = cloneSettings(options.settings);
366
- this.savedServerEnabled = serverEnabledMap(options.servers);
367
- this.savedChainEnabled = chainEnabledMap(options.chains);
368
- this.applyDraftToolPolicy();
369
- }
339
+ ) {}
370
340
 
371
341
  get focused(): boolean {
372
342
  return this._focused;
@@ -401,12 +371,7 @@ class ServerManagerModal implements Component, Focusable {
401
371
  }
402
372
 
403
373
  handleInput(data: string): void {
404
- if (matchesKey(data, Key.ctrl("s"))) {
405
- this.saveDraft();
406
- return;
407
- }
408
- if (this.settingsBusy || this.closePromptBusy) return;
409
- if (data === "R") {
374
+ if (data === "R" && this.activeTab !== "chains") {
410
375
  this.focusProblemReport();
411
376
  this.requestRender();
412
377
  return;
@@ -418,8 +383,7 @@ class ServerManagerModal implements Component, Focusable {
418
383
  this.requestRender();
419
384
  return;
420
385
  }
421
- if (this.hasUnsavedChanges()) this.resolveUnsavedClose();
422
- else this.close();
386
+ this.close();
423
387
  return;
424
388
  }
425
389
  if (matchesKey(data, Key.tab)) {
@@ -443,7 +407,7 @@ class ServerManagerModal implements Component, Focusable {
443
407
  }
444
408
 
445
409
  private handleServerInput(data: string): void {
446
- if (data === "d") {
410
+ if (data === "D") {
447
411
  this.discoverSelected();
448
412
  return;
449
413
  }
@@ -490,7 +454,7 @@ class ServerManagerModal implements Component, Focusable {
490
454
  );
491
455
  return;
492
456
  }
493
- if (data === "r") {
457
+ if (data === "R") {
494
458
  this.revalidateSelectedChain();
495
459
  return;
496
460
  }
@@ -510,7 +474,6 @@ class ServerManagerModal implements Component, Focusable {
510
474
  }
511
475
 
512
476
  private handleSettingsInput(data: string): void {
513
- if (this.settingsBusy) return;
514
477
  if (this.keybindings.matches(data, "tui.select.up") || matchesKey(data, Key.up)) {
515
478
  this.selectedSettingIndex = cycleIndex(
516
479
  this.selectedSettingIndex,
@@ -613,7 +576,7 @@ class ServerManagerModal implements Component, Focusable {
613
576
  this.theme.fg("dim", `${server.transport}${server.auth ? ` · ${server.auth}` : ""}`),
614
577
  server.busy
615
578
  ? this.theme.fg("warning", "Working…")
616
- : `${this.theme.fg("accent", "[d]")} Discover tools ${this.theme.fg("accent", "[space]")} ${server.enabled ? "Disable server" : "Enable server"}`,
579
+ : `${this.theme.fg("accent", "[D]")} Discover tools ${this.theme.fg("accent", "[space]")} ${server.enabled ? "Disable server" : "Enable server"}`,
617
580
  ...(server.error ? [this.theme.fg("warning", `Error: ${server.error}`)] : []),
618
581
  this.theme.fg("dim", "─".repeat(Math.max(1, width))),
619
582
  this.theme.fg("dim", this.theme.bold("TOOLS")),
@@ -729,7 +692,7 @@ class ServerManagerModal implements Component, Focusable {
729
692
  this.theme.fg("muted", `${chain.scope} · ${chain.status} · ${chain.nativeTool}`),
730
693
  chain.busy
731
694
  ? this.theme.fg("warning", "Working…")
732
- : `${this.theme.fg("accent", "[space]")} ${chain.enabled ? "Disable" : "Enable"} ${this.theme.fg("accent", "[r]")} Revalidate ${this.theme.fg("accent", "[del]")} Delete`,
695
+ : `${this.theme.fg("accent", "[space]")} ${chain.enabled ? "Disable" : "Enable"} ${this.theme.fg("accent", "[R]")} Revalidate ${this.theme.fg("accent", "[del]")} Delete`,
733
696
  ...(chain.error ? [this.theme.fg("warning", `Error: ${chain.error}`)] : []),
734
697
  "",
735
698
  ...wrapPlainText(chain.description, width).map((line) => this.theme.fg("muted", line)),
@@ -798,6 +761,11 @@ class ServerManagerModal implements Component, Focusable {
798
761
  ),
799
762
  );
800
763
  }
764
+ lines.push("", this.theme.fg("dim", this.theme.bold("OUTCOMES")));
765
+ if (stats.outcomes.length === 0) lines.push(this.theme.fg("muted", "No outcomes yet"));
766
+ for (const outcome of stats.outcomes) {
767
+ lines.push(`${outcome.name.padEnd(22)} ${outcome.count.toLocaleString().padStart(8)}`);
768
+ }
801
769
  lines.push("", this.theme.fg("dim", this.theme.bold("FAILURE STAGES")));
802
770
  if (stats.failures.length === 0) lines.push(this.theme.fg("muted", "No failures yet"));
803
771
  for (const failure of stats.failures) {
@@ -824,7 +792,7 @@ class ServerManagerModal implements Component, Focusable {
824
792
  for (const [index, definition] of SETTING_DEFINITIONS.entries()) {
825
793
  const selected = index === this.selectedSettingIndex;
826
794
  const prefix = selected ? this.theme.fg("accent", "→") : " ";
827
- const value = settingLabel(definition, this.draftSettings[definition.key]);
795
+ const value = settingLabel(definition, this.options.settings[definition.key]);
828
796
  const reserved = visibleWidth(prefix) + visibleWidth(value) + 3;
829
797
  const label = truncateToWidth(definition.label, Math.max(4, leftWidth - reserved), "…");
830
798
  const gap = " ".repeat(Math.max(1, leftWidth - reserved - visibleWidth(label) + 1));
@@ -862,18 +830,13 @@ class ServerManagerModal implements Component, Focusable {
862
830
  : definition
863
831
  ? [
864
832
  this.theme.fg("accent", this.theme.bold(definition.label)),
865
- this.theme.fg("muted", settingLabel(definition, this.draftSettings[definition.key])),
833
+ this.theme.fg("muted", settingLabel(definition, this.options.settings[definition.key])),
866
834
  "",
867
835
  ...wrapPlainText(definition.description, rightWidth).map((line) =>
868
836
  this.theme.fg("muted", line),
869
837
  ),
870
838
  "",
871
- this.theme.fg("dim", "←/→ change · enter next · ctrl+s save"),
872
- ...(this.settingsBusy
873
- ? ["", this.theme.fg("warning", "Saving staged changes…")]
874
- : this.hasUnsavedChanges()
875
- ? ["", this.theme.fg("warning", "Unsaved changes")]
876
- : []),
839
+ this.theme.fg("dim", "←/→ change · enter next"),
877
840
  ...(this.settingsError
878
841
  ? ["", this.theme.fg("warning", `Error: ${this.settingsError}`)]
879
842
  : []),
@@ -889,18 +852,17 @@ class ServerManagerModal implements Component, Focusable {
889
852
  }
890
853
 
891
854
  private footer(): string {
892
- const pending = this.hasUnsavedChanges() ? " · * unsaved · ctrl+s save" : "";
893
855
  const report = ` · ${PROBLEM_REPORT_SHORTCUT}`;
894
856
  if (this.activeTab === "settings") {
895
- return `tab servers · ↑/↓ navigate · ←/→ change · enter select · ctrl+s save · esc close${pending}${report}`;
857
+ return `tab servers · ↑/↓ navigate · ←/→ change · enter select · esc close${report}`;
896
858
  }
897
859
  if (this.activeTab === "chains") {
898
- return `tab stats · ↑/↓ navigate · space toggle · r revalidate · del delete · esc close${pending}${report}`;
860
+ return "tab stats · ↑/↓ navigate · space toggle · R revalidate · del delete · esc close";
899
861
  }
900
862
  if (this.activeTab === "stats") {
901
- return `tab settings · bounded local rollups · esc close${pending}${report}`;
863
+ return `tab settings · bounded local rollups · esc close${report}`;
902
864
  }
903
- return `tab chains · ←/→ pane · ↑/↓ navigate · space toggle · d discover · esc close${pending}${report}`;
865
+ return `tab chains · ←/→ pane · ↑/↓ navigate · space toggle · D discover · esc close${report}`;
904
866
  }
905
867
 
906
868
  private moveSelection(direction: -1 | 1): void {
@@ -923,31 +885,31 @@ class ServerManagerModal implements Component, Focusable {
923
885
 
924
886
  private toggleSelectedServer(): void {
925
887
  const server = this.selectedServer();
926
- if (!server || server.busy || this.settingsBusy) return;
927
- server.enabled = !server.enabled;
888
+ if (!server || server.busy) return;
889
+ const enabled = !server.enabled;
890
+ server.busy = true;
928
891
  delete server.error;
892
+ void this.options
893
+ .onSetServerEnabled(server, enabled)
894
+ .then((updated) => applyServerUpdate(server, updated))
895
+ .catch((error: unknown) => {
896
+ server.error = summarizeError(error);
897
+ })
898
+ .finally(() => {
899
+ server.busy = false;
900
+ this.requestRender();
901
+ });
929
902
  }
930
903
 
931
904
  private discoverSelected(): void {
932
905
  const server = this.selectedServer();
933
- if (!server || server.busy || this.settingsBusy) return;
934
- if (server.enabled !== this.savedServerEnabled.get(server.name)) {
935
- server.error = "Save this server change before discovering tools";
936
- return;
937
- }
938
- if (!server.enabled) {
939
- server.error = "Enable and save this server before discovering tools";
940
- return;
941
- }
906
+ if (!server || server.busy) return;
942
907
  server.busy = true;
943
908
  delete server.error;
944
909
  this.requestRender();
945
910
  void this.options
946
911
  .onDiscover(server)
947
- .then((updated) => {
948
- applyServerUpdate(server, updated);
949
- this.applyDraftToolPolicy();
950
- })
912
+ .then((updated) => applyServerUpdate(server, updated))
951
913
  .catch((error: unknown) => {
952
914
  server.error = summarizeError(error);
953
915
  })
@@ -959,31 +921,43 @@ class ServerManagerModal implements Component, Focusable {
959
921
 
960
922
  private toggleSelectedTool(): void {
961
923
  const server = this.selectedServer();
962
- if (!server || server.busy || this.settingsBusy) return;
924
+ if (!server || server.busy) return;
963
925
  const tools = this.filteredTools(server);
964
926
  const tool = tools[this.selectedToolIndex];
965
927
  if (!tool || tool.busy) return;
966
- const enabled = !tool.enabled;
967
- this.draftSettings = setToolEnabled(this.draftSettings, server.name, tool.name, enabled);
968
- tool.enabled = enabled;
969
- server.toolCount = server.tools.filter((candidate) => candidate.enabled).length;
970
- this.settingsError = undefined;
928
+ tool.busy = true;
929
+ void this.options
930
+ .onSetToolEnabled(server, tool, !tool.enabled)
931
+ .then((updated) => applyServerUpdate(server, updated))
932
+ .catch((error: unknown) => {
933
+ server.error = summarizeError(error);
934
+ })
935
+ .finally(() => {
936
+ tool.busy = false;
937
+ this.requestRender();
938
+ });
971
939
  }
972
940
 
973
941
  private toggleSelectedChain(): void {
974
942
  const chain = this.selectedChain();
975
- if (!chain || chain.busy || this.settingsBusy) return;
976
- applyChainEnabled(chain, !chain.enabled);
943
+ if (!chain || chain.busy) return;
944
+ chain.busy = true;
977
945
  delete chain.error;
946
+ void this.options
947
+ .onSetChainEnabled(chain, !chain.enabled)
948
+ .then((updated) => this.replaceChains(updated, chain))
949
+ .catch((error: unknown) => {
950
+ chain.error = summarizeError(error);
951
+ })
952
+ .finally(() => {
953
+ chain.busy = false;
954
+ this.requestRender();
955
+ });
978
956
  }
979
957
 
980
958
  private revalidateSelectedChain(): void {
981
959
  const chain = this.selectedChain();
982
- if (!chain || chain.busy || this.settingsBusy) return;
983
- if (chain.enabled !== this.savedChainEnabled.get(chainKey(chain))) {
984
- chain.error = "Save this chain change before revalidation";
985
- return;
986
- }
960
+ if (!chain || chain.busy) return;
987
961
  chain.busy = true;
988
962
  delete chain.error;
989
963
  void this.options
@@ -998,20 +972,8 @@ class ServerManagerModal implements Component, Focusable {
998
972
  });
999
973
  }
1000
974
 
1001
- private replaceChains(
1002
- updated: ChainModalState[],
1003
- selected?: ChainModalState,
1004
- preserveDrafts = true,
1005
- ): void {
1006
- const drafts = preserveDrafts ? this.chainEnabledChanges() : [];
975
+ private replaceChains(updated: ChainModalState[], selected?: ChainModalState): void {
1007
976
  this.options.chains.splice(0, this.options.chains.length, ...updated);
1008
- this.savedChainEnabled = chainEnabledMap(updated);
1009
- for (const draft of drafts) {
1010
- const chain = this.options.chains.find(
1011
- (candidate) => candidate.name === draft.name && candidate.scope === draft.scope,
1012
- );
1013
- if (chain) applyChainEnabled(chain, draft.enabled);
1014
- }
1015
977
  if (selected) {
1016
978
  const index = this.filteredChains().findIndex(
1017
979
  (chain) => chain.name === selected.name && chain.scope === selected.scope,
@@ -1043,8 +1005,8 @@ class ServerManagerModal implements Component, Focusable {
1043
1005
 
1044
1006
  private cycleSelectedSetting(direction: -1 | 1): void {
1045
1007
  const definition = SETTING_DEFINITIONS[this.selectedSettingIndex];
1046
- if (!definition || this.settingsBusy) return;
1047
- const current = this.draftSettings[definition.key];
1008
+ if (!definition) return;
1009
+ const current = this.options.settings[definition.key];
1048
1010
  const currentIndex = Math.max(
1049
1011
  0,
1050
1012
  definition.choices.findIndex((choice) => choice.value === current),
@@ -1053,43 +1015,15 @@ class ServerManagerModal implements Component, Focusable {
1053
1015
  definition.choices[cycleIndex(currentIndex, direction, definition.choices.length)];
1054
1016
  if (!choice) return;
1055
1017
  this.settingsError = undefined;
1056
- try {
1057
- this.draftSettings = setEditableSetting(this.draftSettings, definition.key, choice.value);
1058
- } catch (error) {
1059
- this.settingsError = summarizeError(error);
1060
- }
1061
- }
1062
-
1063
- private saveDraft(): void {
1064
- void this.persistDraft();
1065
- }
1066
-
1067
- private async persistDraft(): Promise<boolean> {
1068
- if (this.settingsBusy) return false;
1069
- if (!this.hasUnsavedChanges()) return true;
1070
- this.settingsBusy = true;
1071
- this.settingsError = undefined;
1072
- this.requestRender();
1073
- try {
1074
- const result = await this.options.onSaveChanges(
1075
- cloneSettings(this.draftSettings),
1076
- this.serverEnabledChanges(),
1077
- this.chainEnabledChanges(),
1078
- );
1079
- this.savedSettings = cloneSettings(result.settings);
1080
- this.draftSettings = cloneSettings(result.settings);
1081
- this.options.servers.splice(0, this.options.servers.length, ...result.servers);
1082
- this.savedServerEnabled = serverEnabledMap(result.servers);
1083
- this.replaceChains(result.chains, undefined, false);
1084
- this.applyDraftToolPolicy();
1085
- return true;
1086
- } catch (error) {
1087
- this.settingsError = summarizeError(error);
1088
- return false;
1089
- } finally {
1090
- this.settingsBusy = false;
1091
- this.requestRender();
1092
- }
1018
+ void this.options
1019
+ .onSetSetting(definition.key, choice.value)
1020
+ .then((settings) => {
1021
+ this.options.settings = settings;
1022
+ })
1023
+ .catch((error: unknown) => {
1024
+ this.settingsError = summarizeError(error);
1025
+ })
1026
+ .finally(() => this.requestRender());
1093
1027
  }
1094
1028
 
1095
1029
  private focusProblemReport(): void {
@@ -1100,70 +1034,7 @@ class ServerManagerModal implements Component, Focusable {
1100
1034
  }
1101
1035
 
1102
1036
  private openProblemReport(): void {
1103
- if (this.hasUnsavedChanges()) this.resolveUnsavedClose("report-problem");
1104
- else this.close("report-problem");
1105
- }
1106
-
1107
- private resolveUnsavedClose(result?: ServerManagerResult): void {
1108
- if (this.closePromptBusy || this.settingsBusy) return;
1109
- this.closePromptBusy = true;
1110
- void this.options
1111
- .onResolveUnsaved()
1112
- .then(async (action) => {
1113
- if (action === "discard") {
1114
- this.close(result);
1115
- return;
1116
- }
1117
- if (action === "save" && (await this.persistDraft())) this.close(result);
1118
- })
1119
- .catch((error: unknown) => {
1120
- this.settingsError = summarizeError(error);
1121
- })
1122
- .finally(() => {
1123
- this.closePromptBusy = false;
1124
- this.requestRender();
1125
- });
1126
- }
1127
-
1128
- private hasUnsavedChanges(): boolean {
1129
- return (
1130
- !settingsEqual(this.savedSettings, this.draftSettings) ||
1131
- this.serverEnabledChanges().length > 0 ||
1132
- this.chainEnabledChanges().length > 0
1133
- );
1134
- }
1135
-
1136
- private serverEnabledChanges(): ServerEnabledChange[] {
1137
- return this.options.servers.flatMap((server) => {
1138
- const previousEnabled = this.savedServerEnabled.get(server.name);
1139
- return previousEnabled === undefined || previousEnabled === server.enabled
1140
- ? []
1141
- : [{ name: server.name, previousEnabled, enabled: server.enabled }];
1142
- });
1143
- }
1144
-
1145
- private chainEnabledChanges(): ChainEnabledChange[] {
1146
- return this.options.chains.flatMap((chain) => {
1147
- const previousEnabled = this.savedChainEnabled.get(chainKey(chain));
1148
- return previousEnabled === undefined || previousEnabled === chain.enabled
1149
- ? []
1150
- : [
1151
- {
1152
- name: chain.name,
1153
- scope: chain.scope,
1154
- previousEnabled,
1155
- enabled: chain.enabled,
1156
- },
1157
- ];
1158
- });
1159
- }
1160
-
1161
- private applyDraftToolPolicy(): void {
1162
- for (const server of this.options.servers) {
1163
- const disabled = new Set(this.draftSettings.disabledTools[server.name] ?? []);
1164
- for (const tool of server.tools) tool.enabled = !disabled.has(tool.name);
1165
- server.toolCount = server.tools.filter((tool) => tool.enabled).length;
1166
- }
1037
+ this.close("report-problem");
1167
1038
  }
1168
1039
 
1169
1040
  private filteredServers(): ServerModalState[] {
@@ -1389,38 +1260,6 @@ function serverIcon(server: ServerModalState, theme: Theme): string {
1389
1260
  return theme.fg("warning", "◌");
1390
1261
  }
1391
1262
 
1392
- function serverEnabledMap(servers: readonly ServerModalState[]): Map<string, boolean> {
1393
- return new Map(servers.map((server) => [server.name, server.enabled]));
1394
- }
1395
-
1396
- function chainEnabledMap(chains: readonly ChainModalState[]): Map<string, boolean> {
1397
- return new Map(chains.map((chain) => [chainKey(chain), chain.enabled]));
1398
- }
1399
-
1400
- function chainKey(chain: Pick<ChainModalState, "name" | "scope">): string {
1401
- return `${chain.scope}:${chain.name}`;
1402
- }
1403
-
1404
- function applyChainEnabled(chain: ChainModalState, enabled: boolean): void {
1405
- const shadowed = chain.status === "shadowed";
1406
- chain.enabled = enabled;
1407
- if (shadowed) return;
1408
- chain.status = enabled ? (chain.staleDependencies.length > 0 ? "stale" : "ready") : "disabled";
1409
- }
1410
-
1411
- function cloneSettings(settings: CodeMcpSettings): CodeMcpSettings {
1412
- return {
1413
- ...settings,
1414
- disabledTools: Object.fromEntries(
1415
- Object.entries(settings.disabledTools).map(([server, tools]) => [server, [...tools]]),
1416
- ),
1417
- };
1418
- }
1419
-
1420
- function settingsEqual(left: CodeMcpSettings, right: CodeMcpSettings): boolean {
1421
- return JSON.stringify(left) === JSON.stringify(right);
1422
- }
1423
-
1424
1263
  function settingLabel(definition: SettingDefinition, value: EditableSettingValue): string {
1425
1264
  return definition.choices.find((choice) => choice.value === value)?.label ?? String(value);
1426
1265
  }
package/src/prompts.ts CHANGED
@@ -8,10 +8,10 @@ export const INSPECT_PROMPT_GUIDELINES = [
8
8
  ] as const;
9
9
 
10
10
  export const EXECUTE_PROMPT_GUIDELINES = [
11
- "Use programmatic execution for a bounded workflow when code can deterministically filter, join, aggregate, deduplicate, validate, or reduce intermediate results.",
11
+ "Use bounded execution to deterministically filter, aggregate, sample, join, or reduce upstream data in the sandbox; do not return raw payloads.",
12
12
  "Keep a model turn between calls when an intermediate result changes the semantic decision or user approval is required.",
13
- "Return the smallest result that answers the request; oversized results fail explicitly with bounded structural inspection data.",
14
- "SDK facades are prebound globals and must not be imported. Import supported stdlib normally (for example, `import asyncio`); class declarations and `__import__` are unsupported.",
13
+ "Return the smallest answer. If an oversized result provides result_ref, refine it via inputRef instead of repeating upstream calls.",
14
+ "SDK facades are prebound globals and must not be imported. Use `import asyncio` for `asyncio.gather`; other imports, classes, `asyncio.create_task`, and `__import__` are unsupported.",
15
15
  ] as const;
16
16
 
17
17
  export const SAVE_CHAIN_PROMPT_GUIDELINES = [