@topolo/sdk 0.12.0 → 0.13.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/dist/index.cjs CHANGED
@@ -1229,16 +1229,29 @@ var TopoloClient = class _TopoloClient {
1229
1229
  clearTimeout(timeoutId);
1230
1230
  }
1231
1231
  };
1232
+ const fetchWithReadRetry = async () => {
1233
+ const maxAttempts = method === "GET" ? 2 : 1;
1234
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
1235
+ try {
1236
+ return await fetchOnce();
1237
+ } catch (error) {
1238
+ const transportFailure = !(error instanceof TopoloSdkError) && !opts.signal?.aborted;
1239
+ if (!transportFailure || attempt === maxAttempts) throw error;
1240
+ await new Promise((resolve) => setTimeout(resolve, 250));
1241
+ }
1242
+ }
1243
+ throw new TopoloSdkError("network_error", "Topolo read retry loop exhausted.");
1244
+ };
1232
1245
  let res;
1233
1246
  try {
1234
1247
  const attemptedCredential = this.credential;
1235
- res = await fetchOnce();
1248
+ res = await fetchWithReadRetry();
1236
1249
  if (res.status === 401 && attemptedCredential.kind === "access_token" && this.refreshCredential) {
1237
1250
  await res.body?.cancel().catch(() => void 0);
1238
1251
  const refreshed = await this.refreshRejectedAccessToken(attemptedCredential.accessToken);
1239
1252
  if (refreshed) {
1240
1253
  applyAuthHeaders(headers, refreshed);
1241
- res = await fetchOnce();
1254
+ res = await fetchWithReadRetry();
1242
1255
  }
1243
1256
  }
1244
1257
  } catch (err) {
@@ -1890,26 +1903,29 @@ var TopoloClient = class _TopoloClient {
1890
1903
  };
1891
1904
  function validateActionResource(entry, resource) {
1892
1905
  const permissionName = entry.permissionName || entry.requiredPermission.split(".").at(-1) || "";
1893
- if (!permissionName.startsWith("workspace:")) return null;
1906
+ const declaredContext = entry.agentContract?.resourceContext;
1907
+ const declaredKind = typeof declaredContext?.kind === "string" ? declaredContext.kind.trim() : "";
1908
+ const expectedType = declaredKind || (permissionName.startsWith("workspace:") ? "workspace" : "");
1909
+ if (!expectedType || declaredContext?.required === false && !permissionName.startsWith("workspace:")) return null;
1894
1910
  if (!resource) {
1895
1911
  return {
1896
1912
  path: "$.resource",
1897
1913
  code: "required",
1898
- message: "Select a workspace with the application resource catalog before planning or calling this action."
1914
+ message: `Select resourceType "${expectedType}" with the application resource catalog before planning or calling this action.`
1899
1915
  };
1900
1916
  }
1901
- if (resource.resourceType !== "workspace") {
1917
+ if (resource.resourceType !== expectedType) {
1902
1918
  return {
1903
1919
  path: "$.resource.resourceType",
1904
1920
  code: "const",
1905
- message: 'This action requires resourceType "workspace".'
1921
+ message: `This action requires resourceType "${expectedType}".`
1906
1922
  };
1907
1923
  }
1908
1924
  if (!resource.resourceId.trim()) {
1909
1925
  return {
1910
1926
  path: "$.resource.resourceId",
1911
1927
  code: "required",
1912
- message: "This action requires a workspace resourceId."
1928
+ message: `This action requires a ${expectedType} resourceId.`
1913
1929
  };
1914
1930
  }
1915
1931
  return null;
@@ -2283,7 +2299,8 @@ function compactActionCatalogEntry(action) {
2283
2299
  agentAccess: action.agentAccess ?? (action.requiresConfirmation ? "confirm" : action.readOnly ? "auto" : "confirm"),
2284
2300
  requiredPermission: action.requiredPermission,
2285
2301
  requiredInput: required,
2286
- resourceType: action.agentContract?.resource?.kind ?? null,
2302
+ resourceType: action.agentContract?.resourceContext?.kind ?? (action.permissionName.startsWith("workspace:") ? "workspace" : null),
2303
+ resultResourceType: action.agentContract?.resource?.kind ?? null,
2287
2304
  exampleInput: example.value,
2288
2305
  exampleInputAvailable: example.available,
2289
2306
  docsUrl: action.agentContract?.docsUrl ?? null
@@ -3494,6 +3511,8 @@ function validateLocalization(value, path, errors) {
3494
3511
  nonEmptyString(value.locale, `${path}.locale`, errors);
3495
3512
  nonEmptyString(value.titleKey, `${path}.titleKey`, errors);
3496
3513
  optionalNonEmptyString(value.descriptionKey, `${path}.descriptionKey`, errors);
3514
+ optionalNonEmptyString(value.title, `${path}.title`, errors);
3515
+ optionalNonEmptyString(value.description, `${path}.description`, errors);
3497
3516
  }
3498
3517
  function validateAccessibility(value, path, errors) {
3499
3518
  if (!recordValue(value, path, errors)) return;
package/dist/index.d.cts CHANGED
@@ -254,6 +254,10 @@ interface TopoloActionCatalogEntry {
254
254
  }
255
255
  interface TopoloActionAgentContract {
256
256
  docsUrl?: string;
257
+ resourceContext?: {
258
+ kind?: string;
259
+ required?: boolean;
260
+ };
257
261
  resource?: {
258
262
  kind?: string;
259
263
  idPath?: string;
@@ -513,6 +517,7 @@ declare function compactActionCatalogEntry(action: TopoloActionCatalogEntry): {
513
517
  requiredPermission: string;
514
518
  requiredInput: string[];
515
519
  resourceType: string | null;
520
+ resultResourceType: string | null;
516
521
  exampleInput: Record<string, unknown> | null;
517
522
  exampleInputAvailable: boolean;
518
523
  docsUrl: string | null;
@@ -1325,6 +1330,16 @@ interface TopoloWidgetLocalization {
1325
1330
  locale: string;
1326
1331
  titleKey: string;
1327
1332
  descriptionKey?: string;
1333
+ /**
1334
+ * English source text for `titleKey`. Hosts already fall back to their own
1335
+ * title when the key misses, so this changes no rendering — it exists so the
1336
+ * key HAS a source string: the i18n extractor reads a sibling literal named
1337
+ * after the key's field, and without one the key never reaches a catalog and
1338
+ * cannot be translated into any locale.
1339
+ */
1340
+ title?: string;
1341
+ /** English source text for `descriptionKey`. See `title`. */
1342
+ description?: string;
1328
1343
  }
1329
1344
  interface TopoloWidgetAccessibility {
1330
1345
  label: string;
package/dist/index.d.ts CHANGED
@@ -254,6 +254,10 @@ interface TopoloActionCatalogEntry {
254
254
  }
255
255
  interface TopoloActionAgentContract {
256
256
  docsUrl?: string;
257
+ resourceContext?: {
258
+ kind?: string;
259
+ required?: boolean;
260
+ };
257
261
  resource?: {
258
262
  kind?: string;
259
263
  idPath?: string;
@@ -513,6 +517,7 @@ declare function compactActionCatalogEntry(action: TopoloActionCatalogEntry): {
513
517
  requiredPermission: string;
514
518
  requiredInput: string[];
515
519
  resourceType: string | null;
520
+ resultResourceType: string | null;
516
521
  exampleInput: Record<string, unknown> | null;
517
522
  exampleInputAvailable: boolean;
518
523
  docsUrl: string | null;
@@ -1325,6 +1330,16 @@ interface TopoloWidgetLocalization {
1325
1330
  locale: string;
1326
1331
  titleKey: string;
1327
1332
  descriptionKey?: string;
1333
+ /**
1334
+ * English source text for `titleKey`. Hosts already fall back to their own
1335
+ * title when the key misses, so this changes no rendering — it exists so the
1336
+ * key HAS a source string: the i18n extractor reads a sibling literal named
1337
+ * after the key's field, and without one the key never reaches a catalog and
1338
+ * cannot be translated into any locale.
1339
+ */
1340
+ title?: string;
1341
+ /** English source text for `descriptionKey`. See `title`. */
1342
+ description?: string;
1328
1343
  }
1329
1344
  interface TopoloWidgetAccessibility {
1330
1345
  label: string;
package/dist/index.js CHANGED
@@ -1145,16 +1145,29 @@ var TopoloClient = class _TopoloClient {
1145
1145
  clearTimeout(timeoutId);
1146
1146
  }
1147
1147
  };
1148
+ const fetchWithReadRetry = async () => {
1149
+ const maxAttempts = method === "GET" ? 2 : 1;
1150
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
1151
+ try {
1152
+ return await fetchOnce();
1153
+ } catch (error) {
1154
+ const transportFailure = !(error instanceof TopoloSdkError) && !opts.signal?.aborted;
1155
+ if (!transportFailure || attempt === maxAttempts) throw error;
1156
+ await new Promise((resolve) => setTimeout(resolve, 250));
1157
+ }
1158
+ }
1159
+ throw new TopoloSdkError("network_error", "Topolo read retry loop exhausted.");
1160
+ };
1148
1161
  let res;
1149
1162
  try {
1150
1163
  const attemptedCredential = this.credential;
1151
- res = await fetchOnce();
1164
+ res = await fetchWithReadRetry();
1152
1165
  if (res.status === 401 && attemptedCredential.kind === "access_token" && this.refreshCredential) {
1153
1166
  await res.body?.cancel().catch(() => void 0);
1154
1167
  const refreshed = await this.refreshRejectedAccessToken(attemptedCredential.accessToken);
1155
1168
  if (refreshed) {
1156
1169
  applyAuthHeaders(headers, refreshed);
1157
- res = await fetchOnce();
1170
+ res = await fetchWithReadRetry();
1158
1171
  }
1159
1172
  }
1160
1173
  } catch (err) {
@@ -1806,26 +1819,29 @@ var TopoloClient = class _TopoloClient {
1806
1819
  };
1807
1820
  function validateActionResource(entry, resource) {
1808
1821
  const permissionName = entry.permissionName || entry.requiredPermission.split(".").at(-1) || "";
1809
- if (!permissionName.startsWith("workspace:")) return null;
1822
+ const declaredContext = entry.agentContract?.resourceContext;
1823
+ const declaredKind = typeof declaredContext?.kind === "string" ? declaredContext.kind.trim() : "";
1824
+ const expectedType = declaredKind || (permissionName.startsWith("workspace:") ? "workspace" : "");
1825
+ if (!expectedType || declaredContext?.required === false && !permissionName.startsWith("workspace:")) return null;
1810
1826
  if (!resource) {
1811
1827
  return {
1812
1828
  path: "$.resource",
1813
1829
  code: "required",
1814
- message: "Select a workspace with the application resource catalog before planning or calling this action."
1830
+ message: `Select resourceType "${expectedType}" with the application resource catalog before planning or calling this action.`
1815
1831
  };
1816
1832
  }
1817
- if (resource.resourceType !== "workspace") {
1833
+ if (resource.resourceType !== expectedType) {
1818
1834
  return {
1819
1835
  path: "$.resource.resourceType",
1820
1836
  code: "const",
1821
- message: 'This action requires resourceType "workspace".'
1837
+ message: `This action requires resourceType "${expectedType}".`
1822
1838
  };
1823
1839
  }
1824
1840
  if (!resource.resourceId.trim()) {
1825
1841
  return {
1826
1842
  path: "$.resource.resourceId",
1827
1843
  code: "required",
1828
- message: "This action requires a workspace resourceId."
1844
+ message: `This action requires a ${expectedType} resourceId.`
1829
1845
  };
1830
1846
  }
1831
1847
  return null;
@@ -2199,7 +2215,8 @@ function compactActionCatalogEntry(action) {
2199
2215
  agentAccess: action.agentAccess ?? (action.requiresConfirmation ? "confirm" : action.readOnly ? "auto" : "confirm"),
2200
2216
  requiredPermission: action.requiredPermission,
2201
2217
  requiredInput: required,
2202
- resourceType: action.agentContract?.resource?.kind ?? null,
2218
+ resourceType: action.agentContract?.resourceContext?.kind ?? (action.permissionName.startsWith("workspace:") ? "workspace" : null),
2219
+ resultResourceType: action.agentContract?.resource?.kind ?? null,
2203
2220
  exampleInput: example.value,
2204
2221
  exampleInputAvailable: example.available,
2205
2222
  docsUrl: action.agentContract?.docsUrl ?? null
@@ -3410,6 +3427,8 @@ function validateLocalization(value, path, errors) {
3410
3427
  nonEmptyString(value.locale, `${path}.locale`, errors);
3411
3428
  nonEmptyString(value.titleKey, `${path}.titleKey`, errors);
3412
3429
  optionalNonEmptyString(value.descriptionKey, `${path}.descriptionKey`, errors);
3430
+ optionalNonEmptyString(value.title, `${path}.title`, errors);
3431
+ optionalNonEmptyString(value.description, `${path}.description`, errors);
3413
3432
  }
3414
3433
  function validateAccessibility(value, path, errors) {
3415
3434
  if (!recordValue(value, path, errors)) return;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@topolo/sdk",
3
- "version": "0.12.0",
4
- "topoloPublishedVersion": "0.12.0",
3
+ "version": "0.13.0",
4
+ "topoloPublishedVersion": "0.13.0",
5
5
  "description": "Typed client SDK for the Topolo platform. Used by TopoloCli, TopoloMCP, and third-party agents.",
6
6
  "license": "UNLICENSED",
7
7
  "author": "Topolo",