@topolo/mcp 0.11.11 → 0.12.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.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/index.ts
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
 
6
- // node_modules/@topolo/sdk/dist/index.js
6
+ // node_modules/.pnpm/@topolo+sdk@0.17.1/node_modules/@topolo/sdk/dist/index.js
7
7
  function applyAuthHeaders(headers, credential) {
8
8
  if (credential.kind === "api_key") {
9
9
  headers.set("X-Api-Key", credential.apiKey);
@@ -80,6 +80,7 @@ var ERROR_CODE_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
80
80
  var TOPOLO_REQUEST_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
81
81
  var CLOUDFLARE_RAY_ID_PATTERN = /^[0-9a-f]{16}-[A-Z]{3}$/;
82
82
  var PERMISSION_PATTERN = /^[a-z][a-z0-9_-]{0,63}(?:\.[a-z][a-z0-9_-]{0,63})*:(?:[a-z][a-z0-9_-]{0,63}|\*)$/;
83
+ var PUBLIC_FIELD_PATH_PATTERN = /^[$.a-z0-9_[\]-]{1,160}$/i;
83
84
  var RETRY_AFTER_DATE_PATTERN = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
84
85
  var PUBLIC_DETAIL_SOURCES = /* @__PURE__ */ new Set([
85
86
  "api_key_introspection",
@@ -166,10 +167,10 @@ var TopoloPermissionError = class extends TopoloSdkError {
166
167
  };
167
168
  function extractTopoloServiceErrorCode(body) {
168
169
  if (!body || typeof body !== "object" || Array.isArray(body)) return null;
169
- const record = body;
170
- const topLevel = canonicalTopoloErrorCode(record.code);
170
+ const record2 = body;
171
+ const topLevel = canonicalTopoloErrorCode(record2.code);
171
172
  if (topLevel) return topLevel;
172
- const nested = record.error;
173
+ const nested = record2.error;
173
174
  const nestedString = canonicalTopoloErrorCode(nested);
174
175
  if (nestedString) return nestedString;
175
176
  if (nested && typeof nested === "object" && !Array.isArray(nested)) {
@@ -184,15 +185,19 @@ function serviceErrorCode(body, fallback) {
184
185
  }
185
186
  function extractTopoloServiceErrorDetails(body) {
186
187
  if (!body || typeof body !== "object" || Array.isArray(body)) return void 0;
187
- const record = body;
188
- let candidate = record.details;
189
- const nested = record.error;
188
+ const record2 = body;
189
+ let candidate = record2.details;
190
+ const nested = record2.error;
190
191
  if (nested && typeof nested === "object" && !Array.isArray(nested)) {
191
192
  const nestedRecord = nested;
192
193
  if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
193
194
  }
194
- if (candidate === void 0) candidate = record;
195
- if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
195
+ if (candidate === void 0) candidate = record2;
196
+ if (Array.isArray(candidate)) {
197
+ const paths = canonicalTopoloValidationPaths(candidate);
198
+ return paths.length ? { paths } : void 0;
199
+ }
200
+ if (!candidate || typeof candidate !== "object") return void 0;
196
201
  const source = candidate;
197
202
  const safe = {};
198
203
  for (const [key, value] of Object.entries(source)) {
@@ -229,12 +234,42 @@ function extractTopoloServiceErrorDetails(body) {
229
234
  continue;
230
235
  }
231
236
  if (["missingFields", "fields", "paths"].includes(key) && Array.isArray(value)) {
232
- const paths = value.filter((item) => typeof item === "string" && /^[$.a-z0-9_[\]-]{1,160}$/i.test(item));
237
+ const paths = value.filter((item) => typeof item === "string" && PUBLIC_FIELD_PATH_PATTERN.test(item));
233
238
  if (paths.length) safe[key] = paths;
234
239
  }
235
240
  }
236
241
  return Object.keys(safe).length ? safe : void 0;
237
242
  }
243
+ function canonicalTopoloValidationPaths(issues) {
244
+ const paths = [];
245
+ for (const issue of issues) {
246
+ if (!issue || typeof issue !== "object" || Array.isArray(issue)) continue;
247
+ const record2 = issue;
248
+ const path = canonicalTopoloValidationPath(record2.field) ?? canonicalTopoloValidationPath(record2.path);
249
+ if (path && !paths.includes(path)) paths.push(path);
250
+ if (paths.length === 32) break;
251
+ }
252
+ return paths;
253
+ }
254
+ function canonicalTopoloValidationPath(value) {
255
+ if (typeof value === "string") {
256
+ const path2 = value.trim();
257
+ return PUBLIC_FIELD_PATH_PATTERN.test(path2) ? path2 : void 0;
258
+ }
259
+ if (!Array.isArray(value) || value.length === 0 || value.length > 32) return void 0;
260
+ const segments = value.map((segment) => {
261
+ if (typeof segment === "number" && Number.isSafeInteger(segment) && segment >= 0) {
262
+ return String(segment);
263
+ }
264
+ if (typeof segment === "string" && /^[a-z0-9_$-]{1,80}$/i.test(segment)) {
265
+ return segment;
266
+ }
267
+ return null;
268
+ });
269
+ if (segments.includes(null)) return void 0;
270
+ const path = segments.join(".");
271
+ return PUBLIC_FIELD_PATH_PATTERN.test(path) ? path : void 0;
272
+ }
238
273
  function publicTopoloErrorMessage(error) {
239
274
  const code = canonicalTopoloErrorCode(
240
275
  typeof error === "string" ? error : error.code ?? extractTopoloServiceErrorCode(error.body)
@@ -677,6 +712,222 @@ function isMultipleOf(value, divisor) {
677
712
  const quotient = value / divisor;
678
713
  return Math.abs(quotient - Math.round(quotient)) < Number.EPSILON * Math.max(1, Math.abs(quotient)) * 4;
679
714
  }
715
+ var ACTOR_ENVELOPE_VERSION = 1;
716
+ var TOPOLO_ACTOR_ENVELOPE_HEADER = "X-Topolo-Actor-Envelope";
717
+ var PRINCIPAL_TYPES = /* @__PURE__ */ new Set([
718
+ "human",
719
+ "managed_agent",
720
+ "external_agent",
721
+ "service",
722
+ "browser_automation"
723
+ ]);
724
+ var ACCOUNTABLE_SUBJECT_TYPES = /* @__PURE__ */ new Set(["human", "organization"]);
725
+ var CHANNELS = /* @__PURE__ */ new Set(["web", "mobile", "cli", "mcp", "api", "workflow", "browser_automation"]);
726
+ var ASSURANCE_LEVELS = /* @__PURE__ */ new Set(["authenticated", "workload_identity", "user_verified"]);
727
+ var HASH_PATTERN = /^sha256:[a-f0-9]{64}$/;
728
+ var ActorEnvelopeValidationError = class extends Error {
729
+ path;
730
+ constructor(path, message) {
731
+ super(`${path}: ${message}`);
732
+ this.name = "ActorEnvelopeValidationError";
733
+ this.path = path;
734
+ }
735
+ };
736
+ function parseActorEnvelope(value) {
737
+ const envelope = record(value, "actorEnvelope");
738
+ exactKeys(envelope, [
739
+ "schemaVersion",
740
+ "organizationId",
741
+ "accountableSubject",
742
+ "performer",
743
+ "delegation",
744
+ "channel",
745
+ "assurance",
746
+ "action",
747
+ "issuedAt",
748
+ "expiresAt"
749
+ ], "actorEnvelope");
750
+ if (envelope.schemaVersion !== ACTOR_ENVELOPE_VERSION) {
751
+ fail("actorEnvelope.schemaVersion", `must equal ${ACTOR_ENVELOPE_VERSION}`);
752
+ }
753
+ const accountableSubject = parseAccountableSubject(envelope.accountableSubject);
754
+ const performer = parseIdentity(envelope.performer, "actorEnvelope.performer");
755
+ const organizationId = text(envelope.organizationId, "actorEnvelope.organizationId");
756
+ const channel = member(envelope.channel, CHANNELS, "actorEnvelope.channel");
757
+ const issuedAt = timestamp(envelope.issuedAt, "actorEnvelope.issuedAt");
758
+ const expiresAt = envelope.expiresAt === void 0 ? void 0 : timestamp(envelope.expiresAt, "actorEnvelope.expiresAt");
759
+ if (expiresAt && Date.parse(expiresAt) <= Date.parse(issuedAt)) {
760
+ fail("actorEnvelope.expiresAt", "must be later than issuedAt");
761
+ }
762
+ const assuranceValue = record(envelope.assurance, "actorEnvelope.assurance");
763
+ exactKeys(assuranceValue, ["level", "methods"], "actorEnvelope.assurance");
764
+ const assurance = {
765
+ level: member(assuranceValue.level, ASSURANCE_LEVELS, "actorEnvelope.assurance.level"),
766
+ methods: textArray(assuranceValue.methods, "actorEnvelope.assurance.methods")
767
+ };
768
+ const actionValue = record(envelope.action, "actorEnvelope.action");
769
+ exactKeys(actionValue, ["requestId", "actionId", "resourceType", "resourceId", "inputHash"], "actorEnvelope.action");
770
+ const inputHash = text(actionValue.inputHash, "actorEnvelope.action.inputHash");
771
+ if (!HASH_PATTERN.test(inputHash)) fail("actorEnvelope.action.inputHash", "must be a lowercase sha256 digest");
772
+ const action = {
773
+ requestId: text(actionValue.requestId, "actorEnvelope.action.requestId"),
774
+ actionId: text(actionValue.actionId, "actorEnvelope.action.actionId"),
775
+ ...actionValue.resourceType === void 0 ? {} : { resourceType: text(actionValue.resourceType, "actorEnvelope.action.resourceType") },
776
+ ...actionValue.resourceId === void 0 ? {} : { resourceId: text(actionValue.resourceId, "actorEnvelope.action.resourceId") },
777
+ inputHash
778
+ };
779
+ if (action.resourceType === void 0 !== (action.resourceId === void 0)) {
780
+ fail("actorEnvelope.action", "resourceType and resourceId must be provided together");
781
+ }
782
+ const delegation = parseDelegation(envelope.delegation);
783
+ const directHuman = performer.principalType === "human" && performer.principalId === accountableSubject.principalId;
784
+ if (directHuman && delegation !== null) fail("actorEnvelope.delegation", "must be null for direct human action");
785
+ if (!directHuman && delegation === null) fail("actorEnvelope.delegation", "is required when performer differs from accountable subject");
786
+ if (delegation) {
787
+ const finalHop = delegation.lineage.at(-1);
788
+ if (!finalHop || finalHop.principalId !== performer.principalId || finalHop.principalType !== performer.principalType) {
789
+ fail("actorEnvelope.delegation.lineage", "must end with the performer");
790
+ }
791
+ }
792
+ if (performer.principalType === "external_agent" && (!performer.installationId || !performer.provider)) {
793
+ fail("actorEnvelope.performer", "external_agent requires installationId and provider");
794
+ }
795
+ if (performer.principalType === "managed_agent" && !performer.agentInstanceId) {
796
+ fail("actorEnvelope.performer.agentInstanceId", "is required for managed_agent");
797
+ }
798
+ if (performer.principalType === "browser_automation" && (!performer.installationId || !performer.browserSessionId)) {
799
+ fail("actorEnvelope.performer", "browser_automation requires installationId and browserSessionId");
800
+ }
801
+ return {
802
+ schemaVersion: ACTOR_ENVELOPE_VERSION,
803
+ organizationId,
804
+ accountableSubject,
805
+ performer,
806
+ delegation,
807
+ channel,
808
+ assurance,
809
+ action,
810
+ issuedAt,
811
+ ...expiresAt ? { expiresAt } : {}
812
+ };
813
+ }
814
+ function canonicalizeActorEnvelope(value) {
815
+ return canonicalJson(parseActorEnvelope(value));
816
+ }
817
+ function encodeActorEnvelopeHeader(value) {
818
+ const bytes = new TextEncoder().encode(canonicalizeActorEnvelope(value));
819
+ const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
820
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
821
+ }
822
+ function canonicalJson(value) {
823
+ if (value === null) return "null";
824
+ if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
825
+ if (typeof value === "number") {
826
+ if (!Number.isFinite(value)) throw new TypeError("Canonical JSON accepts finite numbers only.");
827
+ return JSON.stringify(value);
828
+ }
829
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
830
+ if (typeof value !== "object" || !value || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
831
+ throw new TypeError("Canonical JSON accepts JSON objects, arrays, and primitives only.");
832
+ }
833
+ const entries = Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right));
834
+ return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
835
+ }
836
+ function parseAccountableSubject(value) {
837
+ const path = "actorEnvelope.accountableSubject";
838
+ const subject = record(value, path);
839
+ exactKeys(subject, ["principalType", "principalId", "displayName"], path);
840
+ return {
841
+ principalType: member(subject.principalType, ACCOUNTABLE_SUBJECT_TYPES, `${path}.principalType`),
842
+ principalId: text(subject.principalId, `${path}.principalId`),
843
+ ...subject.displayName === void 0 ? {} : { displayName: text(subject.displayName, `${path}.displayName`) }
844
+ };
845
+ }
846
+ async function sha256CanonicalValue(value) {
847
+ const bytes = new TextEncoder().encode(canonicalJson(value));
848
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
849
+ return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
850
+ }
851
+ function parseIdentity(value, path) {
852
+ const identity = record(value, path);
853
+ exactKeys(identity, [
854
+ "principalType",
855
+ "principalId",
856
+ "displayName",
857
+ "installationId",
858
+ "agentInstanceId",
859
+ "browserSessionId",
860
+ "provider",
861
+ "model"
862
+ ], path);
863
+ return {
864
+ principalType: member(identity.principalType, PRINCIPAL_TYPES, `${path}.principalType`),
865
+ principalId: text(identity.principalId, `${path}.principalId`),
866
+ ...optionalTextFields(identity, path, [
867
+ "displayName",
868
+ "installationId",
869
+ "agentInstanceId",
870
+ "browserSessionId",
871
+ "provider",
872
+ "model"
873
+ ])
874
+ };
875
+ }
876
+ function parseDelegation(value) {
877
+ if (value === null) return null;
878
+ const delegation = record(value, "actorEnvelope.delegation");
879
+ exactKeys(delegation, ["taskId", "runId", "lineage"], "actorEnvelope.delegation");
880
+ if (!Array.isArray(delegation.lineage) || delegation.lineage.length === 0 || delegation.lineage.length > 16) {
881
+ fail("actorEnvelope.delegation.lineage", "must contain between 1 and 16 hops");
882
+ }
883
+ return {
884
+ taskId: text(delegation.taskId, "actorEnvelope.delegation.taskId"),
885
+ runId: text(delegation.runId, "actorEnvelope.delegation.runId"),
886
+ lineage: delegation.lineage.map((value2, index) => {
887
+ const hop = record(value2, `actorEnvelope.delegation.lineage[${index}]`);
888
+ exactKeys(hop, ["principalType", "principalId"], `actorEnvelope.delegation.lineage[${index}]`);
889
+ return {
890
+ principalType: member(hop.principalType, PRINCIPAL_TYPES, `actorEnvelope.delegation.lineage[${index}].principalType`),
891
+ principalId: text(hop.principalId, `actorEnvelope.delegation.lineage[${index}].principalId`)
892
+ };
893
+ })
894
+ };
895
+ }
896
+ function record(value, path) {
897
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "must be an object");
898
+ return value;
899
+ }
900
+ function exactKeys(value, allowed, path) {
901
+ const unknown = Object.keys(value).filter((key) => !allowed.includes(key));
902
+ if (unknown.length) fail(path, `contains unknown field ${unknown[0]}`);
903
+ }
904
+ function text(value, path) {
905
+ if (typeof value !== "string" || value.trim() !== value || value.length === 0 || value.length > 256) {
906
+ fail(path, "must be a non-empty trimmed string of at most 256 characters");
907
+ }
908
+ return value;
909
+ }
910
+ function timestamp(value, path) {
911
+ const result = text(value, path);
912
+ if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(result) || Number.isNaN(Date.parse(result))) {
913
+ fail(path, "must be an RFC 3339 UTC timestamp");
914
+ }
915
+ return result;
916
+ }
917
+ function member(value, values, path) {
918
+ if (typeof value !== "string" || !values.has(value)) fail(path, "contains an unsupported value");
919
+ return value;
920
+ }
921
+ function textArray(value, path) {
922
+ if (!Array.isArray(value) || value.length === 0 || value.length > 16) fail(path, "must contain between 1 and 16 values");
923
+ return value.map((item, index) => text(item, `${path}[${index}]`));
924
+ }
925
+ function optionalTextFields(value, path, keys) {
926
+ return Object.fromEntries(keys.flatMap((key) => value[key] === void 0 ? [] : [[key, text(value[key], `${path}.${key}`)]]));
927
+ }
928
+ function fail(path, message) {
929
+ throw new ActorEnvelopeValidationError(path, message);
930
+ }
680
931
  function parseTopoloActionUploadContract(value) {
681
932
  if (!isRecord2(value) || value.profile !== "inline-base64" && value.profile !== "url-session") {
682
933
  throw new TopoloSdkError("upload_contract_unavailable", "This action does not publish a supported upload contract.");
@@ -881,6 +1132,7 @@ var NEGATIVE_CACHE_TTL_MS = 60 * 1e3;
881
1132
  var TopoloClient = class _TopoloClient {
882
1133
  credential;
883
1134
  agent;
1135
+ actorContext;
884
1136
  serviceUrls;
885
1137
  environment;
886
1138
  appIds;
@@ -911,6 +1163,11 @@ var TopoloClient = class _TopoloClient {
911
1163
  if (!options.agent?.clientName) throw new TopoloAuthError("agent.clientName is required");
912
1164
  this.credential = options.credential;
913
1165
  this.agent = options.agent;
1166
+ this.actorContext = options.actorContext ? {
1167
+ channel: options.actorContext.channel,
1168
+ taskId: options.actorContext.taskId ?? `task_${generateRequestId()}`,
1169
+ runId: options.actorContext.runId ?? `run_${generateRequestId()}`
1170
+ } : void 0;
914
1171
  this.environment = options.environment;
915
1172
  this.serviceUrls = options.environment ? topoloServiceOrigins(options.serviceUrls, options.environment) : options.serviceUrls;
916
1173
  this.appIds = options.appIds;
@@ -964,11 +1221,15 @@ var TopoloClient = class _TopoloClient {
964
1221
  const platformAppId = resolveConfiguredAppId(resolvedService.serviceKey, this.appIds);
965
1222
  if (platformAppId) headers.set("X-App-ID", platformAppId);
966
1223
  applyAuthHeaders(headers, this.credential);
967
- const requestId = generateRequestId();
1224
+ const actorEnvelope = opts.actorEnvelope === void 0 ? void 0 : parseActorEnvelope(opts.actorEnvelope);
1225
+ const requestId = actorEnvelope?.action.requestId ?? generateRequestId();
968
1226
  applyAuditHeaders(headers, this.agent, requestId);
969
1227
  if (opts.headers) {
970
1228
  for (const [k, v] of Object.entries(opts.headers)) headers.set(k, v);
971
1229
  }
1230
+ if (actorEnvelope) {
1231
+ headers.set(TOPOLO_ACTOR_ENVELOPE_HEADER, encodeActorEnvelopeHeader(actorEnvelope));
1232
+ }
972
1233
  if (opts.resource) {
973
1234
  const resourceType = opts.resource.resourceType.trim();
974
1235
  const resourceId = opts.resource.resourceId.trim();
@@ -1011,16 +1272,29 @@ var TopoloClient = class _TopoloClient {
1011
1272
  clearTimeout(timeoutId);
1012
1273
  }
1013
1274
  };
1275
+ const fetchWithReadRetry = async () => {
1276
+ const maxAttempts = method === "GET" ? 2 : 1;
1277
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
1278
+ try {
1279
+ return await fetchOnce();
1280
+ } catch (error) {
1281
+ const transportFailure = !(error instanceof TopoloSdkError) && !opts.signal?.aborted;
1282
+ if (!transportFailure || attempt === maxAttempts) throw error;
1283
+ await new Promise((resolve2) => setTimeout(resolve2, 250));
1284
+ }
1285
+ }
1286
+ throw new TopoloSdkError("network_error", "Topolo read retry loop exhausted.");
1287
+ };
1014
1288
  let res;
1015
1289
  try {
1016
1290
  const attemptedCredential = this.credential;
1017
- res = await fetchOnce();
1291
+ res = await fetchWithReadRetry();
1018
1292
  if (res.status === 401 && attemptedCredential.kind === "access_token" && this.refreshCredential) {
1019
1293
  await res.body?.cancel().catch(() => void 0);
1020
1294
  const refreshed = await this.refreshRejectedAccessToken(attemptedCredential.accessToken);
1021
1295
  if (refreshed) {
1022
1296
  applyAuthHeaders(headers, refreshed);
1023
- res = await fetchOnce();
1297
+ res = await fetchWithReadRetry();
1024
1298
  }
1025
1299
  }
1026
1300
  } catch (err) {
@@ -1402,14 +1676,14 @@ var TopoloClient = class _TopoloClient {
1402
1676
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
1403
1677
  throw new TopoloSdkError("action_catalog_summary_unavailable", "Action catalog summary is unavailable.");
1404
1678
  }
1405
- const record = raw;
1406
- if (record.algorithm !== "sha256-v1" || typeof record.digest !== "string" || !/^[a-f0-9]{64}$/.test(record.digest) || !Number.isSafeInteger(record.actionCount) || Number(record.actionCount) < 0) {
1679
+ const record2 = raw;
1680
+ if (record2.algorithm !== "sha256-v1" || typeof record2.digest !== "string" || !/^[a-f0-9]{64}$/.test(record2.digest) || !Number.isSafeInteger(record2.actionCount) || Number(record2.actionCount) < 0) {
1407
1681
  throw new TopoloSdkError("action_catalog_summary_unavailable", "Action catalog summary is invalid.");
1408
1682
  }
1409
1683
  return {
1410
1684
  algorithm: "sha256-v1",
1411
- digest: record.digest,
1412
- actionCount: Number(record.actionCount)
1685
+ digest: record2.digest,
1686
+ actionCount: Number(record2.actionCount)
1413
1687
  };
1414
1688
  }
1415
1689
  /** @deprecated Prefer searchActions() so callers retain the continuation cursor. */
@@ -1609,6 +1883,20 @@ var TopoloClient = class _TopoloClient {
1609
1883
  );
1610
1884
  }
1611
1885
  const mapped = mapActionInput(entry, input);
1886
+ const resolvedActorEnvelope = options.actorEnvelope ?? await this.buildVerifiedAgentActorEnvelope(entry.actionId, input, options.resource);
1887
+ if (resolvedActorEnvelope) {
1888
+ const actorEnvelope = parseActorEnvelope(resolvedActorEnvelope);
1889
+ if (actorEnvelope.action.actionId !== entry.actionId) {
1890
+ throw new TopoloSdkError("actor_envelope_action_mismatch", "Actor envelope actionId does not match the executed action.");
1891
+ }
1892
+ if (actorEnvelope.action.inputHash !== await sha256CanonicalValue(input)) {
1893
+ throw new TopoloSdkError("actor_envelope_input_mismatch", "Actor envelope inputHash does not match the executed action input.");
1894
+ }
1895
+ const envelopeResource = actorEnvelope.action.resourceType && actorEnvelope.action.resourceId ? { resourceType: actorEnvelope.action.resourceType, resourceId: actorEnvelope.action.resourceId } : void 0;
1896
+ if (envelopeResource?.resourceType !== options.resource?.resourceType || envelopeResource?.resourceId !== options.resource?.resourceId) {
1897
+ throw new TopoloSdkError("actor_envelope_resource_mismatch", "Actor envelope resource does not match the executed action resource.");
1898
+ }
1899
+ }
1612
1900
  const service = entry.appSlug || entry.appId;
1613
1901
  const result = await this.request({
1614
1902
  service,
@@ -1618,6 +1906,7 @@ var TopoloClient = class _TopoloClient {
1618
1906
  ...mapped.query ? { query: mapped.query } : {},
1619
1907
  ...mapped.body !== void 0 ? { body: mapped.body } : {},
1620
1908
  ...options.resource ? { resource: options.resource } : {},
1909
+ ...resolvedActorEnvelope ? { actorEnvelope: resolvedActorEnvelope } : {},
1621
1910
  headers: {
1622
1911
  [TOPOLO_ACTION_ID_HEADER]: entry.actionId,
1623
1912
  [TOPOLO_ACTION_NAME_HEADER]: entry.name
@@ -1626,6 +1915,56 @@ var TopoloClient = class _TopoloClient {
1626
1915
  });
1627
1916
  return redactActionResult(result);
1628
1917
  }
1918
+ async buildVerifiedAgentActorEnvelope(actionId, input, resource) {
1919
+ if (!this.actorContext) return void 0;
1920
+ const identity = await this.introspect();
1921
+ const principalType = identity.user.principalType;
1922
+ if (principalType !== "agent_employee" && principalType !== "external_agent") return void 0;
1923
+ if (!identity.organization || !identity.user.accountableUserId || !identity.user.principalId) {
1924
+ throw new TopoloSdkError(
1925
+ "incomplete_agent_identity",
1926
+ "Verified agent credentials must resolve an organization, accountable user, and principal id."
1927
+ );
1928
+ }
1929
+ const performer = principalType === "external_agent" ? {
1930
+ principalType: "external_agent",
1931
+ principalId: identity.user.principalId,
1932
+ installationId: identity.user.installationId ?? void 0,
1933
+ provider: identity.user.provider ?? void 0,
1934
+ model: identity.user.model ?? void 0
1935
+ } : {
1936
+ principalType: "managed_agent",
1937
+ principalId: identity.user.principalId,
1938
+ agentInstanceId: identity.user.agentInstanceId ?? void 0,
1939
+ model: identity.user.model ?? void 0
1940
+ };
1941
+ if (performer.principalType === "external_agent" && (!performer.installationId || !performer.provider) || performer.principalType === "managed_agent" && !performer.agentInstanceId) {
1942
+ throw new TopoloSdkError(
1943
+ "incomplete_agent_identity",
1944
+ "Verified agent credentials are missing the performer identity required for provenance."
1945
+ );
1946
+ }
1947
+ return parseActorEnvelope({
1948
+ schemaVersion: 1,
1949
+ organizationId: identity.organization.id,
1950
+ accountableSubject: { principalType: "human", principalId: identity.user.accountableUserId },
1951
+ performer,
1952
+ delegation: {
1953
+ taskId: this.actorContext.taskId,
1954
+ runId: this.actorContext.runId,
1955
+ lineage: [{ principalType: performer.principalType, principalId: performer.principalId }]
1956
+ },
1957
+ channel: this.actorContext.channel,
1958
+ assurance: { level: "workload_identity", methods: ["client_credentials"] },
1959
+ action: {
1960
+ requestId: generateRequestId(),
1961
+ actionId,
1962
+ ...resource ? resource : {},
1963
+ inputHash: await sha256CanonicalValue(input)
1964
+ },
1965
+ issuedAt: (/* @__PURE__ */ new Date()).toISOString()
1966
+ });
1967
+ }
1629
1968
  /**
1630
1969
  * Introspect the current credential. Returns the caller's resolved identity,
1631
1970
  * organization, and permission set. Used by CLI `whoami` and by MCP to gate
@@ -1653,7 +1992,18 @@ var TopoloClient = class _TopoloClient {
1653
1992
  email: user.email,
1654
1993
  name: user.name ?? null,
1655
1994
  role: user.role ?? (kind === "api_key" ? "service" : "member"),
1656
- permissions: payload.permissions ?? user.permissions ?? []
1995
+ permissions: payload.permissions ?? user.permissions ?? [],
1996
+ ...normalizeAgentPrincipalType(user.principalType) ? {
1997
+ principalId: readOptionalString(user.principalId),
1998
+ principalType: normalizeAgentPrincipalType(user.principalType),
1999
+ accountableUserId: readOptionalString(user.accountableUserId),
2000
+ agentInstanceId: readOptionalString(user.agentInstanceId),
2001
+ installationId: readOptionalString(user.installationId),
2002
+ provider: readOptionalString(user.provider),
2003
+ model: readOptionalString(user.model),
2004
+ serviceScopes: stringArray(user.serviceScopes),
2005
+ policyRef: readOptionalString(user.policyRef)
2006
+ } : {}
1657
2007
  },
1658
2008
  organization: organization ? {
1659
2009
  id: organization.id,
@@ -1672,26 +2022,29 @@ var TopoloClient = class _TopoloClient {
1672
2022
  };
1673
2023
  function validateActionResource(entry, resource) {
1674
2024
  const permissionName = entry.permissionName || entry.requiredPermission.split(".").at(-1) || "";
1675
- if (!permissionName.startsWith("workspace:")) return null;
2025
+ const declaredContext = entry.agentContract?.resourceContext;
2026
+ const declaredKind = typeof declaredContext?.kind === "string" ? declaredContext.kind.trim() : "";
2027
+ const expectedType = declaredKind || (permissionName.startsWith("workspace:") ? "workspace" : "");
2028
+ if (!expectedType || declaredContext?.required === false && !permissionName.startsWith("workspace:")) return null;
1676
2029
  if (!resource) {
1677
2030
  return {
1678
2031
  path: "$.resource",
1679
2032
  code: "required",
1680
- message: "Select a workspace with the application resource catalog before planning or calling this action."
2033
+ message: `Select resourceType "${expectedType}" with the application resource catalog before planning or calling this action.`
1681
2034
  };
1682
2035
  }
1683
- if (resource.resourceType !== "workspace") {
2036
+ if (resource.resourceType !== expectedType) {
1684
2037
  return {
1685
2038
  path: "$.resource.resourceType",
1686
2039
  code: "const",
1687
- message: 'This action requires resourceType "workspace".'
2040
+ message: `This action requires resourceType "${expectedType}".`
1688
2041
  };
1689
2042
  }
1690
2043
  if (!resource.resourceId.trim()) {
1691
2044
  return {
1692
2045
  path: "$.resource.resourceId",
1693
2046
  code: "required",
1694
- message: "This action requires a workspace resourceId."
2047
+ message: `This action requires a ${expectedType} resourceId.`
1695
2048
  };
1696
2049
  }
1697
2050
  return null;
@@ -1779,12 +2132,21 @@ function redactActionResult(value) {
1779
2132
  if (!isRecord3(value)) return value;
1780
2133
  return Object.fromEntries(Object.entries(value).map(([key, item]) => [
1781
2134
  key,
1782
- isSensitiveActionResultKey(key) ? "[REDACTED]" : redactActionResult(item)
2135
+ isSensitiveActionResultKey(key, item) ? "[REDACTED]" : redactActionResult(item)
1783
2136
  ]));
1784
2137
  }
1785
- function isSensitiveActionResultKey(key) {
2138
+ function isSensitiveActionResultKey(key, value) {
1786
2139
  const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
1787
- return ["authorization", "cookie", "setcookie", "proxyauthorization", "capability"].includes(normalized) || /(token|secret|password|privatekey|apikey)$/.test(normalized);
2140
+ if (normalized === "capability") return isCredentialShaped(value);
2141
+ return ["authorization", "cookie", "setcookie", "proxyauthorization"].includes(normalized) || /(token|secret|password|privatekey|apikey)$/.test(normalized);
2142
+ }
2143
+ var PLAIN_ENUM_VALUE = /^[a-z][a-z0-9_-]{0,31}$/;
2144
+ function isCredentialShaped(value) {
2145
+ if (typeof value !== "string") return value !== null && value !== void 0 && typeof value !== "boolean" && typeof value !== "number";
2146
+ const trimmed = value.trim();
2147
+ if (!trimmed) return false;
2148
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) return true;
2149
+ return !PLAIN_ENUM_VALUE.test(trimmed);
1788
2150
  }
1789
2151
  function resolveConfiguredServiceUrl(slug, serviceUrls) {
1790
2152
  for (const serviceKey of serviceKeyCandidates2(slug)) {
@@ -2006,6 +2368,9 @@ function normalizeCredentialAuthorizationMode(value, kind) {
2006
2368
  if (value === "context_rbac" || value === "oauth_scopes" || value === "session_rbac") return value;
2007
2369
  return "unknown";
2008
2370
  }
2371
+ function normalizeAgentPrincipalType(value) {
2372
+ return value === "agent_employee" || value === "external_agent" ? value : null;
2373
+ }
2009
2374
  function describeError(body, fallback) {
2010
2375
  if (body && typeof body === "object") {
2011
2376
  const maybe = body;
@@ -2030,10 +2395,10 @@ function extractRequired(body) {
2030
2395
  if (Array.isArray(maybe.required)) return canonicalTopoloRequired(maybe.required);
2031
2396
  for (const nested of [maybe.details, maybe.error]) {
2032
2397
  if (!nested || typeof nested !== "object" || Array.isArray(nested)) continue;
2033
- const record = nested;
2034
- if (Array.isArray(record.required)) return canonicalTopoloRequired(record.required);
2035
- if (record.details && typeof record.details === "object" && !Array.isArray(record.details)) {
2036
- const required = record.details.required;
2398
+ const record2 = nested;
2399
+ if (Array.isArray(record2.required)) return canonicalTopoloRequired(record2.required);
2400
+ if (record2.details && typeof record2.details === "object" && !Array.isArray(record2.details)) {
2401
+ const required = record2.details.required;
2037
2402
  if (Array.isArray(required)) return canonicalTopoloRequired(required);
2038
2403
  }
2039
2404
  }
@@ -2063,7 +2428,8 @@ function compactActionCatalogEntry(action) {
2063
2428
  agentAccess: action.agentAccess ?? (action.requiresConfirmation ? "confirm" : action.readOnly ? "auto" : "confirm"),
2064
2429
  requiredPermission: action.requiredPermission,
2065
2430
  requiredInput: required,
2066
- resourceType: action.agentContract?.resource?.kind ?? null,
2431
+ resourceType: action.agentContract?.resourceContext?.kind ?? (action.permissionName.startsWith("workspace:") ? "workspace" : null),
2432
+ resultResourceType: action.agentContract?.resource?.kind ?? null,
2067
2433
  exampleInput: example.value,
2068
2434
  exampleInputAvailable: example.available,
2069
2435
  docsUrl: action.agentContract?.docsUrl ?? null
@@ -2107,8 +2473,8 @@ function compactResultValue(value, depth, budget) {
2107
2473
  return value.length > ACTION_RESULT_MAX_ITEMS ? { count: value.length, items, omittedItems: value.length - ACTION_RESULT_MAX_ITEMS } : items;
2108
2474
  }
2109
2475
  if (typeof value === "object") {
2110
- const record = value;
2111
- const allKeys = Object.keys(record);
2476
+ const record2 = value;
2477
+ const allKeys = Object.keys(record2);
2112
2478
  const ranked = allKeys.sort((left, right) => resultKeyPriority(left) - resultKeyPriority(right));
2113
2479
  const prioritized = ranked.filter((key) => resultKeyPriority(key) < 2);
2114
2480
  const ordinary = ranked.filter((key) => resultKeyPriority(key) === 2);
@@ -2117,7 +2483,7 @@ function compactResultValue(value, depth, budget) {
2117
2483
  ...prioritized,
2118
2484
  ...ordinary.slice(0, ordinaryLimit)
2119
2485
  ].slice(0, ACTION_RESULT_MAX_KEYS);
2120
- const compact = Object.fromEntries(keys.map((key) => [key, compactResultValue(record[key], depth + 1, budget)]));
2486
+ const compact = Object.fromEntries(keys.map((key) => [key, compactResultValue(record2[key], depth + 1, budget)]));
2121
2487
  if (allKeys.length > keys.length) compact["_omittedKeys"] = allKeys.length - keys.length;
2122
2488
  return compact;
2123
2489
  }
@@ -2338,6 +2704,461 @@ var WorkspacesModule = class {
2338
2704
  return { organizationId: identity.organization.id, appId: service.appId };
2339
2705
  }
2340
2706
  };
2707
+ var nonEmptyString2 = { type: "string", minLength: 1 };
2708
+ var stringArray3 = { type: "array", items: nonEmptyString2 };
2709
+ var actionBinding = {
2710
+ type: "object",
2711
+ properties: {
2712
+ id: nonEmptyString2,
2713
+ label: nonEmptyString2,
2714
+ actionId: nonEmptyString2,
2715
+ resourceType: nonEmptyString2,
2716
+ resourceId: nonEmptyString2,
2717
+ input: { type: "object" },
2718
+ requiresConfirmation: { type: "boolean" },
2719
+ destructive: { type: "boolean" },
2720
+ effects: stringArray3,
2721
+ verification: stringArray3,
2722
+ recovery: {
2723
+ type: "object",
2724
+ properties: {
2725
+ mode: { enum: ["not_applicable", "retry", "compensating_action"] },
2726
+ actionId: nonEmptyString2,
2727
+ instructions: nonEmptyString2
2728
+ },
2729
+ required: ["mode", "instructions"],
2730
+ additionalProperties: false
2731
+ }
2732
+ },
2733
+ required: [
2734
+ "id",
2735
+ "label",
2736
+ "actionId",
2737
+ "resourceType",
2738
+ "input",
2739
+ "requiresConfirmation",
2740
+ "destructive",
2741
+ "effects",
2742
+ "verification",
2743
+ "recovery"
2744
+ ],
2745
+ additionalProperties: false
2746
+ };
2747
+ var dimension = {
2748
+ type: "object",
2749
+ properties: { key: nonEmptyString2, label: nonEmptyString2, value: nonEmptyString2 },
2750
+ required: ["key", "label", "value"],
2751
+ additionalProperties: false
2752
+ };
2753
+ var period = {
2754
+ type: "object",
2755
+ properties: {
2756
+ start: nonEmptyString2,
2757
+ end: nonEmptyString2,
2758
+ timezone: nonEmptyString2
2759
+ },
2760
+ required: ["start", "end", "timezone"],
2761
+ additionalProperties: false
2762
+ };
2763
+ var series = {
2764
+ type: "object",
2765
+ properties: {
2766
+ id: nonEmptyString2,
2767
+ label: nonEmptyString2,
2768
+ unit: nonEmptyString2,
2769
+ points: {
2770
+ type: "array",
2771
+ items: {
2772
+ type: "object",
2773
+ properties: {
2774
+ timestamp: nonEmptyString2,
2775
+ value: { type: "number" },
2776
+ dimensions: { type: "array", items: dimension }
2777
+ },
2778
+ required: ["timestamp", "value", "dimensions"],
2779
+ additionalProperties: false
2780
+ }
2781
+ }
2782
+ },
2783
+ required: ["id", "label", "unit", "points"],
2784
+ additionalProperties: false
2785
+ };
2786
+ var contribution = {
2787
+ type: "object",
2788
+ properties: {
2789
+ summary: nonEmptyString2,
2790
+ identity: {
2791
+ type: "object",
2792
+ properties: { iconSource: { const: "catalog" } },
2793
+ required: ["iconSource"],
2794
+ additionalProperties: false
2795
+ },
2796
+ workspaceSelector: {
2797
+ type: "object",
2798
+ properties: {
2799
+ mode: { enum: ["none", "optional", "required"] },
2800
+ resourceType: nonEmptyString2,
2801
+ label: nonEmptyString2,
2802
+ supportsMultiple: { type: "boolean" }
2803
+ },
2804
+ required: ["mode", "resourceType", "label", "supportsMultiple"],
2805
+ additionalProperties: false
2806
+ },
2807
+ metricDefinitions: {
2808
+ type: "array",
2809
+ items: {
2810
+ type: "object",
2811
+ properties: {
2812
+ key: nonEmptyString2,
2813
+ label: nonEmptyString2,
2814
+ description: nonEmptyString2,
2815
+ unit: nonEmptyString2,
2816
+ semantic: {
2817
+ enum: ["gauge", "counter", "rate", "duration", "currency", "percentage"]
2818
+ },
2819
+ aggregation: {
2820
+ enum: ["last", "sum", "average", "minimum", "maximum", "count", "distinct_count"]
2821
+ }
2822
+ },
2823
+ required: ["key", "label", "description", "unit", "semantic", "aggregation"],
2824
+ additionalProperties: false
2825
+ }
2826
+ },
2827
+ seriesDefinitions: {
2828
+ type: "array",
2829
+ items: {
2830
+ type: "object",
2831
+ properties: {
2832
+ key: nonEmptyString2,
2833
+ label: nonEmptyString2,
2834
+ metricKey: nonEmptyString2,
2835
+ unit: nonEmptyString2
2836
+ },
2837
+ required: ["key", "label", "metricKey", "unit"],
2838
+ additionalProperties: false
2839
+ }
2840
+ },
2841
+ dimensions: {
2842
+ type: "array",
2843
+ items: {
2844
+ type: "object",
2845
+ properties: {
2846
+ key: nonEmptyString2,
2847
+ label: nonEmptyString2,
2848
+ description: nonEmptyString2,
2849
+ valueType: { enum: ["string", "number", "boolean", "date", "datetime"] }
2850
+ },
2851
+ required: ["key", "label", "description", "valueType"],
2852
+ additionalProperties: false
2853
+ }
2854
+ },
2855
+ lifecycleStages: {
2856
+ type: "array",
2857
+ items: {
2858
+ type: "object",
2859
+ properties: {
2860
+ key: nonEmptyString2,
2861
+ label: nonEmptyString2,
2862
+ order: { type: "number" },
2863
+ terminal: { type: "boolean" }
2864
+ },
2865
+ required: ["key", "label", "order", "terminal"],
2866
+ additionalProperties: false
2867
+ }
2868
+ },
2869
+ events: {
2870
+ type: "array",
2871
+ items: {
2872
+ type: "object",
2873
+ properties: {
2874
+ key: nonEmptyString2,
2875
+ label: nonEmptyString2,
2876
+ description: nonEmptyString2
2877
+ },
2878
+ required: ["key", "label", "description"],
2879
+ additionalProperties: false
2880
+ }
2881
+ },
2882
+ datasets: {
2883
+ type: "array",
2884
+ minItems: 1,
2885
+ items: {
2886
+ type: "object",
2887
+ properties: {
2888
+ id: nonEmptyString2,
2889
+ label: nonEmptyString2,
2890
+ description: nonEmptyString2,
2891
+ grain: nonEmptyString2,
2892
+ metricKeys: stringArray3,
2893
+ seriesKeys: stringArray3,
2894
+ dimensionKeys: stringArray3,
2895
+ eventKeys: stringArray3,
2896
+ actionIds: stringArray3
2897
+ },
2898
+ required: [
2899
+ "id",
2900
+ "label",
2901
+ "description",
2902
+ "grain",
2903
+ "metricKeys",
2904
+ "seriesKeys",
2905
+ "dimensionKeys",
2906
+ "eventKeys",
2907
+ "actionIds"
2908
+ ],
2909
+ additionalProperties: false
2910
+ }
2911
+ },
2912
+ compatibleVisualizations: {
2913
+ type: "array",
2914
+ minItems: 1,
2915
+ items: {
2916
+ enum: ["number", "line", "area", "bar", "donut", "table", "funnel", "timeline", "actions"]
2917
+ }
2918
+ },
2919
+ defaultVisualization: {
2920
+ enum: ["number", "line", "area", "bar", "donut", "table", "funnel", "timeline", "actions"]
2921
+ }
2922
+ },
2923
+ required: [
2924
+ "summary",
2925
+ "identity",
2926
+ "workspaceSelector",
2927
+ "metricDefinitions",
2928
+ "seriesDefinitions",
2929
+ "dimensions",
2930
+ "lifecycleStages",
2931
+ "events",
2932
+ "datasets",
2933
+ "compatibleVisualizations",
2934
+ "defaultVisualization"
2935
+ ],
2936
+ additionalProperties: false
2937
+ };
2938
+ var widgetItem = {
2939
+ type: "object",
2940
+ properties: {
2941
+ id: nonEmptyString2,
2942
+ version: nonEmptyString2,
2943
+ appId: nonEmptyString2,
2944
+ serviceName: nonEmptyString2,
2945
+ lastUpdated: nonEmptyString2,
2946
+ type: { enum: ["stats", "chart", "activity", "notifications", "actions"] },
2947
+ presentation: {
2948
+ type: "object",
2949
+ properties: {
2950
+ title: nonEmptyString2,
2951
+ description: nonEmptyString2,
2952
+ contexts: {
2953
+ type: "array",
2954
+ minItems: 1,
2955
+ items: { enum: ["personal", "organization", "workspace", "resource"] }
2956
+ },
2957
+ supportedSizes: {
2958
+ type: "array",
2959
+ minItems: 1,
2960
+ items: { enum: ["compact", "medium", "wide", "full"] }
2961
+ },
2962
+ defaultSize: { enum: ["compact", "medium", "wide", "full"] }
2963
+ },
2964
+ required: ["title", "contexts", "supportedSizes", "defaultSize"],
2965
+ additionalProperties: false
2966
+ },
2967
+ resource: {
2968
+ type: "object",
2969
+ properties: {
2970
+ resourceType: nonEmptyString2,
2971
+ resourceId: nonEmptyString2,
2972
+ requiredPermissions: stringArray3
2973
+ },
2974
+ required: ["resourceType", "requiredPermissions"],
2975
+ additionalProperties: false
2976
+ },
2977
+ state: {
2978
+ type: "object",
2979
+ properties: {
2980
+ status: { enum: ["ready", "empty", "unavailable", "stale", "error"] },
2981
+ observedAt: nonEmptyString2,
2982
+ lastSuccessfulAt: nonEmptyString2,
2983
+ code: nonEmptyString2,
2984
+ message: nonEmptyString2
2985
+ },
2986
+ required: ["status", "observedAt"],
2987
+ additionalProperties: false
2988
+ },
2989
+ refresh: {
2990
+ type: "object",
2991
+ properties: {
2992
+ intervalSeconds: { type: "number" },
2993
+ timeoutSeconds: { type: "number" },
2994
+ maxAgeSeconds: { type: "number" },
2995
+ staleWhileRevalidateSeconds: { type: "number" }
2996
+ },
2997
+ required: ["intervalSeconds", "timeoutSeconds", "maxAgeSeconds", "staleWhileRevalidateSeconds"],
2998
+ additionalProperties: false
2999
+ },
3000
+ configuration: {
3001
+ type: "object",
3002
+ properties: {
3003
+ allowMultipleInstances: { type: "boolean" },
3004
+ fields: {
3005
+ type: "array",
3006
+ items: {
3007
+ type: "object",
3008
+ properties: {
3009
+ key: nonEmptyString2,
3010
+ label: nonEmptyString2,
3011
+ type: { enum: ["string", "number", "boolean", "select", "multi-select", "date-range"] },
3012
+ required: { type: "boolean" },
3013
+ options: {
3014
+ type: "array",
3015
+ items: {
3016
+ type: "object",
3017
+ properties: { value: nonEmptyString2, label: nonEmptyString2 },
3018
+ required: ["value", "label"],
3019
+ additionalProperties: false
3020
+ }
3021
+ }
3022
+ },
3023
+ required: ["key", "label", "type", "required"],
3024
+ additionalProperties: false
3025
+ }
3026
+ },
3027
+ defaults: { type: "object" }
3028
+ },
3029
+ required: ["allowMultipleInstances", "fields", "defaults"],
3030
+ additionalProperties: false
3031
+ },
3032
+ drilldowns: {
3033
+ type: "array",
3034
+ items: {
3035
+ type: "object",
3036
+ properties: { id: nonEmptyString2, label: nonEmptyString2, href: nonEmptyString2 },
3037
+ required: ["id", "label", "href"],
3038
+ additionalProperties: false
3039
+ }
3040
+ },
3041
+ controls: { type: "array", items: actionBinding },
3042
+ localization: {
3043
+ type: "object",
3044
+ properties: {
3045
+ locale: nonEmptyString2,
3046
+ titleKey: nonEmptyString2,
3047
+ descriptionKey: nonEmptyString2,
3048
+ title: nonEmptyString2,
3049
+ description: nonEmptyString2
3050
+ },
3051
+ required: ["locale", "titleKey"],
3052
+ additionalProperties: false
3053
+ },
3054
+ accessibility: {
3055
+ type: "object",
3056
+ properties: { label: nonEmptyString2, description: nonEmptyString2 },
3057
+ required: ["label"],
3058
+ additionalProperties: false
3059
+ },
3060
+ contribution,
3061
+ metrics: {
3062
+ type: "array",
3063
+ items: {
3064
+ type: "object",
3065
+ properties: {
3066
+ key: nonEmptyString2,
3067
+ label: nonEmptyString2,
3068
+ value: { type: "number" },
3069
+ unit: nonEmptyString2,
3070
+ semantic: {
3071
+ enum: ["gauge", "counter", "rate", "duration", "currency", "percentage"]
3072
+ },
3073
+ aggregation: {
3074
+ enum: ["last", "sum", "average", "minimum", "maximum", "count", "distinct_count"]
3075
+ },
3076
+ period,
3077
+ dimensions: { type: "array", items: dimension },
3078
+ series
3079
+ },
3080
+ required: ["key", "label", "value", "unit", "semantic", "aggregation", "period", "dimensions"],
3081
+ additionalProperties: false
3082
+ }
3083
+ },
3084
+ chartType: { enum: ["line", "bar", "area", "pie", "donut"] },
3085
+ series: { type: "array", items: series },
3086
+ activities: { type: "array", items: { type: "object" } },
3087
+ notifications: { type: "array", items: { type: "object" } },
3088
+ unreadCount: { type: "number" },
3089
+ actions: { type: "array", items: actionBinding }
3090
+ },
3091
+ required: [
3092
+ "id",
3093
+ "version",
3094
+ "appId",
3095
+ "serviceName",
3096
+ "lastUpdated",
3097
+ "type",
3098
+ "presentation",
3099
+ "resource",
3100
+ "state",
3101
+ "refresh",
3102
+ "configuration",
3103
+ "drilldowns",
3104
+ "controls",
3105
+ "localization",
3106
+ "accessibility",
3107
+ "contribution"
3108
+ ],
3109
+ allOf: [
3110
+ { if: { properties: { type: { const: "stats" } } }, then: { required: ["metrics"] } },
3111
+ { if: { properties: { type: { const: "chart" } } }, then: { required: ["chartType", "series"] } },
3112
+ { if: { properties: { type: { const: "activity" } } }, then: { required: ["activities"] } },
3113
+ {
3114
+ if: { properties: { type: { const: "notifications" } } },
3115
+ then: { required: ["notifications", "unreadCount"] }
3116
+ },
3117
+ { if: { properties: { type: { const: "actions" } } }, then: { required: ["actions"] } }
3118
+ ],
3119
+ additionalProperties: false
3120
+ };
3121
+ var TOPOLO_WIDGET_OUTPUT_SCHEMA = Object.freeze({
3122
+ $schema: "https://json-schema.org/draft/2020-12/schema",
3123
+ type: "object",
3124
+ properties: {
3125
+ success: { type: "boolean", const: true },
3126
+ contractVersion: { type: "string", const: "3.0.0" },
3127
+ appId: nonEmptyString2,
3128
+ serviceName: nonEmptyString2,
3129
+ generatedAt: nonEmptyString2,
3130
+ widgets: { type: "array", minItems: 1, items: widgetItem },
3131
+ workspace: {
3132
+ anyOf: [
3133
+ {
3134
+ type: "object",
3135
+ properties: {
3136
+ id: { anyOf: [{ type: "string" }, { type: "number" }] },
3137
+ name: nonEmptyString2
3138
+ },
3139
+ required: ["id", "name"],
3140
+ additionalProperties: true
3141
+ },
3142
+ { type: "null" }
3143
+ ]
3144
+ },
3145
+ workspaces: {
3146
+ type: "array",
3147
+ items: {
3148
+ type: "object",
3149
+ properties: {
3150
+ id: { anyOf: [{ type: "string" }, { type: "number" }] },
3151
+ name: nonEmptyString2,
3152
+ isDefault: { type: "boolean" }
3153
+ },
3154
+ required: ["id", "name"],
3155
+ additionalProperties: false
3156
+ }
3157
+ }
3158
+ },
3159
+ required: ["success", "contractVersion", "appId", "serviceName", "generatedAt", "widgets"],
3160
+ additionalProperties: false
3161
+ });
2341
3162
  function createTopolo(options) {
2342
3163
  const client = new TopoloClient(options);
2343
3164
  return {
@@ -2348,7 +3169,7 @@ function createTopolo(options) {
2348
3169
  };
2349
3170
  }
2350
3171
 
2351
- // node_modules/@topolo/sdk/dist/node/credential-store.js
3172
+ // node_modules/.pnpm/@topolo+sdk@0.17.1/node_modules/@topolo/sdk/dist/node/credential-store.js
2352
3173
  import { mkdir, readFile, writeFile, chmod, rm, stat } from "fs/promises";
2353
3174
  import { existsSync } from "fs";
2354
3175
  import { homedir, platform } from "os";
@@ -2411,6 +3232,7 @@ var ERROR_CODE_PATTERN2 = /^[a-z][a-z0-9_]{0,63}$/;
2411
3232
  var TOPOLO_REQUEST_ID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
2412
3233
  var CLOUDFLARE_RAY_ID_PATTERN2 = /^[0-9a-f]{16}-[A-Z]{3}$/;
2413
3234
  var PERMISSION_PATTERN2 = /^[a-z][a-z0-9_-]{0,63}(?:\.[a-z][a-z0-9_-]{0,63})*:(?:[a-z][a-z0-9_-]{0,63}|\*)$/;
3235
+ var PUBLIC_FIELD_PATH_PATTERN2 = /^[$.a-z0-9_[\]-]{1,160}$/i;
2414
3236
  var RETRY_AFTER_DATE_PATTERN2 = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
2415
3237
  var PUBLIC_DETAIL_SOURCES2 = /* @__PURE__ */ new Set([
2416
3238
  "api_key_introspection",
@@ -2470,10 +3292,10 @@ var TopoloAuthError2 = class extends TopoloSdkError2 {
2470
3292
  };
2471
3293
  function extractTopoloServiceErrorCode2(body) {
2472
3294
  if (!body || typeof body !== "object" || Array.isArray(body)) return null;
2473
- const record = body;
2474
- const topLevel = canonicalTopoloErrorCode2(record.code);
3295
+ const record2 = body;
3296
+ const topLevel = canonicalTopoloErrorCode2(record2.code);
2475
3297
  if (topLevel) return topLevel;
2476
- const nested = record.error;
3298
+ const nested = record2.error;
2477
3299
  const nestedString = canonicalTopoloErrorCode2(nested);
2478
3300
  if (nestedString) return nestedString;
2479
3301
  if (nested && typeof nested === "object" && !Array.isArray(nested)) {
@@ -2488,15 +3310,19 @@ function serviceErrorCode2(body, fallback) {
2488
3310
  }
2489
3311
  function extractTopoloServiceErrorDetails2(body) {
2490
3312
  if (!body || typeof body !== "object" || Array.isArray(body)) return void 0;
2491
- const record = body;
2492
- let candidate = record.details;
2493
- const nested = record.error;
3313
+ const record2 = body;
3314
+ let candidate = record2.details;
3315
+ const nested = record2.error;
2494
3316
  if (nested && typeof nested === "object" && !Array.isArray(nested)) {
2495
3317
  const nestedRecord = nested;
2496
3318
  if (candidate === void 0) candidate = nestedRecord.details ?? nestedRecord;
2497
3319
  }
2498
- if (candidate === void 0) candidate = record;
2499
- if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) return void 0;
3320
+ if (candidate === void 0) candidate = record2;
3321
+ if (Array.isArray(candidate)) {
3322
+ const paths = canonicalTopoloValidationPaths2(candidate);
3323
+ return paths.length ? { paths } : void 0;
3324
+ }
3325
+ if (!candidate || typeof candidate !== "object") return void 0;
2500
3326
  const source = candidate;
2501
3327
  const safe = {};
2502
3328
  for (const [key, value] of Object.entries(source)) {
@@ -2533,12 +3359,42 @@ function extractTopoloServiceErrorDetails2(body) {
2533
3359
  continue;
2534
3360
  }
2535
3361
  if (["missingFields", "fields", "paths"].includes(key) && Array.isArray(value)) {
2536
- const paths = value.filter((item) => typeof item === "string" && /^[$.a-z0-9_[\]-]{1,160}$/i.test(item));
3362
+ const paths = value.filter((item) => typeof item === "string" && PUBLIC_FIELD_PATH_PATTERN2.test(item));
2537
3363
  if (paths.length) safe[key] = paths;
2538
3364
  }
2539
3365
  }
2540
3366
  return Object.keys(safe).length ? safe : void 0;
2541
3367
  }
3368
+ function canonicalTopoloValidationPaths2(issues) {
3369
+ const paths = [];
3370
+ for (const issue of issues) {
3371
+ if (!issue || typeof issue !== "object" || Array.isArray(issue)) continue;
3372
+ const record2 = issue;
3373
+ const path = canonicalTopoloValidationPath2(record2.field) ?? canonicalTopoloValidationPath2(record2.path);
3374
+ if (path && !paths.includes(path)) paths.push(path);
3375
+ if (paths.length === 32) break;
3376
+ }
3377
+ return paths;
3378
+ }
3379
+ function canonicalTopoloValidationPath2(value) {
3380
+ if (typeof value === "string") {
3381
+ const path2 = value.trim();
3382
+ return PUBLIC_FIELD_PATH_PATTERN2.test(path2) ? path2 : void 0;
3383
+ }
3384
+ if (!Array.isArray(value) || value.length === 0 || value.length > 32) return void 0;
3385
+ const segments = value.map((segment) => {
3386
+ if (typeof segment === "number" && Number.isSafeInteger(segment) && segment >= 0) {
3387
+ return String(segment);
3388
+ }
3389
+ if (typeof segment === "string" && /^[a-z0-9_$-]{1,80}$/i.test(segment)) {
3390
+ return segment;
3391
+ }
3392
+ return null;
3393
+ });
3394
+ if (segments.includes(null)) return void 0;
3395
+ const path = segments.join(".");
3396
+ return PUBLIC_FIELD_PATH_PATTERN2.test(path) ? path : void 0;
3397
+ }
2542
3398
  var TopoloHttpError2 = class extends TopoloSdkError2 {
2543
3399
  status;
2544
3400
  body;
@@ -3047,7 +3903,7 @@ function summarizeActionCapabilities(service, actions) {
3047
3903
  mutating: actions.filter((action) => !action.readOnly).length,
3048
3904
  destructive: actions.filter((action) => action.destructive).length,
3049
3905
  confirmationRequired: actions.filter((action) => action.requiresConfirmation).length,
3050
- resourceTypes: [...new Set(actions.map((action) => action.agentContract?.resource?.kind).filter(Boolean))].sort()
3906
+ resourceTypes: [...new Set(actions.map((action) => action.agentContract?.resourceContext?.kind ?? (action.permissionName.startsWith("workspace:") ? "workspace" : null)).filter(Boolean))].sort()
3051
3907
  };
3052
3908
  }
3053
3909
  function coerceObjectParam(value, name) {
@@ -3953,6 +4809,7 @@ async function runStdioMcp(dependencies = DEFAULT_DEPENDENCIES2) {
3953
4809
  const topolo = dependencies.createTopolo({
3954
4810
  credential,
3955
4811
  environment,
4812
+ actorContext: { channel: "mcp" },
3956
4813
  agent: {
3957
4814
  clientName: "topolo-mcp",
3958
4815
  clientVersion: MCP_VERSION,