@topolo/sdk 0.15.0 → 0.16.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
@@ -843,6 +843,242 @@ function isMultipleOf(value, divisor) {
843
843
  return Math.abs(quotient - Math.round(quotient)) < Number.EPSILON * Math.max(1, Math.abs(quotient)) * 4;
844
844
  }
845
845
 
846
+ // src/actor-envelope.ts
847
+ var ACTOR_ENVELOPE_VERSION = 1;
848
+ var TOPOLO_ACTOR_ENVELOPE_HEADER = "X-Topolo-Actor-Envelope";
849
+ var PRINCIPAL_TYPES = /* @__PURE__ */ new Set([
850
+ "human",
851
+ "managed_agent",
852
+ "external_agent",
853
+ "service",
854
+ "browser_automation"
855
+ ]);
856
+ var ACCOUNTABLE_SUBJECT_TYPES = /* @__PURE__ */ new Set(["human", "organization"]);
857
+ var CHANNELS = /* @__PURE__ */ new Set(["web", "mobile", "cli", "mcp", "api", "workflow", "browser_automation"]);
858
+ var ASSURANCE_LEVELS = /* @__PURE__ */ new Set(["authenticated", "workload_identity", "user_verified"]);
859
+ var HASH_PATTERN = /^sha256:[a-f0-9]{64}$/;
860
+ var ActorEnvelopeValidationError = class extends Error {
861
+ path;
862
+ constructor(path, message) {
863
+ super(`${path}: ${message}`);
864
+ this.name = "ActorEnvelopeValidationError";
865
+ this.path = path;
866
+ }
867
+ };
868
+ function parseActorEnvelope(value) {
869
+ const envelope = record(value, "actorEnvelope");
870
+ exactKeys(envelope, [
871
+ "schemaVersion",
872
+ "organizationId",
873
+ "accountableSubject",
874
+ "performer",
875
+ "delegation",
876
+ "channel",
877
+ "assurance",
878
+ "action",
879
+ "issuedAt",
880
+ "expiresAt"
881
+ ], "actorEnvelope");
882
+ if (envelope.schemaVersion !== ACTOR_ENVELOPE_VERSION) {
883
+ fail("actorEnvelope.schemaVersion", `must equal ${ACTOR_ENVELOPE_VERSION}`);
884
+ }
885
+ const accountableSubject = parseAccountableSubject(envelope.accountableSubject);
886
+ const performer = parseIdentity(envelope.performer, "actorEnvelope.performer");
887
+ const organizationId = text(envelope.organizationId, "actorEnvelope.organizationId");
888
+ const channel = member(envelope.channel, CHANNELS, "actorEnvelope.channel");
889
+ const issuedAt = timestamp(envelope.issuedAt, "actorEnvelope.issuedAt");
890
+ const expiresAt = envelope.expiresAt === void 0 ? void 0 : timestamp(envelope.expiresAt, "actorEnvelope.expiresAt");
891
+ if (expiresAt && Date.parse(expiresAt) <= Date.parse(issuedAt)) {
892
+ fail("actorEnvelope.expiresAt", "must be later than issuedAt");
893
+ }
894
+ const assuranceValue = record(envelope.assurance, "actorEnvelope.assurance");
895
+ exactKeys(assuranceValue, ["level", "methods"], "actorEnvelope.assurance");
896
+ const assurance = {
897
+ level: member(assuranceValue.level, ASSURANCE_LEVELS, "actorEnvelope.assurance.level"),
898
+ methods: textArray(assuranceValue.methods, "actorEnvelope.assurance.methods")
899
+ };
900
+ const actionValue = record(envelope.action, "actorEnvelope.action");
901
+ exactKeys(actionValue, ["requestId", "actionId", "resourceType", "resourceId", "inputHash"], "actorEnvelope.action");
902
+ const inputHash = text(actionValue.inputHash, "actorEnvelope.action.inputHash");
903
+ if (!HASH_PATTERN.test(inputHash)) fail("actorEnvelope.action.inputHash", "must be a lowercase sha256 digest");
904
+ const action = {
905
+ requestId: text(actionValue.requestId, "actorEnvelope.action.requestId"),
906
+ actionId: text(actionValue.actionId, "actorEnvelope.action.actionId"),
907
+ ...actionValue.resourceType === void 0 ? {} : { resourceType: text(actionValue.resourceType, "actorEnvelope.action.resourceType") },
908
+ ...actionValue.resourceId === void 0 ? {} : { resourceId: text(actionValue.resourceId, "actorEnvelope.action.resourceId") },
909
+ inputHash
910
+ };
911
+ if (action.resourceType === void 0 !== (action.resourceId === void 0)) {
912
+ fail("actorEnvelope.action", "resourceType and resourceId must be provided together");
913
+ }
914
+ const delegation = parseDelegation(envelope.delegation);
915
+ const directHuman = performer.principalType === "human" && performer.principalId === accountableSubject.principalId;
916
+ if (directHuman && delegation !== null) fail("actorEnvelope.delegation", "must be null for direct human action");
917
+ if (!directHuman && delegation === null) fail("actorEnvelope.delegation", "is required when performer differs from accountable subject");
918
+ if (delegation) {
919
+ const finalHop = delegation.lineage.at(-1);
920
+ if (!finalHop || finalHop.principalId !== performer.principalId || finalHop.principalType !== performer.principalType) {
921
+ fail("actorEnvelope.delegation.lineage", "must end with the performer");
922
+ }
923
+ }
924
+ if (performer.principalType === "external_agent" && (!performer.installationId || !performer.provider)) {
925
+ fail("actorEnvelope.performer", "external_agent requires installationId and provider");
926
+ }
927
+ if (performer.principalType === "managed_agent" && !performer.agentInstanceId) {
928
+ fail("actorEnvelope.performer.agentInstanceId", "is required for managed_agent");
929
+ }
930
+ if (performer.principalType === "browser_automation" && (!performer.installationId || !performer.browserSessionId)) {
931
+ fail("actorEnvelope.performer", "browser_automation requires installationId and browserSessionId");
932
+ }
933
+ return {
934
+ schemaVersion: ACTOR_ENVELOPE_VERSION,
935
+ organizationId,
936
+ accountableSubject,
937
+ performer,
938
+ delegation,
939
+ channel,
940
+ assurance,
941
+ action,
942
+ issuedAt,
943
+ ...expiresAt ? { expiresAt } : {}
944
+ };
945
+ }
946
+ function canonicalizeActorEnvelope(value) {
947
+ return canonicalJson(parseActorEnvelope(value));
948
+ }
949
+ function encodeActorEnvelopeHeader(value) {
950
+ const bytes = new TextEncoder().encode(canonicalizeActorEnvelope(value));
951
+ const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
952
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
953
+ }
954
+ function decodeActorEnvelopeHeader(value) {
955
+ if (!/^[A-Za-z0-9_-]+$/.test(value)) {
956
+ fail("actorEnvelopeHeader", "must be unpadded base64url");
957
+ }
958
+ const padded = value.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - value.length % 4) % 4);
959
+ let bytes;
960
+ try {
961
+ bytes = Uint8Array.from(atob(padded), (character) => character.charCodeAt(0));
962
+ } catch {
963
+ fail("actorEnvelopeHeader", "must be valid base64url");
964
+ }
965
+ try {
966
+ return parseActorEnvelope(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)));
967
+ } catch (error) {
968
+ if (error instanceof ActorEnvelopeValidationError) throw error;
969
+ fail("actorEnvelopeHeader", "must contain a valid UTF-8 actor envelope");
970
+ }
971
+ }
972
+ function canonicalJson(value) {
973
+ if (value === null) return "null";
974
+ if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
975
+ if (typeof value === "number") {
976
+ if (!Number.isFinite(value)) throw new TypeError("Canonical JSON accepts finite numbers only.");
977
+ return JSON.stringify(value);
978
+ }
979
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
980
+ if (typeof value !== "object" || !value || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
981
+ throw new TypeError("Canonical JSON accepts JSON objects, arrays, and primitives only.");
982
+ }
983
+ const entries = Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right));
984
+ return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
985
+ }
986
+ function parseAccountableSubject(value) {
987
+ const path = "actorEnvelope.accountableSubject";
988
+ const subject = record(value, path);
989
+ exactKeys(subject, ["principalType", "principalId", "displayName"], path);
990
+ return {
991
+ principalType: member(subject.principalType, ACCOUNTABLE_SUBJECT_TYPES, `${path}.principalType`),
992
+ principalId: text(subject.principalId, `${path}.principalId`),
993
+ ...subject.displayName === void 0 ? {} : { displayName: text(subject.displayName, `${path}.displayName`) }
994
+ };
995
+ }
996
+ async function sha256CanonicalValue(value) {
997
+ const bytes = new TextEncoder().encode(canonicalJson(value));
998
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
999
+ return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
1000
+ }
1001
+ function parseIdentity(value, path) {
1002
+ const identity = record(value, path);
1003
+ exactKeys(identity, [
1004
+ "principalType",
1005
+ "principalId",
1006
+ "displayName",
1007
+ "installationId",
1008
+ "agentInstanceId",
1009
+ "browserSessionId",
1010
+ "provider",
1011
+ "model"
1012
+ ], path);
1013
+ return {
1014
+ principalType: member(identity.principalType, PRINCIPAL_TYPES, `${path}.principalType`),
1015
+ principalId: text(identity.principalId, `${path}.principalId`),
1016
+ ...optionalTextFields(identity, path, [
1017
+ "displayName",
1018
+ "installationId",
1019
+ "agentInstanceId",
1020
+ "browserSessionId",
1021
+ "provider",
1022
+ "model"
1023
+ ])
1024
+ };
1025
+ }
1026
+ function parseDelegation(value) {
1027
+ if (value === null) return null;
1028
+ const delegation = record(value, "actorEnvelope.delegation");
1029
+ exactKeys(delegation, ["taskId", "runId", "lineage"], "actorEnvelope.delegation");
1030
+ if (!Array.isArray(delegation.lineage) || delegation.lineage.length === 0 || delegation.lineage.length > 16) {
1031
+ fail("actorEnvelope.delegation.lineage", "must contain between 1 and 16 hops");
1032
+ }
1033
+ return {
1034
+ taskId: text(delegation.taskId, "actorEnvelope.delegation.taskId"),
1035
+ runId: text(delegation.runId, "actorEnvelope.delegation.runId"),
1036
+ lineage: delegation.lineage.map((value2, index) => {
1037
+ const hop = record(value2, `actorEnvelope.delegation.lineage[${index}]`);
1038
+ exactKeys(hop, ["principalType", "principalId"], `actorEnvelope.delegation.lineage[${index}]`);
1039
+ return {
1040
+ principalType: member(hop.principalType, PRINCIPAL_TYPES, `actorEnvelope.delegation.lineage[${index}].principalType`),
1041
+ principalId: text(hop.principalId, `actorEnvelope.delegation.lineage[${index}].principalId`)
1042
+ };
1043
+ })
1044
+ };
1045
+ }
1046
+ function record(value, path) {
1047
+ if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "must be an object");
1048
+ return value;
1049
+ }
1050
+ function exactKeys(value, allowed, path) {
1051
+ const unknown = Object.keys(value).filter((key) => !allowed.includes(key));
1052
+ if (unknown.length) fail(path, `contains unknown field ${unknown[0]}`);
1053
+ }
1054
+ function text(value, path) {
1055
+ if (typeof value !== "string" || value.trim() !== value || value.length === 0 || value.length > 256) {
1056
+ fail(path, "must be a non-empty trimmed string of at most 256 characters");
1057
+ }
1058
+ return value;
1059
+ }
1060
+ function timestamp(value, path) {
1061
+ const result = text(value, path);
1062
+ if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(result) || Number.isNaN(Date.parse(result))) {
1063
+ fail(path, "must be an RFC 3339 UTC timestamp");
1064
+ }
1065
+ return result;
1066
+ }
1067
+ function member(value, values, path) {
1068
+ if (typeof value !== "string" || !values.has(value)) fail(path, "contains an unsupported value");
1069
+ return value;
1070
+ }
1071
+ function textArray(value, path) {
1072
+ if (!Array.isArray(value) || value.length === 0 || value.length > 16) fail(path, "must contain between 1 and 16 values");
1073
+ return value.map((item, index) => text(item, `${path}[${index}]`));
1074
+ }
1075
+ function optionalTextFields(value, path, keys) {
1076
+ return Object.fromEntries(keys.flatMap((key) => value[key] === void 0 ? [] : [[key, text(value[key], `${path}.${key}`)]]));
1077
+ }
1078
+ function fail(path, message) {
1079
+ throw new ActorEnvelopeValidationError(path, message);
1080
+ }
1081
+
846
1082
  // src/action-upload.ts
