dsh-loop-engine-allinone 0.4.1 → 0.7.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/lib/index.js CHANGED
@@ -89,10 +89,10 @@ var __callDispose = (stack, error, hasError) => {
89
89
  };
90
90
 
91
91
  // src/index.ts
92
- import { readFileSync as readFileSync4 } from "node:fs";
92
+ import { readFileSync as readFileSync5 } from "node:fs";
93
93
  import { mkdir as mkdir2, readFile as readFile6, rename as rename2, writeFile as writeFile2 } from "node:fs/promises";
94
94
  import { randomUUID as randomUUID3 } from "node:crypto";
95
- import { dirname as dirname7, join as join20 } from "node:path";
95
+ import { dirname as dirname7, join as join21 } from "node:path";
96
96
  import z12 from "@deepseek-ai/schemastery";
97
97
  import { resolveDshHome as resolveDshHome2 } from "@deepseek-ai/dsh-home-paths";
98
98
 
@@ -145,6 +145,20 @@ var ENGINE_REGISTRY = {
145
145
  loginHint: "run `codex login`, or set CODEX_API_KEY, or point `codexBin` at a signed-in install",
146
146
  authPaths: [".codex/auth.json"],
147
147
  localeKey: "engineCodex",
148
+ // Read out of the bundled CLI binary (0.149.1), which carries the ids its
149
+ // `thread/start` accepts; the driver passes the picked id straight through as
150
+ // `model`. Only the codex-family ids are listed: the binary also mentions
151
+ // `gpt-4.1`/`gpt-5.6` in contexts this build did not verify, and a wrong id
152
+ // fails loudly at the engine rather than silently falling back.
153
+ models: [
154
+ { id: "gpt-5.3-codex", label: "GPT-5.3 Codex" },
155
+ { id: "gpt-5.2-codex", label: "GPT-5.2 Codex" },
156
+ { id: "gpt-5.1-codex-max", label: "GPT-5.1 Codex Max" },
157
+ { id: "gpt-5.1-codex", label: "GPT-5.1 Codex" },
158
+ { id: "gpt-5.4", label: "GPT-5.4" },
159
+ { id: "gpt-5.2", label: "GPT-5.2" },
160
+ { id: "gpt-5.1", label: "GPT-5.1" }
161
+ ],
148
162
  capabilities: {
149
163
  streaming: true,
150
164
  protocol: "jsonrpc",
@@ -188,6 +202,20 @@ var ENGINE_REGISTRY = {
188
202
  loginHint: "run `kimi` once and sign in (or `kimi login`)",
189
203
  authPaths: [".kimi-code/credentials.json", ".kimi-code/auth.json"],
190
204
  localeKey: "engineKimi",
205
+ // Read out of `~/.kimi-code/config.toml`, which the Kimi Code app writes for
206
+ // the signed-in account: one `[models."kimi-code/<id>"]` block per model the
207
+ // account is provisioned for, with the vendor's own display name. The ids are
208
+ // what the app passes as `model`, so they are the right vocabulary for this
209
+ // driver's `session/set_model`. The catalog was NOT read off the ACP surface
210
+ // itself, because the `kimi` CLI is not installed on the machine this was
211
+ // measured on (only the desktop app is) — a stale id fails loudly at the
212
+ // engine rather than silently falling back.
213
+ models: [
214
+ { id: "k3", label: "K3" },
215
+ { id: "k3-256k", label: "K3-256k" },
216
+ { id: "kimi-for-coding", label: "K2.8 Preview" },
217
+ { id: "kimi-for-coding-highspeed", label: "K2.7 Code Highspeed" }
218
+ ],
191
219
  capabilities: {
192
220
  streaming: true,
193
221
  protocol: "acp",
@@ -1029,10 +1057,14 @@ var HostedEngineRouteAdapter = class extends LlmAdapter {
1029
1057
  /**
1030
1058
  * The engine-native models this route advertises, beyond `default`.
1031
1059
  *
1032
- * Empty for the shared `external` route: an engine with no declared catalog
1033
- * has nothing to add to "the engine decides".
1060
+ * A THUNK rather than a fixed list, because the catalog can change after the
1061
+ * route is registered: an ACP engine publishes its models in `session/new`, and a
1062
+ * config file can gain a model when the vendor ships one. Reading it per catalog
1063
+ * build is what makes a discovered model appear without a plugin restart (see
1064
+ * `model-registry.ts`). A route with nothing to add returns an empty list, which
1065
+ * is the shared `external` route's state.
1034
1066
  */
1035
- models;
1067
+ modelsOf;
1036
1068
  /**
1037
1069
  * The name this route's group shows in the model menu.
1038
1070
  *
@@ -1052,7 +1084,7 @@ var HostedEngineRouteAdapter = class extends LlmAdapter {
1052
1084
  super();
1053
1085
  this.contextWindow = contextWindow !== void 0 && Number.isInteger(contextWindow) && contextWindow > 0 ? contextWindow : DEFAULT_ENGINE_CONTEXT_WINDOW;
1054
1086
  this.label = label;
1055
- this.models = models;
1087
+ this.modelsOf = typeof models === "function" ? models : () => models;
1056
1088
  this.displayName = displayName;
1057
1089
  }
1058
1090
  /**
@@ -1102,7 +1134,7 @@ var HostedEngineRouteAdapter = class extends LlmAdapter {
1102
1134
  },
1103
1135
  // The engine's own models, from its own subscription. Selecting one hands
1104
1136
  // its id to the engine; the plugin still serves no model on this route.
1105
- ...this.models.map((model) => ({
1137
+ ...this.modelsOf().map((model) => ({
1106
1138
  provider,
1107
1139
  id: model.id,
1108
1140
  name: model.label,
@@ -5698,6 +5730,138 @@ function createTerminalBridge(shell, defaultCwd) {
5698
5730
  };
5699
5731
  }
5700
5732
 
5733
+ // src/model-registry.ts
5734
+ import { readFileSync as readFileSync2 } from "node:fs";
5735
+ import { join as join5 } from "node:path";
5736
+
5737
+ // src/engine-kimi/process.ts
5738
+ import { existsSync } from "node:fs";
5739
+ import { homedir } from "node:os";
5740
+ import { join as join4 } from "node:path";
5741
+
5742
+ // src/driver-acp/subprocess.ts
5743
+ function acpSubprocessSpec(spec, graceMs) {
5744
+ return {
5745
+ argv: [...spec.argv],
5746
+ cwd: spec.cwd,
5747
+ stdio: { stdin: "pipe", stdout: "pipe", stderr: "pipe" },
5748
+ graceMs,
5749
+ env: spec.env,
5750
+ ...spec.signal === void 0 ? {} : { signal: spec.signal }
5751
+ };
5752
+ }
5753
+ function fromSubprocess2(handle) {
5754
+ const { stdin, stdout, stderr } = handle;
5755
+ if (stdin === void 0 || stdout === void 0 || stderr === void 0) {
5756
+ throw new Error("driver-acp: spawned child must pipe stdin/stdout/stderr");
5757
+ }
5758
+ return {
5759
+ stdin,
5760
+ stdout,
5761
+ stderr,
5762
+ done: handle.done,
5763
+ terminate: () => handle.terminate()
5764
+ };
5765
+ }
5766
+
5767
+ // src/engine-kimi/process.ts
5768
+ function kimiHomeDir() {
5769
+ const envHome = process.env.KIMI_CODE_HOME;
5770
+ return envHome !== void 0 && envHome !== "" ? envHome : join4(homedir(), ".kimi-code");
5771
+ }
5772
+ function kimiBinResolver(configBin) {
5773
+ if (configBin !== void 0 && configBin !== "") return configBin;
5774
+ const executable = process.platform === "win32" ? "kimi.exe" : "kimi";
5775
+ const candidate = join4(kimiHomeDir(), "bin", executable);
5776
+ return existsSync(candidate) ? candidate : "kimi";
5777
+ }
5778
+ function kimiAcpArgv(bin) {
5779
+ return [bin, "acp"];
5780
+ }
5781
+
5782
+ // src/model-discovery.ts
5783
+ function parseKimiConfigModels(text) {
5784
+ const models = [];
5785
+ const seen = /* @__PURE__ */ new Set();
5786
+ let currentId;
5787
+ let currentLabel;
5788
+ const flush = () => {
5789
+ if (currentId !== void 0 && !seen.has(currentId)) {
5790
+ seen.add(currentId);
5791
+ models.push({ id: currentId, label: currentLabel ?? currentId });
5792
+ }
5793
+ currentId = void 0;
5794
+ currentLabel = void 0;
5795
+ };
5796
+ for (const rawLine of text.split(/\r?\n/u)) {
5797
+ const line = rawLine.trim();
5798
+ if (line.startsWith("[")) {
5799
+ flush();
5800
+ const header = /^\[models\."([^"]+)"\]$/u.exec(line);
5801
+ if (header !== null) {
5802
+ const key = header[1];
5803
+ const slash = key.indexOf("/");
5804
+ currentId = slash === -1 ? key : key.slice(slash + 1);
5805
+ }
5806
+ continue;
5807
+ }
5808
+ if (currentId === void 0) continue;
5809
+ const label = /^display_name\s*=\s*"([^"]*)"/u.exec(line);
5810
+ if (label !== null) currentLabel = label[1];
5811
+ }
5812
+ flush();
5813
+ return models;
5814
+ }
5815
+ function modelsFromAcpConfigOptions(configOptions) {
5816
+ if (!Array.isArray(configOptions)) return void 0;
5817
+ for (const option of configOptions) {
5818
+ const entry = option;
5819
+ if (entry?.id !== "model" || !Array.isArray(entry.options)) continue;
5820
+ const models = [];
5821
+ for (const candidate of entry.options) {
5822
+ const value = candidate?.value;
5823
+ if (typeof value !== "string" || value === "") continue;
5824
+ const name2 = candidate?.name;
5825
+ models.push({ id: value, label: typeof name2 === "string" && name2 !== "" ? name2 : value });
5826
+ }
5827
+ return models.length === 0 ? void 0 : models;
5828
+ }
5829
+ return void 0;
5830
+ }
5831
+
5832
+ // src/model-registry.ts
5833
+ var discovered = /* @__PURE__ */ new Map();
5834
+ function discoverKimiCatalog() {
5835
+ try {
5836
+ const text = readFileSync2(join5(kimiHomeDir(), "config.toml"), "utf8");
5837
+ const models = parseKimiConfigModels(text);
5838
+ return models.length === 0 ? void 0 : { models, source: "kimi-config" };
5839
+ } catch {
5840
+ return void 0;
5841
+ }
5842
+ }
5843
+ function recordDiscoveredCatalog(engine, models) {
5844
+ if (models.length === 0) return;
5845
+ discovered.set(engine, { models, source: "acp-session" });
5846
+ }
5847
+ function bootCatalog(engine) {
5848
+ const free = engine === "kimi" ? discoverKimiCatalog() : void 0;
5849
+ if (free !== void 0) discovered.set(engine, free);
5850
+ const declared = declaredModels(engine);
5851
+ if (free !== void 0) return { models: free.models, origin: "discovered" };
5852
+ if (declared !== void 0 && declared.length > 0) return { models: declared, origin: "declared" };
5853
+ return { models: [], origin: "none" };
5854
+ }
5855
+ function liveCatalog(engine) {
5856
+ const found = discovered.get(engine);
5857
+ if (found !== void 0 && found.models.length > 0) return found.models;
5858
+ return declaredModels(engine) ?? [];
5859
+ }
5860
+ function declaredModels(engine) {
5861
+ const descriptor = ENGINE_REGISTRY[engine];
5862
+ return descriptor.models;
5863
+ }
5864
+
5701
5865
  // src/driver-acp/approval.ts
5702
5866
  function acpDefaultApproval(session) {
5703
5867
  return sessionApprovalPolicy(session.snapshotEvents()) !== "ask";
@@ -5814,6 +5978,15 @@ var AcpClient = class _AcpClient {
5814
5978
  handlers;
5815
5979
  /** Every terminal this client owns, so a disposed client cannot leak a process. */
5816
5980
  terminals = /* @__PURE__ */ new Map();
5981
+ /**
5982
+ * The model catalog the agent published on its last `session/new`.
5983
+ *
5984
+ * Free discovery: the frame was already paid for by a session the user opened, so
5985
+ * keeping it costs nothing and spares the maintainer from re-declaring the list
5986
+ * every time the vendor ships a model. `undefined` means the agent published no
5987
+ * model option (every engine except the ACP ones that do).
5988
+ */
5989
+ lastCatalog;
5817
5990
  /** Monotonic id source for the terminals the agent creates. */
5818
5991
  nextTerminalId = 1;
5819
5992
  /** Whether this client was sealed or its process exited. */
@@ -5919,8 +6092,20 @@ var AcpClient = class _AcpClient {
5919
6092
  if (typeof sessionId !== "string" || sessionId.length === 0) {
5920
6093
  throw new Error(`${this.label} acp session/new returned no session id`);
5921
6094
  }
6095
+ const discovered2 = modelsFromAcpConfigOptions(result.configOptions);
6096
+ if (discovered2 !== void 0) this.lastCatalog = discovered2;
5922
6097
  return sessionId;
5923
6098
  }
6099
+ /**
6100
+ * The model catalog this agent published on its last `session/new`, if any.
6101
+ *
6102
+ * Empty until the first session is opened, which is the honest state: the plugin
6103
+ * does not know the catalog before an engine has been asked.
6104
+ * @returns the discovered models, or undefined when the agent published none.
6105
+ */
6106
+ discoveredModels() {
6107
+ return this.lastCatalog;
6108
+ }
5924
6109
  /**
5925
6110
  * Load (resume) an existing ACP session by id.
5926
6111
  *
@@ -6761,6 +6946,8 @@ var AcpEngineAgent = class {
6761
6946
  signal.throwIfAborted();
6762
6947
  client.onPermission(() => (this.profile.approve ?? acpDefaultApproval)(this.session));
6763
6948
  const acpSessionId = await this.openSession(client, cwd);
6949
+ const discovered2 = client.discoveredModels();
6950
+ if (discovered2 !== void 0) recordDiscoveredCatalog(this.profile.engine, discovered2);
6764
6951
  signal.throwIfAborted();
6765
6952
  const model = override?.model ?? this.config.model;
6766
6953
  if (!handedOver && model !== void 0 && this.profile.selectModel !== void 0) {
@@ -7039,51 +7226,6 @@ function kimiModelEnv(handover) {
7039
7226
  };
7040
7227
  }
7041
7228
 
7042
- // src/engine-kimi/process.ts
7043
- import { existsSync } from "node:fs";
7044
- import { homedir } from "node:os";
7045
- import { join as join4 } from "node:path";
7046
-
7047
- // src/driver-acp/subprocess.ts
7048
- function acpSubprocessSpec(spec, graceMs) {
7049
- return {
7050
- argv: [...spec.argv],
7051
- cwd: spec.cwd,
7052
- stdio: { stdin: "pipe", stdout: "pipe", stderr: "pipe" },
7053
- graceMs,
7054
- env: spec.env,
7055
- ...spec.signal === void 0 ? {} : { signal: spec.signal }
7056
- };
7057
- }
7058
- function fromSubprocess2(handle) {
7059
- const { stdin, stdout, stderr } = handle;
7060
- if (stdin === void 0 || stdout === void 0 || stderr === void 0) {
7061
- throw new Error("driver-acp: spawned child must pipe stdin/stdout/stderr");
7062
- }
7063
- return {
7064
- stdin,
7065
- stdout,
7066
- stderr,
7067
- done: handle.done,
7068
- terminate: () => handle.terminate()
7069
- };
7070
- }
7071
-
7072
- // src/engine-kimi/process.ts
7073
- function kimiHomeDir() {
7074
- const envHome = process.env.KIMI_CODE_HOME;
7075
- return envHome !== void 0 && envHome !== "" ? envHome : join4(homedir(), ".kimi-code");
7076
- }
7077
- function kimiBinResolver(configBin) {
7078
- if (configBin !== void 0 && configBin !== "") return configBin;
7079
- const executable = process.platform === "win32" ? "kimi.exe" : "kimi";
7080
- const candidate = join4(kimiHomeDir(), "bin", executable);
7081
- return existsSync(candidate) ? candidate : "kimi";
7082
- }
7083
- function kimiAcpArgv(bin) {
7084
- return [bin, "acp"];
7085
- }
7086
-
7087
7229
  // src/engine-kimi/permission.ts
7088
7230
  function resolveToolApproval(events) {
7089
7231
  return sessionApprovalPolicy(events) !== "ask";
@@ -8177,7 +8319,7 @@ function unsupportedImagesRefusal(messages, descriptor) {
8177
8319
 
8178
8320
  // src/engine-agy/process.ts
8179
8321
  import { existsSync as existsSync2 } from "node:fs";
8180
- import { join as join5 } from "node:path";
8322
+ import { join as join6 } from "node:path";
8181
8323
  var AGY_DISPOSE_GRACE_MS = 3e3;
8182
8324
  var AGY_MODES = ["accept-edits", "auto", "plan"];
8183
8325
  var AGY_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
@@ -8186,7 +8328,7 @@ function agyBinResolver(configBin) {
8186
8328
  const local = process.env.LOCALAPPDATA;
8187
8329
  if (local !== void 0 && local !== "") {
8188
8330
  const executable = process.platform === "win32" ? "agy.EXE" : "agy";
8189
- const candidate = join5(local, "agy", "bin", executable);
8331
+ const candidate = join6(local, "agy", "bin", executable);
8190
8332
  if (existsSync2(candidate)) return candidate;
8191
8333
  }
8192
8334
  return "agy";
@@ -8741,16 +8883,16 @@ function mapStreamJsonDocument(document, accept, run) {
8741
8883
  // src/engine-qoderclicn/process.ts
8742
8884
  import { existsSync as existsSync4 } from "node:fs";
8743
8885
  import { homedir as homedir2 } from "node:os";
8744
- import { join as join6 } from "node:path";
8886
+ import { join as join7 } from "node:path";
8745
8887
  var QODERCLICN_DISPOSE_GRACE_MS = 3e3;
8746
8888
  function qoderclicnHomeDir() {
8747
8889
  const envHome = process.env.QODER_HOME;
8748
- return envHome !== void 0 && envHome !== "" ? envHome : join6(homedir2(), ".qoder-cn");
8890
+ return envHome !== void 0 && envHome !== "" ? envHome : join7(homedir2(), ".qoder-cn");
8749
8891
  }
8750
8892
  function qoderclicnBinResolver(configBin) {
8751
8893
  if (configBin !== void 0 && configBin !== "") return configBin;
8752
8894
  const executable = process.platform === "win32" ? "qoderclicn.exe" : "qoderclicn";
8753
- const candidate = join6(qoderclicnHomeDir(), "bin", "qoderclicn", executable);
8895
+ const candidate = join7(qoderclicnHomeDir(), "bin", "qoderclicn", executable);
8754
8896
  return existsSync4(candidate) ? candidate : "qoderclicn";
8755
8897
  }
8756
8898
  function qoderclicnPermissionMode(sandbox, approval) {
@@ -8879,14 +9021,14 @@ import z8 from "@deepseek-ai/schemastery";
8879
9021
 
8880
9022
  // src/engine-hermes/process.ts
8881
9023
  import { existsSync as existsSync5 } from "node:fs";
8882
- import { delimiter, join as join7 } from "node:path";
9024
+ import { delimiter, join as join8 } from "node:path";
8883
9025
  var HERMES_DISPOSE_GRACE_MS = 3e3;
8884
9026
  var HERMES_PROMPT_FLAG = "-z";
8885
9027
  function wellKnownHermes() {
8886
9028
  if (process.platform !== "win32") return void 0;
8887
9029
  const local = process.env.LOCALAPPDATA;
8888
9030
  if (local === void 0 || local === "") return void 0;
8889
- const candidate = join7(local, "hermes", "hermes-agent", "venv", "Scripts", "hermes.exe");
9031
+ const candidate = join8(local, "hermes", "hermes-agent", "venv", "Scripts", "hermes.exe");
8890
9032
  return existsSync5(candidate) ? candidate : void 0;
8891
9033
  }
8892
9034
  function hermesBinResolver(configBin) {
@@ -8984,11 +9126,11 @@ import z9 from "@deepseek-ai/schemastery";
8984
9126
  // src/engine-grok/process.ts
8985
9127
  import { existsSync as existsSync7 } from "node:fs";
8986
9128
  import { homedir as homedir3 } from "node:os";
8987
- import { join as join9 } from "node:path";
9129
+ import { join as join10 } from "node:path";
8988
9130
 
8989
9131
  // src/driver-acp/executable.ts
8990
9132
  import { existsSync as existsSync6 } from "node:fs";
8991
- import { join as join8 } from "node:path";
9133
+ import { join as join9 } from "node:path";
8992
9134
  function execArgvPrefix(bin) {
8993
9135
  if (/\.(?:c|m)?js$/i.test(bin)) return [process.execPath, bin];
8994
9136
  if (/\.(?:cmd|bat)$/i.test(bin)) return [process.env.ComSpec ?? "cmd.exe", "/c", bin];
@@ -9002,7 +9144,7 @@ function resolveOnPath(name2, env = process.env) {
9002
9144
  for (const directory of path.split(separator)) {
9003
9145
  if (directory === "") continue;
9004
9146
  for (const extension of extensions) {
9005
- const candidate = join8(directory, `${name2}${extension}`);
9147
+ const candidate = join9(directory, `${name2}${extension}`);
9006
9148
  if (existsSync6(candidate)) return candidate;
9007
9149
  }
9008
9150
  }
@@ -9013,8 +9155,8 @@ function npmPackageEntry(prefix, pkg, entry) {
9013
9155
  const scope = scoped ? pkg.slice(0, pkg.indexOf("/")) : "";
9014
9156
  const name2 = scoped ? pkg.slice(pkg.indexOf("/") + 1) : pkg;
9015
9157
  if (name2 === "" || scoped && scope === "") return void 0;
9016
- const relative = scoped ? join8("node_modules", scope, name2, entry) : join8("node_modules", name2, entry);
9017
- for (const candidate of [join8(prefix, relative), join8(prefix, "lib", relative)]) {
9158
+ const relative = scoped ? join9("node_modules", scope, name2, entry) : join9("node_modules", name2, entry);
9159
+ for (const candidate of [join9(prefix, relative), join9(prefix, "lib", relative)]) {
9018
9160
  if (existsSync6(candidate)) return candidate;
9019
9161
  }
9020
9162
  return void 0;
@@ -9024,12 +9166,12 @@ function npmPackageEntry(prefix, pkg, entry) {
9024
9166
  var GROK_DISPOSE_GRACE_MS = 3e3;
9025
9167
  function grokHomeDir() {
9026
9168
  const envHome = process.env.GROK_HOME;
9027
- return envHome !== void 0 && envHome !== "" ? envHome : join9(homedir3(), ".grok");
9169
+ return envHome !== void 0 && envHome !== "" ? envHome : join10(homedir3(), ".grok");
9028
9170
  }
9029
9171
  function grokBinResolver(configBin) {
9030
9172
  if (configBin !== void 0 && configBin !== "") return configBin;
9031
9173
  const executable = process.platform === "win32" ? "grok.exe" : "grok";
9032
- const candidate = join9(grokHomeDir(), "bin", executable);
9174
+ const candidate = join10(grokHomeDir(), "bin", executable);
9033
9175
  if (existsSync7(candidate)) return candidate;
9034
9176
  return resolveOnPath("grok") ?? "grok";
9035
9177
  }
@@ -9134,7 +9276,7 @@ import z10 from "@deepseek-ai/schemastery";
9134
9276
 
9135
9277
  // src/engine-minimaxcode/process.ts
9136
9278
  import { homedir as homedir4 } from "node:os";
9137
- import { dirname as dirname3, join as join10 } from "node:path";
9279
+ import { dirname as dirname3, join as join11 } from "node:path";
9138
9280
  var MINIMAXCODE_DISPOSE_GRACE_MS = 3e3;
9139
9281
  var MINIMAXCODE_NPM_PACKAGE = "@minimax-ai/code";
9140
9282
  var MINIMAXCODE_ENTRYPOINT = "cli.js";
@@ -9142,7 +9284,7 @@ function mcodeBinResolver(configBin, env = process.env) {
9142
9284
  if (configBin !== void 0 && configBin !== "") return configBin;
9143
9285
  const prefixes = [
9144
9286
  env.MCODE_INSTALL_ROOT,
9145
- env.APPDATA === void 0 ? void 0 : join10(env.APPDATA, "npm"),
9287
+ env.APPDATA === void 0 ? void 0 : join11(env.APPDATA, "npm"),
9146
9288
  // A standard Windows node install puts the global prefix beside node.exe.
9147
9289
  dirname3(process.execPath),
9148
9290
  dirname3(dirname3(process.execPath)),
@@ -9151,7 +9293,7 @@ function mcodeBinResolver(configBin, env = process.env) {
9151
9293
  // `HOME`, and the whole point of this resolver is to find an install the
9152
9294
  // inherited environment may not describe.
9153
9295
  "/usr/local",
9154
- join10(homedir4(), ".npm-global")
9296
+ join11(homedir4(), ".npm-global")
9155
9297
  ];
9156
9298
  for (const prefix of prefixes) {
9157
9299
  if (prefix === void 0 || prefix === "") continue;
@@ -9451,7 +9593,7 @@ ${block}`);
9451
9593
  // src/preset.ts
9452
9594
  import { mkdir, readFile as readFile2, rename, writeFile } from "node:fs/promises";
9453
9595
  import { randomUUID } from "node:crypto";
9454
- import { dirname as dirname4, join as join11 } from "node:path";
9596
+ import { dirname as dirname4, join as join12 } from "node:path";
9455
9597
  var USER_PRESET_DIR = ".agent-presets";
9456
9598
  var COMPOSITION_FILE = "agent.cordis.yml";
9457
9599
  var METADATA_FILE = "preset.yml";
@@ -9651,9 +9793,9 @@ async function ensureEnginePresets(dshHome, source, target) {
9651
9793
  ${applySummarizerTarget(stripPresetRows(composition), target)}`;
9652
9794
  let changed = false;
9653
9795
  for (const engine of HOSTED_ENGINE_IDS) {
9654
- const dir = join11(dshHome, USER_PRESET_DIR, enginePresetId(engine));
9655
- const compositionChanged = await writeIfDifferent(join11(dir, COMPOSITION_FILE), stripped);
9656
- const metadataChanged = await writeIfDifferent(join11(dir, METADATA_FILE), managedMetadata(engine));
9796
+ const dir = join12(dshHome, USER_PRESET_DIR, enginePresetId(engine));
9797
+ const compositionChanged = await writeIfDifferent(join12(dir, COMPOSITION_FILE), stripped);
9798
+ const metadataChanged = await writeIfDifferent(join12(dir, METADATA_FILE), managedMetadata(engine));
9657
9799
  changed = changed || compositionChanged || metadataChanged;
9658
9800
  }
9659
9801
  return changed;
@@ -9907,9 +10049,9 @@ function usableSelection(answered) {
9907
10049
  }
9908
10050
 
9909
10051
  // src/commands.ts
9910
- import { readdirSync, readFileSync as readFileSync2 } from "node:fs";
10052
+ import { readdirSync, readFileSync as readFileSync3 } from "node:fs";
9911
10053
  import { homedir as homedir5 } from "node:os";
9912
- import { join as join12 } from "node:path";
10054
+ import { join as join13 } from "node:path";
9913
10055
  import { createUserMessage as createUserMessage6 } from "@deepseek-ai/dsh-llm";
9914
10056
  var COMMAND_NAME = /^[a-z][a-z0-9_-]*$/;
9915
10057
  function forwardClaudeCodeCommand(name2) {
@@ -9943,10 +10085,10 @@ function discoverUserSlashCommands() {
9943
10085
  if (!entry.endsWith(".md")) continue;
9944
10086
  const name2 = entry.slice(0, -".md".length);
9945
10087
  if (!COMMAND_NAME.test(name2) || seen.has(name2)) continue;
9946
- const path = join12(userCommandsDir(), entry);
10088
+ const path = join13(userCommandsDir(), entry);
9947
10089
  let raw;
9948
10090
  try {
9949
- raw = readFileSync2(path, "utf8");
10091
+ raw = readFileSync3(path, "utf8");
9950
10092
  } catch {
9951
10093
  continue;
9952
10094
  }
@@ -9958,7 +10100,7 @@ function discoverUserSlashCommands() {
9958
10100
  return definitions;
9959
10101
  }
9960
10102
  function userCommandsDir() {
9961
- return join12(homedir5(), ".claude", "commands");
10103
+ return join13(homedir5(), ".claude", "commands");
9962
10104
  }
9963
10105
  function commandDescription(raw) {
9964
10106
  const trimmed = raw.trim();
@@ -10009,7 +10151,7 @@ var KIMI_COMMANDS = [
10009
10151
  // src/skills.ts
10010
10152
  import { readFile as readFile3, readdir, stat } from "node:fs/promises";
10011
10153
  import { homedir as homedir6 } from "node:os";
10012
- import { join as join13, resolve } from "node:path";
10154
+ import { join as join14, resolve } from "node:path";
10013
10155
  var SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
10014
10156
  var PROVIDER_NAME = "claude-code";
10015
10157
  var CLAUDE_CODE_RANK = 150;
@@ -10119,10 +10261,10 @@ var ClaudeCodeSkillProvider = class {
10119
10261
  const cwd = options.cwd;
10120
10262
  if (cwd !== void 0) {
10121
10263
  const projectRoot = await findProjectRoot(resolve(cwd));
10122
- await collectSkillsDir(join13(projectRoot, ".claude", "skills"), CLAUDE_CODE_RANK, candidates);
10264
+ await collectSkillsDir(join14(projectRoot, ".claude", "skills"), CLAUDE_CODE_RANK, candidates);
10123
10265
  await collectClaudeMd(projectRoot, candidates);
10124
10266
  }
10125
- await collectSkillsDir(join13(homedir6(), ".claude", "skills"), CLAUDE_CODE_USER_RANK, candidates);
10267
+ await collectSkillsDir(join14(homedir6(), ".claude", "skills"), CLAUDE_CODE_USER_RANK, candidates);
10126
10268
  if (this.control.signal.aborted) return [];
10127
10269
  return candidates;
10128
10270
  }
@@ -10156,11 +10298,11 @@ async function collectSkillsDir(skillsDir, rank, candidates) {
10156
10298
  return;
10157
10299
  }
10158
10300
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
10159
- const entryPath = join13(skillsDir, entry.name);
10301
+ const entryPath = join14(skillsDir, entry.name);
10160
10302
  const info = await stat(entryPath).catch(() => void 0);
10161
10303
  if (info === void 0) continue;
10162
10304
  if (info.isDirectory()) {
10163
- const path = join13(entryPath, "SKILL.md");
10305
+ const path = join14(entryPath, "SKILL.md");
10164
10306
  const skill2 = await tryParseSkill(path);
10165
10307
  if (skill2 === void 0) continue;
10166
10308
  candidates.push(toCandidate(skill2, path, rank, entryPath));
@@ -10173,7 +10315,7 @@ async function collectSkillsDir(skillsDir, rank, candidates) {
10173
10315
  }
10174
10316
  }
10175
10317
  async function collectClaudeMd(projectRoot, candidates) {
10176
- const claudeMd = join13(projectRoot, "CLAUDE.md");
10318
+ const claudeMd = join14(projectRoot, "CLAUDE.md");
10177
10319
  try {
10178
10320
  const info = await stat(claudeMd);
10179
10321
  if (!info.isFile()) return;
@@ -10210,7 +10352,7 @@ async function findProjectRoot(cwd) {
10210
10352
  let current = cwd;
10211
10353
  while (true) {
10212
10354
  try {
10213
- await stat(join13(current, ".git"));
10355
+ await stat(join14(current, ".git"));
10214
10356
  return current;
10215
10357
  } catch {
10216
10358
  }
@@ -10222,15 +10364,15 @@ async function findProjectRoot(cwd) {
10222
10364
 
10223
10365
  // src/engine-codex/skills.ts
10224
10366
  import { homedir as homedir7 } from "node:os";
10225
- import { join as join16 } from "node:path";
10367
+ import { join as join17 } from "node:path";
10226
10368
 
10227
10369
  // src/driver-core/agents-md-skill-provider.ts
10228
10370
  import { readdir as readdir2, readFile as readFile5, stat as stat3 } from "node:fs/promises";
10229
- import { dirname as dirname5, join as join15 } from "node:path";
10371
+ import { dirname as dirname5, join as join16 } from "node:path";
10230
10372
 
10231
10373
  // src/driver-core/context-files.ts
10232
10374
  import { readFile as readFile4, stat as stat2 } from "node:fs/promises";
10233
- import { join as join14, resolve as resolve2 } from "node:path";
10375
+ import { join as join15, resolve as resolve2 } from "node:path";
10234
10376
  async function projectAncestors(cwd) {
10235
10377
  const root = await findProjectRoot(resolve2(cwd));
10236
10378
  const dirs = [];
@@ -10251,11 +10393,11 @@ async function collectProjectContextFiles(cwd, policy) {
10251
10393
  }
10252
10394
  async function dirContextFile(dir, policy) {
10253
10395
  if (policy.override !== void 0) {
10254
- const override = join14(dir, policy.override);
10396
+ const override = join15(dir, policy.override);
10255
10397
  if (await pathExists(override)) return override;
10256
10398
  }
10257
10399
  for (const name2 of policy.primary) {
10258
- const candidate = join14(dir, name2);
10400
+ const candidate = join15(dir, name2);
10259
10401
  if (await pathExists(candidate)) return candidate;
10260
10402
  }
10261
10403
  return void 0;
@@ -10315,18 +10457,18 @@ var AgentsMdSkillProvider = class {
10315
10457
  if (this.spec.skills !== void 0) {
10316
10458
  const catalog = this.spec.skills;
10317
10459
  for (const dir of await projectAncestors(cwd)) {
10318
- await this.collectSkillsDir(join15(dir, ...catalog.project), catalog.projectRank, candidates);
10460
+ await this.collectSkillsDir(join16(dir, ...catalog.project), catalog.projectRank, candidates);
10319
10461
  }
10320
10462
  }
10321
10463
  }
10322
10464
  const userDir = this.spec.userDir();
10323
10465
  const userContext = this.spec.userContext;
10324
10466
  if (userContext !== void 0) {
10325
- const userFile = join15(userDir, userContext.file);
10467
+ const userFile = join16(userDir, userContext.file);
10326
10468
  if (await fileNonEmpty(userFile)) candidates.push(this.agentsCandidate([userFile], userContext.rank));
10327
10469
  }
10328
10470
  if (this.spec.skills !== void 0) {
10329
- await this.collectSkillsDir(join15(userDir, this.spec.skills.userDir), this.spec.skills.userRank, candidates);
10471
+ await this.collectSkillsDir(join16(userDir, this.spec.skills.userDir), this.spec.skills.userRank, candidates);
10330
10472
  }
10331
10473
  if (this.control.signal.aborted) return [];
10332
10474
  return candidates;
@@ -10386,11 +10528,11 @@ var AgentsMdSkillProvider = class {
10386
10528
  return;
10387
10529
  }
10388
10530
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
10389
- const entryPath = join15(skillsDir, entry.name);
10531
+ const entryPath = join16(skillsDir, entry.name);
10390
10532
  const info = await stat3(entryPath).catch(() => void 0);
10391
10533
  if (info === void 0) continue;
10392
10534
  if (info.isDirectory()) {
10393
- const path = join15(entryPath, "SKILL.md");
10535
+ const path = join16(entryPath, "SKILL.md");
10394
10536
  const parsed2 = await this.tryParse(path);
10395
10537
  if (parsed2 === void 0) continue;
10396
10538
  candidates.push(this.skillCandidate(parsed2, path, rank, entryPath));
@@ -10436,7 +10578,7 @@ var CODEX_SPEC = {
10436
10578
  name: "codex",
10437
10579
  agentsMdDescription: "Codex project/user instructions (AGENTS.md)",
10438
10580
  contextPolicy: CODEX_CONTEXT_POLICY,
10439
- userDir: () => join16(homedir7(), ".codex"),
10581
+ userDir: () => join17(homedir7(), ".codex"),
10440
10582
  projectRank: CODEX_PROJECT_RANK,
10441
10583
  userContext: { file: "AGENTS.md", rank: CODEX_USER_RANK }
10442
10584
  };
@@ -10448,7 +10590,7 @@ var CodexSkillProvider = class extends AgentsMdSkillProvider {
10448
10590
 
10449
10591
  // src/engine-pi/skills.ts
10450
10592
  import { homedir as homedir8 } from "node:os";
10451
- import { join as join17, resolve as resolve3 } from "node:path";
10593
+ import { join as join18, resolve as resolve3 } from "node:path";
10452
10594
  var PI_AGENTS_PROJECT_RANK = 140;
10453
10595
  var PI_SKILL_PROJECT_RANK = 150;
10454
10596
  var PI_AGENTS_USER_RANK = 160;
@@ -10460,7 +10602,7 @@ var PI_CONTEXT_POLICY = {
10460
10602
  function piAgentDir2() {
10461
10603
  const override = process.env.PI_CODING_AGENT_DIR;
10462
10604
  if (override !== void 0 && override.length > 0) return resolve3(override);
10463
- return join17(homedir8(), ".pi", "agent");
10605
+ return join18(homedir8(), ".pi", "agent");
10464
10606
  }
10465
10607
  var PI_SPEC = {
10466
10608
  name: "pi",
@@ -10484,7 +10626,7 @@ var PiSkillProvider = class extends AgentsMdSkillProvider {
10484
10626
 
10485
10627
  // src/engine-kimi/skills.ts
10486
10628
  import { homedir as homedir9 } from "node:os";
10487
- import { join as join18, resolve as resolve4 } from "node:path";
10629
+ import { join as join19, resolve as resolve4 } from "node:path";
10488
10630
  var KIMI_AGENTS_PROJECT_RANK = 140;
10489
10631
  var KIMI_SKILL_PROJECT_RANK = 150;
10490
10632
  var KIMI_SKILL_USER_RANK = 160;
@@ -10494,7 +10636,7 @@ var KIMI_CONTEXT_POLICY = {
10494
10636
  function kimiAgentDir() {
10495
10637
  const override = process.env.KIMI_CODE_HOME;
10496
10638
  if (override !== void 0 && override.length > 0) return resolve4(override);
10497
- return join18(homedir9(), ".kimi-code");
10639
+ return join19(homedir9(), ".kimi-code");
10498
10640
  }
10499
10641
  var KIMI_SPEC = {
10500
10642
  name: "kimi",
@@ -11113,15 +11255,15 @@ function handoverOf(handle) {
11113
11255
  }
11114
11256
 
11115
11257
  // src/session-engine-store.ts
11116
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync, writeFileSync as writeFileSync2 } from "node:fs";
11258
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, renameSync, writeFileSync as writeFileSync2 } from "node:fs";
11117
11259
  import { randomUUID as randomUUID2 } from "node:crypto";
11118
- import { dirname as dirname6, join as join19 } from "node:path";
11260
+ import { dirname as dirname6, join as join20 } from "node:path";
11119
11261
  import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
11120
11262
  var ENGINE_RECORD_DIR = ".loop-engine";
11121
11263
  var ENGINE_RECORD_FILE = "engines.json";
11122
11264
  var ENGINE_RECORD_VERSION = 1;
11123
11265
  function resolveEngineRecordPath() {
11124
- return join19(resolveDshHome(), ENGINE_RECORD_DIR, ENGINE_RECORD_FILE);
11266
+ return join20(resolveDshHome(), ENGINE_RECORD_DIR, ENGINE_RECORD_FILE);
11125
11267
  }
11126
11268
  function isMissing(error) {
11127
11269
  return error?.code === "ENOENT";
@@ -11195,7 +11337,7 @@ var SessionEngineStore = class {
11195
11337
  read() {
11196
11338
  let text;
11197
11339
  try {
11198
- text = readFileSync3(this.path, "utf8");
11340
+ text = readFileSync4(this.path, "utf8");
11199
11341
  } catch (error) {
11200
11342
  if (isMissing(error)) return /* @__PURE__ */ new Map();
11201
11343
  return this.degrade(`could not read "${this.path}": ${String(error)}`);
@@ -11276,7 +11418,7 @@ var Config11 = z12.object({
11276
11418
  });
11277
11419
  function resolvePatchPath(config) {
11278
11420
  if (config.patchPath !== void 0 && config.patchPath !== "") return config.patchPath;
11279
- return join20(
11421
+ return join21(
11280
11422
  resolveDshHome2(),
11281
11423
  "profiles",
11282
11424
  config.profile ?? "web",
@@ -11328,7 +11470,7 @@ async function syncManagedBlock(path) {
11328
11470
  }
11329
11471
  function readPatchFileSync(path) {
11330
11472
  try {
11331
- return readFileSync4(path, "utf8");
11473
+ return readFileSync5(path, "utf8");
11332
11474
  } catch (error) {
11333
11475
  if (isMissing2(error)) return "";
11334
11476
  throw error;
@@ -11395,8 +11537,9 @@ function apply(ctx, config) {
11395
11537
  }];
11396
11538
  for (const engine of HOSTED_ENGINE_IDS) {
11397
11539
  const descriptor = ENGINE_REGISTRY[engine];
11398
- if (descriptor.models === void 0) continue;
11399
- routes.push({ label: engine, models: descriptor.models, displayName: descriptor.label });
11540
+ const boot = bootCatalog(engine);
11541
+ if (boot.models.length === 0) continue;
11542
+ routes.push({ label: engine, models: () => liveCatalog(engine), displayName: descriptor.label });
11400
11543
  }
11401
11544
  for (const route of routes) {
11402
11545
  try {