dsh-agy-link 0.4.22 → 0.4.24

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/index.js +251 -76
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.24 (2026-08-28)
4
+
5
+ - **Fixed: Antigravity Models Missing From Picker When Logged In (已登录/显示余量但模型列表不显示 — issue #1).**
6
+ - **Decoupled Adapter Registration from Synchronous Binary Discovery**: `registerAdapter` now executes regardless of initial `bin()` probe results (using resilient fallback model catalog), ensuring the Antigravity provider is never dropped during startup even if CLI path resolution is deferred.
7
+ - **Expanded macOS / Linux GUI App Path Discovery**: extended `resolveAgyBin` search candidates to include `~/.bun/bin`, `~/.cargo/bin`, `~/.local/share/pnpm`, `~/Library/pnpm`, `~/.yarn/bin`, `~/.npm-global/bin`, NVM (`~/.nvm/versions/node/*/bin`), FNM, Volta, ASDF shims, Linuxbrew, etc., fixing GUI desktop apps starting with minimal default system PATH.
8
+ - **Account-Aware Model Discovery & Environment Isolation**: model discovery now inherits current active/primary account isolated environment and proxy settings, falling back across ready pool accounts if default system HOME is unauthenticated.
9
+ - **Defensive Model & Effort Sanitization**: defensive catalog cleaning against empty IDs, blank names, empty reasoning effort arrays, and out-of-bounds `defaultEffort` to strictly satisfy DSH host validation and prevent fail-all provider group drops (`INVALID_CATALOG` / `INVALID_MODEL_*`).
10
+ - **Cross-Platform & Unit Tests**: added defensive catalog normalization and explicit binary path resolution tests across test suites.
11
+
12
+ ## 0.4.23 (2026-08-27)
13
+
14
+ - **Enhanced: Antigravity Tool Mirroring with Native Git +/- Diff Cards & Full Tool Vocabulary.**
15
+ - **Tool Vocabulary & Parameter Mapping**: mapped agy's official editing tool `replace_file_content` (`TargetFile`, `TargetContent`, `ReplacementContent`, `Description`, `Instruction`) and write tool `write_to_file` (`TargetFile`, `CodeContent`, `Overwrite`, `Description`) to DSH native `DiffCallView` / `DiffResultView` cards.
16
+ - **Git Head Base Line Diffing**: added cross-platform `getGitHeadContent` to retrieve committed file content for full-file writes (`write_to_file`), allowing DSH to render line-by-line git `+`/`-` additions and deletions rather than treating existing modified files as blank new files.
17
+ - **Expanded Tool Support**: mapped `view_file` (with `AbsolutePath`/`StartLine` line location navigation), `grep_search`, `find_by_name`, `ask_question`, `read_url_content`, and `generate_image` onto their respective native/generic tool cards with descriptive titles.
18
+ - **Cross-Platform Hardened**: path normalization with forward-slash compatibility across Windows/macOS/Linux, `windowsHide: true`, strict subprocess timeout, and safe error fallback.
19
+
3
20
  ## 0.4.22 (2026-08-27)
4
21
 
5
22
  - **Fixed: `registration.adapter.prepareCall is not a function` on new DSH hosts (本轮运行失败 UNKNOWN).**
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { execFile, execFileSync, spawn } from "node:child_process";
3
- import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
3
+ import path, { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
4
4
  import { accessSync, chmodSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
5
5
  import { homedir, tmpdir } from "node:os";
6
6
  import { CallId, LlmAdapter, LlmError } from "@deepseek-ai/dsh-llm";
@@ -549,6 +549,48 @@ const MIRROR_TOOL_NAME = "agy_tool";
549
549
  /** The only tool this DSH deployment lets a model call directly. */
550
550
  const WRAPPER_TOOL_NAME = "run_code";
551
551
  /**
552
+ * Retrieve the committed HEAD content of a file via git, for computing
553
+ * line diffs on full-file writes. Pure & safe: catches all failures (not in git,
554
+ * untracked, git missing, binary) and returns null in <=500ms.
555
+ */
556
+ function getGitHeadContent(filePath, execFn = execFileSync) {
557
+ if (!filePath || typeof filePath !== "string" || filePath === "file") return null;
558
+ try {
559
+ const resolved = path.resolve(filePath);
560
+ const toplevel = execFn("git", ["rev-parse", "--show-toplevel"], {
561
+ cwd: path.dirname(resolved),
562
+ encoding: "utf-8",
563
+ stdio: [
564
+ "ignore",
565
+ "pipe",
566
+ "ignore"
567
+ ],
568
+ timeout: 500,
569
+ windowsHide: true
570
+ });
571
+ const root = typeof toplevel === "string" ? toplevel.trim() : "";
572
+ if (!root) return null;
573
+ const relPath = path.relative(root, resolved).replace(/\\/g, "/");
574
+ if (relPath.startsWith("..")) return null;
575
+ const content = execFn("git", ["show", `HEAD:${relPath}`], {
576
+ cwd: root,
577
+ encoding: "utf-8",
578
+ stdio: [
579
+ "ignore",
580
+ "pipe",
581
+ "ignore"
582
+ ],
583
+ timeout: 500,
584
+ windowsHide: true
585
+ });
586
+ const str = typeof content === "string" ? content : "";
587
+ if (str.includes("\0")) return null;
588
+ return str;
589
+ } catch {
590
+ return null;
591
+ }
592
+ }
593
+ /**
552
594
  * Build the run_code tool-call arguments that replay one recorded agy tool
553
595
  * step. The deployment's tool-dispatch policy only allows `run_code` as a
554
596
  * direct model call (everything else must be invoked from inside a
@@ -581,8 +623,9 @@ function toolInput(args) {
581
623
  }
582
624
  /**
583
625
  * agy serializes tool args with PascalCase keys (CommandLine, AbsolutePath,
584
- * SearchDirectory, …); pick() accepts both spellings so cards always read
585
- * the real field instead of falling back to raw JSON.
626
+ * SearchDirectory, TargetFile, TargetContent, ReplacementContent, CodeContent…);
627
+ * pick() accepts all casing spellings so cards always read the real field
628
+ * instead of falling back to raw JSON.
586
629
  */
587
630
  function pick$1(input, ...keys) {
588
631
  for (const k of keys) {
@@ -598,41 +641,50 @@ function presentMirrorCall(args) {
598
641
  switch (name) {
599
642
  case "run_command":
600
643
  case "bash":
601
- case "execute_command": return {
602
- card: "terminal",
603
- title: pick$1(input, "command", "cmd", "CommandLine", "Cmd") ?? JSON.stringify(input),
604
- ...pick$1(input, "description", "Description") !== void 0 ? { description: pick$1(input, "description", "Description") } : {},
605
- ...pick$1(input, "cwd", "Cwd", "WorkingDirectory") !== void 0 ? { cwd: pick$1(input, "cwd", "Cwd", "WorkingDirectory") } : {}
606
- };
607
- case "write_to_file":
608
- case "write_file":
609
- case "create_file": {
610
- const path = pick$1(input, "path", "file_path", "filename", "Path", "FilePath", "FileName", "AbsolutePath") ?? "file";
611
- const content = pick$1(input, "content", "Content", "FileContents", "contents") ?? "";
644
+ case "execute_command": {
645
+ const command = pick$1(input, "CommandLine", "command_line", "command", "cmd", "Cmd") ?? JSON.stringify(input);
646
+ const desc = pick$1(input, "Description", "description", "toolAction", "toolSummary");
647
+ const cwd = pick$1(input, "Cwd", "cwd", "WorkingDirectory", "working_directory");
648
+ return {
649
+ card: "terminal",
650
+ title: command,
651
+ ...desc !== void 0 ? { description: desc } : {},
652
+ ...cwd !== void 0 ? { cwd } : {}
653
+ };
654
+ }
655
+ case "replace_file_content":
656
+ case "edit_file":
657
+ case "replace_in_file":
658
+ case "edit": {
659
+ const path = pick$1(input, "TargetFile", "target_file", "path", "file_path", "Path", "FilePath", "AbsolutePath", "targetFile") ?? "file";
660
+ const oldText = pick$1(input, "TargetContent", "target_content", "old_string", "oldText", "OldString", "OldText", "targetContent") ?? null;
661
+ const newText = pick$1(input, "ReplacementContent", "replacement_content", "new_string", "newText", "content", "NewString", "NewText", "Content", "replacementContent") ?? "";
662
+ const desc = pick$1(input, "Description", "description", "Instruction", "instruction", "toolAction", "toolSummary");
612
663
  return {
613
664
  card: "diff",
614
- title: "Write " + path,
665
+ title: desc ? `${desc} · ${path}` : "Edit " + path,
615
666
  diffs: [{
616
667
  path,
617
- oldText: null,
618
- newText: content
668
+ oldText,
669
+ newText
619
670
  }],
620
671
  locations: [{ path }]
621
672
  };
622
673
  }
623
- case "edit_file":
624
- case "replace_in_file":
625
- case "edit": {
626
- const path = pick$1(input, "path", "file_path", "Path", "FilePath", "AbsolutePath") ?? "file";
627
- const newText = pick$1(input, "new_string", "newText", "content", "NewString", "NewText", "Content") ?? "";
628
- const oldText = pick$1(input, "old_string", "oldText", "OldString", "OldText") ?? null;
674
+ case "write_to_file":
675
+ case "write_file":
676
+ case "create_file": {
677
+ const path = pick$1(input, "TargetFile", "target_file", "path", "file_path", "filename", "Path", "FilePath", "FileName", "AbsolutePath", "targetFile") ?? "file";
678
+ const content = pick$1(input, "CodeContent", "code_content", "content", "Content", "FileContents", "contents", "codeContent") ?? "";
679
+ const desc = pick$1(input, "Description", "description", "toolAction", "toolSummary");
680
+ const oldText = pick$1(input, "old_string", "oldText", "OldString", "OldText") ?? getGitHeadContent(path);
629
681
  return {
630
682
  card: "diff",
631
- title: "Edit " + path,
683
+ title: desc ? `${desc} · ${path}` : "Write " + path,
632
684
  diffs: [{
633
685
  path,
634
686
  oldText,
635
- newText
687
+ newText: content
636
688
  }],
637
689
  locations: [{ path }]
638
690
  };
@@ -641,11 +693,12 @@ function presentMirrorCall(args) {
641
693
  case "view_file":
642
694
  case "read":
643
695
  case "open_file": {
644
- const path = pick$1(input, "path", "file_path", "filename", "Path", "FilePath", "FileName", "AbsolutePath") ?? "";
645
- const offset = typeof input.offset === "number" ? input.offset : typeof input.Offset === "number" ? input.Offset : void 0;
696
+ const path = pick$1(input, "AbsolutePath", "absolute_path", "TargetFile", "target_file", "path", "file_path", "filename", "Path", "FilePath", "FileName", "targetFile") ?? "";
697
+ const offset = typeof input.offset === "number" ? input.offset : typeof input.Offset === "number" ? input.Offset : typeof input.StartLine === "number" ? input.StartLine - 1 : typeof input.start_line === "number" ? input.start_line - 1 : void 0;
698
+ const desc = pick$1(input, "Description", "description", "toolAction", "toolSummary");
646
699
  return {
647
700
  card: "generic",
648
- title: "Read " + path,
701
+ title: desc ? `${desc} · ${path}` : "Read " + path,
649
702
  kind: "read",
650
703
  ...path !== "" ? { locations: [{
651
704
  path,
@@ -655,35 +708,70 @@ function presentMirrorCall(args) {
655
708
  }
656
709
  case "find_by_name":
657
710
  case "glob":
658
- case "search_files":
711
+ case "search_files": {
712
+ const q = pick$1(input, "pattern", "Pattern", "query", "Query", "regex", "Regex", "QueryString") ?? "";
713
+ return {
714
+ card: "generic",
715
+ title: pick$1(input, "Description", "description", "toolAction", "toolSummary") ?? (q !== "" ? "Search " + q : "Search"),
716
+ kind: "search"
717
+ };
718
+ }
719
+ case "grep_search":
659
720
  case "search":
660
721
  case "search_file_content":
661
722
  case "grep": {
662
- const q = pick$1(input, "pattern", "query", "regex", "Pattern", "Query", "Regex", "QueryString") ?? "";
723
+ const q = pick$1(input, "query", "Query", "pattern", "Pattern", "regex", "Regex", "QueryString") ?? "";
663
724
  return {
664
725
  card: "generic",
665
- title: q !== "" ? "Search " + q : "Search",
726
+ title: pick$1(input, "Description", "description", "toolAction", "toolSummary") ?? (q !== "" ? "Search " + q : "Search"),
666
727
  kind: "search"
667
728
  };
668
729
  }
669
730
  case "list_dir":
670
731
  case "ls": {
671
- const path = pick$1(input, "path", "directory", "Path", "Directory", "SearchDirectory", "AbsolutePath") ?? "";
732
+ const path = pick$1(input, "DirectoryPath", "directory_path", "path", "directory", "Path", "Directory", "SearchDirectory", "search_directory", "AbsolutePath") ?? "";
672
733
  return {
673
734
  card: "generic",
674
- title: path !== "" ? "List " + path : "List directory"
735
+ title: pick$1(input, "Description", "description", "toolAction", "toolSummary") ?? (path !== "" ? "List " + path : "List directory")
675
736
  };
676
737
  }
677
738
  case "delete_file":
678
739
  case "remove_file":
679
- case "rm": return {
680
- card: "generic",
681
- title: "Delete " + (pick$1(input, "path", "file_path", "Path", "FilePath", "AbsolutePath") ?? ""),
682
- kind: "delete"
683
- };
740
+ case "rm": {
741
+ const path = pick$1(input, "TargetFile", "target_file", "path", "file_path", "Path", "FilePath", "AbsolutePath") ?? "";
742
+ return {
743
+ card: "generic",
744
+ title: pick$1(input, "Description", "description", "toolAction", "toolSummary") ?? "Delete " + path,
745
+ kind: "delete"
746
+ };
747
+ }
748
+ case "ask_question": {
749
+ const questions = input.questions;
750
+ const firstQ = Array.isArray(questions) && questions.length > 0 && typeof questions[0].question === "string" ? questions[0].question : void 0;
751
+ return {
752
+ card: "generic",
753
+ title: firstQ ? `Ask Question: ${firstQ}` : "Ask Question",
754
+ rawInput: input
755
+ };
756
+ }
757
+ case "read_url_content": {
758
+ const url = pick$1(input, "Url", "url") ?? "";
759
+ return {
760
+ card: "generic",
761
+ title: pick$1(input, "Description", "description", "toolAction", "toolSummary") ?? (url ? `Fetch ${url}` : "Fetch URL"),
762
+ kind: "fetch"
763
+ };
764
+ }
765
+ case "generate_image": {
766
+ const prompt = pick$1(input, "Prompt", "prompt", "ImageName", "image_name") ?? "";
767
+ return {
768
+ card: "generic",
769
+ title: prompt ? `Generate Image: ${prompt}` : "Generate Image"
770
+ };
771
+ }
684
772
  default: return {
685
773
  card: "generic",
686
- title: name !== "" ? name : "agy tool",
774
+ title: pick$1(input, "Description", "description", "toolAction", "toolSummary") ?? (name !== "" ? name : "agy tool"),
687
775
  rawInput: input
688
776
  };
689
777
  }
@@ -700,6 +788,7 @@ function presentMirrorResult(args, result) {
700
788
  card: "terminal",
701
789
  output: text
702
790
  };
791
+ case "replace_file_content":
703
792
  case "write_to_file":
704
793
  case "write_file":
705
794
  case "create_file":
@@ -1079,7 +1168,7 @@ function parseModelsOutput(stdout) {
1079
1168
  const out = [];
1080
1169
  for (const line of text.split(/\n/)) {
1081
1170
  const t = line.trim();
1082
- if (t === "" || t.startsWith("Fetching") || t.startsWith("Error")) continue;
1171
+ if (t === "" || t.startsWith("Fetching") || t.startsWith("Error") || /^(please sign in|warning|tip:)/i.test(t)) continue;
1083
1172
  const m = t.match(/^(\S+)(?:\t+|\s{2,})(.+)$/);
1084
1173
  if (m && m[1] !== void 0 && m[2] !== void 0) out.push({
1085
1174
  slug: m[1],
@@ -1097,9 +1186,14 @@ function dedupeBySlug(raw) {
1097
1186
  const seen = /* @__PURE__ */ new Set();
1098
1187
  const out = [];
1099
1188
  for (const r of raw) {
1100
- if (seen.has(r.slug)) continue;
1101
- seen.add(r.slug);
1102
- out.push(r);
1189
+ if (!r.slug || typeof r.slug !== "string") continue;
1190
+ const slug = r.slug.trim();
1191
+ if (slug === "" || seen.has(slug)) continue;
1192
+ seen.add(slug);
1193
+ out.push({
1194
+ slug,
1195
+ label: typeof r.label === "string" && r.label.trim() !== "" ? r.label.trim() : slug
1196
+ });
1103
1197
  }
1104
1198
  return out;
1105
1199
  }
@@ -1207,10 +1301,10 @@ function stripEffortLabel(label, eff) {
1207
1301
  return label.replace(re, "").trim();
1208
1302
  }
1209
1303
  function buildFallbackCatalog(defs) {
1210
- return defs.map((d) => ({
1211
- id: d.id,
1212
- name: d.name,
1213
- efforts: d.efforts ?? null
1304
+ return defs.filter((d) => Boolean(d && typeof d.id === "string" && d.id.trim() !== "")).map((d) => ({
1305
+ id: d.id.trim(),
1306
+ name: typeof d.name === "string" && d.name.trim() !== "" ? d.name.trim() : d.id.trim(),
1307
+ efforts: Array.isArray(d.efforts) && d.efforts.length > 0 ? d.efforts.filter((e) => typeof e === "string" && e.trim() !== "") : null
1214
1308
  }));
1215
1309
  }
1216
1310
  var ModelCatalog = class {
@@ -1288,7 +1382,7 @@ function findEntry(catalog, id) {
1288
1382
  if (resolved !== id) return catalog.models.find((m) => m.id === resolved);
1289
1383
  }
1290
1384
  function defaultEffortFor(entry, cfg) {
1291
- if (!entry.efforts) return void 0;
1385
+ if (!entry.efforts || entry.efforts.length === 0) return void 0;
1292
1386
  if (cfg.defaultEffort !== "" && entry.efforts.includes(cfg.defaultEffort)) return cfg.defaultEffort;
1293
1387
  for (const pref of [
1294
1388
  "high",
@@ -1790,15 +1884,47 @@ function resolveAgyBin(cfg) {
1790
1884
  if (cfg.agyBin !== "") candidates.push(cfg.agyBin);
1791
1885
  const pathEnv = process.env.PATH ?? "";
1792
1886
  for (const dir of pathEnv.split(delimiter)) if (dir !== "") candidates.push(...binCandidates(dir));
1887
+ const home = homedir();
1793
1888
  if (IS_WIN) {
1794
1889
  const local = process.env.LOCALAPPDATA ?? "";
1795
- if (local !== "") candidates.push(join(local, "Programs", "agy", "agy.exe"));
1796
- candidates.push(join(homedir(), ".local", "bin", "agy.exe"));
1797
- candidates.push(join(homedir(), "AppData", "Roaming", "npm", "agy.cmd"));
1890
+ const appData = process.env.APPDATA ?? "";
1891
+ if (local !== "") {
1892
+ candidates.push(join(local, "Programs", "agy", "agy.exe"));
1893
+ candidates.push(join(local, "pnpm", "agy.cmd"));
1894
+ candidates.push(join(local, "pnpm", "agy.exe"));
1895
+ }
1896
+ if (appData !== "") {
1897
+ candidates.push(join(appData, "npm", "agy.cmd"));
1898
+ candidates.push(join(appData, "Roaming", "npm", "agy.cmd"));
1899
+ }
1900
+ candidates.push(join(home, ".local", "bin", "agy.exe"));
1901
+ candidates.push(join(home, ".local", "bin", "agy.cmd"));
1902
+ candidates.push(join(home, ".bun", "bin", "agy.exe"));
1903
+ candidates.push(join(home, ".cargo", "bin", "agy.exe"));
1904
+ candidates.push(join(home, "scoop", "shims", "agy.exe"));
1798
1905
  } else {
1799
- candidates.push(join(homedir(), ".local", "bin", "agy"));
1906
+ candidates.push(join(home, ".local", "bin", "agy"));
1800
1907
  candidates.push("/usr/local/bin/agy");
1801
1908
  candidates.push("/opt/homebrew/bin/agy");
1909
+ candidates.push("/opt/homebrew/sbin/agy");
1910
+ candidates.push("/home/linuxbrew/.linuxbrew/bin/agy");
1911
+ candidates.push(join(home, ".bun", "bin", "agy"));
1912
+ candidates.push(join(home, ".cargo", "bin", "agy"));
1913
+ candidates.push(join(home, ".local", "share", "pnpm", "agy"));
1914
+ candidates.push(join(home, "Library", "pnpm", "agy"));
1915
+ candidates.push(join(home, ".yarn", "bin", "agy"));
1916
+ candidates.push(join(home, ".npm-global", "bin", "agy"));
1917
+ candidates.push(join(home, ".volta", "bin", "agy"));
1918
+ candidates.push(join(home, ".asdf", "shims", "agy"));
1919
+ candidates.push(join(home, ".nix-profile", "bin", "agy"));
1920
+ candidates.push("/run/current-system/sw/bin/agy");
1921
+ try {
1922
+ const nvmNodeDir = join(home, ".nvm", "versions", "node");
1923
+ if (existsSync(nvmNodeDir)) for (const v of readdirSync(nvmNodeDir)) candidates.push(join(nvmNodeDir, v, "bin", "agy"));
1924
+ } catch {}
1925
+ candidates.push(join(home, ".local", "share", "fnm", "current", "bin", "agy"));
1926
+ candidates.push(join(home, ".fnm", "current", "bin", "agy"));
1927
+ candidates.push(join(home, "Library", "Application Support", "fnm", "current", "bin", "agy"));
1802
1928
  }
1803
1929
  const hits = [];
1804
1930
  for (const c of candidates) try {
@@ -2087,15 +2213,19 @@ var AgyAdapter = class extends LlmAdapter {
2087
2213
  const models = [];
2088
2214
  const dropped = [];
2089
2215
  for (const m of cat.models) {
2090
- if (seen.has(m.id)) {
2091
- dropped.push(m.id);
2216
+ if (!m || typeof m.id !== "string") continue;
2217
+ const id = m.id.trim();
2218
+ if (id === "") continue;
2219
+ if (seen.has(id)) {
2220
+ dropped.push(id);
2092
2221
  continue;
2093
2222
  }
2094
- seen.add(m.id);
2223
+ seen.add(id);
2224
+ const name = typeof m.name === "string" && m.name.trim() !== "" ? m.name.trim() : id;
2095
2225
  models.push({
2096
2226
  provider: PROVIDER_ID,
2097
- id: m.id,
2098
- name: m.name,
2227
+ id,
2228
+ name,
2099
2229
  inputModalities: ["text", "image"]
2100
2230
  });
2101
2231
  }
@@ -2104,26 +2234,37 @@ var AgyAdapter = class extends LlmAdapter {
2104
2234
  }
2105
2235
  async resolveModel(_provider, model, _signal) {
2106
2236
  const cfg = this.deps.getConfig();
2107
- const entry = findEntry(this.deps.catalog.get(), model);
2108
- const name = entry ? entry.name : model;
2109
- const contextWindow = cfg.contextWindowDefault;
2237
+ const cat = this.deps.catalog.get();
2238
+ const rawModel = typeof model === "string" ? model.trim() : "";
2239
+ const cleanId = rawModel !== "" ? rawModel : cfg.defaultModel;
2240
+ const entry = findEntry(cat, cleanId);
2241
+ const rawName = entry ? entry.name : cleanId;
2242
+ const name = typeof rawName === "string" && rawName.trim() !== "" ? rawName.trim() : cleanId;
2243
+ const contextWindow = typeof cfg.contextWindowDefault === "number" && cfg.contextWindowDefault > 0 ? cfg.contextWindowDefault : 2e5;
2110
2244
  const resolved = {
2111
2245
  provider: PROVIDER_ID,
2112
- id: model,
2246
+ id: cleanId,
2113
2247
  name,
2114
2248
  inputModalities: ["text", "image"],
2115
2249
  context: { contextWindow },
2116
- defaultMaxTokens: cfg.maxTokensDefault
2250
+ defaultMaxTokens: typeof cfg.maxTokensDefault === "number" && cfg.maxTokensDefault > 0 ? cfg.maxTokensDefault : 8192
2117
2251
  };
2118
- if (entry && entry.efforts) {
2119
- const def = defaultEffortFor(entry, cfg);
2120
- resolved.reasoning = {
2121
- efforts: entry.efforts.map((e) => ({
2122
- id: e,
2123
- name: e
2124
- })),
2125
- ...def ? { defaultEffort: def } : {}
2126
- };
2252
+ if (entry && Array.isArray(entry.efforts) && entry.efforts.length > 0) {
2253
+ const cleanEfforts = Array.from(new Set(entry.efforts.filter((e) => typeof e === "string" && e.trim() !== "")));
2254
+ if (cleanEfforts.length > 0) {
2255
+ const def = defaultEffortFor({
2256
+ ...entry,
2257
+ efforts: cleanEfforts
2258
+ }, cfg);
2259
+ const validDef = def && cleanEfforts.includes(def) ? def : cleanEfforts[0];
2260
+ resolved.reasoning = {
2261
+ efforts: cleanEfforts.map((e) => ({
2262
+ id: e,
2263
+ name: e
2264
+ })),
2265
+ ...validDef ? { defaultEffort: validDef } : {}
2266
+ };
2267
+ }
2127
2268
  }
2128
2269
  return resolved;
2129
2270
  }
@@ -28346,7 +28487,7 @@ function apply(ctx, entryConfig = {}) {
28346
28487
  let lastParser = new StreamJsonParser();
28347
28488
  const getConfig = () => resolveConfig(entryConfig);
28348
28489
  const bin = () => {
28349
- if (binCache === void 0) binCache = resolveAgyBin(getConfig());
28490
+ if (binCache === void 0 || binCache === null) binCache = resolveAgyBin(getConfig());
28350
28491
  return binCache;
28351
28492
  };
28352
28493
  const version = () => versionCache;
@@ -28354,10 +28495,41 @@ function apply(ctx, entryConfig = {}) {
28354
28495
  const pool = new AccountPoolManager();
28355
28496
  const quota = new QuotaService(pool);
28356
28497
  const semaphore = new Semaphore(() => getConfig().maxConcurrent);
28498
+ const getDiscoveryEnv = (account) => {
28499
+ const cfg = getConfig();
28500
+ return {
28501
+ ...process.env,
28502
+ ...cfg.disableTelemetry ? {
28503
+ DO_NOT_TRACK: "1",
28504
+ DISABLE_TELEMETRY: "1",
28505
+ GOOGLE_CLOUD_DISABLE_TELEMETRY: "1",
28506
+ ANTIGRAVITY_DISABLE_TELEMETRY: "1"
28507
+ } : {},
28508
+ ...account && account.dir ? isolatedHomeEnv(account.dir) : {},
28509
+ ...account?.proxyUrl ? {
28510
+ ALL_PROXY: account.proxyUrl,
28511
+ HTTPS_PROXY: account.proxyUrl,
28512
+ HTTP_PROXY: account.proxyUrl,
28513
+ all_proxy: account.proxyUrl,
28514
+ https_proxy: account.proxyUrl,
28515
+ http_proxy: account.proxyUrl
28516
+ } : {}
28517
+ };
28518
+ };
28357
28519
  const catalog = new ModelCatalog(async (signal) => {
28358
28520
  const b = bin();
28359
28521
  if (!b) throw new Error("agy binary not found");
28360
- const out = await probeProcess(b, ["models"], 3e4, signal);
28522
+ const poolData = pool.getPoolData();
28523
+ const primaryAcc = poolData.primaryAccountId ? pool.getAccount(poolData.primaryAccountId) : void 0;
28524
+ const candidateAccount = primaryAcc && primaryAcc.enabled && !primaryAcc.authRequired ? primaryAcc : pool.getAccounts().find((a) => a.enabled && !a.authRequired);
28525
+ let out = await probeProcess(b, ["models"], 3e4, signal, getDiscoveryEnv(candidateAccount));
28526
+ if ((out.code !== 0 || out.stdout.includes("Please sign in") || /sign in|auth|not logged in/i.test(out.stderrTail)) && (!candidateAccount || !candidateAccount.dir)) {
28527
+ const secondary = pool.getAccounts().find((a) => a.enabled && !a.authRequired && a.dir);
28528
+ if (secondary) {
28529
+ const secondaryOut = await probeProcess(b, ["models"], 3e4, signal, getDiscoveryEnv(secondary));
28530
+ if (secondaryOut.code === 0 && secondaryOut.stdout.trim() !== "") out = secondaryOut;
28531
+ }
28532
+ }
28361
28533
  if (out.code !== 0 && out.stdout.trim() === "") throw new Error("agy models failed: " + (out.stderrTail.trim() !== "" ? out.stderrTail.trim().slice(-200) : "exit " + String(out.code)));
28362
28534
  return {
28363
28535
  stdout: out.stdout,
@@ -28411,6 +28583,7 @@ function apply(ctx, entryConfig = {}) {
28411
28583
  const file = overridesPath();
28412
28584
  const current = readOverrides(file);
28413
28585
  current[key] = value;
28586
+ binCache = void 0;
28414
28587
  try {
28415
28588
  mkdirSync(stateDir(), { recursive: true });
28416
28589
  writeFileSync(file, JSON.stringify(current, null, 2), "utf8");
@@ -28424,25 +28597,27 @@ function apply(ctx, entryConfig = {}) {
28424
28597
  log("dormant: disabled by config");
28425
28598
  return;
28426
28599
  }
28427
- if (!bin()) {
28600
+ const currentBin = bin();
28601
+ if (!currentBin) {
28428
28602
  dormantReason = "agy binary not found — install via https://antigravity.google/docs/cli/install";
28429
28603
  log("dormant: agy binary not found");
28430
28604
  return;
28431
28605
  }
28432
28606
  try {
28433
- versionCache = parseVersion((await probeProcess(bin(), ["--version"], 1e4)).stdout);
28607
+ versionCache = parseVersion((await probeProcess(currentBin, ["--version"], 1e4)).stdout);
28434
28608
  if (versionCache && compareVersions(versionCache, "1.1.8") < 0) {
28435
28609
  dormantReason = "agy " + versionCache + " is older than 1.1.8 — run: agy update";
28436
28610
  log("dormant: " + dormantReason);
28437
28611
  return;
28438
28612
  }
28613
+ dormantReason = null;
28439
28614
  log("agy detected: " + (versionCache ?? "unknown version"));
28440
28615
  } catch {
28441
28616
  log("version probe failed — continuing with fallback catalog");
28442
28617
  }
28443
28618
  await catalog.refreshIfNeeded().catch(() => void 0);
28444
28619
  })();
28445
- if (getConfig().enabled && bin()) try {
28620
+ if (getConfig().enabled) try {
28446
28621
  ctx.llm.registerAdapter([PROVIDER_ID], adapter);
28447
28622
  log("registered provider route: antigravity");
28448
28623
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-agy-link",
3
- "version": "0.4.22",
3
+ "version": "0.4.24",
4
4
  "description": "Google Antigravity (agy CLI) models for DeepSeek Harness — stream Gemini/Claude/GPT-OSS subscriptions into DSH with thinking, tool activity, token usage and in-GUI Google OAuth login.",
5
5
  "type": "module",
6
6
  "license": "MIT",