dsh-opencode-go 0.1.11 → 0.1.13

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
@@ -749,9 +749,27 @@ function parseGoModelCatalog(value) {
749
749
  return {
750
750
  models: parseGoModels(catalog.models),
751
751
  stale: catalog.stale,
752
- ...catalog.error === void 0 ? {} : { error: catalog.error }
752
+ ...catalog.error === void 0 ? {} : { error: catalog.error },
753
+ ...catalog.sources === void 0 ? {} : { sources: parseSources(catalog.sources) }
753
754
  };
754
755
  }
756
+ function parseSources(value) {
757
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid OpenCode Go catalog sources");
758
+ const sources = value;
759
+ const parse = (value2) => {
760
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error("Invalid OpenCode Go catalog source status");
761
+ const source = value2;
762
+ if (source.updatedAt !== void 0 && (typeof source.updatedAt !== "number" || !Number.isSafeInteger(source.updatedAt) || source.updatedAt < 0 || source.updatedAt > 864e13)) {
763
+ throw new Error("Invalid OpenCode Go catalog source timestamp");
764
+ }
765
+ if (source.error !== void 0 && typeof source.error !== "string") throw new Error("Invalid OpenCode Go catalog source error");
766
+ return {
767
+ ...source.updatedAt === void 0 ? {} : { updatedAt: source.updatedAt },
768
+ ...source.error === void 0 ? {} : { error: source.error }
769
+ };
770
+ };
771
+ return { listing: parse(sources.listing), metadata: parse(sources.metadata) };
772
+ }
755
773
  var codec = {
756
774
  mode: "strict",
757
775
  typeSymbol: "dsh-opencode-go#GoModelCatalog",
@@ -973,8 +991,30 @@ var PROVIDER_ID = "opencode-go";
973
991
  var DISPLAY_NAME = "OpenCode Go";
974
992
  var DEFAULT_BASE_URL = "https://opencode.ai/zen/go/v1";
975
993
  var MODELS_FETCH_TIMEOUT_MS = 1e4;
994
+ var METADATA_FETCH_TIMEOUT_MS = 3e4;
995
+ var STALE_WHILE_REVALIDATE_MS = 5 * 6e4;
976
996
  var MODEL_LISTING_MAX_BYTES = 1024 * 1024;
977
997
  var MODEL_METADATA_MAX_BYTES = 16 * 1024 * 1024;
998
+ var RETRY_MIN_MS = 5e3;
999
+ var RETRY_MAX_MS = 6e4;
1000
+ function waitForSnapshot(pending, signal) {
1001
+ if (signal === void 0) return pending;
1002
+ return new Promise((resolve, reject) => {
1003
+ const abort = () => {
1004
+ signal.removeEventListener("abort", abort);
1005
+ reject(new LlmError5("opencode-go catalog request aborted by caller", "ABORTED", { cause: signal.reason }));
1006
+ };
1007
+ pending.then((value) => {
1008
+ signal.removeEventListener("abort", abort);
1009
+ resolve(value);
1010
+ }, (error) => {
1011
+ signal.removeEventListener("abort", abort);
1012
+ reject(error);
1013
+ });
1014
+ if (signal.aborted) abort();
1015
+ else signal.addEventListener("abort", abort, { once: true });
1016
+ });
1017
+ }
978
1018
  function builtinModels(baseURL) {
979
1019
  return new Map(getBuiltinModels("opencode-go").map((model) => [model.id, {
980
1020
  ...model,
@@ -1047,17 +1087,26 @@ var OpencodeGoCatalog = class {
1047
1087
  pending;
1048
1088
  metadata;
1049
1089
  metadataETag;
1050
- snapshot(force = false) {
1051
- if (!force && this.served !== void 0 && Date.now() - this.served.fetchedAtMs < this.refreshMs) {
1090
+ metadataUpdatedAtMs;
1091
+ failures = 0;
1092
+ refreshAtMs = 0;
1093
+ snapshot(force = false, signal) {
1094
+ if (signal?.aborted) {
1095
+ return Promise.reject(new LlmError5("opencode-go catalog request aborted by caller", "ABORTED", { cause: signal.reason }));
1096
+ }
1097
+ if (!force && this.served !== void 0 && Date.now() < this.refreshAtMs) {
1052
1098
  return Promise.resolve(this.served);
1053
1099
  }
1054
1100
  this.pending ??= this.build().then((snapshot) => {
1055
1101
  this.served = snapshot;
1102
+ this.failures = snapshot.live && snapshot.metadataLive ? 0 : Math.min(this.failures + 1, 5);
1103
+ const lifetime = this.failures === 0 ? this.refreshMs : Math.min(this.refreshMs, RETRY_MIN_MS * 2 ** (this.failures - 1), RETRY_MAX_MS);
1104
+ this.refreshAtMs = snapshot.fetchedAtMs + lifetime;
1056
1105
  return snapshot;
1057
1106
  }).finally(() => {
1058
1107
  this.pending = void 0;
1059
1108
  });
1060
- return this.pending;
1109
+ return waitForSnapshot(this.pending, signal);
1061
1110
  }
1062
1111
  /** Conditional HTTP requests save bandwidth while still checking for updated metadata. */
1063
1112
  async refreshMetadata(builtin) {
@@ -1068,23 +1117,37 @@ var OpencodeGoCatalog = class {
1068
1117
  ...this.metadataETag === void 0 ? {} : { "if-none-match": this.metadataETag }
1069
1118
  },
1070
1119
  cache: "no-cache",
1071
- signal: AbortSignal.timeout(MODELS_FETCH_TIMEOUT_MS)
1120
+ signal: AbortSignal.timeout(METADATA_FETCH_TIMEOUT_MS)
1072
1121
  }, MODEL_METADATA_MAX_BYTES);
1073
- if (response.status === 304 && this.metadata !== void 0) return this.metadata;
1074
- if (!response.ok) throw new Error(`models.dev answered ${response.status}`);
1075
- const metadata = readModelMetadata(body, this.baseURL, builtin);
1122
+ if (response.status === 304 && this.metadata !== void 0) {
1123
+ this.metadataUpdatedAtMs = Date.now();
1124
+ return this.metadata;
1125
+ }
1126
+ if (!response.ok) throw new LlmError5(`${MODEL_METADATA_URL} answered HTTP ${response.status}`, "DISCOVERY_FAILED");
1127
+ let metadata;
1128
+ try {
1129
+ metadata = readModelMetadata(body, this.baseURL, builtin);
1130
+ } catch (error) {
1131
+ throw new LlmError5(`${MODEL_METADATA_URL} returned invalid model configuration`, "DISCOVERY_FAILED", { cause: error });
1132
+ }
1076
1133
  this.metadata = metadata;
1077
1134
  this.metadataETag = response.headers.get("etag") ?? void 0;
1135
+ this.metadataUpdatedAtMs = Date.now();
1078
1136
  return metadata;
1079
1137
  }
1080
1138
  /** Gateway ids decide membership; online metadata decides how to call each model. */
1081
1139
  async build() {
1082
1140
  const builtin = builtinModels(this.baseURL);
1083
1141
  const [listing, metadataResult] = await Promise.allSettled([
1084
- fetchLiveModelIds(this.baseURL),
1142
+ fetchLiveModelIds(this.baseURL).then((ids) => ({ ids, updatedAtMs: Date.now() })),
1085
1143
  this.refreshMetadata(builtin)
1086
1144
  ]);
1087
1145
  const metadata = metadataResult.status === "fulfilled" ? metadataResult.value : this.metadata;
1146
+ const metadataStatus = {
1147
+ metadataLive: metadataResult.status === "fulfilled",
1148
+ ...this.metadataUpdatedAtMs === void 0 ? {} : { metadataUpdatedAtMs: this.metadataUpdatedAtMs },
1149
+ ...metadataResult.status === "rejected" ? { metadataFailure: metadataResult.reason } : {}
1150
+ };
1088
1151
  const known = new Map([...builtin, ...this.served?.models ?? [], ...metadata?.models ?? []]);
1089
1152
  if (metadataResult.status === "rejected") {
1090
1153
  this.onFallback({ url: MODEL_METADATA_URL, error: metadataResult.reason, kept: known.size });
@@ -1099,12 +1162,14 @@ var OpencodeGoCatalog = class {
1099
1162
  provider: buildProvider(this.baseURL, [...models2.values()]),
1100
1163
  live: false,
1101
1164
  fetchedAtMs: Date.now(),
1165
+ ...metadataStatus,
1166
+ ...this.served?.listingUpdatedAtMs === void 0 ? {} : { listingUpdatedAtMs: this.served.listingUpdatedAtMs },
1102
1167
  listingFailure: listing.reason
1103
1168
  };
1104
1169
  }
1105
1170
  const models = /* @__PURE__ */ new Map();
1106
1171
  const unavailable = /* @__PURE__ */ new Map();
1107
- for (const id of listing.value) {
1172
+ for (const id of listing.value.ids) {
1108
1173
  const error = metadata?.errors.get(id);
1109
1174
  const model = known.get(id);
1110
1175
  if (error !== void 0 || model === void 0) {
@@ -1115,19 +1180,28 @@ var OpencodeGoCatalog = class {
1115
1180
  }
1116
1181
  if (unavailable.size > 0) this.onOmitted([...unavailable.keys()]);
1117
1182
  return {
1118
- details: new Map(listing.value.map((id) => [id, metadata?.details.get(id) ?? {}])),
1183
+ details: new Map(listing.value.ids.map((id) => [id, metadata?.details.get(id) ?? {}])),
1119
1184
  models,
1120
1185
  unavailable,
1121
1186
  provider: buildProvider(this.baseURL, [...models.values()]),
1122
1187
  live: true,
1123
- fetchedAtMs: Date.now()
1188
+ fetchedAtMs: Date.now(),
1189
+ listingUpdatedAtMs: listing.value.updatedAtMs,
1190
+ ...metadataStatus
1124
1191
  };
1125
1192
  }
1126
1193
  /** New or previously unconfigured ids get a fresh lookup even during the runtime TTL. */
1127
- async forModel(id) {
1194
+ async forModel(id, signal) {
1128
1195
  const cached = this.served;
1129
- let snapshot = await this.snapshot();
1130
- if (!snapshot.models.has(id) && snapshot === cached) snapshot = await this.snapshot(true);
1196
+ if (signal?.aborted) throw new LlmError5("opencode-go catalog request aborted by caller", "ABORTED", { cause: signal.reason });
1197
+ if (cached?.models.has(id) && cached.listingUpdatedAtMs !== void 0 && cached.metadataUpdatedAtMs !== void 0 && Date.now() < Math.min(cached.listingUpdatedAtMs, cached.metadataUpdatedAtMs) + this.refreshMs + STALE_WHILE_REVALIDATE_MS) {
1198
+ void this.snapshot().catch(() => {
1199
+ });
1200
+ return cached;
1201
+ }
1202
+ let snapshot = await this.snapshot(false, signal);
1203
+ if (!snapshot.models.has(id) && snapshot === cached) snapshot = await this.snapshot(true, signal);
1204
+ if (signal?.aborted) throw new LlmError5("opencode-go catalog request aborted by caller", "ABORTED", { cause: signal.reason });
1131
1205
  if (snapshot.unavailable.has(id)) {
1132
1206
  throw new LlmError5(
1133
1207
  `opencode-go model "${id}" is advertised but cannot be configured: ${snapshot.unavailable.get(id)}; refresh the model list to retry`,
@@ -1166,15 +1240,31 @@ function describeCatalog(snapshot) {
1166
1240
  }
1167
1241
  async function discoverSettingsModels(catalog) {
1168
1242
  const snapshot = await catalog.snapshot(true);
1243
+ const metadataError = snapshot.metadataLive ? void 0 : metadataFailureMessage(snapshot.metadataFailure);
1244
+ const errors = [snapshot.live ? void 0 : listingError(snapshot).message, metadataError].filter((message) => message !== void 0);
1169
1245
  return {
1170
1246
  models: sortModels([
1171
1247
  ...describeConfiguredModels(snapshot),
1172
1248
  ...[...snapshot.unavailable.keys()].map((id) => ({ id, name: id, configurationMissing: true }))
1173
1249
  ].map((model) => ({ ...model, ...snapshot.details.get(model.id) }))),
1174
- stale: !snapshot.live,
1175
- ...snapshot.live ? {} : { error: listingError(snapshot).message }
1250
+ stale: errors.length > 0,
1251
+ ...errors.length === 0 ? {} : { error: errors.join("; ") },
1252
+ sources: {
1253
+ listing: {
1254
+ ...snapshot.listingUpdatedAtMs === void 0 ? {} : { updatedAt: snapshot.listingUpdatedAtMs },
1255
+ ...snapshot.live ? {} : { error: listingError(snapshot).message }
1256
+ },
1257
+ metadata: {
1258
+ ...snapshot.metadataUpdatedAtMs === void 0 ? {} : { updatedAt: snapshot.metadataUpdatedAtMs },
1259
+ ...metadataError === void 0 ? {} : { error: metadataError }
1260
+ }
1261
+ }
1176
1262
  };
1177
1263
  }
1264
+ function metadataFailureMessage(error) {
1265
+ const detail = error instanceof LlmError5 ? error.message : `${MODEL_METADATA_URL}: ${error instanceof SyntaxError ? "invalid JSON response" : error instanceof RangeError ? `response exceeds the ${MODEL_METADATA_MAX_BYTES} byte limit` : transportFailure(error)}`;
1266
+ return `could not refresh model configuration (${detail}); using previously fetched or built-in configuration where available`;
1267
+ }
1178
1268
 
1179
1269
  // src/config.ts
1180
1270
  import { MAX_TIMER_DELAY_MS } from "@deepseek-ai/dsh-timeout";
@@ -1288,9 +1378,9 @@ var OpencodeGoAdapter = class extends LlmAdapter {
1288
1378
  inputModalities: [...model.input]
1289
1379
  }));
1290
1380
  }
1291
- async resolveModel(_provider, model, _signal) {
1381
+ async resolveModel(_provider, model, signal) {
1292
1382
  const config = this.options.config();
1293
- const snapshot = await this.catalogOf(config).forModel(model);
1383
+ const snapshot = await this.catalogOf(config).forModel(model, signal);
1294
1384
  const resolved = snapshot.models.get(model);
1295
1385
  if (resolved === void 0) {
1296
1386
  throw new LlmError6(`opencode-go has no model "${model}"`, "UNKNOWN_MODEL");
@@ -1338,7 +1428,17 @@ var OpencodeGoAdapter = class extends LlmAdapter {
1338
1428
  throw new LlmError6("llm-opencode-go does not support GenerateOptions.stop", "UNSUPPORTED_OPTION");
1339
1429
  }
1340
1430
  const config = this.options.config();
1341
- const snapshot = await this.catalogOf(config).forModel(options.model);
1431
+ let snapshot;
1432
+ try {
1433
+ snapshot = await this.catalogOf(config).forModel(options.model, options.signal);
1434
+ } catch (error) {
1435
+ if (!options.signal?.aborted) throw error;
1436
+ yield { type: "finish", reason: {
1437
+ kind: "aborted",
1438
+ failure: { code: "ABORTED", message: "opencode-go request aborted by caller" }
1439
+ } };
1440
+ return;
1441
+ }
1342
1442
  const advertised = snapshot.models.get(options.model);
1343
1443
  if (advertised === void 0) {
1344
1444
  throw new LlmError6(`opencode-go has no model "${options.model}"`, "UNKNOWN_MODEL");
@@ -5,8 +5,8 @@
5
5
  * attribution User-Agent (`deepseek-harness/<version>`), which pi-ai's client
6
6
  * lets request headers override, and `x-opencode-session`, which the gateway
7
7
  * requires and uses to route a conversation and share its prompt cache. The
8
- * header value is the request's session id — stable per conversation, so
9
- * caching and billing attribution stay correct; a request arriving with no
8
+ * header value is the request's session id, stable across continuation and
9
+ * restore. Actual cache reads remain an upstream usage fact. A request with no
10
10
  * session id gets a fresh random value rather than a shared constant, because
11
11
  * a constant would merge unrelated traffic into one cache bucket.
12
12
  *
@@ -91,7 +91,7 @@ export declare class OpencodeGoAdapter extends LlmAdapter {
91
91
  name: string;
92
92
  };
93
93
  listModels(_provider: string): Promise<readonly LlmModelInfo[]>;
94
- resolveModel(_provider: string, model: string, _signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
94
+ resolveModel(_provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
95
95
  /** Describe one model: capacities plus the reasoning levels it actually offers. */
96
96
  private modelInfo;
97
97
  /** Validate an explicit effort against the model's own levels, without clamping. */
@@ -12,8 +12,13 @@ export interface CatalogSnapshot {
12
12
  readonly unavailable: ReadonlyMap<string, string>;
13
13
  readonly provider: Provider;
14
14
  readonly live: boolean;
15
+ readonly metadataLive: boolean;
15
16
  /** Retained for explicit discovery; runtime callers may still use the last catalog. */
16
17
  readonly listingFailure?: unknown;
18
+ readonly metadataFailure?: unknown;
19
+ /** Last successful fetch or revalidation, not the time of a failed attempt. */
20
+ readonly listingUpdatedAtMs?: number;
21
+ readonly metadataUpdatedAtMs?: number;
17
22
  readonly fetchedAtMs: number;
18
23
  }
19
24
  /** A valid empty listing means the gateway serves nothing; malformed replies are failures. */
@@ -29,6 +34,9 @@ export declare class OpencodeGoCatalog {
29
34
  private pending;
30
35
  private metadata;
31
36
  private metadataETag;
37
+ private metadataUpdatedAtMs;
38
+ private failures;
39
+ private refreshAtMs;
32
40
  constructor(baseURL: string, refreshMs: number, onFallback: (detail: {
33
41
  url: string;
34
42
  error: unknown;
@@ -36,13 +44,13 @@ export declare class OpencodeGoCatalog {
36
44
  }) => void,
37
45
  /** Kept for API compatibility; now reports unconfigured ids rather than hiding them. */
38
46
  onOmitted: (ids: readonly string[]) => void);
39
- snapshot(force?: boolean): Promise<CatalogSnapshot>;
47
+ snapshot(force?: boolean, signal?: AbortSignal): Promise<CatalogSnapshot>;
40
48
  /** Conditional HTTP requests save bandwidth while still checking for updated metadata. */
41
49
  private refreshMetadata;
42
50
  /** Gateway ids decide membership; online metadata decides how to call each model. */
43
51
  private build;
44
52
  /** New or previously unconfigured ids get a fresh lookup even during the runtime TTL. */
45
- forModel(id: string): Promise<CatalogSnapshot>;
53
+ forModel(id: string, signal?: AbortSignal): Promise<CatalogSnapshot>;
46
54
  }
47
55
  /** Explicit discovery always revalidates both sources, including during the runtime TTL. */
48
56
  export declare function discoverCatalogModels(catalog: OpencodeGoCatalog): Promise<readonly LlmDiscoveredModel[]>;
@@ -0,0 +1,6 @@
1
+ import { type SessionEventSource } from './session-cache.ts';
2
+ export declare function SessionCache({ events, t, getLocale }: {
3
+ events: SessionEventSource;
4
+ t: (key: string) => string;
5
+ getLocale?: () => string;
6
+ }): import("react").JSX.Element;
@@ -1,11 +1,13 @@
1
1
  import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
2
2
  import type { ModelDirectoryState } from '@deepseek-ai/dsh-client-ui-model-selection/client';
3
3
  import type { GoUsage } from '../usage-contract.ts';
4
+ import type { SessionEventSource } from './session-cache.ts';
4
5
  export interface UsagePillProps {
5
6
  directory: SnapshotStore<ModelDirectoryState>;
6
7
  readUsage: () => Promise<GoUsage>;
7
8
  t: (key: string) => string;
8
9
  getLocale?: () => string;
10
+ sessionEvents?: SessionEventSource;
9
11
  }
10
12
  /** Only the selected Go provider mounts a poller, so other models send no usage traffic. */
11
13
  export declare function UsagePill({ directory, ...props }: UsagePillProps): import("react").JSX.Element | null;
@@ -1,5 +1,16 @@
1
1
  /** Copy dictionaries for the OpenCode Go settings page. English is the key-set source of truth. */
2
2
  export declare const en: {
3
+ cacheTitle: string;
4
+ cacheReadRatio: string;
5
+ cacheReadTokens: string;
6
+ cacheInputTokens: string;
7
+ cacheWriteTokens: string;
8
+ cacheResponses: string;
9
+ cacheEmpty: string;
10
+ cacheExcluded: string;
11
+ cacheScope: string;
12
+ cacheMore: string;
13
+ cacheRoutingHint: string;
3
14
  usageTitle: string;
4
15
  usageRollingShort: string;
5
16
  usageHint: string;
@@ -20,6 +31,8 @@ export declare const en: {
20
31
  nav: string;
21
32
  title: string;
22
33
  intro: string;
34
+ starProject: string;
35
+ starProjectTitle: string;
23
36
  enabledLabel: string;
24
37
  enabledHint: string;
25
38
  enabledOff: string;
@@ -34,6 +47,13 @@ export declare const en: {
34
47
  modelsLoading: string;
35
48
  modelsFailed: string;
36
49
  modelsStale: string;
50
+ modelsMetadataFailed: string;
51
+ modelsListingSource: string;
52
+ modelsMetadataSource: string;
53
+ modelsSourceFailed: string;
54
+ modelsSourceCurrent: string;
55
+ modelsSourceNeverFetched: string;
56
+ modelsSourceUpdated: string;
37
57
  modelsRefreshing: string;
38
58
  modelsEmpty: string;
39
59
  modelsCount: string;
@@ -51,6 +71,8 @@ export declare const en: {
51
71
  modelVisibleLabel: string;
52
72
  configurationMissing: string;
53
73
  configurationMissingHint: string;
74
+ configurationUnavailable: string;
75
+ configurationUnavailableHint: string;
54
76
  pickerFailed: string;
55
77
  deprecatedBadge: string;
56
78
  deprecatedHint: string;
@@ -67,11 +67,13 @@ export type OpencodeGoModels =
67
67
  readonly stale?: boolean;
68
68
  readonly message?: string;
69
69
  readonly refreshing?: boolean;
70
+ readonly sources?: GoModelCatalog['sources'];
70
71
  }
71
72
  /** The listing could not be read; `message` is the Host's own diagnostic. */
72
73
  | {
73
74
  readonly status: 'failed';
74
75
  readonly message?: string;
76
+ readonly sources?: GoModelCatalog['sources'];
75
77
  };
76
78
  /** What the settings page renders. */
77
79
  export interface OpencodeGoSectionState extends FormShell {
@@ -0,0 +1,18 @@
1
+ import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
2
+ /** Small structural face of the optional Host session event feed, across Host versions. */
3
+ export type SessionEventSource = ObservableSnapshot<unknown>;
4
+ export interface SessionCacheStats {
5
+ responses: number;
6
+ missingUsage: number;
7
+ unattributed: number;
8
+ inputTokens: number;
9
+ cacheReadTokens: number;
10
+ cacheWriteTokens: number;
11
+ hasMore: boolean;
12
+ }
13
+ /** Use only already-loaded history; never open a session or fetch more pages for diagnostics. */
14
+ export declare function sessionEventSource(sessions: unknown, sessionId: string): SessionEventSource | undefined;
15
+ /** Aggregate settled Go responses. Pi-ai's input excludes cache reads AND writes. */
16
+ export declare function summarizeSessionCache(window: unknown): SessionCacheStats;
17
+ /** Stable snapshots avoid rendering on unrelated live chunks; history replacement recomputes. */
18
+ export declare function sessionCacheSource(events: SessionEventSource): ObservableSnapshot<SessionCacheStats>;
@@ -11,7 +11,7 @@
11
11
  import z from '@deepseek-ai/schemastery';
12
12
  /** Environment variable resolving the OpenCode API key. */
13
13
  export declare const DEFAULT_API_KEY_ENV = "OPENCODE_API_KEY";
14
- /** Requests and pickers share this cache lifetime; explicit discovery revalidates immediately. */
14
+ /** Successful refresh lifetime; failed refreshes retry sooner and explicit discovery revalidates immediately. */
15
15
  export declare const DEFAULT_REFRESH_MINUTES = 60;
16
16
  /** Default maximum idle interval while a stream read is outstanding. */
17
17
  export declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300000;
@@ -10,11 +10,21 @@ export interface GoModel {
10
10
  /** Advertised by the gateway but lacking a usable protocol and capability configuration. */
11
11
  configurationMissing?: boolean;
12
12
  }
13
- /** A failed refresh retains the Host's last successful listing with an explicit diagnostic. */
13
+ /** Last successful check of one catalog source, plus any current refresh failure. */
14
+ export interface GoCatalogSourceStatus {
15
+ readonly updatedAt?: number;
16
+ readonly error?: string;
17
+ }
18
+ /** A failed refresh retains the Host's usable data with an explicit diagnostic. */
14
19
  export interface GoModelCatalog {
15
20
  readonly models: readonly GoModel[];
16
21
  readonly stale: boolean;
17
22
  readonly error?: string;
23
+ /** Optional for clients reading an older Host response. Timestamps are Unix milliseconds. */
24
+ readonly sources?: {
25
+ readonly listing: GoCatalogSourceStatus;
26
+ readonly metadata: GoCatalogSourceStatus;
27
+ };
18
28
  }
19
29
  /** Missing configuration cannot be enabled; configured models follow explicit switches or lifecycle defaults. */
20
30
  export declare function isModelEnabled(model: Pick<GoModel, 'id' | 'deprecated' | 'configurationMissing'>, modelVisibility?: Readonly<Record<string, boolean>>): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-opencode-go",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "OpenCode Go model provider and settings UI for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "engines": {
16
16
  "node": "^22.19.0 || >=24.0.0",
17
- "dsh": ">=0.1.5-rc.1 <0.1.6-0 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2"
17
+ "dsh": ">=0.1.5-rc.1"
18
18
  },
19
19
  "main": "./lib/index.js",
20
20
  "types": "./lib/types/index.d.ts",
@@ -60,26 +60,26 @@
60
60
  "verify:headless": "node scripts/headless-smoke.mjs"
61
61
  },
62
62
  "peerDependencies": {
63
- "@deepseek-ai/cordis": "4.0.2 || 4.0.3 || 4.0.4",
64
- "@deepseek-ai/dsh-api-gateway": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
65
- "@deepseek-ai/dsh-api-remotes": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
66
- "@deepseek-ai/dsh-api-settings-controller": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
67
- "@deepseek-ai/dsh-attachment": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
68
- "@deepseek-ai/dsh-client-locale": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
69
- "@deepseek-ai/dsh-client-store": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
70
- "@deepseek-ai/dsh-client-ui-conversation": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
71
- "@deepseek-ai/dsh-client-ui-model-selection": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
72
- "@deepseek-ai/dsh-client-ui-renderer": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
73
- "@deepseek-ai/dsh-client-ui-settings": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
74
- "@deepseek-ai/dsh-client-ui-slots": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
75
- "@deepseek-ai/dsh-credentials": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
76
- "@deepseek-ai/dsh-fs": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
77
- "@deepseek-ai/dsh-launch-environment": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
78
- "@deepseek-ai/dsh-llm": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
79
- "@deepseek-ai/dsh-settings": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
80
- "@deepseek-ai/dsh-timeout": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
81
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2",
82
- "@deepseek-ai/dsh-typert-registry": ">=0.1.5-rc.1 <0.1.6 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1 || 0.1.7-alpha.2"
63
+ "@deepseek-ai/cordis": ">=4.0.2",
64
+ "@deepseek-ai/dsh-api-gateway": ">=0.1.5-rc.1",
65
+ "@deepseek-ai/dsh-api-remotes": ">=0.1.5-rc.1",
66
+ "@deepseek-ai/dsh-api-settings-controller": ">=0.1.5-rc.1",
67
+ "@deepseek-ai/dsh-attachment": ">=0.1.5-rc.1",
68
+ "@deepseek-ai/dsh-client-locale": ">=0.1.5-rc.1",
69
+ "@deepseek-ai/dsh-client-store": ">=0.1.5-rc.1",
70
+ "@deepseek-ai/dsh-client-ui-conversation": ">=0.1.5-rc.1",
71
+ "@deepseek-ai/dsh-client-ui-model-selection": ">=0.1.5-rc.1",
72
+ "@deepseek-ai/dsh-client-ui-renderer": ">=0.1.5-rc.1",
73
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.1.5-rc.1",
74
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.1.5-rc.1",
75
+ "@deepseek-ai/dsh-credentials": ">=0.1.5-rc.1",
76
+ "@deepseek-ai/dsh-fs": ">=0.1.5-rc.1",
77
+ "@deepseek-ai/dsh-launch-environment": ">=0.1.5-rc.1",
78
+ "@deepseek-ai/dsh-llm": ">=0.1.5-rc.1",
79
+ "@deepseek-ai/dsh-settings": ">=0.1.5-rc.1",
80
+ "@deepseek-ai/dsh-timeout": ">=0.1.5-rc.1",
81
+ "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-rc.1",
82
+ "@deepseek-ai/dsh-typert-registry": ">=0.1.5-rc.1"
83
83
  },
84
84
  "dependencies": {
85
85
  "@deepseek-ai/dsh-brand": "0.1.6-alpha.1",
@@ -110,6 +110,7 @@
110
110
  "@deepseek-ai/dsh-home-paths": "0.1.6-alpha.1",
111
111
  "@deepseek-ai/dsh-launch-environment": "0.1.6-alpha.1",
112
112
  "@deepseek-ai/dsh-llm": "0.1.6-alpha.1",
113
+ "@deepseek-ai/dsh-session": "0.1.6-alpha.1",
113
114
  "@deepseek-ai/dsh-settings": "0.1.6-alpha.1",
114
115
  "@deepseek-ai/dsh-settings-file": "0.1.6-alpha.1",
115
116
  "@deepseek-ai/dsh-timeout": "0.1.6-alpha.1",