dsh-agy-link 0.4.44 → 0.4.46

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/dist/client.js CHANGED
@@ -39,6 +39,25 @@ window.__ModuleLoader__.load({
39
39
  "section.label": "Antigravity",
40
40
  "code.title": "代码",
41
41
  "code.inspect": "检查",
42
+ "tv.command": "命令",
43
+ "tv.write": "写入",
44
+ "tv.edit": "编辑",
45
+ "tv.read": "读取",
46
+ "tv.search": "搜索",
47
+ "tv.webSearch": "网页搜索",
48
+ "tv.list": "列表",
49
+ "tv.delete": "删除",
50
+ "tv.tool": "工具",
51
+ "tv.noOutput": "(无输出)",
52
+ "tv.noMatches": "(无匹配)",
53
+ "tv.noChanges": "(无文本变更)",
54
+ "tv.emptyDir": "(空目录)",
55
+ "tv.noDetail": "(无详情)",
56
+ "tv.hint.expandInspect": "点击展开 · 右键检查",
57
+ "tv.hint.collapseInspect": "点击收起 · 右键检查",
58
+ "tv.hint.expand": "点击展开",
59
+ "tv.hint.collapse": "点击收起",
60
+ "tv.diffMore": "… 其余 {count} 行未渲染",
42
61
  "code.copy": "复制",
43
62
  "code.copied": "已复制",
44
63
  "console.title": "Antigravity 管理控制台",
@@ -74,6 +93,7 @@ window.__ModuleLoader__.load({
74
93
  "quota.weekly": "周额度",
75
94
  "quota.refreshed": "额度已刷新",
76
95
  "proxy.button": "代理",
96
+ "pool.winIsolationWarn": "Windows 提示:agy 的登录凭据存在系统级 Credential Manager(单槽位),多账号池无法隔离凭据——轮巡只切换工作目录,实际仍消耗系统登录账号(issue #45)。",
77
97
  "proxy.placeholder": "专属代理 URL(如:http://127.0.0.1:7890;留空则使用全局)",
78
98
  "proxy.save": "保存",
79
99
  "proxy.saved": "代理已保存",
@@ -134,6 +154,25 @@ window.__ModuleLoader__.load({
134
154
  "console.title": "Antigravity Management Console",
135
155
  "code.title": "Code",
136
156
  "code.inspect": "Inspect",
157
+ "tv.command": "Command",
158
+ "tv.write": "Write",
159
+ "tv.edit": "Edit",
160
+ "tv.read": "Read",
161
+ "tv.search": "Search",
162
+ "tv.webSearch": "Web Search",
163
+ "tv.list": "List",
164
+ "tv.delete": "Delete",
165
+ "tv.tool": "Tool",
166
+ "tv.noOutput": "(no output)",
167
+ "tv.noMatches": "(no matches)",
168
+ "tv.noChanges": "(no textual changes)",
169
+ "tv.emptyDir": "(empty directory)",
170
+ "tv.noDetail": "(no detail)",
171
+ "tv.hint.expandInspect": "Click to expand · right-click to inspect",
172
+ "tv.hint.collapseInspect": "Click to collapse · right-click to inspect",
173
+ "tv.hint.expand": "Click to expand",
174
+ "tv.hint.collapse": "Click to collapse",
175
+ "tv.diffMore": "… {count} more lines not rendered",
137
176
  "code.copy": "Copy",
138
177
  "code.copied": "Copied",
139
178
  "status.ready": "Ready",
@@ -168,6 +207,7 @@ window.__ModuleLoader__.load({
168
207
  "quota.weekly": "Weekly quota",
169
208
  "quota.refreshed": "Quota refreshed",
170
209
  "proxy.button": "Proxy",
210
+ "pool.winIsolationWarn": "Windows: agy keeps its login in the system-wide Credential Manager (single slot), so the account pool cannot isolate credentials — rotation switches workspaces only, and generation still bills the system login (issue #45).",
171
211
  "proxy.placeholder": "Dedicated proxy URL (for example: http://127.0.0.1:7890; leave blank to use the global proxy)",
172
212
  "proxy.save": "Save",
173
213
  "proxy.saved": "Proxy saved",
@@ -228,6 +268,25 @@ window.__ModuleLoader__.load({
228
268
  "console.title": "Console de gerenciamento do Antigravity",
229
269
  "code.title": "Código",
230
270
  "code.inspect": "Inspecionar",
271
+ "tv.command": "Comando",
272
+ "tv.write": "Escrever",
273
+ "tv.edit": "Editar",
274
+ "tv.read": "Ler",
275
+ "tv.search": "Buscar",
276
+ "tv.webSearch": "Busca na web",
277
+ "tv.list": "Listar",
278
+ "tv.delete": "Excluir",
279
+ "tv.tool": "Ferramenta",
280
+ "tv.noOutput": "(sem saída)",
281
+ "tv.noMatches": "(nenhuma correspondência)",
282
+ "tv.noChanges": "(sem alterações de texto)",
283
+ "tv.emptyDir": "(diretório vazio)",
284
+ "tv.noDetail": "(sem detalhes)",
285
+ "tv.hint.expandInspect": "Clique para expandir · clique direito para inspecionar",
286
+ "tv.hint.collapseInspect": "Clique para recolher · clique direito para inspecionar",
287
+ "tv.hint.expand": "Clique para expandir",
288
+ "tv.hint.collapse": "Clique para recolher",
289
+ "tv.diffMore": "… {count} linhas restantes não renderizadas",
231
290
  "code.copy": "Copiar",
232
291
  "code.copied": "Copiado",
233
292
  "status.ready": "Pronto",
@@ -262,6 +321,7 @@ window.__ModuleLoader__.load({
262
321
  "quota.weekly": "Cota semanal",
263
322
  "quota.refreshed": "Cota atualizada",
264
323
  "proxy.button": "Proxy",
324
+ "pool.winIsolationWarn": "Windows: o agy guarda o login no Credential Manager do sistema (um único slot), então o pool de contas não isola credenciais — a rotação troca apenas os diretórios e o consumo continua na conta do sistema (issue #45).",
265
325
  "proxy.placeholder": "URL do proxy exclusivo (por exemplo: http://127.0.0.1:7890; deixe vazio para usar o proxy global)",
266
326
  "proxy.save": "Salvar",
267
327
  "proxy.saved": "Proxy salvo",
@@ -322,6 +382,25 @@ window.__ModuleLoader__.load({
322
382
  "console.title": "Consola de administración de Antigravity",
323
383
  "code.title": "Código",
324
384
  "code.inspect": "Inspeccionar",
385
+ "tv.command": "Comando",
386
+ "tv.write": "Escribir",
387
+ "tv.edit": "Editar",
388
+ "tv.read": "Leer",
389
+ "tv.search": "Buscar",
390
+ "tv.webSearch": "Búsqueda web",
391
+ "tv.list": "Listar",
392
+ "tv.delete": "Eliminar",
393
+ "tv.tool": "Herramienta",
394
+ "tv.noOutput": "(sin salida)",
395
+ "tv.noMatches": "(sin coincidencias)",
396
+ "tv.noChanges": "(sin cambios de texto)",
397
+ "tv.emptyDir": "(directorio vacío)",
398
+ "tv.noDetail": "(sin detalles)",
399
+ "tv.hint.expandInspect": "Clic para expandir · clic derecho para inspeccionar",
400
+ "tv.hint.collapseInspect": "Clic para contraer · clic derecho para inspeccionar",
401
+ "tv.hint.expand": "Clic para expandir",
402
+ "tv.hint.collapse": "Clic para contraer",
403
+ "tv.diffMore": "… {count} líneas restantes no renderizadas",
325
404
  "code.copy": "Copiar",
326
405
  "code.copied": "Copiado",
327
406
  "status.ready": "Listo",
@@ -356,6 +435,7 @@ window.__ModuleLoader__.load({
356
435
  "quota.weekly": "Cuota semanal",
357
436
  "quota.refreshed": "Cuota actualizada",
358
437
  "proxy.button": "Proxy",
438
+ "pool.winIsolationWarn": "Windows: agy guarda el inicio de sesión en el Credential Manager del sistema (un único espacio), por lo que el pool de cuentas no puede aislar credenciales — la rotación solo cambia de directorio y el consumo sigue en la cuenta del sistema (issue #45).",
359
439
  "proxy.placeholder": "URL de proxy exclusivo (por ejemplo: http://127.0.0.1:7890; déjelo vacío para usar el proxy global)",
360
440
  "proxy.save": "Guardar",
361
441
  "proxy.saved": "Proxy guardado",
@@ -705,7 +785,9 @@ window.__ModuleLoader__.load({
705
785
  "grep_search",
706
786
  "search",
707
787
  "search_file_content",
708
- "grep"
788
+ "grep",
789
+ "search_web",
790
+ "web_search"
709
791
  ]);
710
792
  const LIST_TOOLS = /* @__PURE__ */ new Set(["list_dir", "ls"]);
711
793
  const DELETE_TOOLS = /* @__PURE__ */ new Set([
@@ -833,15 +915,30 @@ window.__ModuleLoader__.load({
833
915
  function cls(...names) {
834
916
  return names.filter((n) => typeof n === "string" && n !== "").join(" ");
835
917
  }
836
- function displayTitle(model) {
918
+ /**
919
+ * Locale lookup with an English fallback: the mirror card renders through
920
+ * host-provided `t` when the registration declares the namespace, but tests
921
+ * and hosts without the seat call it bare. The old literals were mixed
922
+ * 中文 and English as BOTH fallback and primary, so non-Chinese users saw
923
+ * Chinese buttons on mirror cards.
924
+ */
925
+ function tr(t, key, fallback, params) {
926
+ if (typeof t === "function") try {
927
+ const v = t(key, params);
928
+ if (typeof v === "string" && v !== "" && v !== key) return v;
929
+ } catch {}
930
+ if (params !== void 0 && params.count !== void 0 && fallback.includes("{count}")) return fallback.replace("{count}", String(params.count));
931
+ return fallback;
932
+ }
933
+ function displayTitle(model, t) {
837
934
  switch (model.kind) {
838
- case "terminal": return "Command";
839
- case "diff": return model.tool === "write_to_file" || model.tool === "write_file" || model.tool === "create_file" ? "Write" : "Edit";
840
- case "read": return "Read";
841
- case "search": return "Search";
842
- case "list": return "List";
843
- case "delete": return "Delete";
844
- default: return model.tool ? model.tool.replace(/_/g, " ") : "Tool";
935
+ case "terminal": return tr(t, "tv.command", "Command");
936
+ case "diff": return model.tool === "write_to_file" || model.tool === "write_file" || model.tool === "create_file" ? tr(t, "tv.write", "Write") : tr(t, "tv.edit", "Edit");
937
+ case "read": return tr(t, "tv.read", "Read");
938
+ case "search": return model.tool === "search_web" || model.tool === "web_search" ? tr(t, "tv.webSearch", "Web Search") : tr(t, "tv.search", "Search");
939
+ case "list": return tr(t, "tv.list", "List");
940
+ case "delete": return tr(t, "tv.delete", "Delete");
941
+ default: return model.tool ? model.tool.replace(/_/g, " ") : tr(t, "tv.tool", "Tool");
845
942
  }
846
943
  }
847
944
  function displaySummary(model, cwd, home) {
@@ -851,7 +948,7 @@ window.__ModuleLoader__.load({
851
948
  const rawPath = model.path ?? "";
852
949
  return rawPath !== "" ? relativize(rawPath, cwd, home) : model.title;
853
950
  }
854
- if (model.kind === "search") return model.title.replace(/^Search\s*/i, "") || model.title;
951
+ if (model.kind === "search") return model.title.replace(/^(Web search|Search)\s*/i, "") || model.title;
855
952
  if (model.kind === "list") return model.title.replace(/^List\s*(directory\s*)?/i, "") || model.title;
856
953
  return model.title;
857
954
  }
@@ -865,16 +962,18 @@ window.__ModuleLoader__.load({
865
962
  if (oldLines === 0) return `+${newLines}`;
866
963
  return `+${newLines} -${oldLines}`;
867
964
  }
868
- function CopyButton({ text, labels }) {
965
+ function CopyButton({ text, labels, t }) {
869
966
  const [copied, setCopied] = react().useState(false);
870
- const copyLabel = labels?.[0] ?? "复制";
871
- const copiedLabel = labels?.[1] ?? "已复制";
967
+ const copyLabel = labels?.[0] ?? tr(t, "code.copy", "Copy");
968
+ const copiedLabel = labels?.[1] ?? tr(t, "code.copied", "Copied");
872
969
  const onCopy = (e) => {
873
970
  e.stopPropagation();
874
971
  if (typeof navigator !== "undefined" && navigator.clipboard) navigator.clipboard.writeText(text).then(() => {
875
972
  setCopied(true);
876
973
  setTimeout(() => setCopied(false), 1500);
877
- }).catch(() => {});
974
+ }).catch(() => {
975
+ setCopied(false);
976
+ });
878
977
  };
879
978
  return hx("button", {
880
979
  type: "button",
@@ -883,39 +982,51 @@ window.__ModuleLoader__.load({
883
982
  title: copied ? copiedLabel : copyLabel
884
983
  }, copied ? IconCheckOutline14(11) : IconCopyOutline16(11), hx("span", null, copied ? copiedLabel : copyLabel));
885
984
  }
886
- function DiffBody({ model }) {
985
+ /** Per-side render cap: a 5000-line write_to_file used to build 10k DOM rows
986
+ * inside a max-height:280px pane (99% of them invisible). */
987
+ const DIFF_MAX_LINES = 400;
988
+ function DiffBody({ model, t }) {
887
989
  const h = hx;
888
990
  const oldText = model.oldText ?? "";
889
991
  const newText = model.newText ?? "";
890
992
  const rows = [];
993
+ const moreRow = (count, key) => h("div", {
994
+ key,
995
+ className: "agy-tv-diff-line",
996
+ style: { color: "var(--dsw-alias-label-tertiary)" }
997
+ }, tr(t, "tv.diffMore", "… {count} more lines not rendered", { count }));
891
998
  if (oldText !== "") {
892
999
  const lines = oldText.split("\n");
893
- for (let i = 0; i < lines.length; i++) {
1000
+ const shown = Math.min(lines.length, DIFF_MAX_LINES);
1001
+ for (let i = 0; i < shown; i++) {
894
1002
  const line = lines[i];
895
1003
  rows.push(h("div", {
896
1004
  key: "del-" + i,
897
1005
  className: "agy-tv-diff-line agy-tv-diff-del"
898
1006
  }, h("span", { className: "agy-tv-diff-marker" }, "-"), h("span", { style: { flex: 1 } }, line === "" ? " " : line)));
899
1007
  }
1008
+ if (lines.length > shown) rows.push(moreRow(lines.length - shown, "del-more"));
900
1009
  }
901
1010
  if (newText !== "") {
902
1011
  const lines = newText.split("\n");
903
- for (let i = 0; i < lines.length; i++) {
1012
+ const shown = Math.min(lines.length, DIFF_MAX_LINES);
1013
+ for (let i = 0; i < shown; i++) {
904
1014
  const line = lines[i];
905
1015
  rows.push(h("div", {
906
1016
  key: "add-" + i,
907
1017
  className: "agy-tv-diff-line agy-tv-diff-add"
908
1018
  }, h("span", { className: "agy-tv-diff-marker" }, "+"), h("span", { style: { flex: 1 } }, line === "" ? " " : line)));
909
1019
  }
1020
+ if (lines.length > shown) rows.push(moreRow(lines.length - shown, "add-more"));
910
1021
  }
911
1022
  if (rows.length === 0) rows.push(h("div", {
912
1023
  key: "empty",
913
1024
  className: "agy-tv-diff-line",
914
1025
  style: { color: "var(--dsw-alias-label-tertiary)" }
915
- }, "(no textual changes)"));
1026
+ }, tr(t, "tv.noChanges", "(no textual changes)")));
916
1027
  return h("div", { className: "agy-tv-diff-list" }, ...rows);
917
1028
  }
918
- function CardBody({ model, openFile, cwd, home, inspect }) {
1029
+ function CardBody({ model, openFile, cwd, home, inspect, t }) {
919
1030
  const h = hx;
920
1031
  const inspectNode = inspect !== void 0 ? h("div", {
921
1032
  key: "ins",
@@ -924,10 +1035,10 @@ window.__ModuleLoader__.load({
924
1035
  type: "button",
925
1036
  className: "agy-tv-inspect-btn",
926
1037
  onClick: inspect
927
- }, IconInspectOutline12(12), "检查")) : null;
1038
+ }, IconInspectOutline12(12), tr(t, "code.inspect", "Inspect"))) : null;
928
1039
  switch (model.kind) {
929
1040
  case "terminal": {
930
- const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : "(no output)";
1041
+ const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : tr(t, "tv.noOutput", "(no output)");
931
1042
  const copyText = model.output !== void 0 && model.output !== "" ? model.output : model.command ?? "";
932
1043
  return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, h("span", { className: "agy-tv-prompt-glyph" }, "❯"), ...model.cwd ? [h("span", {
933
1044
  key: "cwd",
@@ -935,7 +1046,10 @@ window.__ModuleLoader__.load({
935
1046
  }, model.cwd)] : [], h("span", {
936
1047
  className: "agy-tv-card-cmd",
937
1048
  title: model.command
938
- }, model.command ?? "")), h(CopyButton, { text: copyText })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
1049
+ }, model.command ?? "")), h(CopyButton, {
1050
+ text: copyText,
1051
+ t
1052
+ })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
939
1053
  }
940
1054
  case "diff": {
941
1055
  const pathLabel = relativize(model.path ?? "file", cwd, home);
@@ -948,38 +1062,51 @@ window.__ModuleLoader__.load({
948
1062
  model,
949
1063
  openFile,
950
1064
  cwd,
951
- home
1065
+ home,
1066
+ t
952
1067
  }), inspectNode);
953
1068
  }
954
1069
  case "read": {
955
1070
  const pathLabel = relativize(model.path ?? "file", cwd, home);
956
- const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : "(no output)";
957
- return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconBrowseOutline16(13), h("span", { className: "agy-tv-card-path" }, pathLabel)), h(CopyButton, { text: model.output ?? "" })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
1071
+ const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : tr(t, "tv.noOutput", "(no output)");
1072
+ return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconBrowseOutline16(13), h("span", { className: "agy-tv-card-path" }, pathLabel)), h(CopyButton, {
1073
+ text: model.output ?? "",
1074
+ t
1075
+ })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
958
1076
  }
959
1077
  case "search": {
960
- const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : "(no matches)";
961
- return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconSearchOutline16(13), h("span", { className: "agy-tv-card-path" }, model.title)), h(CopyButton, { text: model.output ?? "" })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
1078
+ const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : tr(t, "tv.noMatches", "(no matches)");
1079
+ return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconSearchOutline16(13), h("span", { className: "agy-tv-card-path" }, model.title)), h(CopyButton, {
1080
+ text: model.output ?? "",
1081
+ t
1082
+ })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
962
1083
  }
963
1084
  case "list": {
964
- const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : "(empty directory)";
965
- return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconBrowseOutline16(13), h("span", { className: "agy-tv-card-path" }, model.title)), h(CopyButton, { text: model.output ?? "" })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
1085
+ const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : tr(t, "tv.emptyDir", "(empty directory)");
1086
+ return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconBrowseOutline16(13), h("span", { className: "agy-tv-card-path" }, model.title)), h(CopyButton, {
1087
+ text: model.output ?? "",
1088
+ t
1089
+ })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
966
1090
  }
967
1091
  default: {
968
1092
  const copyText = model.output !== void 0 && model.output !== "" ? model.output : model.raw ?? "";
969
- const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : model.raw !== void 0 && model.raw !== "" ? clip(model.raw, 4e3) : "(no detail)";
970
- return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconCodeOutline16(13), h("span", { className: "agy-tv-card-path" }, model.tool || "Tool")), h(CopyButton, { text: copyText })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
1093
+ const rawBody = model.output !== void 0 && model.output !== "" ? trimTrailingBlankLines(model.output) : model.raw !== void 0 && model.raw !== "" ? clip(model.raw, 4e3) : tr(t, "tv.noDetail", "(no detail)");
1094
+ return h("div", { className: "agy-tv-card" }, h("div", { className: "agy-tv-card-header" }, h("div", { className: "agy-tv-card-header-left" }, IconCodeOutline16(13), h("span", { className: "agy-tv-card-path" }, model.tool || "Tool")), h(CopyButton, {
1095
+ text: copyText,
1096
+ t
1097
+ })), h("pre", { className: "agy-tv-card-content" }, clip(rawBody, 16e3)), inspectNode);
971
1098
  }
972
1099
  }
973
1100
  }
974
1101
  function AgyMirrorToolView(props) {
975
1102
  const [open, setOpen] = useToggle(false);
976
1103
  const h = hx;
977
- const { block, cwd, home, openFile, inspect } = props;
1104
+ const { block, cwd, home, openFile, inspect, t } = props;
978
1105
  const settle = isSettled(block);
979
1106
  const err = isError(block);
980
1107
  const model = mirrorCardModel(block, cwd, home);
981
1108
  const state = !settle ? "run" : err ? "err" : "ok";
982
- const title = displayTitle(model);
1109
+ const title = displayTitle(model, t);
983
1110
  const summary = displaySummary(model, cwd, home);
984
1111
  const stat = diffStat(model);
985
1112
  const preview = !open && model.kind !== "diff" ? previewLine(model, cwd, home) : void 0;
@@ -1016,7 +1143,7 @@ window.__ModuleLoader__.load({
1016
1143
  setOpen();
1017
1144
  }
1018
1145
  },
1019
- title: inspect !== void 0 ? open ? "Click to collapse · right-click to inspect" : "Click to expand · right-click to inspect" : open ? "Click to collapse" : "Click to expand",
1146
+ title: inspect !== void 0 ? open ? tr(t, "tv.hint.collapseInspect", "Click to collapse · right-click to inspect") : tr(t, "tv.hint.expandInspect", "Click to expand · right-click to inspect") : open ? tr(t, "tv.hint.collapse", "Click to collapse") : tr(t, "tv.hint.expand", "Click to expand"),
1020
1147
  onContextMenu: inspect !== void 0 ? (e) => {
1021
1148
  e.preventDefault();
1022
1149
  inspect();
@@ -1044,7 +1171,8 @@ window.__ModuleLoader__.load({
1044
1171
  openFile,
1045
1172
  cwd,
1046
1173
  home,
1047
- inspect
1174
+ inspect,
1175
+ t
1048
1176
  }))] : []);
1049
1177
  }
1050
1178
  /** Extract the agy mirror cursor (and tool name) from a run_code program. */
@@ -1116,7 +1244,7 @@ window.__ModuleLoader__.load({
1116
1244
  setOpen();
1117
1245
  }
1118
1246
  },
1119
- title: inspect !== void 0 ? open ? "Click to collapse · right-click to inspect" : "Click to expand · right-click to inspect" : open ? "Click to collapse" : "Click to expand",
1247
+ title: inspect !== void 0 ? open ? tr(t, "tv.hint.collapseInspect", "Click to collapse · right-click to inspect") : tr(t, "tv.hint.expandInspect", "Click to expand · right-click to inspect") : open ? tr(t, "tv.hint.collapse", "Click to collapse") : tr(t, "tv.hint.expand", "Click to expand"),
1120
1248
  onContextMenu: inspect !== void 0 ? (e) => {
1121
1249
  e.preventDefault();
1122
1250
  inspect();
@@ -1159,18 +1287,21 @@ window.__ModuleLoader__.load({
1159
1287
  const block = typed?.block;
1160
1288
  const mirror = parseAgyMirrorFromCode(block !== void 0 ? parsedArgsRaw(block) : "{}");
1161
1289
  if (mirror === null) return AgyCodeToolView(typed);
1162
- return AgyMirrorToolView({ block: {
1163
- ...block,
1164
- call: {
1165
- ...block?.call ?? {},
1166
- name: "agy_tool",
1167
- argsRaw: JSON.stringify({
1168
- run: mirror.run,
1169
- step: mirror.step,
1170
- ...mirror.tool !== void 0 ? { tool: mirror.tool } : {}
1171
- })
1172
- }
1173
- } });
1290
+ return AgyMirrorToolView({
1291
+ block: {
1292
+ ...block,
1293
+ call: {
1294
+ ...block?.call ?? {},
1295
+ name: "agy_tool",
1296
+ argsRaw: JSON.stringify({
1297
+ run: mirror.run,
1298
+ step: mirror.step,
1299
+ ...mirror.tool !== void 0 ? { tool: mirror.tool } : {}
1300
+ })
1301
+ }
1302
+ },
1303
+ t: typed?.t
1304
+ });
1174
1305
  }
1175
1306
  function installAgyToolView(ctx) {
1176
1307
  ctx.slots.inject("tool.call.toolview", () => {
@@ -1178,7 +1309,8 @@ window.__ModuleLoader__.load({
1178
1309
  name: "tool.call.toolview",
1179
1310
  key: "agy_tool",
1180
1311
  id: "agy-tool-view",
1181
- label: "Antigravity tool"
1312
+ label: "Antigravity tool",
1313
+ locale: NS
1182
1314
  }, AgyMirrorToolView);
1183
1315
  const d2 = ctx.slots.register({
1184
1316
  name: "tool.call.toolview",
@@ -1601,12 +1733,25 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1601
1733
  white-space: pre-wrap;
1602
1734
  padding: 10px 12px;
1603
1735
  }
1736
+ /* Prevent Mermaid's internal error DOM or leaked temporary nodes from escaping into document.body.
1737
+ The generic .error-icon/.error-text classes are scoped to OUR mounts and the
1738
+ leaked-body id shapes only — unqualified, they hid host UI using the same
1739
+ class names. */
1740
+ div[data-agy-mermaid-svg] .error-icon,
1741
+ div[data-agy-mermaid-svg] .error-text,
1742
+ body > [id^="dagy-mmd"] .error-icon,
1743
+ body > [id^="iagy-mmd"] .error-icon {
1744
+ display: none !important;
1745
+ }
1604
1746
  `;
1605
- const CDN_URLS = [
1606
- "/plugins/agy-link/mermaid.min.js",
1607
- "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js",
1608
- "https://unpkg.com/mermaid@11/dist/mermaid.min.js"
1609
- ];
1747
+ /**
1748
+ * The bundle ships mermaid.min.js and serves it from the plugin route. There
1749
+ * is deliberately NO public-CDN fallback: a floating-version, unsigned
1750
+ * script executing inside the DSH web origin (with reach into every local
1751
+ * HTTP API) is a supply-chain hazard that outweighs offline convenience.
1752
+ * When the local asset is missing the source block simply stays visible.
1753
+ */
1754
+ const CDN_URLS = ["/plugins/agy-link/mermaid.min.js"];
1610
1755
  let loadPromise = null;
1611
1756
  let loadFailedAt = 0;
1612
1757
  const LOAD_RETRY_MS = 6e4;
@@ -1624,9 +1769,13 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1624
1769
  const s = document.createElement("script");
1625
1770
  s.src = src;
1626
1771
  s.async = true;
1772
+ let settled = false;
1627
1773
  const done = (ok) => {
1774
+ if (settled) return;
1775
+ settled = true;
1628
1776
  s.removeEventListener("load", onLoad);
1629
1777
  s.removeEventListener("error", onError);
1778
+ if (!ok) s.remove();
1630
1779
  resolve(ok);
1631
1780
  };
1632
1781
  const onLoad = () => done(true);
@@ -1649,7 +1798,8 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1649
1798
  startOnLoad: false,
1650
1799
  securityLevel: "strict",
1651
1800
  theme: "neutral",
1652
- fontFamily: "inherit"
1801
+ fontFamily: "inherit",
1802
+ suppressErrorRendering: true
1653
1803
  });
1654
1804
  } catch {}
1655
1805
  return window.mermaid;
@@ -1731,6 +1881,16 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1731
1881
  function readSource(block) {
1732
1882
  return ((block.querySelector("[data-code-block-content] code") ?? block.querySelector("[data-code-block-content] pre") ?? block.querySelector("[data-code-block-content]"))?.textContent ?? "").trim();
1733
1883
  }
1884
+ function cleanupTempElements(id) {
1885
+ if (typeof document === "undefined") return;
1886
+ if (id) {
1887
+ document.getElementById("d" + id)?.remove();
1888
+ document.getElementById("i" + id)?.remove();
1889
+ document.getElementById(id)?.remove();
1890
+ }
1891
+ const stray = document.querySelectorAll("body > [id^=\"dagy-mmd\"], body > [id^=\"iagy-mmd\"], body > [id^=\"d_agy-mmd\"], body > [id^=\"i_agy-mmd\"], body > [id^=\"agy-mmd-\"]");
1892
+ for (let i = 0; i < stray.length; i++) stray[i]?.remove();
1893
+ }
1734
1894
  async function renderInto(block, source) {
1735
1895
  block.querySelector("[data-agy-mermaid-svg]")?.remove();
1736
1896
  const mount = document.createElement("div");
@@ -1743,28 +1903,32 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1743
1903
  mount.textContent = "Mermaid renderer unavailable (offline). Diagram source stays below.";
1744
1904
  return;
1745
1905
  }
1906
+ renderSeq += 1;
1907
+ const id = "agy-mmd-" + renderSeq;
1746
1908
  try {
1747
- renderSeq += 1;
1748
- const id = "agy-mmd-" + renderSeq;
1749
- const { svg } = await api.render(id, source);
1909
+ if (typeof api.parse === "function") await api.parse(source);
1910
+ const { svg } = await api.render(id, source, mount);
1750
1911
  mount.innerHTML = svg;
1751
1912
  block.setAttribute("data-agy-mermaid", "done");
1752
1913
  } catch (e) {
1753
1914
  block.setAttribute("data-agy-mermaid", "error");
1754
1915
  mount.setAttribute("data-agy-mermaid-error", "1");
1755
1916
  mount.textContent = "Mermaid render failed: " + (e instanceof Error ? e.message : String(e));
1917
+ } finally {
1918
+ cleanupTempElements(id);
1756
1919
  }
1757
1920
  }
1758
- let scanTimer = null;
1759
- function scheduleScan() {
1760
- if (scanTimer !== null) return;
1761
- scanTimer = setTimeout(() => {
1762
- scanTimer = null;
1921
+ let scanTimer$1 = null;
1922
+ function scheduleScan$1() {
1923
+ if (scanTimer$1 !== null) return;
1924
+ scanTimer$1 = setTimeout(() => {
1925
+ scanTimer$1 = null;
1763
1926
  scan();
1764
1927
  }, 150);
1765
1928
  }
1766
1929
  function scan() {
1767
1930
  if (typeof document === "undefined") return;
1931
+ cleanupTempElements();
1768
1932
  const blocks = document.querySelectorAll(".md-code-block");
1769
1933
  for (let i = 0; i < blocks.length; i++) {
1770
1934
  const block = blocks[i];
@@ -1779,14 +1943,25 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1779
1943
  renderInto(block, source);
1780
1944
  }
1781
1945
  }
1946
+ let activeObserver$1 = null;
1947
+ let domContentLoadedHandler$1 = null;
1948
+ /**
1949
+ * Install the mermaid overlay. Returns a disposer that disconnects the
1950
+ * MutationObserver and cancels pending setup — wrap it in ctx.effect so a
1951
+ * plugin reload cannot stack observers over the same document.
1952
+ */
1782
1953
  function installMermaidRendering() {
1783
- if (typeof window === "undefined" || typeof document === "undefined") return;
1954
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {};
1955
+ disposeMermaidRendering();
1784
1956
  injectStyles$1();
1957
+ cleanupTempElements();
1785
1958
  const observer = new MutationObserver(() => {
1786
- scheduleScan();
1959
+ scheduleScan$1();
1787
1960
  });
1961
+ activeObserver$1 = observer;
1788
1962
  const setup = () => {
1789
1963
  if (document.body) {
1964
+ cleanupTempElements();
1790
1965
  observer.observe(document.body, {
1791
1966
  childList: true,
1792
1967
  subtree: true
@@ -1794,8 +1969,26 @@ div[data-agy-mermaid-svg][data-agy-mermaid-error] {
1794
1969
  scan();
1795
1970
  }
1796
1971
  };
1797
- if (document.readyState === "loading") window.addEventListener("DOMContentLoaded", setup, { once: true });
1798
- else setup();
1972
+ if (document.readyState === "loading") {
1973
+ domContentLoadedHandler$1 = setup;
1974
+ window.addEventListener("DOMContentLoaded", setup, { once: true });
1975
+ } else setup();
1976
+ return disposeMermaidRendering;
1977
+ }
1978
+ /** Disconnect the observer and cancel pending setup work. */
1979
+ function disposeMermaidRendering() {
1980
+ if (activeObserver$1 !== null) {
1981
+ activeObserver$1.disconnect();
1982
+ activeObserver$1 = null;
1983
+ }
1984
+ if (domContentLoadedHandler$1 !== null) {
1985
+ window.removeEventListener("DOMContentLoaded", domContentLoadedHandler$1);
1986
+ domContentLoadedHandler$1 = null;
1987
+ }
1988
+ if (scanTimer$1 !== null) {
1989
+ clearTimeout(scanTimer$1);
1990
+ scanTimer$1 = null;
1991
+ }
1799
1992
  }
1800
1993
  //#endregion
1801
1994
  //#region src/client/reasoning.ts
@@ -1857,9 +2050,16 @@ div[data-variant="think"] [class*="thinkBody"] {
1857
2050
  * Try to expand a thinking row once when its body contains real prose
1858
2051
  * (not a bare [agy thinking turn · N tokens] chip). Uses the host's own
1859
2052
  * disclosure control when present — never mutates thinkBody children.
2053
+ *
2054
+ * A row the user manually COLLAPSED is recorded in a WeakSet keyed by the
2055
+ * row element; React keeps the same element identity across its own
2056
+ * re-renders, so the scanner stops re-expanding it (the old data attribute
2057
+ * survived only until React rebuilt the node).
1860
2058
  */
2059
+ const userCollapsed = typeof WeakSet !== "undefined" ? /* @__PURE__ */ new WeakSet() : null;
1861
2060
  function tryExpandThinkRow(root) {
1862
2061
  if (root.dataset.agyExpanded === "true") return;
2062
+ if (userCollapsed?.has(root) === true) return;
1863
2063
  const body = root.querySelector("[class*=\"thinkBody\"]");
1864
2064
  if (!body) return;
1865
2065
  const text = (body.textContent ?? "").trim();
@@ -1871,10 +2071,15 @@ div[data-variant="think"] [class*="thinkBody"] {
1871
2071
  return;
1872
2072
  }
1873
2073
  const toggle = root.querySelector("[data-disclosure-row]") ?? root.querySelector("[class*=\"disclosure\"]") ?? root.querySelector("button");
1874
- if (toggle) try {
1875
- toggle.click();
1876
- root.dataset.agyExpanded = "true";
1877
- } catch {}
2074
+ if (toggle) {
2075
+ userCollapsed?.add(root);
2076
+ try {
2077
+ toggle.click();
2078
+ root.dataset.agyExpanded = "true";
2079
+ } catch {
2080
+ userCollapsed?.delete(root);
2081
+ }
2082
+ }
1878
2083
  }
1879
2084
  function scanThinkingRows() {
1880
2085
  if (typeof document === "undefined") return;
@@ -1884,12 +2089,29 @@ div[data-variant="think"] [class*="thinkBody"] {
1884
2089
  if (row) tryExpandThinkRow(row);
1885
2090
  }
1886
2091
  }
2092
+ let scanTimer = null;
2093
+ function scheduleScan() {
2094
+ if (scanTimer !== null) return;
2095
+ scanTimer = setTimeout(() => {
2096
+ scanTimer = null;
2097
+ scanThinkingRows();
2098
+ }, 200);
2099
+ }
2100
+ let activeObserver = null;
2101
+ let domContentLoadedHandler = null;
2102
+ /**
2103
+ * Install the auto-expand reasoning overlay. Returns a disposer that
2104
+ * disconnects the MutationObserver — wrap it in ctx.effect so a plugin
2105
+ * reload cannot stack observers.
2106
+ */
1887
2107
  function installAutoExpandReasoning() {
1888
- if (typeof window === "undefined" || typeof document === "undefined") return;
2108
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {};
2109
+ disposeAutoExpandReasoning();
1889
2110
  injectStyles();
1890
2111
  const observer = new MutationObserver(() => {
1891
- scanThinkingRows();
2112
+ scheduleScan();
1892
2113
  });
2114
+ activeObserver = observer;
1893
2115
  const setupObserver = () => {
1894
2116
  if (document.body) {
1895
2117
  observer.observe(document.body, {
@@ -1899,8 +2121,26 @@ div[data-variant="think"] [class*="thinkBody"] {
1899
2121
  scanThinkingRows();
1900
2122
  }
1901
2123
  };
1902
- if (document.readyState === "loading") window.addEventListener("DOMContentLoaded", setupObserver, { once: true });
1903
- else setupObserver();
2124
+ if (document.readyState === "loading") {
2125
+ domContentLoadedHandler = setupObserver;
2126
+ window.addEventListener("DOMContentLoaded", setupObserver, { once: true });
2127
+ } else setupObserver();
2128
+ return disposeAutoExpandReasoning;
2129
+ }
2130
+ /** Disconnect the observer and cancel pending work. */
2131
+ function disposeAutoExpandReasoning() {
2132
+ if (activeObserver !== null) {
2133
+ activeObserver.disconnect();
2134
+ activeObserver = null;
2135
+ }
2136
+ if (domContentLoadedHandler !== null) {
2137
+ window.removeEventListener("DOMContentLoaded", domContentLoadedHandler);
2138
+ domContentLoadedHandler = null;
2139
+ }
2140
+ if (scanTimer !== null) {
2141
+ clearTimeout(scanTimer);
2142
+ scanTimer = null;
2143
+ }
1904
2144
  }
1905
2145
  //#endregion
1906
2146
  //#region src/client/index.ts
@@ -1918,7 +2158,9 @@ div[data-variant="think"] [class*="thinkBody"] {
1918
2158
  let statusCache = null;
1919
2159
  async function getStatus() {
1920
2160
  try {
1921
- const res = await win.fetch?.(base + "/plugins/agy-link/status");
2161
+ const init = {};
2162
+ if (typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function") init.signal = AbortSignal.timeout(5e3);
2163
+ const res = await win.fetch?.(base + "/plugins/agy-link/status", init);
1922
2164
  if (!res || !res.ok) return null;
1923
2165
  const payload = await res.json();
1924
2166
  statusCache = payload;
@@ -1936,7 +2178,12 @@ div[data-variant="think"] [class*="thinkBody"] {
1936
2178
  });
1937
2179
  if (!res) return null;
1938
2180
  try {
1939
- return await res.json();
2181
+ const payload = await res.json();
2182
+ if (!res.ok && payload !== null && typeof payload === "object") return {
2183
+ ok: false,
2184
+ ...payload
2185
+ };
2186
+ return payload;
1940
2187
  } catch {
1941
2188
  return { ok: res.ok };
1942
2189
  }
@@ -1944,6 +2191,58 @@ div[data-variant="think"] [class*="thinkBody"] {
1944
2191
  return null;
1945
2192
  }
1946
2193
  }
2194
+ const statusListeners = /* @__PURE__ */ new Set();
2195
+ let statusPollTimer = null;
2196
+ let statusFetchSeq = 0;
2197
+ function notifyStatus(st) {
2198
+ statusCache = st;
2199
+ for (const fn of [...statusListeners]) try {
2200
+ fn(st);
2201
+ } catch {}
2202
+ }
2203
+ async function statusPollTick() {
2204
+ const seq = ++statusFetchSeq;
2205
+ const st = await getStatus();
2206
+ if (st !== null && seq === statusFetchSeq) notifyStatus(st);
2207
+ }
2208
+ function ensureStatusPolling() {
2209
+ if (statusPollTimer !== null) return;
2210
+ statusPollTick();
2211
+ statusPollTimer = setInterval(() => {
2212
+ statusPollTick();
2213
+ }, 3e3);
2214
+ }
2215
+ function stopStatusPollingIfIdle() {
2216
+ if (statusListeners.size === 0 && statusPollTimer !== null) {
2217
+ clearInterval(statusPollTimer);
2218
+ statusPollTimer = null;
2219
+ }
2220
+ }
2221
+ /**
2222
+ * Subscribe a component to the shared status poll. Returns the latest
2223
+ * snapshot plus a manual refresh that keeps the last good snapshot on a
2224
+ * transient failure (a failed refresh used to flip the whole panel back to
2225
+ * its loading state).
2226
+ */
2227
+ function useAgyStatus() {
2228
+ const [status, setStatus] = useState(statusCache);
2229
+ useEffect(() => {
2230
+ statusListeners.add(setStatus);
2231
+ ensureStatusPolling();
2232
+ return () => {
2233
+ statusListeners.delete(setStatus);
2234
+ stopStatusPollingIfIdle();
2235
+ };
2236
+ }, []);
2237
+ const refresh = async () => {
2238
+ const st = await getStatus();
2239
+ if (st !== null) notifyStatus(st);
2240
+ };
2241
+ return {
2242
+ status,
2243
+ refresh
2244
+ };
2245
+ }
1947
2246
  /** Global modal state for opening Antigravity console dialog from footer shortcut or header status */
1948
2247
  const agyModalStore = {
1949
2248
  open: false,
@@ -2654,7 +2953,7 @@ body.dark,
2654
2953
  };
2655
2954
  const AgySettingsSection = (props) => {
2656
2955
  const t = useAgyTranslation();
2657
- const [status, setStatus] = useState(statusCache);
2956
+ const { status, refresh } = useAgyStatus();
2658
2957
  const [aliasInput, setAliasInput] = useState("");
2659
2958
  const [proxyInputs, setProxyInputs] = useState({});
2660
2959
  const [editingProxyId, setEditingProxyId] = useState(null);
@@ -2663,24 +2962,10 @@ body.dark,
2663
2962
  const [loadingAction, setLoadingAction] = useState(null);
2664
2963
  const [toast, setToast] = useState(null);
2665
2964
  const [expandedModels, setExpandedModels] = useState({});
2965
+ const [, setTimeTick] = useState(0);
2666
2966
  useEffect(() => {
2667
- let alive = true;
2668
- let lastJson = "";
2669
- const tick = async () => {
2670
- const st = await getStatus();
2671
- if (!alive || !st) return;
2672
- const json = JSON.stringify(st);
2673
- if (json !== lastJson) {
2674
- lastJson = json;
2675
- setStatus(st);
2676
- }
2677
- };
2678
- tick();
2679
- const timer = setInterval(tick, 3e3);
2680
- return () => {
2681
- alive = false;
2682
- clearInterval(timer);
2683
- };
2967
+ const timer = setInterval(() => setTimeTick((n) => n + 1), 3e4);
2968
+ return () => clearInterval(timer);
2684
2969
  }, []);
2685
2970
  const flowStartedRef = useRef(false);
2686
2971
  const safeMessage = (code) => {
@@ -2706,18 +2991,24 @@ body.dark,
2706
2991
  status?.poolAuth?.phase,
2707
2992
  status?.poolAuth?.code
2708
2993
  ]);
2994
+ const toastTimerRef = useRef(null);
2995
+ useEffect(() => () => {
2996
+ if (toastTimerRef.current !== null) clearTimeout(toastTimerRef.current);
2997
+ }, []);
2709
2998
  const showToast = (text, type = "info") => {
2710
2999
  setToast({
2711
3000
  text,
2712
3001
  type,
2713
3002
  id: Date.now()
2714
3003
  });
2715
- setTimeout(() => {
2716
- setToast((prev) => prev?.text === text ? null : prev);
3004
+ if (toastTimerRef.current !== null) clearTimeout(toastTimerRef.current);
3005
+ toastTimerRef.current = setTimeout(() => {
3006
+ toastTimerRef.current = null;
3007
+ setToast((prev) => prev !== null && prev.text === text ? null : prev);
2717
3008
  }, 6e3);
2718
3009
  };
2719
- const refresh = async () => {
2720
- setStatus(await getStatus());
3010
+ const actionFailed = () => {
3011
+ showToast(t("api.genericError"), "error");
2721
3012
  };
2722
3013
  const handleBeginAddAccount = async () => {
2723
3014
  setLoadingAction("pool:beginAdd");
@@ -2755,36 +3046,62 @@ body.dark,
2755
3046
  };
2756
3047
  const setCfg = async (key, value) => {
2757
3048
  setLoadingAction(`config:${key}`);
2758
- await postJson("/plugins/agy-link/config", {
3049
+ const res = await postJson("/plugins/agy-link/config", {
2759
3050
  key,
2760
3051
  value
2761
3052
  });
3053
+ if (res === null || res?.ok === false) {
3054
+ setLoadingAction(null);
3055
+ actionFailed();
3056
+ return;
3057
+ }
2762
3058
  await refresh();
2763
3059
  setLoadingAction(null);
2764
3060
  };
3061
+ const hiddenPendingRef = useRef(null);
2765
3062
  const toggleModelHidden = async (id, hide) => {
2766
- const next = new Set(status?.hiddenModels ?? []);
3063
+ const next = new Set(hiddenPendingRef.current ?? status?.hiddenModels ?? []);
2767
3064
  if (hide) next.add(id);
2768
3065
  else next.delete(id);
2769
- await setCfg("hiddenModels", Array.from(next));
3066
+ hiddenPendingRef.current = next;
3067
+ try {
3068
+ await setCfg("hiddenModels", Array.from(next));
3069
+ } finally {
3070
+ if (hiddenPendingRef.current === next) hiddenPendingRef.current = null;
3071
+ }
2770
3072
  };
2771
3073
  const setPrimary = async (id) => {
2772
3074
  setLoadingAction(`primary:${id}`);
2773
- await postJson("/plugins/agy-link/pool/primary", { id });
3075
+ const res = await postJson("/plugins/agy-link/pool/primary", { id });
3076
+ if (res === null || res?.ok === false) {
3077
+ setLoadingAction(null);
3078
+ actionFailed();
3079
+ return;
3080
+ }
2774
3081
  await refresh();
2775
3082
  setLoadingAction(null);
2776
3083
  showToast(t("account.setPrimary"), "success");
2777
3084
  };
2778
3085
  const removeAccount = async (id, alias) => {
2779
3086
  setLoadingAction(`remove:${id}`);
2780
- await postJson("/plugins/agy-link/pool/remove", { id });
3087
+ const res = await postJson("/plugins/agy-link/pool/remove", { id });
3088
+ if (res === null || res?.ok === false) {
3089
+ setLoadingAction(null);
3090
+ actionFailed();
3091
+ return;
3092
+ }
2781
3093
  await refresh();
2782
3094
  setLoadingAction(null);
2783
3095
  showToast(t("account.removed", { alias }), "info");
2784
3096
  };
2785
3097
  const refreshQuota = async (id) => {
2786
3098
  setLoadingAction(id ? `refresh:${id}` : "refresh:all");
2787
- await postJson("/plugins/agy-link/pool/refresh-quota", { id });
3099
+ const res = await postJson("/plugins/agy-link/pool/refresh-quota", { id });
3100
+ if (res === null || res?.ok === false) {
3101
+ setLoadingAction(null);
3102
+ actionFailed();
3103
+ return;
3104
+ }
2788
3105
  await refresh();
2789
3106
  setLoadingAction(null);
2790
3107
  showToast(t("quota.refreshed"), "success");
@@ -2792,10 +3109,15 @@ body.dark,
2792
3109
  const saveProxy = async (id) => {
2793
3110
  setLoadingAction(`proxy:${id}`);
2794
3111
  const proxyUrl = proxyInputs[id];
2795
- await postJson("/plugins/agy-link/pool/proxy", {
3112
+ const res = await postJson("/plugins/agy-link/pool/proxy", {
2796
3113
  id,
2797
3114
  proxyUrl
2798
3115
  });
3116
+ if (res === null || res?.ok === false) {
3117
+ setLoadingAction(null);
3118
+ actionFailed();
3119
+ return;
3120
+ }
2799
3121
  setEditingProxyId(null);
2800
3122
  await refresh();
2801
3123
  setLoadingAction(null);
@@ -2803,13 +3125,23 @@ body.dark,
2803
3125
  };
2804
3126
  const setMode = async (mode) => {
2805
3127
  setLoadingAction(`mode:${mode}`);
2806
- await postJson("/plugins/agy-link/pool/mode", { mode });
3128
+ const res = await postJson("/plugins/agy-link/pool/mode", { mode });
3129
+ if (res === null || res?.ok === false) {
3130
+ setLoadingAction(null);
3131
+ actionFailed();
3132
+ return;
3133
+ }
2807
3134
  await refresh();
2808
3135
  setLoadingAction(null);
2809
3136
  };
2810
3137
  const clearCooldown = async (id) => {
2811
3138
  setLoadingAction(id ? `clearCooldown:${id}` : "clearCooldown:all");
2812
- await postJson("/plugins/agy-link/pool/clear-cooldown", { id });
3139
+ const res = await postJson("/plugins/agy-link/pool/clear-cooldown", { id });
3140
+ if (res === null || res?.ok === false) {
3141
+ setLoadingAction(null);
3142
+ actionFailed();
3143
+ return;
3144
+ }
2813
3145
  await refresh();
2814
3146
  setLoadingAction(null);
2815
3147
  showToast(t("account.clearCooldown"), "success");
@@ -3333,7 +3665,21 @@ body.dark,
3333
3665
  },
3334
3666
  disabled: isBusy,
3335
3667
  onClick: () => void refreshQuota()
3336
- }, loadingAction === "refresh:all" ? [renderSpinner(), t("header.refreshing")] : [uiIcon("refresh", 12, "var(--agy-text-btn)"), ` ${t("header.refreshQuota")}`]))), renderToastBanner(), addAccountSection, renderedAccountCards, h("div", { style: {
3668
+ }, loadingAction === "refresh:all" ? [renderSpinner(), t("header.refreshing")] : [uiIcon("refresh", 12, "var(--agy-text-btn)"), ` ${t("header.refreshQuota")}`]))), renderToastBanner(), status?.credentialIsolationLimited === true ? h("div", {
3669
+ key: "win-isolation",
3670
+ style: {
3671
+ display: "flex",
3672
+ alignItems: "flex-start",
3673
+ gap: "6px",
3674
+ padding: "8px 10px",
3675
+ marginBottom: "8px",
3676
+ background: "var(--agy-amber-soft, rgba(245, 158, 11, 0.12))",
3677
+ border: "1px solid rgba(245, 158, 11, 0.45)",
3678
+ borderRadius: "7px",
3679
+ fontSize: "11.5px",
3680
+ color: "var(--agy-text-primary)"
3681
+ }
3682
+ }, h("span", { style: { flexShrink: 0 } }, "⚠️"), h("span", null, t("pool.winIsolationWarn"))) : null, addAccountSection, renderedAccountCards, h("div", { style: {
3337
3683
  marginTop: "16px",
3338
3684
  paddingTop: "12px",
3339
3685
  borderTop: "1px solid var(--agy-border-divider)"
@@ -3512,37 +3858,24 @@ body.dark,
3512
3858
  /** Session Header badge in chat toolbar */
3513
3859
  const AgySessionStatus = () => {
3514
3860
  const t = useAgyTranslation();
3515
- const [status, setStatus] = useState(statusCache);
3861
+ const { status } = useAgyStatus();
3862
+ const [, setTimeTick] = useState(0);
3516
3863
  useEffect(() => {
3517
- let alive = true;
3518
- let lastJson = "";
3519
- const tick = async () => {
3520
- const st = await getStatus();
3521
- if (!alive || !st) return;
3522
- const json = JSON.stringify(st);
3523
- if (json !== lastJson) {
3524
- lastJson = json;
3525
- setStatus(st);
3526
- }
3527
- };
3528
- tick();
3529
- const timer = setInterval(tick, 3e3);
3530
- return () => {
3531
- alive = false;
3532
- clearInterval(timer);
3533
- };
3864
+ const timer = setInterval(() => setTimeTick((n) => n + 1), 3e4);
3865
+ return () => clearInterval(timer);
3534
3866
  }, []);
3535
3867
  const pool = status?.pool;
3536
3868
  const accounts = pool?.accounts ?? [];
3537
3869
  const rawActiveId = pool?.activeAccountId ?? pool?.lastActiveAccountId ?? pool?.activeAccountIds?.google ?? pool?.primaryAccountId;
3538
3870
  const activeAcc = accounts.find((a) => a.id === rawActiveId && a.enabled && !a.authRequired) ?? accounts.find((a) => a.enabled && !a.authRequired) ?? accounts[0];
3539
- const activeHasCooldown = activeAcc ? Object.entries(activeAcc.cooldowns).some(([, cd]) => cd && cd.cooldownUntil > Date.now()) : false;
3540
- const allInCooldown = accounts.length > 0 && accounts.every((a) => !a.enabled || a.authRequired || Object.entries(a.cooldowns).some(([, cd]) => cd && cd.cooldownUntil > Date.now()));
3871
+ const activeHasCooldown = activeAcc ? Object.entries(activeAcc.cooldowns ?? {}).some(([, cd]) => cd && cd.cooldownUntil > Date.now()) : false;
3872
+ const allInCooldown = accounts.length > 0 && accounts.every((a) => !a.enabled || a.authRequired || Object.entries(a.cooldowns ?? {}).some(([, cd]) => cd && cd.cooldownUntil > Date.now()));
3541
3873
  const isAuthed = status?.auth?.phase === "ok" || accounts.length > 0;
3542
3874
  const color = status === null ? "#64748b" : status.dormantReason ? "#f59e0b" : allInCooldown ? "#f59e0b" : activeHasCooldown ? "#f59e0b" : isAuthed ? "#10b981" : "#f59e0b";
3543
3875
  const legacyDefault = activeAcc && activeAcc.defaultAlias === void 0 && activeAcc.systemHome && activeAcc.alias === "主账号 (系统登录)";
3544
3876
  const displayAlias = activeAcc ? activeAcc.defaultAlias || legacyDefault ? activeAcc.systemHome ? t("account.defaultAlias") : t("account.aliasDefault", { number: accounts.indexOf(activeAcc) + 1 }) : activeAcc.alias : "";
3545
- const activeDetail = activeAcc ? ` · ${t("status.inUse")}: ${displayAlias}${activeAcc.email ? ` (${activeAcc.email})` : ""}` : "";
3877
+ const activeEligible = activeAcc !== void 0 && activeAcc.enabled && !activeAcc.authRequired;
3878
+ const activeDetail = activeAcc !== void 0 && activeEligible ? ` · ${t("status.inUse")}: ${displayAlias}${activeAcc.email ? ` (${activeAcc.email})` : ""}` : "";
3546
3879
  const badgeTitle = `${t("header.badgeTitle", { count: accounts.length })}${activeDetail}`;
3547
3880
  const buttonLabel = displayAlias ? `AGY · ${displayAlias}` : `AGY (${accounts.length})`;
3548
3881
  const badge = h("button", {
@@ -3593,8 +3926,8 @@ body.dark,
3593
3926
  }, AgySettingsSection);
3594
3927
  });
3595
3928
  installAgyToolView(ctx);
3596
- installAutoExpandReasoning();
3597
- installMermaidRendering();
3929
+ ctx.effect(() => installAutoExpandReasoning());
3930
+ ctx.effect(() => installMermaidRendering());
3598
3931
  }
3599
3932
  //#endregion
3600
3933
  exports.apply = apply;