@norman-else/dsh-claude 0.1.38 → 0.1.39

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.mjs CHANGED
@@ -1180,6 +1180,80 @@ function normalizeSdkMessage(message) {
1180
1180
  }];
1181
1181
  }
1182
1182
  //#endregion
1183
+ //#region src/model-catalog.ts
1184
+ /** What the selector shows before any session has initialized in this Host
1185
+ * process -- a fresh app launch lands here. `default` is the only id that is
1186
+ * valid on every release and plan; the aliases after it are the stable
1187
+ * `/model` spellings Claude Code has kept across releases, so the menu is
1188
+ * usable at first paint instead of a single row. The first initialize
1189
+ * response replaces the whole list with the CLI's own lineup. */
1190
+ const SEED = [
1191
+ {
1192
+ id: "default",
1193
+ name: "Default (recommended)",
1194
+ description: ""
1195
+ },
1196
+ {
1197
+ id: "opus[1m]",
1198
+ name: "Opus (1M context)",
1199
+ description: "",
1200
+ contextWindow: 1e6
1201
+ },
1202
+ {
1203
+ id: "fable",
1204
+ name: "Fable",
1205
+ description: ""
1206
+ },
1207
+ {
1208
+ id: "sonnet",
1209
+ name: "Sonnet",
1210
+ description: ""
1211
+ },
1212
+ {
1213
+ id: "haiku",
1214
+ name: "Haiku",
1215
+ description: ""
1216
+ }
1217
+ ];
1218
+ /** A 1M-context route spells it in the id (`opus[1m]`, `claude-fable-5[1m]`),
1219
+ * so this needs no capacity table either. It is only a floor: the supervisor
1220
+ * overrides it with the window the CLI reports once a turn has run. */
1221
+ function declaredContextWindow(row) {
1222
+ return /\[1m\]$/u.test(row.resolvedModel ?? row.value) ? 1e6 : void 0;
1223
+ }
1224
+ function projectModel(row) {
1225
+ const contextWindow = declaredContextWindow(row);
1226
+ return {
1227
+ id: row.value,
1228
+ name: row.displayName,
1229
+ description: row.description,
1230
+ ...contextWindow === void 0 ? {} : { contextWindow }
1231
+ };
1232
+ }
1233
+ let latest$1;
1234
+ /**
1235
+ * Learn the lineup from one session's initialize response.
1236
+ * @param models - the CLI's own `/model` rows; an empty list is ignored so a
1237
+ * CLI that answers without a catalog cannot blank the selector.
1238
+ */
1239
+ function recordClaudeModels(models) {
1240
+ if (models.length === 0) return;
1241
+ latest$1 = models.map(projectModel);
1242
+ }
1243
+ /** The lineup to advertise: whatever the CLI last reported, else the seed. */
1244
+ function latestClaudeModels() {
1245
+ return latest$1 ?? SEED;
1246
+ }
1247
+ /**
1248
+ * Look one id up in the current lineup.
1249
+ * @param id - the id DSH persisted on the session, which may name a model the
1250
+ * running CLI no longer lists.
1251
+ * @returns the row, or undefined when the lineup does not cover the id.
1252
+ */
1253
+ function claudeModelRow(id) {
1254
+ return latestClaudeModels().find((row) => row.id === id);
1255
+ }
1256
+ //#endregion
1183
1257
  //#region src/plan-usage.ts
1184
1258
  /** Claude.ai plan rate-limit windows behind the CLI's `/usage` panel.
1185
1259
  *
@@ -1508,9 +1582,11 @@ var ClaudeSupervisor = class {
1508
1582
  return this.#runMetadata(agent, model, (query) => query.supportedCommands());
1509
1583
  }
1510
1584
  async contextUsage(agent, model = this.#config.defaultModel) {
1511
- const usage = await this.#runMetadata(agent, model, (query) => query.getContextUsage());
1512
- this.#recordContextWindow(model, usage);
1513
- return usage;
1585
+ return this.#runMetadata(agent, model, async (query, entry) => {
1586
+ const usage = await query.getContextUsage();
1587
+ this.#recordContextWindow(entry.model, usage);
1588
+ return usage;
1589
+ });
1514
1590
  }
1515
1591
  /** Cache a window under both the selector id the caller asked for and the
1516
1592
  * concrete model the CLI reports, so either name resolves it later. */
@@ -1581,19 +1657,13 @@ var ClaudeSupervisor = class {
1581
1657
  entry.idleTimer = void 0;
1582
1658
  }
1583
1659
  const model = request.model ?? this.#config.defaultModel;
