@topolo/sdk 0.12.0 → 0.14.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
@@ -3304,7 +3321,7 @@ var TopoloOAuth = class {
3304
3321
  };
3305
3322
 
3306
3323
  // src/widget-validation.ts
3307
- var CONTRACT_VERSION = "2.0.0";
3324
+ var CONTRACT_VERSION = "3.0.0";
3308
3325
  var WIDGET_TYPES = ["stats", "chart", "activity", "notifications", "actions"];
3309
3326
  var CONTEXTS = ["personal", "organization", "workspace", "resource"];
3310
3327
  var SIZES = ["compact", "medium", "wide", "full"];
@@ -3315,6 +3332,9 @@ var AGGREGATIONS = ["last", "sum", "average", "minimum", "maximum", "count", "di
3315
3332
  var CHART_TYPES = ["line", "bar", "area", "pie", "donut"];
3316
3333
  var SEVERITIES = ["info", "warning", "error", "success"];
3317
3334
  var RECOVERY_MODES = ["not_applicable", "retry", "compensating_action"];
3335
+ var VISUALIZATION_FAMILIES = ["number", "line", "area", "bar", "donut", "table", "funnel", "timeline", "actions"];
3336
+ var WORKSPACE_SELECTOR_MODES = ["none", "optional", "required"];
3337
+ var DIMENSION_VALUE_TYPES = ["string", "number", "boolean", "date", "datetime"];
3318
3338
  var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
3319
3339
  function validateTopoloWidgetResponse(response) {
3320
3340
  const errors = [];
@@ -3365,6 +3385,7 @@ function validateWidget(value, path, errors) {
3365
3385
  validateActions(value.controls, `${path}.controls`, errors);
3366
3386
  validateLocalization(value.localization, `${path}.localization`, errors);
3367
3387
  validateAccessibility(value.accessibility, `${path}.accessibility`, errors);
3388
+ validateContribution(value.contribution, `${path}.contribution`, value, errors);
3368
3389
  switch (value.type) {
3369
3390
  case "stats":
3370
3391
  validateMetrics(value.metrics, `${path}.metrics`, errors, requiresContent(value));
@@ -3494,12 +3515,216 @@ function validateLocalization(value, path, errors) {
3494
3515
  nonEmptyString(value.locale, `${path}.locale`, errors);
3495
3516
  nonEmptyString(value.titleKey, `${path}.titleKey`, errors);
3496
3517
  optionalNonEmptyString(value.descriptionKey, `${path}.descriptionKey`, errors);
3518
+ optionalNonEmptyString(value.title, `${path}.title`, errors);
3519
+ optionalNonEmptyString(value.description, `${path}.description`, errors);
3497
3520
  }
3498
3521
  function validateAccessibility(value, path, errors) {
3499
3522
  if (!recordValue(value, path, errors)) return;
3500
3523
  nonEmptyString(value.label, `${path}.label`, errors);
3501
3524
  optionalNonEmptyString(value.description, `${path}.description`, errors);
3502
3525
  }
3526
+ function validateContribution(value, path, widget, errors) {
3527
+ if (!recordValue(value, path, errors)) return;
3528
+ nonEmptyString(value.summary, `${path}.summary`, errors);
3529
+ if (recordValue(value.identity, `${path}.identity`, errors)) {
3530
+ exactValue(value.identity.iconSource, "catalog", `${path}.identity.iconSource`, errors);
3531
+ }
3532
+ if (recordValue(value.workspaceSelector, `${path}.workspaceSelector`, errors)) {
3533
+ enumValue(value.workspaceSelector.mode, WORKSPACE_SELECTOR_MODES, `${path}.workspaceSelector.mode`, errors);
3534
+ nonEmptyString(value.workspaceSelector.resourceType, `${path}.workspaceSelector.resourceType`, errors);
3535
+ nonEmptyString(value.workspaceSelector.label, `${path}.workspaceSelector.label`, errors);
3536
+ booleanValue(value.workspaceSelector.supportsMultiple, `${path}.workspaceSelector.supportsMultiple`, errors);
3537
+ if (value.workspaceSelector.mode === "required" && requiresContent(widget) && isRecord5(widget.resource) && typeof widget.resource.resourceId !== "string") {
3538
+ errors.push(`${path}.workspaceSelector requires a bound resourceId for ready or stale data`);
3539
+ }
3540
+ }
3541
+ const metricKeys = validateMetricDefinitions(value.metricDefinitions, `${path}.metricDefinitions`, errors);
3542
+ const seriesKeys = validateSeriesDefinitions(value.seriesDefinitions, `${path}.seriesDefinitions`, metricKeys, errors);
3543
+ const dimensionKeys = validateDimensionDefinitions(value.dimensions, `${path}.dimensions`, errors);
3544
+ const lifecycleKeys = validateLifecycleStages(value.lifecycleStages, `${path}.lifecycleStages`, errors);
3545
+ const eventKeys = validateEventDefinitions(value.events, `${path}.events`, errors);
3546
+ const actionKeys = widgetActionKeys(widget);
3547
+ validateDatasets(value.datasets, `${path}.datasets`, {
3548
+ metricKeys,
3549
+ seriesKeys,
3550
+ dimensionKeys,
3551
+ eventKeys,
3552
+ actionKeys
3553
+ }, errors);
3554
+ enumArray(
3555
+ value.compatibleVisualizations,
3556
+ VISUALIZATION_FAMILIES,
3557
+ `${path}.compatibleVisualizations`,
3558
+ errors,
3559
+ true
3560
+ );
3561
+ enumValue(value.defaultVisualization, VISUALIZATION_FAMILIES, `${path}.defaultVisualization`, errors);
3562
+ if (typeof value.defaultVisualization === "string" && Array.isArray(value.compatibleVisualizations) && !value.compatibleVisualizations.includes(value.defaultVisualization)) {
3563
+ errors.push(`${path}.defaultVisualization must be included in compatibleVisualizations`);
3564
+ }
3565
+ if (Array.isArray(value.compatibleVisualizations)) {
3566
+ for (const family of value.compatibleVisualizations) {
3567
+ if (family === "number" && metricKeys.size === 0) errors.push(`${path}.compatibleVisualizations number requires a metric definition`);
3568
+ if ((family === "line" || family === "area") && seriesKeys.size === 0) errors.push(`${path}.compatibleVisualizations ${family} requires a series definition`);
3569
+ if (family === "donut" && (metricKeys.size === 0 || dimensionKeys.size === 0)) errors.push(`${path}.compatibleVisualizations donut requires metric and dimension definitions`);
3570
+ if (family === "funnel" && lifecycleKeys.size < 2) errors.push(`${path}.compatibleVisualizations funnel requires at least two lifecycle stages`);
3571
+ if (family === "timeline" && eventKeys.size === 0) errors.push(`${path}.compatibleVisualizations timeline requires an event definition`);
3572
+ if (family === "actions" && actionKeys.size === 0) errors.push(`${path}.compatibleVisualizations actions requires a published action`);
3573
+ if (family === "bar" && metricKeys.size === 0 && seriesKeys.size === 0) errors.push(`${path}.compatibleVisualizations bar requires a metric or series definition`);
3574
+ }
3575
+ }
3576
+ validateRuntimeSemantics(widget, path, { metricKeys, seriesKeys, dimensionKeys }, errors);
3577
+ }
3578
+ function validateMetricDefinitions(value, path, errors) {
3579
+ const keys = /* @__PURE__ */ new Set();
3580
+ if (!arrayValue(value, path, errors)) return keys;
3581
+ value.forEach((definition, index) => {
3582
+ const itemPath = `${path}[${index}]`;
3583
+ if (!recordValue(definition, itemPath, errors)) return;
3584
+ nonEmptyString(definition.key, `${itemPath}.key`, errors);
3585
+ nonEmptyString(definition.label, `${itemPath}.label`, errors);
3586
+ nonEmptyString(definition.description, `${itemPath}.description`, errors);
3587
+ nonEmptyString(definition.unit, `${itemPath}.unit`, errors);
3588
+ enumValue(definition.semantic, SEMANTICS, `${itemPath}.semantic`, errors);
3589
+ enumValue(definition.aggregation, AGGREGATIONS, `${itemPath}.aggregation`, errors);
3590
+ addUniqueKey(definition.key, itemPath, keys, errors);
3591
+ });
3592
+ return keys;
3593
+ }
3594
+ function validateSeriesDefinitions(value, path, metricKeys, errors) {
3595
+ const keys = /* @__PURE__ */ new Set();
3596
+ if (!arrayValue(value, path, errors)) return keys;
3597
+ value.forEach((definition, index) => {
3598
+ const itemPath = `${path}[${index}]`;
3599
+ if (!recordValue(definition, itemPath, errors)) return;
3600
+ nonEmptyString(definition.key, `${itemPath}.key`, errors);
3601
+ nonEmptyString(definition.label, `${itemPath}.label`, errors);
3602
+ nonEmptyString(definition.metricKey, `${itemPath}.metricKey`, errors);
3603
+ nonEmptyString(definition.unit, `${itemPath}.unit`, errors);
3604
+ addUniqueKey(definition.key, itemPath, keys, errors);
3605
+ if (typeof definition.metricKey === "string" && !metricKeys.has(definition.metricKey)) {
3606
+ errors.push(`${itemPath}.metricKey must reference a declared metric definition`);
3607
+ }
3608
+ });
3609
+ return keys;
3610
+ }
3611
+ function validateDimensionDefinitions(value, path, errors) {
3612
+ const keys = /* @__PURE__ */ new Set();
3613
+ if (!arrayValue(value, path, errors)) return keys;
3614
+ value.forEach((definition, index) => {
3615
+ const itemPath = `${path}[${index}]`;
3616
+ if (!recordValue(definition, itemPath, errors)) return;
3617
+ nonEmptyString(definition.key, `${itemPath}.key`, errors);
3618
+ nonEmptyString(definition.label, `${itemPath}.label`, errors);
3619
+ nonEmptyString(definition.description, `${itemPath}.description`, errors);
3620
+ enumValue(definition.valueType, DIMENSION_VALUE_TYPES, `${itemPath}.valueType`, errors);
3621
+ addUniqueKey(definition.key, itemPath, keys, errors);
3622
+ });
3623
+ return keys;
3624
+ }
3625
+ function validateLifecycleStages(value, path, errors) {
3626
+ const keys = /* @__PURE__ */ new Set();
3627
+ const orders = /* @__PURE__ */ new Set();
3628
+ if (!arrayValue(value, path, errors)) return keys;
3629
+ value.forEach((stage, index) => {
3630
+ const itemPath = `${path}[${index}]`;
3631
+ if (!recordValue(stage, itemPath, errors)) return;
3632
+ nonEmptyString(stage.key, `${itemPath}.key`, errors);
3633
+ nonEmptyString(stage.label, `${itemPath}.label`, errors);
3634
+ nonNegativeInteger(stage.order, `${itemPath}.order`, errors);
3635
+ booleanValue(stage.terminal, `${itemPath}.terminal`, errors);
3636
+ addUniqueKey(stage.key, itemPath, keys, errors);
3637
+ if (typeof stage.order === "number") {
3638
+ if (orders.has(stage.order)) errors.push(`${itemPath}.order must be unique`);
3639
+ orders.add(stage.order);
3640
+ }
3641
+ });
3642
+ return keys;
3643
+ }
3644
+ function validateEventDefinitions(value, path, errors) {
3645
+ const keys = /* @__PURE__ */ new Set();
3646
+ if (!arrayValue(value, path, errors)) return keys;
3647
+ value.forEach((event, index) => {
3648
+ const itemPath = `${path}[${index}]`;
3649
+ if (!recordValue(event, itemPath, errors)) return;
3650
+ nonEmptyString(event.key, `${itemPath}.key`, errors);
3651
+ nonEmptyString(event.label, `${itemPath}.label`, errors);
3652
+ nonEmptyString(event.description, `${itemPath}.description`, errors);
3653
+ addUniqueKey(event.key, itemPath, keys, errors);
3654
+ });
3655
+ return keys;
3656
+ }
3657
+ function validateDatasets(value, path, references, errors) {
3658
+ const ids = /* @__PURE__ */ new Set();
3659
+ if (!arrayValue(value, path, errors, true)) return;
3660
+ value.forEach((dataset, index) => {
3661
+ const itemPath = `${path}[${index}]`;
3662
+ if (!recordValue(dataset, itemPath, errors)) return;
3663
+ nonEmptyString(dataset.id, `${itemPath}.id`, errors);
3664
+ nonEmptyString(dataset.label, `${itemPath}.label`, errors);
3665
+ nonEmptyString(dataset.description, `${itemPath}.description`, errors);
3666
+ nonEmptyString(dataset.grain, `${itemPath}.grain`, errors);
3667
+ addUniqueKey(dataset.id, itemPath, ids, errors);
3668
+ const fields = ["metricKeys", "seriesKeys", "dimensionKeys", "eventKeys", "actionIds"];
3669
+ for (const field of fields) stringArray2(dataset[field], `${itemPath}.${field}`, errors);
3670
+ const totalReferences = fields.reduce((total, field) => total + (Array.isArray(dataset[field]) ? dataset[field].length : 0), 0);
3671
+ if (totalReferences === 0) errors.push(`${itemPath} must reference at least one declared metric, series, dimension, event, or action`);
3672
+ validateReferences(dataset.metricKeys, references.metricKeys, `${itemPath}.metricKeys`, errors);
3673
+ validateReferences(dataset.seriesKeys, references.seriesKeys, `${itemPath}.seriesKeys`, errors);
3674
+ validateReferences(dataset.dimensionKeys, references.dimensionKeys, `${itemPath}.dimensionKeys`, errors);
3675
+ validateReferences(dataset.eventKeys, references.eventKeys, `${itemPath}.eventKeys`, errors);
3676
+ validateReferences(dataset.actionIds, references.actionKeys, `${itemPath}.actionIds`, errors);
3677
+ });
3678
+ }
3679
+ function validateReferences(value, known, path, errors) {
3680
+ if (!Array.isArray(value)) return;
3681
+ for (const reference of value) {
3682
+ if (typeof reference === "string" && !known.has(reference)) errors.push(`${path} references undeclared key ${reference}`);
3683
+ }
3684
+ }
3685
+ function widgetActionKeys(widget) {
3686
+ const keys = /* @__PURE__ */ new Set();
3687
+ for (const collection of [widget.controls, widget.actions]) {
3688
+ if (!Array.isArray(collection)) continue;
3689
+ for (const action of collection) if (isRecord5(action) && typeof action.id === "string") keys.add(action.id);
3690
+ }
3691
+ return keys;
3692
+ }
3693
+ function validateRuntimeSemantics(widget, contributionPath, declared, errors) {
3694
+ const metrics = Array.isArray(widget.metrics) ? widget.metrics : [];
3695
+ for (const metric of metrics) {
3696
+ if (!isRecord5(metric) || typeof metric.key !== "string") continue;
3697
+ if (!declared.metricKeys.has(metric.key)) errors.push(`${contributionPath}.metricDefinitions is missing runtime metric ${metric.key}`);
3698
+ validateRuntimeDimensions(metric.dimensions, declared.dimensionKeys, contributionPath, errors);
3699
+ if (isRecord5(metric.series)) {
3700
+ if (typeof metric.series.id === "string" && !declared.seriesKeys.has(metric.series.id)) errors.push(`${contributionPath}.seriesDefinitions is missing runtime series ${metric.series.id}`);
3701
+ validateSeriesDimensions(metric.series, declared.dimensionKeys, contributionPath, errors);
3702
+ }
3703
+ }
3704
+ const series = Array.isArray(widget.series) ? widget.series : [];
3705
+ for (const item of series) {
3706
+ if (!isRecord5(item)) continue;
3707
+ if (typeof item.id === "string" && !declared.seriesKeys.has(item.id)) errors.push(`${contributionPath}.seriesDefinitions is missing runtime series ${item.id}`);
3708
+ validateSeriesDimensions(item, declared.dimensionKeys, contributionPath, errors);
3709
+ }
3710
+ }
3711
+ function validateSeriesDimensions(series, known, path, errors) {
3712
+ if (!Array.isArray(series.points)) return;
3713
+ for (const point of series.points) if (isRecord5(point)) validateRuntimeDimensions(point.dimensions, known, path, errors);
3714
+ }
3715
+ function validateRuntimeDimensions(value, known, path, errors) {
3716
+ if (!Array.isArray(value)) return;
3717
+ for (const dimension of value) {
3718
+ if (isRecord5(dimension) && typeof dimension.key === "string" && !known.has(dimension.key)) {
3719
+ errors.push(`${path}.dimensions is missing runtime dimension ${dimension.key}`);
3720
+ }
3721
+ }
3722
+ }
3723
+ function addUniqueKey(value, path, keys, errors) {
3724
+ if (typeof value !== "string" || value.trim().length === 0) return;
3725
+ if (keys.has(value)) errors.push(`${path}.key must be unique`);
3726
+ keys.add(value);
3727
+ }
3503
3728
  function validateMetrics(value, path, errors, requireOne) {
3504
3729
  if (!arrayValue(value, path, errors, requireOne)) return;
3505
3730
  value.forEach((metric, index) => {
@@ -3667,7 +3892,7 @@ function isRecord5(value) {
3667
3892
  // src/widgets.ts
3668
3893
  var TOPOLO_WIDGET_ENDPOINT = "/api/widget";
3669
3894
  var TOPOLO_WIDGET_REFRESH_INTERVAL_MS = 6e4;
3670
- var TOPOLO_WIDGET_CONTRACT_VERSION = "2.0.0";
3895
+ var TOPOLO_WIDGET_CONTRACT_VERSION = "3.0.0";
3671
3896
  function defineTopoloStatsWidget(widget) {
3672
3897
  return { ...widget, type: "stats" };
3673
3898
  }
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;
@@ -1252,7 +1257,7 @@ declare function validateTopoloWidgetResponse(response: unknown): TopoloWidgetVa
1252
1257
 
1253
1258
  declare const TOPOLO_WIDGET_ENDPOINT = "/api/widget";
1254
1259
  declare const TOPOLO_WIDGET_REFRESH_INTERVAL_MS = 60000;
1255
- declare const TOPOLO_WIDGET_CONTRACT_VERSION: "2.0.0";
1260
+ declare const TOPOLO_WIDGET_CONTRACT_VERSION: "3.0.0";
1256
1261
  type TopoloWidgetType = "stats" | "chart" | "activity" | "notifications" | "actions";
1257
1262
  type TopoloWidgetContext = "personal" | "organization" | "workspace" | "resource";
1258
1263
  type TopoloWidgetSize = "compact" | "medium" | "wide" | "full";
@@ -1325,11 +1330,85 @@ 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;
1331
1346
  description?: string;
1332
1347
  }
1348
+ type TopoloWidgetVisualizationFamily = "number" | "line" | "area" | "bar" | "donut" | "table" | "funnel" | "timeline" | "actions";
1349
+ interface TopoloWidgetMetricDefinition {
1350
+ key: string;
1351
+ label: string;
1352
+ description: string;
1353
+ unit: string;
1354
+ semantic: TopoloWidgetMetricSemantic;
1355
+ aggregation: TopoloWidgetMetricAggregation;
1356
+ }
1357
+ interface TopoloWidgetSeriesDefinition {
1358
+ key: string;
1359
+ label: string;
1360
+ metricKey: string;
1361
+ unit: string;
1362
+ }
1363
+ interface TopoloWidgetDimensionDefinition {
1364
+ key: string;
1365
+ label: string;
1366
+ description: string;
1367
+ valueType: "string" | "number" | "boolean" | "date" | "datetime";
1368
+ }
1369
+ interface TopoloWidgetLifecycleStage {
1370
+ key: string;
1371
+ label: string;
1372
+ order: number;
1373
+ terminal: boolean;
1374
+ }
1375
+ interface TopoloWidgetEventDefinition {
1376
+ key: string;
1377
+ label: string;
1378
+ description: string;
1379
+ }
1380
+ interface TopoloWidgetDatasetDefinition {
1381
+ id: string;
1382
+ label: string;
1383
+ description: string;
1384
+ grain: string;
1385
+ metricKeys: string[];
1386
+ seriesKeys: string[];
1387
+ dimensionKeys: string[];
1388
+ eventKeys: string[];
1389
+ actionIds: string[];
1390
+ }
1391
+ interface TopoloWidgetWorkspaceSelector {
1392
+ mode: "none" | "optional" | "required";
1393
+ resourceType: string;
1394
+ label: string;
1395
+ supportsMultiple: boolean;
1396
+ }
1397
+ interface TopoloWidgetContribution {
1398
+ summary: string;
1399
+ identity: {
1400
+ iconSource: "catalog";
1401
+ };
1402
+ workspaceSelector: TopoloWidgetWorkspaceSelector;
1403
+ metricDefinitions: TopoloWidgetMetricDefinition[];
1404
+ seriesDefinitions: TopoloWidgetSeriesDefinition[];
1405
+ dimensions: TopoloWidgetDimensionDefinition[];
1406
+ lifecycleStages: TopoloWidgetLifecycleStage[];
1407
+ events: TopoloWidgetEventDefinition[];
1408
+ datasets: TopoloWidgetDatasetDefinition[];
1409
+ compatibleVisualizations: TopoloWidgetVisualizationFamily[];
1410
+ defaultVisualization: TopoloWidgetVisualizationFamily;
1411
+ }
1333
1412
  interface TopoloWidgetBase {
1334
1413
  id: string;
1335
1414
  version: string;
@@ -1347,6 +1426,7 @@ interface TopoloWidgetBase {
1347
1426
  controls: TopoloWidgetActionBinding[];
1348
1427
  localization: TopoloWidgetLocalization;
1349
1428
  accessibility: TopoloWidgetAccessibility;
1429
+ contribution: TopoloWidgetContribution;
1350
1430
  }
1351
1431
  type TopoloWidgetMetricSemantic = "gauge" | "counter" | "rate" | "duration" | "currency" | "percentage";
1352
1432
  type TopoloWidgetMetricAggregation = "last" | "sum" | "average" | "minimum" | "maximum" | "count" | "distinct_count";
@@ -1509,4 +1589,4 @@ declare function createTopolo(options: TopoloClientOptions): {
1509
1589
  };
1510
1590
  type Topolo = ReturnType<typeof createTopolo>;
1511
1591
 
1512
- export { ACTION_CATALOG_DIGEST_ALGORITHM, type ActionContractManifest, type ActionContractSource, type AgentIdentity, type AppApiId, type ApplicationCatalogEntry, type ApplicationDeployTarget, type ApplicationDirectory, type ApplicationDirectoryApplication, type ApplicationDirectoryClient, type ApplicationDirectorySource, type ApplicationId, type CredentialIntrospection, type CrmContactSummary, CrmModule, DEFAULT_APP_URLS, type DeviceAuthorizationResponse, IdentityModule, type ListContactsOptions, type ListContactsResult, type OAuthHelperOptions, type P2PActionDetail, type P2PActionRequestInput, type P2PActionRequestResult, type P2PActionRow, type P2PActionRun, type P2PCapability, type P2PConnection, type P2PHold, type P2PLedgerEntry, P2PModule, type P2PPolicy, type P2PPricingModel, type P2PSettlementBatch, type P2PSettlementGroup, type PublishCapabilityInput, type RequestOptions, type ServiceCatalogUrlEntry, TOPOLO_ENVIRONMENT_SERVICE_URLS, TOPOLO_RUNTIME_ENVIRONMENTS, TOPOLO_WIDGET_CONTRACT_VERSION, TOPOLO_WIDGET_ENDPOINT, TOPOLO_WIDGET_REFRESH_INTERVAL_MS, type TokenResponse, type Topolo, type TopoloActionAgentContract, type TopoloActionCatalogEntry, type TopoloActionCatalogSummary, type TopoloActionDigestInput, type TopoloActionPlan, type TopoloActionPreparation, type TopoloActionUploadOptions, type TopoloActionValidationIssue, type TopoloActionValidationResult, type TopoloActionsWidget, type TopoloActivityWidget, type TopoloAgentExecutionContext, type TopoloApiKeyResourceMetadata, TopoloAuthError, type TopoloBinaryResponse, type TopoloBindableResource, type TopoloBindableResourceCatalog, type TopoloChartWidget, TopoloClient, type TopoloClientOptions, TopoloConfirmationRequiredError, type TopoloCredential, type TopoloDebugEvent, TopoloHttpError, type TopoloInlineBase64UploadContract, type TopoloNotificationsWidget, TopoloOAuth, TopoloPermissionError, type TopoloRuntimeEnvironment, TopoloSdkError, type TopoloServiceCatalogEntry, type TopoloStatsWidget, type TopoloWidget, type TopoloWidgetAccessibility, type TopoloWidgetActionBinding, type TopoloWidgetActionRecovery, type TopoloWidgetActivityItem, type TopoloWidgetApiError, type TopoloWidgetApiErrorResponse, type TopoloWidgetApiResponse, type TopoloWidgetApiSuccessResponse, type TopoloWidgetBase, type TopoloWidgetConfig, type TopoloWidgetConfiguration, type TopoloWidgetConfigurationField, type TopoloWidgetConfigurationOption, type TopoloWidgetContext, type TopoloWidgetDimension, type TopoloWidgetDraft, type TopoloWidgetDrilldown, type TopoloWidgetErrorResponseOptions, type TopoloWidgetLocalization, type TopoloWidgetMetric, type TopoloWidgetMetricAggregation, type TopoloWidgetMetricComparison, type TopoloWidgetMetricSemantic, type TopoloWidgetNotificationItem, type TopoloWidgetPeriod, type TopoloWidgetPresentation, type TopoloWidgetRefreshPolicy, type TopoloWidgetResourceBinding, type TopoloWidgetResponseOptions, type TopoloWidgetSeries, type TopoloWidgetSeriesPoint, type TopoloWidgetSize, type TopoloWidgetState, type TopoloWidgetStatus, type TopoloWidgetType, type TopoloWidgetValidationResult, type TopoloWorkspace, type TopoloWorkspaceListResult, type TopoloWorkspaceMutationResult, type TopoloWorkspacePolicy, WorkspacesModule, actionContractCompleteness, applicationEntryFromCatalog, assertTopoloServiceUrlEnvironment, canonicalTopoloErrorCode, canonicalTopoloRequestId, canonicalTopoloRequired, canonicalTopoloRetryAfter, compactActionCatalogEntry, compactActionPlan, compactActionPreparation, compactActionResult, compactActionValidation, createTopolo, createTopoloAgentExecutionContext, createTopoloWidgetError, createTopoloWidgetResponse, defineTopoloActionsWidget, defineTopoloActivityWidget, defineTopoloChartWidget, defineTopoloNotificationsWidget, defineTopoloStatsWidget, digestTopoloActionCatalog, encodeTopoloActionBase64, enrichActionContract, extractTopoloServiceErrorCode, extractTopoloServiceErrorDetails, formatDirectoryApplicationIds, friendlyAppId, listApplicationDirectory, parseTopoloActionUploadContract, preferExactActionMatches, prepareTopoloActionBase64UploadInput, prepareTopoloActionUploadInput, publicTopoloErrorMessage, resolveApplicationDirectoryEntry, resolveCatalogServiceUrl, resolveServiceUrl, topoloErrorHint, topoloServiceOrigins, validateTopoloActionInput, validateTopoloWidgetResponse };
1592
+ export { ACTION_CATALOG_DIGEST_ALGORITHM, type ActionContractManifest, type ActionContractSource, type AgentIdentity, type AppApiId, type ApplicationCatalogEntry, type ApplicationDeployTarget, type ApplicationDirectory, type ApplicationDirectoryApplication, type ApplicationDirectoryClient, type ApplicationDirectorySource, type ApplicationId, type CredentialIntrospection, type CrmContactSummary, CrmModule, DEFAULT_APP_URLS, type DeviceAuthorizationResponse, IdentityModule, type ListContactsOptions, type ListContactsResult, type OAuthHelperOptions, type P2PActionDetail, type P2PActionRequestInput, type P2PActionRequestResult, type P2PActionRow, type P2PActionRun, type P2PCapability, type P2PConnection, type P2PHold, type P2PLedgerEntry, P2PModule, type P2PPolicy, type P2PPricingModel, type P2PSettlementBatch, type P2PSettlementGroup, type PublishCapabilityInput, type RequestOptions, type ServiceCatalogUrlEntry, TOPOLO_ENVIRONMENT_SERVICE_URLS, TOPOLO_RUNTIME_ENVIRONMENTS, TOPOLO_WIDGET_CONTRACT_VERSION, TOPOLO_WIDGET_ENDPOINT, TOPOLO_WIDGET_REFRESH_INTERVAL_MS, type TokenResponse, type Topolo, type TopoloActionAgentContract, type TopoloActionCatalogEntry, type TopoloActionCatalogSummary, type TopoloActionDigestInput, type TopoloActionPlan, type TopoloActionPreparation, type TopoloActionUploadOptions, type TopoloActionValidationIssue, type TopoloActionValidationResult, type TopoloActionsWidget, type TopoloActivityWidget, type TopoloAgentExecutionContext, type TopoloApiKeyResourceMetadata, TopoloAuthError, type TopoloBinaryResponse, type TopoloBindableResource, type TopoloBindableResourceCatalog, type TopoloChartWidget, TopoloClient, type TopoloClientOptions, TopoloConfirmationRequiredError, type TopoloCredential, type TopoloDebugEvent, TopoloHttpError, type TopoloInlineBase64UploadContract, type TopoloNotificationsWidget, TopoloOAuth, TopoloPermissionError, type TopoloRuntimeEnvironment, TopoloSdkError, type TopoloServiceCatalogEntry, type TopoloStatsWidget, type TopoloWidget, type TopoloWidgetAccessibility, type TopoloWidgetActionBinding, type TopoloWidgetActionRecovery, type TopoloWidgetActivityItem, type TopoloWidgetApiError, type TopoloWidgetApiErrorResponse, type TopoloWidgetApiResponse, type TopoloWidgetApiSuccessResponse, type TopoloWidgetBase, type TopoloWidgetConfig, type TopoloWidgetConfiguration, type TopoloWidgetConfigurationField, type TopoloWidgetConfigurationOption, type TopoloWidgetContext, type TopoloWidgetContribution, type TopoloWidgetDatasetDefinition, type TopoloWidgetDimension, type TopoloWidgetDimensionDefinition, type TopoloWidgetDraft, type TopoloWidgetDrilldown, type TopoloWidgetErrorResponseOptions, type TopoloWidgetEventDefinition, type TopoloWidgetLifecycleStage, type TopoloWidgetLocalization, type TopoloWidgetMetric, type TopoloWidgetMetricAggregation, type TopoloWidgetMetricComparison, type TopoloWidgetMetricDefinition, type TopoloWidgetMetricSemantic, type TopoloWidgetNotificationItem, type TopoloWidgetPeriod, type TopoloWidgetPresentation, type TopoloWidgetRefreshPolicy, type TopoloWidgetResourceBinding, type TopoloWidgetResponseOptions, type TopoloWidgetSeries, type TopoloWidgetSeriesDefinition, type TopoloWidgetSeriesPoint, type TopoloWidgetSize, type TopoloWidgetState, type TopoloWidgetStatus, type TopoloWidgetType, type TopoloWidgetValidationResult, type TopoloWidgetVisualizationFamily, type TopoloWidgetWorkspaceSelector, type TopoloWorkspace, type TopoloWorkspaceListResult, type TopoloWorkspaceMutationResult, type TopoloWorkspacePolicy, WorkspacesModule, actionContractCompleteness, applicationEntryFromCatalog, assertTopoloServiceUrlEnvironment, canonicalTopoloErrorCode, canonicalTopoloRequestId, canonicalTopoloRequired, canonicalTopoloRetryAfter, compactActionCatalogEntry, compactActionPlan, compactActionPreparation, compactActionResult, compactActionValidation, createTopolo, createTopoloAgentExecutionContext, createTopoloWidgetError, createTopoloWidgetResponse, defineTopoloActionsWidget, defineTopoloActivityWidget, defineTopoloChartWidget, defineTopoloNotificationsWidget, defineTopoloStatsWidget, digestTopoloActionCatalog, encodeTopoloActionBase64, enrichActionContract, extractTopoloServiceErrorCode, extractTopoloServiceErrorDetails, formatDirectoryApplicationIds, friendlyAppId, listApplicationDirectory, parseTopoloActionUploadContract, preferExactActionMatches, prepareTopoloActionBase64UploadInput, prepareTopoloActionUploadInput, publicTopoloErrorMessage, resolveApplicationDirectoryEntry, resolveCatalogServiceUrl, resolveServiceUrl, topoloErrorHint, topoloServiceOrigins, validateTopoloActionInput, validateTopoloWidgetResponse };
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;
@@ -1252,7 +1257,7 @@ declare function validateTopoloWidgetResponse(response: unknown): TopoloWidgetVa
1252
1257
 
1253
1258
  declare const TOPOLO_WIDGET_ENDPOINT = "/api/widget";
1254
1259
  declare const TOPOLO_WIDGET_REFRESH_INTERVAL_MS = 60000;
1255
- declare const TOPOLO_WIDGET_CONTRACT_VERSION: "2.0.0";
1260
+ declare const TOPOLO_WIDGET_CONTRACT_VERSION: "3.0.0";
1256
1261
  type TopoloWidgetType = "stats" | "chart" | "activity" | "notifications" | "actions";
1257
1262
  type TopoloWidgetContext = "personal" | "organization" | "workspace" | "resource";
1258
1263
  type TopoloWidgetSize = "compact" | "medium" | "wide" | "full";
@@ -1325,11 +1330,85 @@ 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;
1331
1346
  description?: string;
1332
1347
  }
1348
+ type TopoloWidgetVisualizationFamily = "number" | "line" | "area" | "bar" | "donut" | "table" | "funnel" | "timeline" | "actions";
1349
+ interface TopoloWidgetMetricDefinition {
1350
+ key: string;
1351
+ label: string;
1352
+ description: string;
1353
+ unit: string;
1354
+ semantic: TopoloWidgetMetricSemantic;
1355
+ aggregation: TopoloWidgetMetricAggregation;
1356
+ }
1357
+ interface TopoloWidgetSeriesDefinition {
1358
+ key: string;
1359
+ label: string;
1360
+ metricKey: string;
1361
+ unit: string;
1362
+ }
1363
+ interface TopoloWidgetDimensionDefinition {
1364
+ key: string;
1365
+ label: string;
1366
+ description: string;
1367
+ valueType: "string" | "number" | "boolean" | "date" | "datetime";
1368
+ }
1369
+ interface TopoloWidgetLifecycleStage {
1370
+ key: string;
1371
+ label: string;
1372
+ order: number;
1373
+ terminal: boolean;
1374
+ }
1375
+ interface TopoloWidgetEventDefinition {
1376
+ key: string;
1377
+ label: string;
1378
+ description: string;
1379
+ }
1380
+ interface TopoloWidgetDatasetDefinition {
1381
+ id: string;
1382
+ label: string;
1383
+ description: string;
1384
+ grain: string;
1385
+ metricKeys: string[];
1386
+ seriesKeys: string[];
1387
+ dimensionKeys: string[];
1388
+ eventKeys: string[];
1389
+ actionIds: string[];
1390
+ }
1391
+ interface TopoloWidgetWorkspaceSelector {
1392
+ mode: "none" | "optional" | "required";
1393
+ resourceType: string;
1394
+ label: string;
1395
+ supportsMultiple: boolean;
1396
+ }
1397
+ interface TopoloWidgetContribution {
1398
+ summary: string;
1399
+ identity: {
1400
+ iconSource: "catalog";
1401
+ };
1402
+ workspaceSelector: TopoloWidgetWorkspaceSelector;
1403
+ metricDefinitions: TopoloWidgetMetricDefinition[];
1404
+ seriesDefinitions: TopoloWidgetSeriesDefinition[];
1405
+ dimensions: TopoloWidgetDimensionDefinition[];
1406
+ lifecycleStages: TopoloWidgetLifecycleStage[];
1407
+ events: TopoloWidgetEventDefinition[];
1408
+ datasets: TopoloWidgetDatasetDefinition[];
1409
+ compatibleVisualizations: TopoloWidgetVisualizationFamily[];
1410
+ defaultVisualization: TopoloWidgetVisualizationFamily;
1411
+ }
1333
1412
  interface TopoloWidgetBase {
1334
1413
  id: string;
1335
1414
  version: string;
@@ -1347,6 +1426,7 @@ interface TopoloWidgetBase {
1347
1426
  controls: TopoloWidgetActionBinding[];
1348
1427
  localization: TopoloWidgetLocalization;
1349
1428
  accessibility: TopoloWidgetAccessibility;
1429
+ contribution: TopoloWidgetContribution;
1350
1430
  }
1351
1431
  type TopoloWidgetMetricSemantic = "gauge" | "counter" | "rate" | "duration" | "currency" | "percentage";
1352
1432
  type TopoloWidgetMetricAggregation = "last" | "sum" | "average" | "minimum" | "maximum" | "count" | "distinct_count";
@@ -1509,4 +1589,4 @@ declare function createTopolo(options: TopoloClientOptions): {
1509
1589
  };
1510
1590
  type Topolo = ReturnType<typeof createTopolo>;
1511
1591
 
1512
- export { ACTION_CATALOG_DIGEST_ALGORITHM, type ActionContractManifest, type ActionContractSource, type AgentIdentity, type AppApiId, type ApplicationCatalogEntry, type ApplicationDeployTarget, type ApplicationDirectory, type ApplicationDirectoryApplication, type ApplicationDirectoryClient, type ApplicationDirectorySource, type ApplicationId, type CredentialIntrospection, type CrmContactSummary, CrmModule, DEFAULT_APP_URLS, type DeviceAuthorizationResponse, IdentityModule, type ListContactsOptions, type ListContactsResult, type OAuthHelperOptions, type P2PActionDetail, type P2PActionRequestInput, type P2PActionRequestResult, type P2PActionRow, type P2PActionRun, type P2PCapability, type P2PConnection, type P2PHold, type P2PLedgerEntry, P2PModule, type P2PPolicy, type P2PPricingModel, type P2PSettlementBatch, type P2PSettlementGroup, type PublishCapabilityInput, type RequestOptions, type ServiceCatalogUrlEntry, TOPOLO_ENVIRONMENT_SERVICE_URLS, TOPOLO_RUNTIME_ENVIRONMENTS, TOPOLO_WIDGET_CONTRACT_VERSION, TOPOLO_WIDGET_ENDPOINT, TOPOLO_WIDGET_REFRESH_INTERVAL_MS, type TokenResponse, type Topolo, type TopoloActionAgentContract, type TopoloActionCatalogEntry, type TopoloActionCatalogSummary, type TopoloActionDigestInput, type TopoloActionPlan, type TopoloActionPreparation, type TopoloActionUploadOptions, type TopoloActionValidationIssue, type TopoloActionValidationResult, type TopoloActionsWidget, type TopoloActivityWidget, type TopoloAgentExecutionContext, type TopoloApiKeyResourceMetadata, TopoloAuthError, type TopoloBinaryResponse, type TopoloBindableResource, type TopoloBindableResourceCatalog, type TopoloChartWidget, TopoloClient, type TopoloClientOptions, TopoloConfirmationRequiredError, type TopoloCredential, type TopoloDebugEvent, TopoloHttpError, type TopoloInlineBase64UploadContract, type TopoloNotificationsWidget, TopoloOAuth, TopoloPermissionError, type TopoloRuntimeEnvironment, TopoloSdkError, type TopoloServiceCatalogEntry, type TopoloStatsWidget, type TopoloWidget, type TopoloWidgetAccessibility, type TopoloWidgetActionBinding, type TopoloWidgetActionRecovery, type TopoloWidgetActivityItem, type TopoloWidgetApiError, type TopoloWidgetApiErrorResponse, type TopoloWidgetApiResponse, type TopoloWidgetApiSuccessResponse, type TopoloWidgetBase, type TopoloWidgetConfig, type TopoloWidgetConfiguration, type TopoloWidgetConfigurationField, type TopoloWidgetConfigurationOption, type TopoloWidgetContext, type TopoloWidgetDimension, type TopoloWidgetDraft, type TopoloWidgetDrilldown, type TopoloWidgetErrorResponseOptions, type TopoloWidgetLocalization, type TopoloWidgetMetric, type TopoloWidgetMetricAggregation, type TopoloWidgetMetricComparison, type TopoloWidgetMetricSemantic, type TopoloWidgetNotificationItem, type TopoloWidgetPeriod, type TopoloWidgetPresentation, type TopoloWidgetRefreshPolicy, type TopoloWidgetResourceBinding, type TopoloWidgetResponseOptions, type TopoloWidgetSeries, type TopoloWidgetSeriesPoint, type TopoloWidgetSize, type TopoloWidgetState, type TopoloWidgetStatus, type TopoloWidgetType, type TopoloWidgetValidationResult, type TopoloWorkspace, type TopoloWorkspaceListResult, type TopoloWorkspaceMutationResult, type TopoloWorkspacePolicy, WorkspacesModule, actionContractCompleteness, applicationEntryFromCatalog, assertTopoloServiceUrlEnvironment, canonicalTopoloErrorCode, canonicalTopoloRequestId, canonicalTopoloRequired, canonicalTopoloRetryAfter, compactActionCatalogEntry, compactActionPlan, compactActionPreparation, compactActionResult, compactActionValidation, createTopolo, createTopoloAgentExecutionContext, createTopoloWidgetError, createTopoloWidgetResponse, defineTopoloActionsWidget, defineTopoloActivityWidget, defineTopoloChartWidget, defineTopoloNotificationsWidget, defineTopoloStatsWidget, digestTopoloActionCatalog, encodeTopoloActionBase64, enrichActionContract, extractTopoloServiceErrorCode, extractTopoloServiceErrorDetails, formatDirectoryApplicationIds, friendlyAppId, listApplicationDirectory, parseTopoloActionUploadContract, preferExactActionMatches, prepareTopoloActionBase64UploadInput, prepareTopoloActionUploadInput, publicTopoloErrorMessage, resolveApplicationDirectoryEntry, resolveCatalogServiceUrl, resolveServiceUrl, topoloErrorHint, topoloServiceOrigins, validateTopoloActionInput, validateTopoloWidgetResponse };
1592
+ export { ACTION_CATALOG_DIGEST_ALGORITHM, type ActionContractManifest, type ActionContractSource, type AgentIdentity, type AppApiId, type ApplicationCatalogEntry, type ApplicationDeployTarget, type ApplicationDirectory, type ApplicationDirectoryApplication, type ApplicationDirectoryClient, type ApplicationDirectorySource, type ApplicationId, type CredentialIntrospection, type CrmContactSummary, CrmModule, DEFAULT_APP_URLS, type DeviceAuthorizationResponse, IdentityModule, type ListContactsOptions, type ListContactsResult, type OAuthHelperOptions, type P2PActionDetail, type P2PActionRequestInput, type P2PActionRequestResult, type P2PActionRow, type P2PActionRun, type P2PCapability, type P2PConnection, type P2PHold, type P2PLedgerEntry, P2PModule, type P2PPolicy, type P2PPricingModel, type P2PSettlementBatch, type P2PSettlementGroup, type PublishCapabilityInput, type RequestOptions, type ServiceCatalogUrlEntry, TOPOLO_ENVIRONMENT_SERVICE_URLS, TOPOLO_RUNTIME_ENVIRONMENTS, TOPOLO_WIDGET_CONTRACT_VERSION, TOPOLO_WIDGET_ENDPOINT, TOPOLO_WIDGET_REFRESH_INTERVAL_MS, type TokenResponse, type Topolo, type TopoloActionAgentContract, type TopoloActionCatalogEntry, type TopoloActionCatalogSummary, type TopoloActionDigestInput, type TopoloActionPlan, type TopoloActionPreparation, type TopoloActionUploadOptions, type TopoloActionValidationIssue, type TopoloActionValidationResult, type TopoloActionsWidget, type TopoloActivityWidget, type TopoloAgentExecutionContext, type TopoloApiKeyResourceMetadata, TopoloAuthError, type TopoloBinaryResponse, type TopoloBindableResource, type TopoloBindableResourceCatalog, type TopoloChartWidget, TopoloClient, type TopoloClientOptions, TopoloConfirmationRequiredError, type TopoloCredential, type TopoloDebugEvent, TopoloHttpError, type TopoloInlineBase64UploadContract, type TopoloNotificationsWidget, TopoloOAuth, TopoloPermissionError, type TopoloRuntimeEnvironment, TopoloSdkError, type TopoloServiceCatalogEntry, type TopoloStatsWidget, type TopoloWidget, type TopoloWidgetAccessibility, type TopoloWidgetActionBinding, type TopoloWidgetActionRecovery, type TopoloWidgetActivityItem, type TopoloWidgetApiError, type TopoloWidgetApiErrorResponse, type TopoloWidgetApiResponse, type TopoloWidgetApiSuccessResponse, type TopoloWidgetBase, type TopoloWidgetConfig, type TopoloWidgetConfiguration, type TopoloWidgetConfigurationField, type TopoloWidgetConfigurationOption, type TopoloWidgetContext, type TopoloWidgetContribution, type TopoloWidgetDatasetDefinition, type TopoloWidgetDimension, type TopoloWidgetDimensionDefinition, type TopoloWidgetDraft, type TopoloWidgetDrilldown, type TopoloWidgetErrorResponseOptions, type TopoloWidgetEventDefinition, type TopoloWidgetLifecycleStage, type TopoloWidgetLocalization, type TopoloWidgetMetric, type TopoloWidgetMetricAggregation, type TopoloWidgetMetricComparison, type TopoloWidgetMetricDefinition, type TopoloWidgetMetricSemantic, type TopoloWidgetNotificationItem, type TopoloWidgetPeriod, type TopoloWidgetPresentation, type TopoloWidgetRefreshPolicy, type TopoloWidgetResourceBinding, type TopoloWidgetResponseOptions, type TopoloWidgetSeries, type TopoloWidgetSeriesDefinition, type TopoloWidgetSeriesPoint, type TopoloWidgetSize, type TopoloWidgetState, type TopoloWidgetStatus, type TopoloWidgetType, type TopoloWidgetValidationResult, type TopoloWidgetVisualizationFamily, type TopoloWidgetWorkspaceSelector, type TopoloWorkspace, type TopoloWorkspaceListResult, type TopoloWorkspaceMutationResult, type TopoloWorkspacePolicy, WorkspacesModule, actionContractCompleteness, applicationEntryFromCatalog, assertTopoloServiceUrlEnvironment, canonicalTopoloErrorCode, canonicalTopoloRequestId, canonicalTopoloRequired, canonicalTopoloRetryAfter, compactActionCatalogEntry, compactActionPlan, compactActionPreparation, compactActionResult, compactActionValidation, createTopolo, createTopoloAgentExecutionContext, createTopoloWidgetError, createTopoloWidgetResponse, defineTopoloActionsWidget, defineTopoloActivityWidget, defineTopoloChartWidget, defineTopoloNotificationsWidget, defineTopoloStatsWidget, digestTopoloActionCatalog, encodeTopoloActionBase64, enrichActionContract, extractTopoloServiceErrorCode, extractTopoloServiceErrorDetails, formatDirectoryApplicationIds, friendlyAppId, listApplicationDirectory, parseTopoloActionUploadContract, preferExactActionMatches, prepareTopoloActionBase64UploadInput, prepareTopoloActionUploadInput, publicTopoloErrorMessage, resolveApplicationDirectoryEntry, resolveCatalogServiceUrl, resolveServiceUrl, topoloErrorHint, topoloServiceOrigins, validateTopoloActionInput, validateTopoloWidgetResponse };
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
@@ -3220,7 +3237,7 @@ var TopoloOAuth = class {
3220
3237
  };
3221
3238
 
3222
3239
  // src/widget-validation.ts
3223
- var CONTRACT_VERSION = "2.0.0";
3240
+ var CONTRACT_VERSION = "3.0.0";
3224
3241
  var WIDGET_TYPES = ["stats", "chart", "activity", "notifications", "actions"];
3225
3242
  var CONTEXTS = ["personal", "organization", "workspace", "resource"];
3226
3243
  var SIZES = ["compact", "medium", "wide", "full"];
@@ -3231,6 +3248,9 @@ var AGGREGATIONS = ["last", "sum", "average", "minimum", "maximum", "count", "di
3231
3248
  var CHART_TYPES = ["line", "bar", "area", "pie", "donut"];
3232
3249
  var SEVERITIES = ["info", "warning", "error", "success"];
3233
3250
  var RECOVERY_MODES = ["not_applicable", "retry", "compensating_action"];
3251
+ var VISUALIZATION_FAMILIES = ["number", "line", "area", "bar", "donut", "table", "funnel", "timeline", "actions"];
3252
+ var WORKSPACE_SELECTOR_MODES = ["none", "optional", "required"];
3253
+ var DIMENSION_VALUE_TYPES = ["string", "number", "boolean", "date", "datetime"];
3234
3254
  var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
3235
3255
  function validateTopoloWidgetResponse(response) {
3236
3256
  const errors = [];
@@ -3281,6 +3301,7 @@ function validateWidget(value, path, errors) {
3281
3301
  validateActions(value.controls, `${path}.controls`, errors);
3282
3302
  validateLocalization(value.localization, `${path}.localization`, errors);
3283
3303
  validateAccessibility(value.accessibility, `${path}.accessibility`, errors);
3304
+ validateContribution(value.contribution, `${path}.contribution`, value, errors);
3284
3305
  switch (value.type) {
3285
3306
  case "stats":
3286
3307
  validateMetrics(value.metrics, `${path}.metrics`, errors, requiresContent(value));
@@ -3410,12 +3431,216 @@ function validateLocalization(value, path, errors) {
3410
3431
  nonEmptyString(value.locale, `${path}.locale`, errors);
3411
3432
  nonEmptyString(value.titleKey, `${path}.titleKey`, errors);
3412
3433
  optionalNonEmptyString(value.descriptionKey, `${path}.descriptionKey`, errors);
3434
+ optionalNonEmptyString(value.title, `${path}.title`, errors);
3435
+ optionalNonEmptyString(value.description, `${path}.description`, errors);
3413
3436
  }
3414
3437
  function validateAccessibility(value, path, errors) {
3415
3438
  if (!recordValue(value, path, errors)) return;
3416
3439
  nonEmptyString(value.label, `${path}.label`, errors);
3417
3440
  optionalNonEmptyString(value.description, `${path}.description`, errors);
3418
3441
  }
3442
+ function validateContribution(value, path, widget, errors) {
3443
+ if (!recordValue(value, path, errors)) return;
3444
+ nonEmptyString(value.summary, `${path}.summary`, errors);
3445
+ if (recordValue(value.identity, `${path}.identity`, errors)) {
3446
+ exactValue(value.identity.iconSource, "catalog", `${path}.identity.iconSource`, errors);
3447
+ }
3448
+ if (recordValue(value.workspaceSelector, `${path}.workspaceSelector`, errors)) {
3449
+ enumValue(value.workspaceSelector.mode, WORKSPACE_SELECTOR_MODES, `${path}.workspaceSelector.mode`, errors);
3450
+ nonEmptyString(value.workspaceSelector.resourceType, `${path}.workspaceSelector.resourceType`, errors);
3451
+ nonEmptyString(value.workspaceSelector.label, `${path}.workspaceSelector.label`, errors);
3452
+ booleanValue(value.workspaceSelector.supportsMultiple, `${path}.workspaceSelector.supportsMultiple`, errors);
3453
+ if (value.workspaceSelector.mode === "required" && requiresContent(widget) && isRecord5(widget.resource) && typeof widget.resource.resourceId !== "string") {
3454
+ errors.push(`${path}.workspaceSelector requires a bound resourceId for ready or stale data`);
3455
+ }
3456
+ }
3457
+ const metricKeys = validateMetricDefinitions(value.metricDefinitions, `${path}.metricDefinitions`, errors);
3458
+ const seriesKeys = validateSeriesDefinitions(value.seriesDefinitions, `${path}.seriesDefinitions`, metricKeys, errors);
3459
+ const dimensionKeys = validateDimensionDefinitions(value.dimensions, `${path}.dimensions`, errors);
3460
+ const lifecycleKeys = validateLifecycleStages(value.lifecycleStages, `${path}.lifecycleStages`, errors);
3461
+ const eventKeys = validateEventDefinitions(value.events, `${path}.events`, errors);
3462
+ const actionKeys = widgetActionKeys(widget);
3463
+ validateDatasets(value.datasets, `${path}.datasets`, {
3464
+ metricKeys,
3465
+ seriesKeys,
3466
+ dimensionKeys,
3467
+ eventKeys,
3468
+ actionKeys
3469
+ }, errors);
3470
+ enumArray(
3471
+ value.compatibleVisualizations,
3472
+ VISUALIZATION_FAMILIES,
3473
+ `${path}.compatibleVisualizations`,
3474
+ errors,
3475
+ true
3476
+ );
3477
+ enumValue(value.defaultVisualization, VISUALIZATION_FAMILIES, `${path}.defaultVisualization`, errors);
3478
+ if (typeof value.defaultVisualization === "string" && Array.isArray(value.compatibleVisualizations) && !value.compatibleVisualizations.includes(value.defaultVisualization)) {
3479
+ errors.push(`${path}.defaultVisualization must be included in compatibleVisualizations`);
3480
+ }
3481
+ if (Array.isArray(value.compatibleVisualizations)) {
3482
+ for (const family of value.compatibleVisualizations) {
3483
+ if (family === "number" && metricKeys.size === 0) errors.push(`${path}.compatibleVisualizations number requires a metric definition`);
3484
+ if ((family === "line" || family === "area") && seriesKeys.size === 0) errors.push(`${path}.compatibleVisualizations ${family} requires a series definition`);
3485
+ if (family === "donut" && (metricKeys.size === 0 || dimensionKeys.size === 0)) errors.push(`${path}.compatibleVisualizations donut requires metric and dimension definitions`);
3486
+ if (family === "funnel" && lifecycleKeys.size < 2) errors.push(`${path}.compatibleVisualizations funnel requires at least two lifecycle stages`);
3487
+ if (family === "timeline" && eventKeys.size === 0) errors.push(`${path}.compatibleVisualizations timeline requires an event definition`);
3488
+ if (family === "actions" && actionKeys.size === 0) errors.push(`${path}.compatibleVisualizations actions requires a published action`);
3489
+ if (family === "bar" && metricKeys.size === 0 && seriesKeys.size === 0) errors.push(`${path}.compatibleVisualizations bar requires a metric or series definition`);
3490
+ }
3491
+ }
3492
+ validateRuntimeSemantics(widget, path, { metricKeys, seriesKeys, dimensionKeys }, errors);
3493
+ }
3494
+ function validateMetricDefinitions(value, path, errors) {
3495
+ const keys = /* @__PURE__ */ new Set();
3496
+ if (!arrayValue(value, path, errors)) return keys;
3497
+ value.forEach((definition, index) => {
3498
+ const itemPath = `${path}[${index}]`;
3499
+ if (!recordValue(definition, itemPath, errors)) return;
3500
+ nonEmptyString(definition.key, `${itemPath}.key`, errors);
3501
+ nonEmptyString(definition.label, `${itemPath}.label`, errors);
3502
+ nonEmptyString(definition.description, `${itemPath}.description`, errors);
3503
+ nonEmptyString(definition.unit, `${itemPath}.unit`, errors);
3504
+ enumValue(definition.semantic, SEMANTICS, `${itemPath}.semantic`, errors);
3505
+ enumValue(definition.aggregation, AGGREGATIONS, `${itemPath}.aggregation`, errors);
3506
+ addUniqueKey(definition.key, itemPath, keys, errors);
3507
+ });
3508
+ return keys;
3509
+ }
3510
+ function validateSeriesDefinitions(value, path, metricKeys, errors) {
3511
+ const keys = /* @__PURE__ */ new Set();
3512
+ if (!arrayValue(value, path, errors)) return keys;
3513
+ value.forEach((definition, index) => {
3514
+ const itemPath = `${path}[${index}]`;
3515
+ if (!recordValue(definition, itemPath, errors)) return;
3516
+ nonEmptyString(definition.key, `${itemPath}.key`, errors);
3517
+ nonEmptyString(definition.label, `${itemPath}.label`, errors);
3518
+ nonEmptyString(definition.metricKey, `${itemPath}.metricKey`, errors);
3519
+ nonEmptyString(definition.unit, `${itemPath}.unit`, errors);
3520
+ addUniqueKey(definition.key, itemPath, keys, errors);
3521
+ if (typeof definition.metricKey === "string" && !metricKeys.has(definition.metricKey)) {
3522
+ errors.push(`${itemPath}.metricKey must reference a declared metric definition`);
3523
+ }
3524
+ });
3525
+ return keys;
3526
+ }
3527
+ function validateDimensionDefinitions(value, path, errors) {
3528
+ const keys = /* @__PURE__ */ new Set();
3529
+ if (!arrayValue(value, path, errors)) return keys;
3530
+ value.forEach((definition, index) => {
3531
+ const itemPath = `${path}[${index}]`;
3532
+ if (!recordValue(definition, itemPath, errors)) return;
3533
+ nonEmptyString(definition.key, `${itemPath}.key`, errors);
3534
+ nonEmptyString(definition.label, `${itemPath}.label`, errors);
3535
+ nonEmptyString(definition.description, `${itemPath}.description`, errors);
3536
+ enumValue(definition.valueType, DIMENSION_VALUE_TYPES, `${itemPath}.valueType`, errors);
3537
+ addUniqueKey(definition.key, itemPath, keys, errors);
3538
+ });
3539
+ return keys;
3540
+ }
3541
+ function validateLifecycleStages(value, path, errors) {
3542
+ const keys = /* @__PURE__ */ new Set();
3543
+ const orders = /* @__PURE__ */ new Set();
3544
+ if (!arrayValue(value, path, errors)) return keys;
3545
+ value.forEach((stage, index) => {
3546
+ const itemPath = `${path}[${index}]`;
3547
+ if (!recordValue(stage, itemPath, errors)) return;
3548
+ nonEmptyString(stage.key, `${itemPath}.key`, errors);
3549
+ nonEmptyString(stage.label, `${itemPath}.label`, errors);
3550
+ nonNegativeInteger(stage.order, `${itemPath}.order`, errors);
3551
+ booleanValue(stage.terminal, `${itemPath}.terminal`, errors);
3552
+ addUniqueKey(stage.key, itemPath, keys, errors);
3553
+ if (typeof stage.order === "number") {
3554
+ if (orders.has(stage.order)) errors.push(`${itemPath}.order must be unique`);
3555
+ orders.add(stage.order);
3556
+ }
3557
+ });
3558
+ return keys;
3559
+ }
3560
+ function validateEventDefinitions(value, path, errors) {
3561
+ const keys = /* @__PURE__ */ new Set();
3562
+ if (!arrayValue(value, path, errors)) return keys;
3563
+ value.forEach((event, index) => {
3564
+ const itemPath = `${path}[${index}]`;
3565
+ if (!recordValue(event, itemPath, errors)) return;
3566
+ nonEmptyString(event.key, `${itemPath}.key`, errors);
3567
+ nonEmptyString(event.label, `${itemPath}.label`, errors);
3568
+ nonEmptyString(event.description, `${itemPath}.description`, errors);
3569
+ addUniqueKey(event.key, itemPath, keys, errors);
3570
+ });
3571
+ return keys;
3572
+ }
3573
+ function validateDatasets(value, path, references, errors) {
3574
+ const ids = /* @__PURE__ */ new Set();
3575
+ if (!arrayValue(value, path, errors, true)) return;
3576
+ value.forEach((dataset, index) => {
3577
+ const itemPath = `${path}[${index}]`;
3578
+ if (!recordValue(dataset, itemPath, errors)) return;
3579
+ nonEmptyString(dataset.id, `${itemPath}.id`, errors);
3580
+ nonEmptyString(dataset.label, `${itemPath}.label`, errors);
3581
+ nonEmptyString(dataset.description, `${itemPath}.description`, errors);
3582
+ nonEmptyString(dataset.grain, `${itemPath}.grain`, errors);
3583
+ addUniqueKey(dataset.id, itemPath, ids, errors);
3584
+ const fields = ["metricKeys", "seriesKeys", "dimensionKeys", "eventKeys", "actionIds"];
3585
+ for (const field of fields) stringArray2(dataset[field], `${itemPath}.${field}`, errors);
3586
+ const totalReferences = fields.reduce((total, field) => total + (Array.isArray(dataset[field]) ? dataset[field].length : 0), 0);
3587
+ if (totalReferences === 0) errors.push(`${itemPath} must reference at least one declared metric, series, dimension, event, or action`);
3588
+ validateReferences(dataset.metricKeys, references.metricKeys, `${itemPath}.metricKeys`, errors);
3589
+ validateReferences(dataset.seriesKeys, references.seriesKeys, `${itemPath}.seriesKeys`, errors);
3590
+ validateReferences(dataset.dimensionKeys, references.dimensionKeys, `${itemPath}.dimensionKeys`, errors);
3591
+ validateReferences(dataset.eventKeys, references.eventKeys, `${itemPath}.eventKeys`, errors);
3592
+ validateReferences(dataset.actionIds, references.actionKeys, `${itemPath}.actionIds`, errors);
3593
+ });
3594
+ }
3595
+ function validateReferences(value, known, path, errors) {
3596
+ if (!Array.isArray(value)) return;
3597
+ for (const reference of value) {
3598
+ if (typeof reference === "string" && !known.has(reference)) errors.push(`${path} references undeclared key ${reference}`);
3599
+ }
3600
+ }
3601
+ function widgetActionKeys(widget) {
3602
+ const keys = /* @__PURE__ */ new Set();
3603
+ for (const collection of [widget.controls, widget.actions]) {
3604
+ if (!Array.isArray(collection)) continue;
3605
+ for (const action of collection) if (isRecord5(action) && typeof action.id === "string") keys.add(action.id);
3606
+ }
3607
+ return keys;
3608
+ }
3609
+ function validateRuntimeSemantics(widget, contributionPath, declared, errors) {
3610
+ const metrics = Array.isArray(widget.metrics) ? widget.metrics : [];
3611
+ for (const metric of metrics) {
3612
+ if (!isRecord5(metric) || typeof metric.key !== "string") continue;
3613
+ if (!declared.metricKeys.has(metric.key)) errors.push(`${contributionPath}.metricDefinitions is missing runtime metric ${metric.key}`);
3614
+ validateRuntimeDimensions(metric.dimensions, declared.dimensionKeys, contributionPath, errors);
3615
+ if (isRecord5(metric.series)) {
3616
+ if (typeof metric.series.id === "string" && !declared.seriesKeys.has(metric.series.id)) errors.push(`${contributionPath}.seriesDefinitions is missing runtime series ${metric.series.id}`);
3617
+ validateSeriesDimensions(metric.series, declared.dimensionKeys, contributionPath, errors);
3618
+ }
3619
+ }
3620
+ const series = Array.isArray(widget.series) ? widget.series : [];
3621
+ for (const item of series) {
3622
+ if (!isRecord5(item)) continue;
3623
+ if (typeof item.id === "string" && !declared.seriesKeys.has(item.id)) errors.push(`${contributionPath}.seriesDefinitions is missing runtime series ${item.id}`);
3624
+ validateSeriesDimensions(item, declared.dimensionKeys, contributionPath, errors);
3625
+ }
3626
+ }
3627
+ function validateSeriesDimensions(series, known, path, errors) {
3628
+ if (!Array.isArray(series.points)) return;
3629
+ for (const point of series.points) if (isRecord5(point)) validateRuntimeDimensions(point.dimensions, known, path, errors);
3630
+ }
3631
+ function validateRuntimeDimensions(value, known, path, errors) {
3632
+ if (!Array.isArray(value)) return;
3633
+ for (const dimension of value) {
3634
+ if (isRecord5(dimension) && typeof dimension.key === "string" && !known.has(dimension.key)) {
3635
+ errors.push(`${path}.dimensions is missing runtime dimension ${dimension.key}`);
3636
+ }
3637
+ }
3638
+ }
3639
+ function addUniqueKey(value, path, keys, errors) {
3640
+ if (typeof value !== "string" || value.trim().length === 0) return;
3641
+ if (keys.has(value)) errors.push(`${path}.key must be unique`);
3642
+ keys.add(value);
3643
+ }
3419
3644
  function validateMetrics(value, path, errors, requireOne) {
3420
3645
  if (!arrayValue(value, path, errors, requireOne)) return;
3421
3646
  value.forEach((metric, index) => {
@@ -3583,7 +3808,7 @@ function isRecord5(value) {
3583
3808
  // src/widgets.ts
3584
3809
  var TOPOLO_WIDGET_ENDPOINT = "/api/widget";
3585
3810
  var TOPOLO_WIDGET_REFRESH_INTERVAL_MS = 6e4;
3586
- var TOPOLO_WIDGET_CONTRACT_VERSION = "2.0.0";
3811
+ var TOPOLO_WIDGET_CONTRACT_VERSION = "3.0.0";
3587
3812
  function defineTopoloStatsWidget(widget) {
3588
3813
  return { ...widget, type: "stats" };
3589
3814
  }
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.14.0",
4
+ "topoloPublishedVersion": "0.14.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",