847
1083
  function parseTopoloActionUploadContract(value) {
848
1084
  if (!isRecord2(value) || value.profile !== "inline-base64" && value.profile !== "url-session") {
@@ -1133,11 +1369,15 @@ var TopoloClient = class _TopoloClient {
1133
1369
  const platformAppId = resolveConfiguredAppId(resolvedService.serviceKey, this.appIds);
1134
1370
  if (platformAppId) headers.set("X-App-ID", platformAppId);
1135
1371
  applyAuthHeaders(headers, this.credential);
1136
- const requestId = generateRequestId();
1372
+ const actorEnvelope = opts.actorEnvelope === void 0 ? void 0 : parseActorEnvelope(opts.actorEnvelope);
1373
+ const requestId = actorEnvelope?.action.requestId ?? generateRequestId();
1137
1374
  applyAuditHeaders(headers, this.agent, requestId);
1138
1375
  if (opts.headers) {
1139
1376
  for (const [k, v] of Object.entries(opts.headers)) headers.set(k, v);
1140
1377
  }
1378
+ if (actorEnvelope) {
1379
+ headers.set(TOPOLO_ACTOR_ENVELOPE_HEADER, encodeActorEnvelopeHeader(actorEnvelope));
1380
+ }
1141
1381
  if (opts.resource) {
1142
1382
  const resourceType = opts.resource.resourceType.trim();
1143
1383
  const resourceId = opts.resource.resourceId.trim();
@@ -1791,6 +2031,19 @@ var TopoloClient = class _TopoloClient {
1791
2031
  );
1792
2032
  }
1793
2033
  const mapped = mapActionInput(entry, input);
2034
+ if (options.actorEnvelope) {
2035
+ const actorEnvelope = parseActorEnvelope(options.actorEnvelope);
2036
+ if (actorEnvelope.action.actionId !== entry.actionId) {
2037
+ throw new TopoloSdkError("actor_envelope_action_mismatch", "Actor envelope actionId does not match the executed action.");
2038
+ }
2039
+ if (actorEnvelope.action.inputHash !== await sha256CanonicalValue(input)) {
2040
+ throw new TopoloSdkError("actor_envelope_input_mismatch", "Actor envelope inputHash does not match the executed action input.");
2041
+ }
2042
+ const envelopeResource = actorEnvelope.action.resourceType && actorEnvelope.action.resourceId ? { resourceType: actorEnvelope.action.resourceType, resourceId: actorEnvelope.action.resourceId } : void 0;
2043
+ if (envelopeResource?.resourceType !== options.resource?.resourceType || envelopeResource?.resourceId !== options.resource?.resourceId) {
2044
+ throw new TopoloSdkError("actor_envelope_resource_mismatch", "Actor envelope resource does not match the executed action resource.");
2045
+ }
2046
+ }
1794
2047
  const service = entry.appSlug || entry.appId;
1795
2048
  const result = await this.request({
1796
2049
  service,
@@ -1800,6 +2053,7 @@ var TopoloClient = class _TopoloClient {
1800
2053
  ...mapped.query ? { query: mapped.query } : {},
1801
2054
  ...mapped.body !== void 0 ? { body: mapped.body } : {},
1802
2055
  ...options.resource ? { resource: options.resource } : {},
2056
+ ...options.actorEnvelope ? { actorEnvelope: options.actorEnvelope } : {},
1803
2057
  headers: {
1804
2058
  [TOPOLO_ACTION_ID_HEADER]: entry.actionId,
1805
2059
  [TOPOLO_ACTION_NAME_HEADER]: entry.name
@@ -2924,218 +3178,6 @@ var IdentityModule = class {
2924
3178
  }
2925
3179
  };
2926
3180
 
2927
- // src/actor-envelope.ts
2928
- var ACTOR_ENVELOPE_VERSION = 1;
2929
- var PRINCIPAL_TYPES = /* @__PURE__ */ new Set([
2930
- "human",
2931
- "managed_agent",
2932
- "external_agent",
2933
- "service",
2934
- "browser_automation"
2935
- ]);
2936
- var ACCOUNTABLE_SUBJECT_TYPES = /* @__PURE__ */ new Set(["human", "organization"]);
2937
- var CHANNELS = /* @__PURE__ */ new Set(["web", "mobile", "cli", "mcp", "api", "workflow", "browser_automation"]);
2938
- var ASSURANCE_LEVELS = /* @__PURE__ */ new Set(["authenticated", "workload_identity", "user_verified"]);
2939
- var HASH_PATTERN = /^sha256:[a-f0-9]{64}$/;
2940
- var ActorEnvelopeValidationError = class extends Error {
2941
- path;
2942
- constructor(path, message) {
2943
- super(`${path}: ${message}`);
2944
- this.name = "ActorEnvelopeValidationError";
2945
- this.path = path;
2946
- }
2947
- };
2948
- function parseActorEnvelope(value) {
2949
- const envelope = record(value, "actorEnvelope");
2950
- exactKeys(envelope, [
2951
- "schemaVersion",
2952
- "organizationId",
2953
- "accountableSubject",
2954
- "performer",
2955
- "delegation",
2956
- "channel",
2957
- "assurance",
2958
- "action",
2959
- "issuedAt",
2960
- "expiresAt"
2961
- ], "actorEnvelope");
2962
- if (envelope.schemaVersion !== ACTOR_ENVELOPE_VERSION) {
2963
- fail("actorEnvelope.schemaVersion", `must equal ${ACTOR_ENVELOPE_VERSION}`);
2964
- }
2965
- const accountableSubject = parseAccountableSubject(envelope.accountableSubject);
2966
- const performer = parseIdentity(envelope.performer, "actorEnvelope.performer");
2967
- const organizationId = text(envelope.organizationId, "actorEnvelope.organizationId");
2968
- const channel = member(envelope.channel, CHANNELS, "actorEnvelope.channel");
2969
- const issuedAt = timestamp(envelope.issuedAt, "actorEnvelope.issuedAt");
2970
- const expiresAt = envelope.expiresAt === void 0 ? void 0 : timestamp(envelope.expiresAt, "actorEnvelope.expiresAt");
2971
- if (expiresAt && Date.parse(expiresAt) <= Date.parse(issuedAt)) {
2972
- fail("actorEnvelope.expiresAt", "must be later than issuedAt");
2973
- }
2974
- const assuranceValue = record(envelope.assurance, "actorEnvelope.assurance");
2975
- exactKeys(assuranceValue, ["level", "methods"], "actorEnvelope.assurance");
2976
- const assurance = {
2977
- level: member(assuranceValue.level, ASSURANCE_LEVELS, "actorEnvelope.assurance.level"),
2978
- methods: textArray(assuranceValue.methods, "actorEnvelope.assurance.methods")
2979
- };
2980
- const actionValue = record(envelope.action, "actorEnvelope.action");
2981
- exactKeys(actionValue, ["requestId", "actionId", "resourceType", "resourceId", "inputHash"], "actorEnvelope.action");
2982
- const inputHash = text(actionValue.inputHash, "actorEnvelope.action.inputHash");
2983
- if (!HASH_PATTERN.test(inputHash)) fail("actorEnvelope.action.inputHash", "must be a lowercase sha256 digest");
2984
- const action = {
2985
- requestId: text(actionValue.requestId, "actorEnvelope.action.requestId"),
2986
- actionId: text(actionValue.actionId, "actorEnvelope.action.actionId"),
2987
- ...actionValue.resourceType === void 0 ? {} : { resourceType: text(actionValue.resourceType, "actorEnvelope.action.resourceType") },
2988
- ...actionValue.resourceId === void 0 ? {} : { resourceId: text(actionValue.resourceId, "actorEnvelope.action.resourceId") },
2989
- inputHash
2990
- };
2991
- if (action.resourceType === void 0 !== (action.resourceId === void 0)) {
2992
- fail("actorEnvelope.action", "resourceType and resourceId must be provided together");
2993
- }
2994
- const delegation = parseDelegation(envelope.delegation);
2995
- const directHuman = performer.principalType === "human" && performer.principalId === accountableSubject.principalId;
2996
- if (directHuman && delegation !== null) fail("actorEnvelope.delegation", "must be null for direct human action");
2997
- if (!directHuman && delegation === null) fail("actorEnvelope.delegation", "is required when performer differs from accountable subject");
2998
- if (delegation) {
2999
- const finalHop = delegation.lineage.at(-1);
3000
- if (!finalHop || finalHop.principalId !== performer.principalId || finalHop.principalType !== performer.principalType) {
3001
- fail("actorEnvelope.delegation.lineage", "must end with the performer");
3002
- }
3003
- }
3004
- if (performer.principalType === "external_agent" && (!performer.installationId || !performer.provider)) {
3005
- fail("actorEnvelope.performer", "external_agent requires installationId and provider");
3006
- }
3007
- if (performer.principalType === "managed_agent" && !performer.agentInstanceId) {
3008
- fail("actorEnvelope.performer.agentInstanceId", "is required for managed_agent");
3009
- }
3010
- if (performer.principalType === "browser_automation" && (!performer.installationId || !performer.browserSessionId)) {
3011
- fail("actorEnvelope.performer", "browser_automation requires installationId and browserSessionId");
3012
- }
3013
- return {
3014
- schemaVersion: ACTOR_ENVELOPE_VERSION,
3015
- organizationId,
3016
- accountableSubject,
3017
- performer,
3018
- delegation,
3019
- channel,
3020
- assurance,
3021
- action,
3022
- issuedAt,
3023
- ...expiresAt ? { expiresAt } : {}
3024
- };
3025
- }
3026
- function canonicalizeActorEnvelope(value) {
3027
- return canonicalJson(parseActorEnvelope(value));
3028
- }
3029
- function canonicalJson(value) {
3030
- if (value === null) return "null";
3031
- if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
3032
- if (typeof value === "number") {
3033
- if (!Number.isFinite(value)) throw new TypeError("Canonical JSON accepts finite numbers only.");
3034
- return JSON.stringify(value);
3035
- }
3036
- if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
3037
- if (typeof value !== "object" || !value || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) {
3038
- throw new TypeError("Canonical JSON accepts JSON objects, arrays, and primitives only.");
3039
- }
3040
- const entries = Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right));
3041
- return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
3042
- }
3043
- function parseAccountableSubject(value) {
3044
- const path = "actorEnvelope.accountableSubject";
3045
- const subject = record(value, path);
3046
- exactKeys(subject, ["principalType", "principalId", "displayName"], path);
3047
- return {
3048
- principalType: member(subject.principalType, ACCOUNTABLE_SUBJECT_TYPES, `${path}.principalType`),
3049
- principalId: text(subject.principalId, `${path}.principalId`),
3050
- ...subject.displayName === void 0 ? {} : { displayName: text(subject.displayName, `${path}.displayName`) }
3051
- };
3052
- }
3053
- async function sha256CanonicalValue(value) {
3054
- const bytes = new TextEncoder().encode(canonicalJson(value));
3055
- const digest = await crypto.subtle.digest("SHA-256", bytes);
3056
- return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
3057
- }
3058
- function parseIdentity(value, path) {
3059
- const identity = record(value, path);
3060
- exactKeys(identity, [
3061
- "principalType",
3062
- "principalId",
3063
- "displayName",
3064
- "installationId",
3065
- "agentInstanceId",
3066
- "browserSessionId",
3067
- "provider",
3068
- "model"
3069
- ], path);
3070
- return {
3071
- principalType: member(identity.principalType, PRINCIPAL_TYPES, `${path}.principalType`),
3072
- principalId: text(identity.principalId, `${path}.principalId`),
3073
- ...optionalTextFields(identity, path, [
3074
- "displayName",
3075
- "installationId",
3076
- "agentInstanceId",
3077
- "browserSessionId",
3078
- "provider",
3079
- "model"
3080
- ])
3081
- };
3082
- }
3083
- function parseDelegation(value) {
3084
- if (value === null) return null;
3085
- const delegation = record(value, "actorEnvelope.delegation");
3086
- exactKeys(delegation, ["taskId", "runId", "lineage"], "actorEnvelope.delegation");
3087
- if (!Array.isArray(delegation.lineage) || delegation.lineage.length === 0 || delegation.lineage.length > 16) {
3088
- fail("actorEnvelope.delegation.lineage", "must contain between 1 and 16 hops");
3089
- }
3090
- return {
3091
- taskId: text(delegation.taskId, "actorEnvelope.delegation.taskId"),
3092
- runId: text(delegation.runId, "actorEnvelope.delegation.runId"),
3093
- lineage: delegation.lineage.map((value2, index) => {
3094
- const hop = record(value2, `actorEnvelope.delegation.lineage[${index}]`);
3095
- exactKeys(hop, ["principalType", "principalId"], `actorEnvelope.delegation.lineage[${index}]`);
3096
- return {
3097
- principalType: member(hop.principalType, PRINCIPAL_TYPES, `actorEnvelope.delegation.lineage[${index}].principalType`),
3098
- principalId: text(hop.principalId, `actorEnvelope.delegation.lineage[${index}].principalId`)
3099
- };
3100
- })
3101
- };
3102
- }
3103
- function record(value, path) {
3104
- if (!value || typeof value !== "object" || Array.isArray(value)) fail(path, "must be an object");
3105
- return value;
3106
- }
3107
- function exactKeys(value, allowed, path) {
3108
- const unknown = Object.keys(value).filter((key) => !allowed.includes(key));
3109
- if (unknown.length) fail(path, `contains unknown field ${unknown[0]}`);
3110
- }
3111
- function text(value, path) {
3112
- if (typeof value !== "string" || value.trim() !== value || value.length === 0 || value.length > 256) {
3113
- fail(path, "must be a non-empty trimmed string of at most 256 characters");
3114
- }
3115
- return value;
3116
- }
3117
- function timestamp(value, path) {
3118
- const result = text(value, path);
3119
- if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(result) || Number.isNaN(Date.parse(result))) {
3120
- fail(path, "must be an RFC 3339 UTC timestamp");
3121
- }
3122
- return result;
3123
- }
3124
- function member(value, values, path) {
3125
- if (typeof value !== "string" || !values.has(value)) fail(path, "contains an unsupported value");
3126
- return value;
3127
- }
3128
- function textArray(value, path) {
3129
- if (!Array.isArray(value) || value.length === 0 || value.length > 16) fail(path, "must contain between 1 and 16 values");
3130
- return value.map((item, index) => text(item, `${path}[${index}]`));
3131
- }
3132
- function optionalTextFields(value, path, keys) {
3133
- return Object.fromEntries(keys.flatMap((key) => value[key] === void 0 ? [] : [[key, text(value[key], `${path}.${key}`)]]));
3134
- }
3135
- function fail(path, message) {
3136
- throw new ActorEnvelopeValidationError(path, message);
3137
- }
3138
-
3139
3181
  // src/modules/workspaces.ts
3140
3182
  var ACTIONS = {
3141
3183
  list: "app_topolo_auth.workspaces.list",
@@ -4632,6 +4674,7 @@ export {
4632
4674
  DEFAULT_APP_URLS,
4633
4675
  IdentityModule,
4634
4676
  P2PModule,
4677
+ TOPOLO_ACTOR_ENVELOPE_HEADER,
4635
4678
  TOPOLO_ENVIRONMENT_SERVICE_URLS,
4636
4679
  TOPOLO_RUNTIME_ENVIRONMENTS,
4637
4680
  TOPOLO_WIDGET_CONTRACT_VERSION,
@@ -4664,12 +4707,14 @@ export {
4664
4707
  createTopoloAgentExecutionContext,
4665
4708
  createTopoloWidgetError,
4666
4709
  createTopoloWidgetResponse,
4710
+ decodeActorEnvelopeHeader,
4667
4711
  defineTopoloActionsWidget,
4668
4712
  defineTopoloActivityWidget,
4669
4713
  defineTopoloChartWidget,
4670
4714
  defineTopoloNotificationsWidget,
4671
4715
  defineTopoloStatsWidget,
4672
4716
  digestTopoloActionCatalog,
4717
+ encodeActorEnvelopeHeader,
4673
4718
  encodeTopoloActionBase64,
4674
4719
  enrichActionContract,
4675
4720
  extractTopoloServiceErrorCode,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@topolo/sdk",
3
3
  "packageManager": "pnpm@10.17.1",
4
- "version": "0.15.0",
5
- "topoloPublishedVersion": "0.15.0",
4
+ "version": "0.16.0",
5
+ "topoloPublishedVersion": "0.16.0",
6
6
  "description": "Typed client SDK for the Topolo platform. Used by TopoloCli, TopoloMCP, and third-party agents.",
7
7
  "license": "UNLICENSED",
8
8
  "author": "Topolo",