1584
- if (request.thinkingMode !== entry.thinkingMode) {
1660
+ if (request.thinkingMode !== entry.thinkingMode || model !== entry.model) {
1585
1661
  this.#entries.delete(sessionId);
1586
1662
  await this.#disposeEntry(entry);
1587
1663
  entry = await this.#createEntry(request.agent, model, request.thinkingMode);
1588
1664
  this.#entries.set(sessionId, entry);
1589
1665
  await entry.sdkInitialization;
1590
- } else {
1591
- await this.#syncPermissionMode(entry);
1592
- if (model !== entry.model) {
1593
- await this.#control(entry, entry.query.setModel(model), "Claude Code model switch");
1594
- entry.model = model;
1595
- }
1596
- }
1666
+ } else await this.#syncPermissionMode(entry);
1597
1667
  const promptUuid = randomUUID();
1598
1668
  const cursor = currentClaudeActivityCursor(request.agent.session.events);
1599
1669
  cursor.nextOrdinal = (await this.#sidecar.read(sessionId)).activities.reduce((next, activity) => activity.turn === cursor.turn && activity.step === cursor.step ? Math.max(next, activity.ordinal + 1) : next, 0);
@@ -1692,10 +1762,6 @@ var ClaudeSupervisor = class {
1692
1762
  entry.idleTimer = void 0;
1693
1763
  }
1694
1764
  await this.#syncPermissionMode(entry);
1695
- if (model !== entry.model) {
1696
- await this.#control(entry, entry.query.setModel(model), "Claude Code model switch");
1697
- entry.model = model;
1698
- }
1699
1765
  return entry;
1700
1766
  }
1701
1767
  /** Run one SDK control request against a live entry, and discard the entry if
@@ -1831,7 +1897,8 @@ var ClaudeSupervisor = class {
1831
1897
  options
1832
1898
  });
1833
1899
  entry.pump = this.#runDetached(() => this.#pump(entry));
1834
- entry.sdkInitialization = withTimeout(entry.query.initializationResult(), CLAUDE_INITIALIZATION_TIMEOUT_MS, "Claude SDK initialization").then(() => {
1900
+ entry.sdkInitialization = withTimeout(entry.query.initializationResult(), CLAUDE_INITIALIZATION_TIMEOUT_MS, "Claude SDK initialization").then((initialization) => {
1901
+ recordClaudeModels(initialization.models);
1835
1902
  if (entry.state === "starting") entry.state = "idle";
1836
1903
  });
1837
1904
  entry.sdkInitialization.catch((error) => this.#handleDisconnect(entry, error));
@@ -2604,34 +2671,6 @@ function formatReviewComments(comments) {
2604
2671
  }
2605
2672
  //#endregion
2606
2673
  //#region src/adapter.ts
2607
- const MODELS = [
2608
- {
2609
- id: "default",
2610
- name: "Default (recommended)",
2611
- description: ""
2612
- },
2613
- {
2614
- id: "opus[1m]",
2615
- name: "Opus (1M context)",
2616
- description: "",
2617
- contextWindow: 1e6
2618
- },
2619
- {
2620
- id: "fable",
2621
- name: "Fable",
2622
- description: ""
2623
- },
2624
- {
2625
- id: "sonnet",
2626
- name: "Sonnet",
2627
- description: ""
2628
- },
2629
- {
2630
- id: "haiku",
2631
- name: "Haiku",
2632
- description: ""
2633
- }
2634
- ];
2635
2674
  const THINKING_MODES = [
2636
2675
  {
2637
2676
  id: "off",
@@ -2818,7 +2857,7 @@ var ClaudeCodeAdapter = class extends LlmAdapter {
2818
2857
  return NO_RETRY_POLICY;
2819
2858
  }
2820
2859
  async listModels(provider) {
2821
- return MODELS.map((model) => ({
2860
+ return latestClaudeModels().map((model) => ({
2822
2861
  provider,
2823
2862
  id: model.id,
2824
2863
  name: model.name,
@@ -2827,8 +2866,8 @@ var ClaudeCodeAdapter = class extends LlmAdapter {
2827
2866
  }));
2828
2867
  }
2829
2868
  async resolveModel(provider, model, _signal) {
2830
- const known = MODELS.find((item) => item.id === model);
2831
- const contextWindow = this.#supervisor.contextWindow(model) ?? (known !== void 0 && "contextWindow" in known ? known.contextWindow : void 0);
2869
+ const known = claudeModelRow(model);
2870
+ const contextWindow = this.#supervisor.contextWindow(model) ?? known?.contextWindow;
2832
2871
  return {
2833
2872
  provider,
2834
2873
  id: model,