@bivy/bivy 0.10.1-staging.367 → 0.10.1-staging.369

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.
@@ -72,6 +72,21 @@ const FALLBACK_MODELS = [
72
72
  { provider: "anthropic", id: "claude-sonnet-5", name: "Claude Sonnet 5", reasoning: true },
73
73
  { provider: "anthropic", id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5", reasoning: true },
74
74
  ];
75
+ // The SDK's supportedModels() labels each row with a bare family+version
76
+ // ("Opus 4.8") or a family alias ("Sonnet", "Haiku") — while a not-yet-started
77
+ // session's picker uses FALLBACK_MODELS' product names ("Claude Opus 4.8"). That
78
+ // left a running session's picker showing a differently-named catalog than a
79
+ // fresh one. Brand the live rows to match by prefixing "Claude " to any known
80
+ // family label, so the catalog reads the same before and after a session starts.
81
+ // The family list is data — a new lineup means adding a row here, not an `if` —
82
+ // and branding derives from the SDK's own labels so the list never goes stale.
83
+ const CLAUDE_MODEL_FAMILIES = ["Opus", "Sonnet", "Haiku", "Fable", "Mythos"];
84
+ function brandClaudeModel(model) {
85
+ if (model.name.startsWith("Claude "))
86
+ return model;
87
+ const family = CLAUDE_MODEL_FAMILIES.find((f) => model.name === f || model.name.startsWith(`${f} `));
88
+ return family ? { ...model, name: `Claude ${model.name}` } : model;
89
+ }
75
90
  /**
76
91
  * Appended to the Claude Code system prompt so the agent DISCOVERS the outbound
77
92
  * attachment capability. `bivy attach` is just a shell command — without this the
@@ -874,13 +889,42 @@ class ClaudeSession {
874
889
  const q = sdk.query({ prompt: this.input, options });
875
890
  this.query = q;
876
891
  void this.consume(q);
877
- // Best-effort: populate the model picker once the agent is up.
878
- if (typeof q.supportedModels === "function") {
879
- q.supportedModels()
880
- .then((models) => {
881
- this.models = (models ?? []).map(toModelInfo);
882
- })
883
- .catch(() => { });
892
+ // Best-effort: warm the model picker once the agent is up. getModels()
893
+ // re-queries on every call too, so a long-running session's list never
894
+ // freezes on whatever the query reported at spawn.
895
+ void this.refreshSupportedModels();
896
+ }
897
+ /**
898
+ * Re-read the live query's authoritative model list and brand each row to the
899
+ * product's naming. Called from getModels() (and once at spawn) so the picker
900
+ * reflects the current lineup rather than the set captured when the query
901
+ * first spawned — a long session used to keep a stale catalog. Best-effort:
902
+ * on any failure (query torn down mid-reload, or an SDK build without the
903
+ * call) it leaves the last-known list in place.
904
+ */
905
+ async refreshSupportedModels() {
906
+ const q = this.query;
907
+ if (!q || typeof q.supportedModels !== "function")
908
+ return;
909
+ // Bound the wait: supportedModels() resolves instantly once the query has
910
+ // initialized, but getModels() is on the models.list request path, so a
911
+ // hung subprocess must not freeze the picker — fall back to the last-known
912
+ // list (or FALLBACK_MODELS) instead of blocking.
913
+ let timer;
914
+ try {
915
+ const models = await Promise.race([
916
+ q.supportedModels(),
917
+ new Promise((resolve) => { timer = setTimeout(() => resolve(undefined), 1500); }),
918
+ ]);
919
+ if (Array.isArray(models) && models.length)
920
+ this.models = models.map((m) => brandClaudeModel(toModelInfo(m)));
921
+ }
922
+ catch {
923
+ // Keep the last-known list (getModels() falls back to FALLBACK_MODELS if empty).
924
+ }
925
+ finally {
926
+ if (timer)
927
+ clearTimeout(timer);
884
928
  }
885
929
  }
886
930
  /**
@@ -1281,9 +1325,12 @@ class ClaudeSession {
1281
1325
  }
1282
1326
  this.emitter.removeAllListeners();
1283
1327
  }
1284
- getModels() {
1285
- // Before the query is up (or if supportedModels() failed/returned nothing)
1286
- // fall back to the known lineup so the picker is never empty.
1328
+ async getModels() {
1329
+ // Re-query the live agent so the picker reflects the current lineup instead
1330
+ // of the set captured when the query first spawned. Before the query is up
1331
+ // (or if supportedModels() failed/returned nothing) fall back to the known
1332
+ // lineup so the picker is never empty.
1333
+ await this.refreshSupportedModels();
1287
1334
  return this.models.length ? this.models : FALLBACK_MODELS;
1288
1335
  }
1289
1336
  getCurrentModel() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.10.1-staging.367",
3
+ "version": "0.10.1-staging.369",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0-only",
6
6
  "description": "Run coding agents on machines you own. Open-source, self-hostable agent workspace.",