@ixo/editor 5.3.1 → 5.5.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.
@@ -49,6 +49,43 @@ function getActionByCan(can) {
49
49
  }
50
50
  return void 0;
51
51
  }
52
+ function getEventsForBlock(action, inputs) {
53
+ if (!action) return [];
54
+ if (action.getDynamicEvents) {
55
+ const parsed = normalizeInputs(inputs);
56
+ try {
57
+ return action.getDynamicEvents(parsed) || [];
58
+ } catch {
59
+ return [];
60
+ }
61
+ }
62
+ return action.events || [];
63
+ }
64
+ function getOutputSchemaForBlock(action, inputs) {
65
+ if (!action) return [];
66
+ if (action.getDynamicOutputSchema) {
67
+ const parsed = normalizeInputs(inputs);
68
+ try {
69
+ return action.getDynamicOutputSchema(parsed) || [];
70
+ } catch {
71
+ return action.outputSchema || [];
72
+ }
73
+ }
74
+ return action.outputSchema || [];
75
+ }
76
+ function normalizeInputs(inputs) {
77
+ if (!inputs) return {};
78
+ if (typeof inputs === "object") return inputs;
79
+ if (typeof inputs === "string") {
80
+ try {
81
+ const parsed = JSON.parse(inputs || "{}");
82
+ return parsed && typeof parsed === "object" ? parsed : {};
83
+ } catch {
84
+ return {};
85
+ }
86
+ }
87
+ return {};
88
+ }
52
89
 
53
90
  // src/core/lib/actionRegistry/canMapping.ts
54
91
  var CAN_TO_TYPE = {
@@ -76,6 +113,16 @@ var CAN_TO_TYPE = {
76
113
  "pod/governance-config": "qi/pod.governance-config",
77
114
  "pod/member-multi-select": "qi/pod.member-multi-select",
78
115
  "pod/list-domain-flows": "qi/pod.list-domain-flows",
116
+ // Calendar integration
117
+ "calendar.event/create": "qi/calendar.event.create",
118
+ "calendar.event/update": "qi/calendar.event.update",
119
+ "calendar.event/list": "qi/calendar.event.list",
120
+ // Xero integration
121
+ "xero.contact/create": "qi/xero.contact.create",
122
+ "xero.invoice/create": "qi/xero.invoice.create",
123
+ "xero.invoice/list": "qi/xero.invoice.list",
124
+ "xero.payment/create": "qi/xero.payment.create",
125
+ // Oracle / wallet / matrix lifecycle
79
126
  "wallet/generate": "qi/wallet.generate",
80
127
  "wallet/fund": "qi/wallet.fund",
81
128
  "iid/create": "qi/iid.create",
@@ -159,6 +206,11 @@ function buildServicesFromHandlers(handlers) {
159
206
  matrix: handlers?.storeMatrixCredential ? {
160
207
  storeCredential: async (params) => handlers.storeMatrixCredential(params)
161
208
  } : void 0,
209
+ integrations: handlers?.integrations?.executeTool ? {
210
+ executeTool: async (args) => handlers.integrations.executeTool(args),
211
+ fetchCurrentState: handlers.integrations.fetchCurrentState ? async (args) => handlers.integrations.fetchCurrentState(args) : void 0,
212
+ getEntityDid: handlers?.getEntityDid ? () => handlers.getEntityDid() : void 0
213
+ } : void 0,
162
214
  oracle: handlers?.generateWallet ? {
163
215
  generateWallet: async () => handlers.generateWallet(),
164
216
  fundWallet: async (params) => handlers.fundWallet(params),
@@ -853,21 +905,153 @@ registerAction({
853
905
  }
854
906
  });
855
907
 
908
+ // src/core/lib/actionRegistry/actions/claim/surveySchema.ts
909
+ var TYPE_MAP = {
910
+ text: "string",
911
+ comment: "string",
912
+ html: "string",
913
+ expression: "string",
914
+ dropdown: "string",
915
+ radiogroup: "string",
916
+ rating: "number",
917
+ number: "number",
918
+ boolean: "boolean",
919
+ checkbox: "array",
920
+ tagbox: "array",
921
+ ranking: "array",
922
+ imagepicker: "array",
923
+ file: "array",
924
+ paneldynamic: "array",
925
+ matrix: "object",
926
+ matrixdropdown: "object",
927
+ matrixdynamic: "object",
928
+ multipletext: "object",
929
+ panel: "object"
930
+ };
931
+ function extractSurveyAnswerSchema(survey) {
932
+ if (!survey || typeof survey !== "object") return [];
933
+ const json = survey;
934
+ const collected = [];
935
+ const seen = /* @__PURE__ */ new Set();
936
+ const walk = (elements) => {
937
+ if (!Array.isArray(elements)) return;
938
+ for (const el of elements) {
939
+ if (!el || typeof el !== "object") continue;
940
+ if (el.type === "panel" && Array.isArray(el.elements)) {
941
+ walk(el.elements);
942
+ continue;
943
+ }
944
+ const name = typeof el.name === "string" ? el.name.trim() : "";
945
+ if (!name) continue;
946
+ if (seen.has(name)) continue;
947
+ seen.add(name);
948
+ const rawType = typeof el.type === "string" ? el.type : "";
949
+ const mapped = TYPE_MAP[rawType] ?? "string";
950
+ let itemSchema;
951
+ if (rawType === "paneldynamic" && Array.isArray(el.templateElements) && el.templateElements.length > 0) {
952
+ const nested = [];
953
+ const nestedSeen = /* @__PURE__ */ new Set();
954
+ const walkNested = (inner) => {
955
+ for (const child of inner) {
956
+ if (!child || typeof child !== "object") continue;
957
+ if (child.type === "panel" && Array.isArray(child.elements)) {
958
+ walkNested(child.elements);
959
+ continue;
960
+ }
961
+ const childName = typeof child.name === "string" ? child.name.trim() : "";
962
+ if (!childName || nestedSeen.has(childName)) continue;
963
+ nestedSeen.add(childName);
964
+ const childType = typeof child.type === "string" ? child.type : "";
965
+ nested.push({
966
+ path: childName,
967
+ displayName: stripHtml(String(child.title || "").trim() || humanize(childName)),
968
+ type: TYPE_MAP[childType] ?? "string",
969
+ description: typeof child.description === "string" && child.description.trim() ? stripHtml(child.description.trim()) : void 0
970
+ });
971
+ }
972
+ };
973
+ walkNested(el.templateElements);
974
+ if (nested.length > 0) itemSchema = nested;
975
+ }
976
+ collected.push({
977
+ path: name,
978
+ displayName: stripHtml(String(el.title || "").trim() || humanize(name)),
979
+ type: mapped,
980
+ description: typeof el.description === "string" && el.description.trim() ? stripHtml(el.description.trim()) : void 0,
981
+ ...itemSchema ? { itemSchema } : {}
982
+ });
983
+ }
984
+ };
985
+ if (Array.isArray(json.pages) && json.pages.length > 0) {
986
+ for (const page of json.pages) walk(page.elements);
987
+ }
988
+ walk(json.elements);
989
+ return collected;
990
+ }
991
+ function prefixSurveyAnswerSchema(fields, namespace = "surveyAnswers") {
992
+ return fields.map((f) => ({
993
+ ...f,
994
+ path: `${namespace}.${f.path}`
995
+ }));
996
+ }
997
+ function humanize(name) {
998
+ return name.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
999
+ }
1000
+ function stripHtml(s) {
1001
+ return s.replace(/<[^>]*>/g, "").replace(/\s+/g, " ").trim();
1002
+ }
1003
+
856
1004
  // src/core/lib/actionRegistry/actions/claim/claim.ts
1005
+ var BASELINE_OUTPUT = [
1006
+ { path: "claimId", displayName: "Claim ID", type: "string", description: "The submitted claim identifier" },
1007
+ { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "Submission transaction hash" },
1008
+ { path: "collectionId", displayName: "Collection ID", type: "string", description: "Claim collection identifier" },
1009
+ { path: "deedDid", displayName: "Deed DID", type: "string", description: "Deed identifier" },
1010
+ { path: "submittedByDid", displayName: "Submitted By DID", type: "string", description: "Actor DID that submitted the claim" },
1011
+ { path: "submittedAt", displayName: "Submitted At", type: "string", description: "ISO timestamp of submission" }
1012
+ ];
857
1013
  registerAction({
858
1014
  type: "qi/claim.submit",
859
1015
  can: "claim/submit",
860
1016
  sideEffect: true,
861
1017
  defaultRequiresConfirmation: true,
862
1018
  requiredCapability: "flow/block/execute",
863
- outputSchema: [
864
- { path: "claimId", displayName: "Claim ID", type: "string", description: "The submitted claim identifier" },
865
- { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "Submission transaction hash" },
866
- { path: "collectionId", displayName: "Collection ID", type: "string", description: "Claim collection identifier" },
867
- { path: "deedDid", displayName: "Deed DID", type: "string", description: "Deed identifier" },
868
- { path: "submittedByDid", displayName: "Submitted By DID", type: "string", description: "Actor DID that submitted the claim" },
869
- { path: "submittedAt", displayName: "Submitted At", type: "string", description: "ISO timestamp of submission" }
870
- ],
1019
+ eligibleForEventTrigger: true,
1020
+ // Static fallback used until a collection is picked and the survey
1021
+ // schema is materialised. `getDynamicOutputSchema` takes over after that.
1022
+ outputSchema: BASELINE_OUTPUT,
1023
+ /**
1024
+ * Per-block output schema same shape as `run()`'s `output`. Picks up the
1025
+ * materialised survey schema so the DataInput reference picker can surface
1026
+ * `output.surveyAnswers.<question>` alongside the baseline claim fields.
1027
+ */
1028
+ getDynamicOutputSchema: (inputs) => {
1029
+ const schema = Array.isArray(inputs?.surveyAnswersSchema) ? inputs.surveyAnswersSchema : [];
1030
+ if (schema.length === 0) return BASELINE_OUTPUT;
1031
+ return [...BASELINE_OUTPUT, ...prefixSurveyAnswerSchema(schema)];
1032
+ },
1033
+ /**
1034
+ * Per-block event resolver. Returns `[]` until a claim collection has been
1035
+ * picked in template mode (which is when we can materialise the survey's
1036
+ * answer schema into inputs). Once present, emits a `submitted` event with
1037
+ * baseline claim fields plus `surveyAnswers.<question>` for every question
1038
+ * in the survey template — letting listener blocks consume typed refs like
1039
+ * `${sourceBlock.payload.surveyAnswers.email}`.
1040
+ */
1041
+ getDynamicEvents: (inputs) => {
1042
+ const schema = Array.isArray(inputs?.surveyAnswersSchema) ? inputs.surveyAnswersSchema : [];
1043
+ if (schema.length === 0) return [];
1044
+ const surveyFields = prefixSurveyAnswerSchema(schema);
1045
+ return [
1046
+ {
1047
+ name: "submitted",
1048
+ displayName: "Claim submitted",
1049
+ description: "Fires when a claim is submitted to this collection. Payload includes the submitted survey answers, fully typed to the collection survey.",
1050
+ payloadSchema: [...BASELINE_OUTPUT, ...surveyFields],
1051
+ pendingDisplayFields: ["claimId", "submittedAt"]
1052
+ }
1053
+ ];
1054
+ },
871
1055
  run: async (inputs, ctx) => {
872
1056
  const service = ctx.services.claim;
873
1057
  if (!service) {
@@ -912,20 +1096,59 @@ registerAction({
912
1096
  if (!claimId) {
913
1097
  throw new Error("submitClaim returned no claim identifier");
914
1098
  }
1099
+ const transactionHash = String(result?.transactionHash || "");
1100
+ const submittedByDid = ctx.actorDid || "";
1101
+ const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
1102
+ const output = {
1103
+ claimId,
1104
+ transactionHash,
1105
+ collectionId,
1106
+ deedDid,
1107
+ submittedByDid,
1108
+ submittedAt,
1109
+ surveyAnswers
1110
+ };
915
1111
  return {
916
- output: {
917
- claimId,
918
- transactionHash: String(result?.transactionHash || ""),
919
- collectionId,
920
- deedDid,
921
- submittedByDid: ctx.actorDid || "",
922
- submittedAt: (/* @__PURE__ */ new Date()).toISOString()
923
- }
1112
+ output,
1113
+ events: [
1114
+ {
1115
+ name: "submitted",
1116
+ payload: output
1117
+ }
1118
+ ]
924
1119
  };
925
1120
  }
926
1121
  });
927
1122
 
928
1123
  // src/core/lib/actionRegistry/actions/evaluateClaim/evaluateClaim.ts
1124
+ var BASELINE_OUTPUT2 = [
1125
+ { path: "claimId", displayName: "Claim ID", type: "string", description: "Evaluated claim identifier" },
1126
+ { path: "decision", displayName: "Decision", type: "string", description: "approve or reject" },
1127
+ { path: "status", displayName: "Status", type: "string", description: "approved or rejected" },
1128
+ { path: "verificationProof", displayName: "Verification Proof", type: "string", description: "UDID URL proof if generated" },
1129
+ { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "On-chain evaluation transaction hash" },
1130
+ { path: "collectionId", displayName: "Collection ID", type: "string", description: "Claim collection identifier" },
1131
+ { path: "deedDid", displayName: "Deed DID", type: "string", description: "Deed identifier" },
1132
+ { path: "evaluatedByDid", displayName: "Evaluated By DID", type: "string", description: "Actor DID that evaluated the claim" },
1133
+ { path: "evaluatedAt", displayName: "Evaluated At", type: "string", description: "ISO timestamp of evaluation" }
1134
+ ];
1135
+ var BASELINE_APPROVED_EVENT = [
1136
+ { path: "claimId", displayName: "Claim ID", type: "string", description: "Approved claim identifier" },
1137
+ { path: "collectionId", displayName: "Collection ID", type: "string" },
1138
+ { path: "deedDid", displayName: "Deed DID", type: "string" },
1139
+ { path: "evaluatedByDid", displayName: "Evaluated By DID", type: "string", description: "Actor DID that approved the claim" },
1140
+ { path: "evaluatedAt", displayName: "Evaluated At", type: "string" },
1141
+ { path: "verificationProof", displayName: "Verification Proof", type: "string" },
1142
+ { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "On-chain approval transaction hash \u2014 proof of payout" }
1143
+ ];
1144
+ var BASELINE_REJECTED_EVENT = [
1145
+ { path: "claimId", displayName: "Claim ID", type: "string", description: "Rejected claim identifier" },
1146
+ { path: "collectionId", displayName: "Collection ID", type: "string" },
1147
+ { path: "deedDid", displayName: "Deed DID", type: "string" },
1148
+ { path: "evaluatedByDid", displayName: "Evaluated By DID", type: "string", description: "Actor DID that rejected the claim" },
1149
+ { path: "evaluatedAt", displayName: "Evaluated At", type: "string" },
1150
+ { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "On-chain rejection transaction hash" }
1151
+ ];
929
1152
  function normalizeDecision2(value) {
930
1153
  const normalized = String(value || "").trim().toLowerCase();
931
1154
  if (normalized === "approve" || normalized === "reject") {
@@ -946,45 +1169,62 @@ registerAction({
946
1169
  sideEffect: true,
947
1170
  defaultRequiresConfirmation: true,
948
1171
  requiredCapability: "flow/block/execute",
949
- outputSchema: [
950
- { path: "claimId", displayName: "Claim ID", type: "string", description: "Evaluated claim identifier" },
951
- { path: "decision", displayName: "Decision", type: "string", description: "approve or reject" },
952
- { path: "status", displayName: "Status", type: "string", description: "approved or rejected" },
953
- { path: "verificationProof", displayName: "Verification Proof", type: "string", description: "UDID URL proof if generated" },
954
- { path: "collectionId", displayName: "Collection ID", type: "string", description: "Claim collection identifier" },
955
- { path: "deedDid", displayName: "Deed DID", type: "string", description: "Deed identifier" },
956
- { path: "evaluatedByDid", displayName: "Evaluated By DID", type: "string", description: "Actor DID that evaluated the claim" },
957
- { path: "evaluatedAt", displayName: "Evaluated At", type: "string", description: "ISO timestamp of evaluation" }
958
- ],
1172
+ // Static fallback — used until a collection is picked and the survey
1173
+ // schema is materialised. `getDynamicOutputSchema` takes over after that.
1174
+ outputSchema: BASELINE_OUTPUT2,
1175
+ /**
1176
+ * Per-block output schema. Surfaces `output.surveyAnswers.<question>`
1177
+ * alongside the baseline evaluation fields once the template author has
1178
+ * picked a collection and the survey schema has been materialised into
1179
+ * inputs. Lets DataInput pickers reference typed survey fields downstream.
1180
+ */
1181
+ getDynamicOutputSchema: (inputs) => {
1182
+ const schema = Array.isArray(inputs?.surveyAnswersSchema) ? inputs.surveyAnswersSchema : [];
1183
+ if (schema.length === 0) return BASELINE_OUTPUT2;
1184
+ return [...BASELINE_OUTPUT2, ...prefixSurveyAnswerSchema(schema)];
1185
+ },
959
1186
  events: [
960
1187
  {
961
1188
  name: "approved",
962
1189
  displayName: "Approved",
963
1190
  description: "Fires when an evaluator approves the claim.",
964
- payloadSchema: [
965
- { path: "claimId", displayName: "Claim ID", type: "string", description: "Approved claim identifier" },
966
- { path: "collectionId", displayName: "Collection ID", type: "string" },
967
- { path: "deedDid", displayName: "Deed DID", type: "string" },
968
- { path: "evaluatedByDid", displayName: "Evaluated By DID", type: "string", description: "Actor DID that approved the claim" },
969
- { path: "evaluatedAt", displayName: "Evaluated At", type: "string" },
970
- { path: "verificationProof", displayName: "Verification Proof", type: "string" }
971
- ],
1191
+ payloadSchema: BASELINE_APPROVED_EVENT,
972
1192
  pendingDisplayFields: ["claimId", "evaluatedAt"]
973
1193
  },
974
1194
  {
975
1195
  name: "rejected",
976
1196
  displayName: "Rejected",
977
1197
  description: "Fires when an evaluator rejects the claim.",
978
- payloadSchema: [
979
- { path: "claimId", displayName: "Claim ID", type: "string", description: "Rejected claim identifier" },
980
- { path: "collectionId", displayName: "Collection ID", type: "string" },
981
- { path: "deedDid", displayName: "Deed DID", type: "string" },
982
- { path: "evaluatedByDid", displayName: "Evaluated By DID", type: "string", description: "Actor DID that rejected the claim" },
983
- { path: "evaluatedAt", displayName: "Evaluated At", type: "string" }
984
- ],
1198
+ payloadSchema: BASELINE_REJECTED_EVENT,
985
1199
  pendingDisplayFields: ["claimId", "evaluatedAt"]
986
1200
  }
987
1201
  ],
1202
+ /**
1203
+ * Per-block event schema — same idea as `getDynamicOutputSchema`. Adds the
1204
+ * survey-answer fields to both `approved` and `rejected` event payloads
1205
+ * so listener blocks (e.g. `qi/xero.invoice.create`) can reference
1206
+ * `${sourceBlock.payload.surveyAnswers.<field>}` with typed paths.
1207
+ */
1208
+ getDynamicEvents: (inputs) => {
1209
+ const schema = Array.isArray(inputs?.surveyAnswersSchema) ? inputs.surveyAnswersSchema : [];
1210
+ const surveyFields = schema.length > 0 ? prefixSurveyAnswerSchema(schema) : [];
1211
+ return [
1212
+ {
1213
+ name: "approved",
1214
+ displayName: "Approved",
1215
+ description: "Fires when an evaluator approves the claim.",
1216
+ payloadSchema: [...BASELINE_APPROVED_EVENT, ...surveyFields],
1217
+ pendingDisplayFields: ["claimId", "evaluatedAt"]
1218
+ },
1219
+ {
1220
+ name: "rejected",
1221
+ displayName: "Rejected",
1222
+ description: "Fires when an evaluator rejects the claim.",
1223
+ payloadSchema: [...BASELINE_REJECTED_EVENT, ...surveyFields],
1224
+ pendingDisplayFields: ["claimId", "evaluatedAt"]
1225
+ }
1226
+ ];
1227
+ },
988
1228
  run: async (inputs, ctx) => {
989
1229
  const service = ctx.services.claim;
990
1230
  if (!service) {
@@ -1032,18 +1272,19 @@ registerAction({
1032
1272
  if (!denom || !tokenAmount) return null;
1033
1273
  return { denom, amount: tokenAmount };
1034
1274
  };
1035
- let normalizedAmounts;
1275
+ let normalizedCoin;
1036
1276
  if (Array.isArray(amount)) {
1037
- normalizedAmounts = amount.map(normalizeCoin).filter((coin) => !!coin);
1038
- if (normalizedAmounts.length !== amount.length) {
1277
+ const coins = amount.map(normalizeCoin).filter((coin) => !!coin);
1278
+ if (coins.length !== amount.length) {
1039
1279
  throw new Error("amount must contain valid coin objects with denom and amount");
1040
1280
  }
1281
+ normalizedCoin = coins[0];
1041
1282
  } else if (amount != null) {
1042
1283
  const coin = normalizeCoin(amount);
1043
1284
  if (!coin) {
1044
- throw new Error("amount must be a coin object or an array of coin objects");
1285
+ throw new Error("amount must be a coin object with denom and amount");
1045
1286
  }
1046
- normalizedAmounts = [coin];
1287
+ normalizedCoin = coin;
1047
1288
  }
1048
1289
  let verificationProof = String(inputs.verificationProof || "").trim();
1049
1290
  const shouldCreateUdid = Boolean(inputs.createUdid);
@@ -1078,14 +1319,27 @@ registerAction({
1078
1319
  if (!granteeAddress) {
1079
1320
  throw new Error("granteeAddress could not be resolved");
1080
1321
  }
1081
- await service.evaluateClaim(granteeAddress, deedDid, {
1322
+ const broadcastResult = await service.evaluateClaim(granteeAddress, deedDid, {
1082
1323
  claimId,
1083
1324
  collectionId,
1084
1325
  adminAddress,
1085
1326
  status: toStatus(decision),
1086
1327
  verificationProof,
1087
- amount: normalizedAmounts && normalizedAmounts.length > 0 ? normalizedAmounts.length === 1 ? normalizedAmounts[0] : normalizedAmounts : void 0
1328
+ amount: normalizedCoin
1088
1329
  });
1330
+ const transactionHash = String(broadcastResult?.transactionHash || "");
1331
+ let surveyAnswers = {};
1332
+ try {
1333
+ const getClaimData = handlers?.getClaimData;
1334
+ if (typeof getClaimData === "function") {
1335
+ const claimData = await getClaimData(collectionId, claimId);
1336
+ const candidate = claimData?.credentialSubject ?? claimData?.surveyAnswers ?? claimData;
1337
+ if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
1338
+ surveyAnswers = candidate;
1339
+ }
1340
+ }
1341
+ } catch {
1342
+ }
1089
1343
  const evaluatedByDid = ctx.actorDid || granteeAddress;
1090
1344
  const evaluatedAt = (/* @__PURE__ */ new Date()).toISOString();
1091
1345
  const output = {
@@ -1093,10 +1347,12 @@ registerAction({
1093
1347
  decision,
1094
1348
  status: decision === "approve" ? "approved" : "rejected",
1095
1349
  verificationProof,
1350
+ transactionHash,
1096
1351
  collectionId,
1097
1352
  deedDid,
1098
1353
  evaluatedByDid,
1099
- evaluatedAt
1354
+ evaluatedAt,
1355
+ surveyAnswers
1100
1356
  };
1101
1357
  const eventName = decision === "approve" ? "approved" : "rejected";
1102
1358
  const eventPayload = decision === "approve" ? {
@@ -1105,13 +1361,17 @@ registerAction({
1105
1361
  deedDid,
1106
1362
  evaluatedByDid,
1107
1363
  evaluatedAt,
1108
- verificationProof
1364
+ verificationProof,
1365
+ transactionHash,
1366
+ surveyAnswers
1109
1367
  } : {
1110
1368
  claimId,
1111
1369
  collectionId,
1112
1370
  deedDid,
1113
1371
  evaluatedByDid,
1114
- evaluatedAt
1372
+ evaluatedAt,
1373
+ transactionHash,
1374
+ surveyAnswers
1115
1375
  };
1116
1376
  return {
1117
1377
  output,
@@ -2763,9 +3023,7 @@ registerAction({
2763
3023
  can: "wallet/fund",
2764
3024
  sideEffect: true,
2765
3025
  defaultRequiresConfirmation: true,
2766
- outputSchema: [
2767
- { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The funding transaction hash" }
2768
- ],
3026
+ outputSchema: [{ path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The funding transaction hash" }],
2769
3027
  run: async (inputs, ctx) => {
2770
3028
  if (!ctx.services.oracle?.fundWallet) {
2771
3029
  throw new Error("oracle.fundWallet handler not available");
@@ -2982,8 +3240,18 @@ registerAction({
2982
3240
  outputSchema: [
2983
3241
  { path: "entityDid", displayName: "Entity DID", type: "string", description: "The oracle entity DID" },
2984
3242
  { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The entity creation transaction hash" },
2985
- { path: "encryptionPublicKeyMultibase", displayName: "Encryption Public Key (multibase)", type: "string", description: "Multibase-encoded P-256 public key registered as a keyAgreement vm on the entity DID" },
2986
- { path: "encryptionVerificationMethodId", displayName: "Encryption Verification Method ID", type: "string", description: "DID verification method id of the P-256 keyAgreement key" }
3243
+ {
3244
+ path: "encryptionPublicKeyMultibase",
3245
+ displayName: "Encryption Public Key (multibase)",
3246
+ type: "string",
3247
+ description: "Multibase-encoded P-256 public key registered as a keyAgreement vm on the entity DID"
3248
+ },
3249
+ {
3250
+ path: "encryptionVerificationMethodId",
3251
+ displayName: "Encryption Verification Method ID",
3252
+ type: "string",
3253
+ description: "DID verification method id of the P-256 keyAgreement key"
3254
+ }
2987
3255
  ],
2988
3256
  run: async (rawInputs, ctx) => {
2989
3257
  const form = (() => {
@@ -3084,12 +3352,7 @@ registerAction({
3084
3352
  });
3085
3353
 
3086
3354
  // src/core/lib/actionRegistry/actions/oracleStoreSecrets.ts
3087
- var SENSITIVE_PLAINTEXT_KEYS = [
3088
- "SECP_MNEMONIC",
3089
- "MATRIX_ORACLE_ADMIN_PASSWORD",
3090
- "MATRIX_RECOVERY_PHRASE",
3091
- "MATRIX_VALUE_PIN"
3092
- ];
3355
+ var SENSITIVE_PLAINTEXT_KEYS = ["SECP_MNEMONIC", "MATRIX_ORACLE_ADMIN_PASSWORD", "MATRIX_RECOVERY_PHRASE", "MATRIX_VALUE_PIN"];
3093
3356
  registerAction({
3094
3357
  type: "qi/oracle.storeSecrets",
3095
3358
  can: "oracle/storeSecrets",
@@ -3196,12 +3459,7 @@ registerAction({
3196
3459
  });
3197
3460
 
3198
3461
  // src/core/lib/actionRegistry/actions/oracleStoreSecretsAndConfig.ts
3199
- var SENSITIVE_PLAINTEXT_KEYS2 = [
3200
- "SECP_MNEMONIC",
3201
- "MATRIX_ORACLE_ADMIN_PASSWORD",
3202
- "MATRIX_RECOVERY_PHRASE",
3203
- "MATRIX_VALUE_PIN"
3204
- ];
3462
+ var SENSITIVE_PLAINTEXT_KEYS2 = ["SECP_MNEMONIC", "MATRIX_ORACLE_ADMIN_PASSWORD", "MATRIX_RECOVERY_PHRASE", "MATRIX_VALUE_PIN"];
3205
3463
  registerAction({
3206
3464
  type: "qi/oracle.storeSecretsAndConfig",
3207
3465
  can: "oracle/storeSecretsAndConfig",
@@ -3304,12 +3562,7 @@ registerAction({
3304
3562
  });
3305
3563
 
3306
3564
  // src/core/lib/actionRegistry/actions/oracleConfigureOracle.ts
3307
- var SENSITIVE_PLAINTEXT_KEYS3 = [
3308
- "SECP_MNEMONIC",
3309
- "MATRIX_ORACLE_ADMIN_PASSWORD",
3310
- "MATRIX_RECOVERY_PHRASE",
3311
- "MATRIX_VALUE_PIN"
3312
- ];
3565
+ var SENSITIVE_PLAINTEXT_KEYS3 = ["SECP_MNEMONIC", "MATRIX_ORACLE_ADMIN_PASSWORD", "MATRIX_RECOVERY_PHRASE", "MATRIX_VALUE_PIN"];
3313
3566
  registerAction({
3314
3567
  type: "qi/oracle.configureOracle",
3315
3568
  can: "oracle/configureOracle",
@@ -3546,6 +3799,628 @@ registerAction({
3546
3799
  }
3547
3800
  });
3548
3801
 
3802
+ // src/core/lib/actionRegistry/actions/calendar/eventCreate.types.ts
3803
+ var EMPTY = {
3804
+ connection: null,
3805
+ calendar_id: "primary",
3806
+ summary: "",
3807
+ description: "",
3808
+ location: "",
3809
+ timezone: "",
3810
+ start_datetime: "",
3811
+ end_datetime: "",
3812
+ attendees: "",
3813
+ send_updates: ""
3814
+ };
3815
+ function parseCalendarEventCreateInputs(raw) {
3816
+ try {
3817
+ const parsed = typeof raw === "string" ? JSON.parse(raw || "{}") : raw || {};
3818
+ const conn = parsed.connection;
3819
+ const connection = conn && typeof conn === "object" && typeof conn.connectedAccountId === "string" && typeof conn.entityDid === "string" ? { connectedAccountId: conn.connectedAccountId, entityDid: conn.entityDid } : null;
3820
+ return {
3821
+ connection,
3822
+ calendar_id: typeof parsed.calendar_id === "string" && parsed.calendar_id ? parsed.calendar_id : "primary",
3823
+ summary: typeof parsed.summary === "string" ? parsed.summary : "",
3824
+ description: typeof parsed.description === "string" ? parsed.description : "",
3825
+ location: typeof parsed.location === "string" ? parsed.location : "",
3826
+ timezone: typeof parsed.timezone === "string" ? parsed.timezone : "",
3827
+ start_datetime: typeof parsed.start_datetime === "string" ? parsed.start_datetime : "",
3828
+ end_datetime: typeof parsed.end_datetime === "string" ? parsed.end_datetime : "",
3829
+ attendees: typeof parsed.attendees === "string" ? parsed.attendees : Array.isArray(parsed.attendees) ? JSON.stringify(parsed.attendees) : "",
3830
+ send_updates: typeof parsed.send_updates === "string" ? parsed.send_updates : ""
3831
+ };
3832
+ } catch {
3833
+ return { ...EMPTY };
3834
+ }
3835
+ }
3836
+ function serializeCalendarEventCreateInputs(inputs) {
3837
+ return JSON.stringify(inputs);
3838
+ }
3839
+ function parseAttendeesField(raw) {
3840
+ const trimmed = (raw || "").trim();
3841
+ if (!trimmed) return [];
3842
+ if (trimmed.startsWith("[")) {
3843
+ try {
3844
+ const parsed = JSON.parse(trimmed);
3845
+ if (Array.isArray(parsed)) return parsed.filter((v) => typeof v === "string" && v.trim()).map((v) => v.trim());
3846
+ } catch {
3847
+ }
3848
+ }
3849
+ return trimmed.split(/[\n,]+/).map((s) => s.trim()).filter(Boolean);
3850
+ }
3851
+
3852
+ // src/core/lib/actionRegistry/actions/calendar/eventCreate.ts
3853
+ var CALENDAR_EVENT_CREATE_SLUG = "GOOGLECALENDAR_CREATE_EVENT";
3854
+ registerAction({
3855
+ type: "qi/calendar.event.create",
3856
+ can: "calendar.event/create",
3857
+ sideEffect: true,
3858
+ defaultRequiresConfirmation: true,
3859
+ requiredCapability: "flow/block/execute",
3860
+ eligibleForEventTrigger: true,
3861
+ outputSchema: [
3862
+ { path: "eventId", displayName: "Event ID", type: "string", description: "Opaque identifier of the created event" },
3863
+ { path: "htmlLink", displayName: "Event link", type: "string", description: "Web UI link to the created event" },
3864
+ { path: "summary", displayName: "Summary", type: "string" },
3865
+ { path: "startIso", displayName: "Start", type: "string", description: "Resolved start time (ISO 8601)" },
3866
+ { path: "endIso", displayName: "End", type: "string", description: "Resolved end time (ISO 8601)" },
3867
+ { path: "hangoutLink", displayName: "Meet link", type: "string" }
3868
+ ],
3869
+ events: [
3870
+ {
3871
+ name: "event.created",
3872
+ displayName: "Calendar event created",
3873
+ description: "Fired after a new event is written to Calendar",
3874
+ payloadSchema: [
3875
+ { path: "eventId", displayName: "Event ID", type: "string" },
3876
+ { path: "htmlLink", displayName: "Event link", type: "string" },
3877
+ { path: "summary", displayName: "Summary", type: "string" }
3878
+ ],
3879
+ pendingDisplayFields: ["summary", "eventId"]
3880
+ }
3881
+ ],
3882
+ run: async (inputs, ctx) => {
3883
+ const svc = ctx.services.integrations;
3884
+ if (!svc?.executeTool) {
3885
+ throw new Error("Calendar integration is not configured");
3886
+ }
3887
+ const connection = inputs.connection;
3888
+ if (!connection?.connectedAccountId) {
3889
+ throw new Error("Connect Calendar before running this action.");
3890
+ }
3891
+ const currentEntityDid = svc.getEntityDid?.();
3892
+ if (currentEntityDid && connection.entityDid && currentEntityDid !== connection.entityDid) {
3893
+ throw new Error("This block was configured for a different workspace. Reconnect Calendar to use it here.");
3894
+ }
3895
+ const start = (inputs.start_datetime || "").trim();
3896
+ if (!start) {
3897
+ throw new Error("Start time is required.");
3898
+ }
3899
+ const args = {
3900
+ start_datetime: start,
3901
+ calendar_id: (inputs.calendar_id || "primary").trim() || "primary"
3902
+ };
3903
+ const end = (inputs.end_datetime || "").trim();
3904
+ if (end) args.end_datetime = end;
3905
+ if (inputs.summary) args.summary = inputs.summary;
3906
+ if (inputs.description) args.description = inputs.description;
3907
+ if (inputs.location) args.location = inputs.location;
3908
+ if (inputs.timezone) args.timezone = inputs.timezone;
3909
+ if (inputs.send_updates) args.send_updates = inputs.send_updates;
3910
+ const attendees = parseAttendeesField(inputs.attendees || "");
3911
+ if (attendees.length > 0) args.attendees = attendees;
3912
+ const result = await svc.executeTool({
3913
+ toolSlug: CALENDAR_EVENT_CREATE_SLUG,
3914
+ connectedAccountId: connection.connectedAccountId,
3915
+ arguments: args
3916
+ });
3917
+ if (!result.successful) {
3918
+ throw new Error(result.error || "Calendar event creation failed");
3919
+ }
3920
+ const envelope = result.data;
3921
+ const event = envelope?.response_data || envelope?.data?.response_data || envelope || {};
3922
+ const eventId = String(event.id || "");
3923
+ const htmlLink = String(event.htmlLink || envelope?.display_url || "");
3924
+ const summary = String(event.summary || inputs.summary || "");
3925
+ const startIso = String(event.start?.dateTime || event.start?.date || start);
3926
+ const endIso = String(event.end?.dateTime || event.end?.date || end || "");
3927
+ const hangoutLink = String(event.hangoutLink || "");
3928
+ return {
3929
+ output: {
3930
+ eventId,
3931
+ htmlLink,
3932
+ summary,
3933
+ startIso,
3934
+ endIso,
3935
+ hangoutLink
3936
+ },
3937
+ events: eventId ? [{ name: "event.created", payload: { eventId, htmlLink, summary } }] : void 0
3938
+ };
3939
+ }
3940
+ });
3941
+
3942
+ // src/core/lib/actionRegistry/actions/calendar/eventUpdate.ts
3943
+ var CALENDAR_EVENT_UPDATE_SLUG = "GOOGLECALENDAR_UPDATE_EVENT";
3944
+ var CALENDAR_EVENT_GET_SLUG = "GOOGLECALENDAR_EVENTS_GET";
3945
+ registerAction({
3946
+ type: "qi/calendar.event.update",
3947
+ can: "calendar.event/update",
3948
+ sideEffect: true,
3949
+ defaultRequiresConfirmation: true,
3950
+ requiredCapability: "flow/block/execute",
3951
+ eligibleForEventTrigger: true,
3952
+ outputSchema: [
3953
+ { path: "eventId", displayName: "Event ID", type: "string" },
3954
+ { path: "htmlLink", displayName: "Event link", type: "string" },
3955
+ { path: "summary", displayName: "Summary", type: "string" },
3956
+ { path: "startIso", displayName: "Start", type: "string" },
3957
+ { path: "endIso", displayName: "End", type: "string" }
3958
+ ],
3959
+ events: [
3960
+ {
3961
+ name: "event.updated",
3962
+ displayName: "Calendar event updated",
3963
+ description: "Fired after an existing event is replaced",
3964
+ payloadSchema: [
3965
+ { path: "eventId", displayName: "Event ID", type: "string" },
3966
+ { path: "htmlLink", displayName: "Event link", type: "string" },
3967
+ { path: "summary", displayName: "Summary", type: "string" }
3968
+ ],
3969
+ pendingDisplayFields: ["summary", "eventId"]
3970
+ }
3971
+ ],
3972
+ run: async (inputs, ctx) => {
3973
+ const svc = ctx.services.integrations;
3974
+ if (!svc?.executeTool) {
3975
+ throw new Error("Calendar integration is not configured");
3976
+ }
3977
+ const connection = inputs.connection;
3978
+ if (!connection?.connectedAccountId) {
3979
+ throw new Error("Connect Calendar before running this action.");
3980
+ }
3981
+ const currentEntityDid = svc.getEntityDid?.();
3982
+ if (currentEntityDid && connection.entityDid && currentEntityDid !== connection.entityDid) {
3983
+ throw new Error("This block was configured for a different workspace. Reconnect Calendar to use it here.");
3984
+ }
3985
+ const eventId = (inputs.event_id || "").trim();
3986
+ if (!eventId) {
3987
+ throw new Error("Event ID is required to update an event.");
3988
+ }
3989
+ const start = (inputs.start_datetime || "").trim();
3990
+ if (!start) {
3991
+ throw new Error("Start time is required.");
3992
+ }
3993
+ const args = {
3994
+ event_id: eventId,
3995
+ start_datetime: start,
3996
+ calendar_id: (inputs.calendar_id || "primary").trim() || "primary"
3997
+ };
3998
+ const end = (inputs.end_datetime || "").trim();
3999
+ if (end) args.end_datetime = end;
4000
+ if (inputs.summary) args.summary = inputs.summary;
4001
+ if (inputs.description) args.description = inputs.description;
4002
+ if (inputs.location) args.location = inputs.location;
4003
+ if (inputs.timezone) args.timezone = inputs.timezone;
4004
+ if (inputs.send_updates) args.send_updates = inputs.send_updates;
4005
+ const attendees = parseAttendeesField(inputs.attendees || "");
4006
+ if (attendees.length > 0) args.attendees = attendees;
4007
+ const result = await svc.executeTool({
4008
+ toolSlug: CALENDAR_EVENT_UPDATE_SLUG,
4009
+ connectedAccountId: connection.connectedAccountId,
4010
+ arguments: args
4011
+ });
4012
+ if (!result.successful) {
4013
+ throw new Error(result.error || "Calendar event update failed");
4014
+ }
4015
+ const envelope = result.data;
4016
+ const event = envelope?.response_data || envelope?.data?.response_data || envelope || {};
4017
+ const outId = String(event.id || eventId);
4018
+ const htmlLink = String(event.htmlLink || envelope?.display_url || "");
4019
+ const summary = String(event.summary || inputs.summary || "");
4020
+ const startIso = String(event.start?.dateTime || event.start?.date || start);
4021
+ const endIso = String(event.end?.dateTime || event.end?.date || end || "");
4022
+ return {
4023
+ output: {
4024
+ eventId: outId,
4025
+ htmlLink,
4026
+ summary,
4027
+ startIso,
4028
+ endIso
4029
+ },
4030
+ events: [{ name: "event.updated", payload: { eventId: outId, htmlLink, summary } }]
4031
+ };
4032
+ }
4033
+ });
4034
+
4035
+ // src/core/lib/actionRegistry/actions/calendar/eventList.ts
4036
+ var CALENDAR_EVENT_LIST_SLUG = "GOOGLECALENDAR_EVENTS_LIST";
4037
+ registerAction({
4038
+ type: "qi/calendar.event.list",
4039
+ can: "calendar.event/list",
4040
+ sideEffect: false,
4041
+ defaultRequiresConfirmation: false,
4042
+ requiredCapability: "flow/block/execute",
4043
+ eligibleForEventTrigger: false,
4044
+ outputSchema: [
4045
+ { path: "items", displayName: "Events", type: "array", description: "Raw event objects as returned by Calendar" },
4046
+ { path: "count", displayName: "Count", type: "number" },
4047
+ { path: "firstId", displayName: "First event ID", type: "string" },
4048
+ { path: "ids", displayName: "Event IDs", type: "array" },
4049
+ { path: "nextPageToken", displayName: "Next page token", type: "string" }
4050
+ ],
4051
+ run: async (inputs, ctx) => {
4052
+ const svc = ctx.services.integrations;
4053
+ if (!svc?.executeTool) {
4054
+ throw new Error("Calendar integration is not configured");
4055
+ }
4056
+ const connection = inputs.connection;
4057
+ if (!connection?.connectedAccountId) {
4058
+ throw new Error("Connect Calendar before running this action.");
4059
+ }
4060
+ const currentEntityDid = svc.getEntityDid?.();
4061
+ if (currentEntityDid && connection.entityDid && currentEntityDid !== connection.entityDid) {
4062
+ throw new Error("This block was configured for a different workspace. Reconnect Calendar to use it here.");
4063
+ }
4064
+ const args = {
4065
+ calendarId: (inputs.calendarId || "primary").trim() || "primary"
4066
+ };
4067
+ if (inputs.q) args.q = inputs.q;
4068
+ if (inputs.timeMin) args.timeMin = inputs.timeMin;
4069
+ if (inputs.timeMax) args.timeMax = inputs.timeMax;
4070
+ if (inputs.timeZone) args.timeZone = inputs.timeZone;
4071
+ if (inputs.orderBy) args.orderBy = inputs.orderBy;
4072
+ if (typeof inputs.singleEvents === "boolean") args.singleEvents = inputs.singleEvents;
4073
+ if (inputs.maxResults) {
4074
+ const n = Number.parseInt(inputs.maxResults, 10);
4075
+ if (Number.isFinite(n) && n > 0) args.maxResults = n;
4076
+ }
4077
+ const result = await svc.executeTool({
4078
+ toolSlug: CALENDAR_EVENT_LIST_SLUG,
4079
+ connectedAccountId: connection.connectedAccountId,
4080
+ arguments: args
4081
+ });
4082
+ if (!result.successful) {
4083
+ throw new Error(result.error || "Calendar list failed");
4084
+ }
4085
+ const envelope = result.data;
4086
+ const list = envelope?.items ? envelope : envelope?.data || envelope;
4087
+ const items = Array.isArray(list?.items) ? list.items : [];
4088
+ const ids = items.map((e) => String(e?.id || "")).filter(Boolean);
4089
+ return {
4090
+ output: {
4091
+ items,
4092
+ count: items.length,
4093
+ firstId: ids[0] ?? null,
4094
+ ids,
4095
+ nextPageToken: String(list?.nextPageToken || "")
4096
+ }
4097
+ };
4098
+ }
4099
+ });
4100
+
4101
+ // src/core/lib/actionRegistry/actions/xero/contactCreate.ts
4102
+ var XERO_CREATE_CONTACT_SLUG = "XERO_CREATE_CONTACT";
4103
+ registerAction({
4104
+ type: "qi/xero.contact.create",
4105
+ can: "xero.contact/create",
4106
+ sideEffect: true,
4107
+ defaultRequiresConfirmation: true,
4108
+ requiredCapability: "flow/block/execute",
4109
+ eligibleForEventTrigger: true,
4110
+ outputSchema: [
4111
+ { path: "contactId", displayName: "Contact ID", type: "string" },
4112
+ { path: "name", displayName: "Name", type: "string" },
4113
+ { path: "emailAddress", displayName: "Email", type: "string" }
4114
+ ],
4115
+ events: [
4116
+ {
4117
+ name: "contact.created",
4118
+ displayName: "Xero contact created",
4119
+ description: "Fired after a new Xero contact is written",
4120
+ payloadSchema: [
4121
+ { path: "contactId", displayName: "Contact ID", type: "string" },
4122
+ { path: "name", displayName: "Name", type: "string" }
4123
+ ],
4124
+ pendingDisplayFields: ["name", "contactId"]
4125
+ }
4126
+ ],
4127
+ run: async (inputs, ctx) => {
4128
+ const svc = ctx.services.integrations;
4129
+ if (!svc?.executeTool) {
4130
+ throw new Error("Xero integration is not configured");
4131
+ }
4132
+ const connection = inputs.connection;
4133
+ if (!connection?.connectedAccountId) {
4134
+ throw new Error("Connect Xero before running this action.");
4135
+ }
4136
+ const currentEntityDid = svc.getEntityDid?.();
4137
+ if (currentEntityDid && connection.entityDid && currentEntityDid !== connection.entityDid) {
4138
+ throw new Error("This block was configured for a different workspace. Reconnect Xero to use it here.");
4139
+ }
4140
+ const name = (inputs.Name || "").trim();
4141
+ if (!name) throw new Error("Name is required.");
4142
+ const args = { Name: name };
4143
+ if (inputs.tenant_id) args.tenant_id = inputs.tenant_id;
4144
+ if (inputs.FirstName) args.FirstName = inputs.FirstName;
4145
+ if (inputs.LastName) args.LastName = inputs.LastName;
4146
+ if (inputs.EmailAddress) args.EmailAddress = inputs.EmailAddress;
4147
+ if (inputs.phone_number) args.phone_number = inputs.phone_number;
4148
+ if (inputs.mobile_number) args.mobile_number = inputs.mobile_number;
4149
+ if (inputs.Website) args.Website = inputs.Website;
4150
+ if (inputs.TaxNumber) args.TaxNumber = inputs.TaxNumber;
4151
+ if (inputs.AccountNumber) args.AccountNumber = inputs.AccountNumber;
4152
+ if (inputs.DefaultCurrency) args.DefaultCurrency = inputs.DefaultCurrency;
4153
+ if (inputs.BankAccountDetails) args.BankAccountDetails = inputs.BankAccountDetails;
4154
+ if (typeof inputs.IsCustomer === "boolean") args.IsCustomer = inputs.IsCustomer;
4155
+ if (typeof inputs.IsSupplier === "boolean") args.IsSupplier = inputs.IsSupplier;
4156
+ const result = await svc.executeTool({
4157
+ toolSlug: XERO_CREATE_CONTACT_SLUG,
4158
+ connectedAccountId: connection.connectedAccountId,
4159
+ arguments: args
4160
+ });
4161
+ if (!result.successful) {
4162
+ throw new Error(result.error || "Xero contact creation failed");
4163
+ }
4164
+ const envelope = result.data;
4165
+ const contact = envelope?.Contacts?.[0] || envelope?.data?.Contacts?.[0] || envelope?.contact || envelope || {};
4166
+ const contactId = String(contact.ContactID || contact.contactID || contact.id || "");
4167
+ return {
4168
+ output: {
4169
+ contactId,
4170
+ name: String(contact.Name || name),
4171
+ emailAddress: String(contact.EmailAddress || inputs.EmailAddress || "")
4172
+ },
4173
+ events: contactId ? [{ name: "contact.created", payload: { contactId, name: String(contact.Name || name) } }] : void 0
4174
+ };
4175
+ }
4176
+ });
4177
+
4178
+ // src/core/lib/actionRegistry/actions/xero/invoiceCreate.ts
4179
+ var XERO_CREATE_INVOICE_SLUG = "XERO_CREATE_INVOICE";
4180
+ registerAction({
4181
+ type: "qi/xero.invoice.create",
4182
+ can: "xero.invoice/create",
4183
+ sideEffect: true,
4184
+ defaultRequiresConfirmation: true,
4185
+ requiredCapability: "flow/block/execute",
4186
+ eligibleForEventTrigger: true,
4187
+ outputSchema: [
4188
+ { path: "invoiceId", displayName: "Invoice ID", type: "string" },
4189
+ { path: "invoiceNumber", displayName: "Invoice number", type: "string" },
4190
+ { path: "total", displayName: "Total", type: "number" },
4191
+ { path: "status", displayName: "Status", type: "string" }
4192
+ ],
4193
+ events: [
4194
+ {
4195
+ name: "invoice.created",
4196
+ displayName: "Xero invoice created",
4197
+ description: "Fired after a new Xero invoice is written",
4198
+ payloadSchema: [
4199
+ { path: "invoiceId", displayName: "Invoice ID", type: "string" },
4200
+ { path: "invoiceNumber", displayName: "Invoice number", type: "string" },
4201
+ { path: "total", displayName: "Total", type: "number" }
4202
+ ],
4203
+ pendingDisplayFields: ["invoiceNumber", "total"]
4204
+ }
4205
+ ],
4206
+ run: async (inputs, ctx) => {
4207
+ const svc = ctx.services.integrations;
4208
+ if (!svc?.executeTool) {
4209
+ throw new Error("Xero integration is not configured");
4210
+ }
4211
+ const connection = inputs.connection;
4212
+ if (!connection?.connectedAccountId) {
4213
+ throw new Error("Connect Xero before running this action.");
4214
+ }
4215
+ const currentEntityDid = svc.getEntityDid?.();
4216
+ if (currentEntityDid && connection.entityDid && currentEntityDid !== connection.entityDid) {
4217
+ throw new Error("This block was configured for a different workspace. Reconnect Xero to use it here.");
4218
+ }
4219
+ const type = (inputs.Type || "").trim().toUpperCase() || "ACCREC";
4220
+ if (type !== "ACCREC" && type !== "ACCPAY") {
4221
+ throw new Error("Invoice type must be ACCREC (sale) or ACCPAY (bill).");
4222
+ }
4223
+ if (!Array.isArray(inputs.LineItems) || inputs.LineItems.length === 0) {
4224
+ throw new Error("At least one line item is required.");
4225
+ }
4226
+ const args = {
4227
+ Type: type,
4228
+ LineItems: inputs.LineItems
4229
+ };
4230
+ if (inputs.tenant_id) args.tenant_id = inputs.tenant_id;
4231
+ if (inputs.Status) args.Status = inputs.Status;
4232
+ if (inputs.Date) args.Date = inputs.Date;
4233
+ if (inputs.DueDate) args.DueDate = inputs.DueDate;
4234
+ if (inputs.ContactID) args.ContactID = inputs.ContactID;
4235
+ else if (inputs.ContactName) args.ContactName = inputs.ContactName;
4236
+ if (inputs.Reference) args.Reference = inputs.Reference;
4237
+ if (inputs.InvoiceNumber) args.InvoiceNumber = inputs.InvoiceNumber;
4238
+ if (inputs.CurrencyCode) args.CurrencyCode = inputs.CurrencyCode;
4239
+ const result = await svc.executeTool({
4240
+ toolSlug: XERO_CREATE_INVOICE_SLUG,
4241
+ connectedAccountId: connection.connectedAccountId,
4242
+ arguments: args
4243
+ });
4244
+ if (!result.successful) {
4245
+ throw new Error(result.error || "Xero invoice creation failed");
4246
+ }
4247
+ const envelope = result.data;
4248
+ const invoice = envelope?.Invoices?.[0] || envelope?.data?.Invoices?.[0] || envelope?.invoice || envelope || {};
4249
+ const invoiceId = String(invoice.InvoiceID || invoice.invoiceID || invoice.id || "");
4250
+ const invoiceNumber = String(invoice.InvoiceNumber || inputs.InvoiceNumber || "");
4251
+ const total = typeof invoice.Total === "number" ? invoice.Total : Number(invoice.Total) || 0;
4252
+ const status = String(invoice.Status || inputs.Status || "DRAFT");
4253
+ return {
4254
+ output: {
4255
+ invoiceId,
4256
+ invoiceNumber,
4257
+ total,
4258
+ status
4259
+ },
4260
+ events: invoiceId ? [{ name: "invoice.created", payload: { invoiceId, invoiceNumber, total } }] : void 0
4261
+ };
4262
+ }
4263
+ });
4264
+
4265
+ // src/core/lib/actionRegistry/actions/xero/invoiceList.ts
4266
+ var XERO_LIST_INVOICES_SLUG = "XERO_LIST_INVOICES";
4267
+ registerAction({
4268
+ type: "qi/xero.invoice.list",
4269
+ can: "xero.invoice/list",
4270
+ sideEffect: false,
4271
+ defaultRequiresConfirmation: false,
4272
+ requiredCapability: "flow/block/execute",
4273
+ eligibleForEventTrigger: false,
4274
+ outputSchema: [
4275
+ { path: "items", displayName: "Invoices", type: "array" },
4276
+ { path: "count", displayName: "Count", type: "number" },
4277
+ { path: "firstId", displayName: "First invoice ID", type: "string" },
4278
+ { path: "ids", displayName: "Invoice IDs", type: "array" }
4279
+ ],
4280
+ run: async (inputs, ctx) => {
4281
+ const svc = ctx.services.integrations;
4282
+ if (!svc?.executeTool) {
4283
+ throw new Error("Xero integration is not configured");
4284
+ }
4285
+ const connection = inputs.connection;
4286
+ if (!connection?.connectedAccountId) {
4287
+ throw new Error("Connect Xero before running this action.");
4288
+ }
4289
+ const currentEntityDid = svc.getEntityDid?.();
4290
+ if (currentEntityDid && connection.entityDid && currentEntityDid !== connection.entityDid) {
4291
+ throw new Error("This block was configured for a different workspace. Reconnect Xero to use it here.");
4292
+ }
4293
+ const args = {};
4294
+ if (inputs.tenantId) args.tenantId = inputs.tenantId;
4295
+ if (inputs.page) {
4296
+ const p = Number.parseInt(inputs.page, 10);
4297
+ if (Number.isFinite(p) && p >= 1) args.page = p;
4298
+ }
4299
+ if (inputs.order) args.order = inputs.order;
4300
+ if (inputs.Statuses) args.Statuses = inputs.Statuses;
4301
+ if (inputs.ContactIDs) args.ContactIDs = inputs.ContactIDs;
4302
+ if (inputs.InvoiceIDs) args.InvoiceIDs = inputs.InvoiceIDs;
4303
+ if (inputs.where) args.where = inputs.where;
4304
+ if (typeof inputs.createdByMyApp === "boolean") args.createdByMyApp = inputs.createdByMyApp;
4305
+ if (typeof inputs.includeArchived === "boolean") args.includeArchived = inputs.includeArchived;
4306
+ const result = await svc.executeTool({
4307
+ toolSlug: XERO_LIST_INVOICES_SLUG,
4308
+ connectedAccountId: connection.connectedAccountId,
4309
+ arguments: args
4310
+ });
4311
+ if (!result.successful) {
4312
+ throw new Error(result.error || "Xero invoice list failed");
4313
+ }
4314
+ const envelope = result.data;
4315
+ const list = envelope?.Invoices ? envelope : envelope?.data || envelope;
4316
+ const items = Array.isArray(list?.Invoices) ? list.Invoices : Array.isArray(list?.items) ? list.items : [];
4317
+ const ids = items.map((i) => String(i?.InvoiceID || i?.id || "")).filter(Boolean);
4318
+ return {
4319
+ output: {
4320
+ items,
4321
+ count: items.length,
4322
+ firstId: ids[0] ?? null,
4323
+ ids
4324
+ }
4325
+ };
4326
+ }
4327
+ });
4328
+
4329
+ // src/core/lib/actionRegistry/actions/xero/paymentCreate.ts
4330
+ var XERO_CREATE_PAYMENT_SLUG = "XERO_CREATE_PAYMENT";
4331
+ registerAction({
4332
+ type: "qi/xero.payment.create",
4333
+ can: "xero.payment/create",
4334
+ sideEffect: true,
4335
+ defaultRequiresConfirmation: true,
4336
+ requiredCapability: "flow/block/execute",
4337
+ eligibleForEventTrigger: true,
4338
+ outputSchema: [
4339
+ { path: "paymentId", displayName: "Payment ID", type: "string" },
4340
+ { path: "invoiceId", displayName: "Invoice ID", type: "string" },
4341
+ { path: "amount", displayName: "Amount", type: "number" },
4342
+ { path: "date", displayName: "Date", type: "string" },
4343
+ { path: "reference", displayName: "Reference", type: "string" },
4344
+ { path: "status", displayName: "Status", type: "string" }
4345
+ ],
4346
+ events: [
4347
+ {
4348
+ name: "payment.created",
4349
+ displayName: "Xero payment recorded",
4350
+ description: "Fired after a payment is recorded against a Xero invoice \u2014 settles the bill in the books.",
4351
+ payloadSchema: [
4352
+ { path: "paymentId", displayName: "Payment ID", type: "string" },
4353
+ { path: "invoiceId", displayName: "Invoice ID", type: "string" },
4354
+ { path: "amount", displayName: "Amount", type: "number" },
4355
+ { path: "reference", displayName: "Reference", type: "string" }
4356
+ ],
4357
+ pendingDisplayFields: ["invoiceId", "amount"]
4358
+ }
4359
+ ],
4360
+ run: async (inputs, ctx) => {
4361
+ const svc = ctx.services.integrations;
4362
+ if (!svc?.executeTool) {
4363
+ throw new Error("Xero integration is not configured");
4364
+ }
4365
+ const connection = inputs.connection;
4366
+ if (!connection?.connectedAccountId) {
4367
+ throw new Error("Connect Xero before running this action.");
4368
+ }
4369
+ const currentEntityDid = svc.getEntityDid?.();
4370
+ if (currentEntityDid && connection.entityDid && currentEntityDid !== connection.entityDid) {
4371
+ throw new Error("This block was configured for a different workspace. Reconnect Xero to use it here.");
4372
+ }
4373
+ const invoiceId = String(inputs.InvoiceID || "").trim();
4374
+ if (!invoiceId) {
4375
+ throw new Error("InvoiceID is required \u2014 usually wired from the upstream Capture Bill step.");
4376
+ }
4377
+ const accountId = String(inputs.AccountID || "").trim();
4378
+ if (!accountId) {
4379
+ throw new Error("AccountID is required \u2014 set the Xero bank/treasury account in template config.");
4380
+ }
4381
+ if (!Number.isFinite(inputs.Amount) || inputs.Amount <= 0) {
4382
+ throw new Error("Amount must be a positive number.");
4383
+ }
4384
+ const date = String(inputs.Date || "").trim() || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4385
+ const args = {
4386
+ InvoiceID: invoiceId,
4387
+ AccountID: accountId,
4388
+ Date: date,
4389
+ Amount: inputs.Amount
4390
+ };
4391
+ if (inputs.tenant_id) args.tenant_id = inputs.tenant_id;
4392
+ if (inputs.Reference) args.Reference = inputs.Reference;
4393
+ if (typeof inputs.CurrencyRate === "number" && Number.isFinite(inputs.CurrencyRate)) {
4394
+ args.CurrencyRate = inputs.CurrencyRate;
4395
+ }
4396
+ const result = await svc.executeTool({
4397
+ toolSlug: XERO_CREATE_PAYMENT_SLUG,
4398
+ connectedAccountId: connection.connectedAccountId,
4399
+ arguments: args
4400
+ });
4401
+ if (!result.successful) {
4402
+ throw new Error(result.error || "Xero payment creation failed");
4403
+ }
4404
+ const envelope = result.data;
4405
+ const payment = envelope?.Payments?.[0] || envelope?.data?.Payments?.[0] || envelope?.payment || envelope || {};
4406
+ const paymentId = String(payment.PaymentID || payment.paymentID || payment.id || "");
4407
+ const amount = typeof payment.Amount === "number" ? payment.Amount : Number(payment.Amount) || inputs.Amount;
4408
+ const status = String(payment.Status || "AUTHORISED");
4409
+ const reference = String(payment.Reference || inputs.Reference || "");
4410
+ return {
4411
+ output: {
4412
+ paymentId,
4413
+ invoiceId,
4414
+ amount,
4415
+ date,
4416
+ reference,
4417
+ status
4418
+ },
4419
+ events: paymentId ? [{ name: "payment.created", payload: { paymentId, invoiceId, amount, reference } }] : void 0
4420
+ };
4421
+ }
4422
+ });
4423
+
3549
4424
  // src/core/lib/actionRegistry/actions/bid/bid.diff.ts
3550
4425
  registerDiffResolver("bid", {
3551
4426
  resolver: async (inputs, _ctx) => {
@@ -3828,6 +4703,344 @@ registerDiffResolver("evaluateClaim", {
3828
4703
  }
3829
4704
  });
3830
4705
 
4706
+ // src/core/lib/actionRegistry/actions/calendar/eventCreate.diff.ts
4707
+ registerDiffResolver("qi/calendar.event.create", {
4708
+ resolver: async (inputs, _ctx) => {
4709
+ const attendees = parseAttendeesField(String(inputs.attendees || ""));
4710
+ const calendarId = String(inputs.calendar_id || "").trim() || "primary";
4711
+ const results = [
4712
+ {
4713
+ key: "summary",
4714
+ label: "Title",
4715
+ before: null,
4716
+ after: String(inputs.summary || "").trim() || "(no title)",
4717
+ changeType: "add"
4718
+ },
4719
+ {
4720
+ key: "start_datetime",
4721
+ label: "Start",
4722
+ before: null,
4723
+ after: String(inputs.start_datetime || "").trim() || "(required \u2014 set start time)",
4724
+ changeType: "add"
4725
+ }
4726
+ ];
4727
+ const end = String(inputs.end_datetime || "").trim();
4728
+ if (end) {
4729
+ results.push({
4730
+ key: "end_datetime",
4731
+ label: "End",
4732
+ before: null,
4733
+ after: end,
4734
+ changeType: "add"
4735
+ });
4736
+ }
4737
+ const timezone = String(inputs.timezone || "").trim();
4738
+ if (timezone) {
4739
+ results.push({
4740
+ key: "timezone",
4741
+ label: "Timezone",
4742
+ before: null,
4743
+ after: timezone,
4744
+ changeType: "add"
4745
+ });
4746
+ }
4747
+ const location = String(inputs.location || "").trim();
4748
+ if (location) {
4749
+ results.push({
4750
+ key: "location",
4751
+ label: "Location",
4752
+ before: null,
4753
+ after: location,
4754
+ changeType: "add"
4755
+ });
4756
+ }
4757
+ if (attendees.length > 0) {
4758
+ results.push({
4759
+ key: "attendees",
4760
+ label: "Attendees",
4761
+ before: null,
4762
+ after: attendees.join(", "),
4763
+ changeType: "add"
4764
+ });
4765
+ }
4766
+ results.push({
4767
+ key: "calendar_id",
4768
+ label: "Calendar",
4769
+ before: null,
4770
+ after: calendarId,
4771
+ changeType: "add"
4772
+ });
4773
+ const description = String(inputs.description || "").trim();
4774
+ if (description) {
4775
+ results.push({
4776
+ key: "description",
4777
+ label: "Description",
4778
+ before: null,
4779
+ after: description.length > 120 ? description.slice(0, 117) + "..." : description,
4780
+ changeType: "add"
4781
+ });
4782
+ }
4783
+ return results;
4784
+ }
4785
+ });
4786
+
4787
+ // src/core/lib/actionRegistry/actions/calendar/eventUpdate.diff.ts
4788
+ registerDiffResolver("qi/calendar.event.update", {
4789
+ resolver: async (inputs, ctx) => {
4790
+ const connection = inputs.connection || {};
4791
+ const connectedAccountId = connection.connectedAccountId;
4792
+ const eventId = String(inputs.event_id || "").trim();
4793
+ const calendarId = String(inputs.calendar_id || "").trim() || "primary";
4794
+ let before = null;
4795
+ if (connectedAccountId && eventId && ctx.handlers?.integrations?.fetchCurrentState) {
4796
+ try {
4797
+ const current = await ctx.handlers.integrations.fetchCurrentState({
4798
+ toolSlug: CALENDAR_EVENT_GET_SLUG,
4799
+ connectedAccountId,
4800
+ arguments: { event_id: eventId, calendar_id: calendarId }
4801
+ });
4802
+ before = current?.data || current || null;
4803
+ } catch {
4804
+ before = null;
4805
+ }
4806
+ }
4807
+ const results = [];
4808
+ const afterSummary = String(inputs.summary || "").trim();
4809
+ results.push({
4810
+ key: "summary",
4811
+ label: "Title",
4812
+ before: before?.summary ?? null,
4813
+ after: afterSummary || null,
4814
+ changeType: diffChange(before?.summary, afterSummary)
4815
+ });
4816
+ const afterStart = String(inputs.start_datetime || "").trim();
4817
+ const beforeStart = before?.start?.dateTime || before?.start?.date || null;
4818
+ results.push({
4819
+ key: "start_datetime",
4820
+ label: "Start",
4821
+ before: beforeStart,
4822
+ after: afterStart || null,
4823
+ changeType: diffChange(beforeStart, afterStart)
4824
+ });
4825
+ const afterEnd = String(inputs.end_datetime || "").trim();
4826
+ const beforeEnd = before?.end?.dateTime || before?.end?.date || null;
4827
+ if (afterEnd || beforeEnd) {
4828
+ results.push({
4829
+ key: "end_datetime",
4830
+ label: "End",
4831
+ before: beforeEnd,
4832
+ after: afterEnd || null,
4833
+ changeType: diffChange(beforeEnd, afterEnd)
4834
+ });
4835
+ }
4836
+ const afterLocation = String(inputs.location || "").trim();
4837
+ if (afterLocation || before?.location) {
4838
+ results.push({
4839
+ key: "location",
4840
+ label: "Location",
4841
+ before: before?.location ?? null,
4842
+ after: afterLocation || null,
4843
+ changeType: diffChange(before?.location, afterLocation)
4844
+ });
4845
+ }
4846
+ const afterAttendees = parseAttendeesField(String(inputs.attendees || ""));
4847
+ const beforeAttendees = Array.isArray(before?.attendees) ? before.attendees.map((a) => a?.email).filter(Boolean) : [];
4848
+ if (afterAttendees.length > 0 || beforeAttendees.length > 0) {
4849
+ const beforeStr = beforeAttendees.join(", ");
4850
+ const afterStr = afterAttendees.join(", ");
4851
+ results.push({
4852
+ key: "attendees",
4853
+ label: "Attendees",
4854
+ before: beforeStr || null,
4855
+ after: afterStr || null,
4856
+ changeType: diffChange(beforeStr, afterStr)
4857
+ });
4858
+ }
4859
+ const afterDescription = String(inputs.description || "").trim();
4860
+ if (afterDescription || before?.description) {
4861
+ const clip = (s) => s && s.length > 120 ? s.slice(0, 117) + "..." : s ?? null;
4862
+ results.push({
4863
+ key: "description",
4864
+ label: "Description",
4865
+ before: clip(before?.description),
4866
+ after: clip(afterDescription) ?? null,
4867
+ changeType: diffChange(before?.description, afterDescription)
4868
+ });
4869
+ }
4870
+ return results;
4871
+ }
4872
+ });
4873
+ function diffChange(beforeVal, afterVal) {
4874
+ const b = beforeVal === void 0 || beforeVal === null ? "" : String(beforeVal).trim();
4875
+ const a = afterVal === void 0 || afterVal === null ? "" : String(afterVal).trim();
4876
+ if (b === a) return "unchanged";
4877
+ if (!b && a) return "add";
4878
+ if (b && !a) return "remove";
4879
+ return "replace";
4880
+ }
4881
+
4882
+ // src/core/lib/actionRegistry/actions/xero/contactCreate.diff.ts
4883
+ registerDiffResolver("qi/xero.contact.create", {
4884
+ resolver: async (inputs, _ctx) => {
4885
+ const results = [
4886
+ {
4887
+ key: "Name",
4888
+ label: "Name",
4889
+ before: null,
4890
+ after: String(inputs.Name || "").trim() || "(required)",
4891
+ changeType: "add"
4892
+ }
4893
+ ];
4894
+ const fields = [
4895
+ ["EmailAddress", "Email"],
4896
+ ["phone_number", "Phone"],
4897
+ ["mobile_number", "Mobile"],
4898
+ ["Website", "Website"],
4899
+ ["TaxNumber", "Tax number"],
4900
+ ["AccountNumber", "Account number"],
4901
+ ["DefaultCurrency", "Default currency"]
4902
+ ];
4903
+ for (const [key, label] of fields) {
4904
+ const v = String(inputs[key] || "").trim();
4905
+ if (v) {
4906
+ results.push({ key: String(key), label, before: null, after: v, changeType: "add" });
4907
+ }
4908
+ }
4909
+ const roles = [];
4910
+ if (inputs.IsCustomer) roles.push("Customer");
4911
+ if (inputs.IsSupplier) roles.push("Supplier");
4912
+ if (roles.length > 0) {
4913
+ results.push({ key: "role", label: "Role", before: null, after: roles.join(" + "), changeType: "add" });
4914
+ }
4915
+ if (inputs.tenant_id) {
4916
+ results.push({ key: "tenant_id", label: "Xero org", before: null, after: String(inputs.tenant_id), changeType: "add" });
4917
+ }
4918
+ return results;
4919
+ }
4920
+ });
4921
+
4922
+ // src/core/lib/actionRegistry/actions/xero/invoiceCreate.diff.ts
4923
+ registerDiffResolver("qi/xero.invoice.create", {
4924
+ resolver: async (inputs, _ctx) => {
4925
+ const results = [
4926
+ {
4927
+ key: "Type",
4928
+ label: "Type",
4929
+ before: null,
4930
+ after: String(inputs.Type || "ACCREC").toUpperCase() === "ACCPAY" ? "ACCPAY (bill)" : "ACCREC (sale)",
4931
+ changeType: "add"
4932
+ },
4933
+ {
4934
+ key: "Status",
4935
+ label: "Status",
4936
+ before: null,
4937
+ after: String(inputs.Status || "DRAFT"),
4938
+ changeType: "add"
4939
+ }
4940
+ ];
4941
+ const contact = String(inputs.ContactID || "").trim() || String(inputs.ContactName || "").trim();
4942
+ if (contact) {
4943
+ results.push({
4944
+ key: "Contact",
4945
+ label: "Contact",
4946
+ before: null,
4947
+ after: contact,
4948
+ changeType: "add"
4949
+ });
4950
+ }
4951
+ let lineItemSummary = "(required)";
4952
+ let totalAmount = 0;
4953
+ const lineItems = inputs.LineItems;
4954
+ if (lineItems && typeof lineItems === "object") {
4955
+ if (lineItems.mode === "manual" && Array.isArray(lineItems.rows)) {
4956
+ lineItemSummary = `${lineItems.rows.length} item${lineItems.rows.length === 1 ? "" : "s"}`;
4957
+ totalAmount = lineItems.rows.reduce((sum, row) => {
4958
+ const qty = Number(row?.Quantity) || 0;
4959
+ const price = Number(row?.UnitAmount) || 0;
4960
+ return sum + qty * price;
4961
+ }, 0);
4962
+ } else if (lineItems.mode === "iterative") {
4963
+ lineItemSummary = lineItems.source ? `per item from ${String(lineItems.source).slice(0, 80)}` : "(source not picked)";
4964
+ }
4965
+ }
4966
+ results.push({
4967
+ key: "LineItems",
4968
+ label: "Line items",
4969
+ before: null,
4970
+ after: lineItemSummary,
4971
+ changeType: "add"
4972
+ });
4973
+ if (totalAmount > 0) {
4974
+ const currency = String(inputs.CurrencyCode || "").trim();
4975
+ results.push({
4976
+ key: "Total",
4977
+ label: "Total (est.)",
4978
+ before: null,
4979
+ after: currency ? `${totalAmount.toFixed(2)} ${currency}` : totalAmount.toFixed(2),
4980
+ changeType: "add"
4981
+ });
4982
+ }
4983
+ if (inputs.DueDate) {
4984
+ results.push({ key: "DueDate", label: "Due", before: null, after: String(inputs.DueDate), changeType: "add" });
4985
+ }
4986
+ if (inputs.Reference) {
4987
+ results.push({ key: "Reference", label: "Reference", before: null, after: String(inputs.Reference), changeType: "add" });
4988
+ }
4989
+ if (inputs.tenant_id) {
4990
+ results.push({ key: "tenant_id", label: "Xero org", before: null, after: String(inputs.tenant_id), changeType: "add" });
4991
+ }
4992
+ return results;
4993
+ }
4994
+ });
4995
+
4996
+ // src/core/lib/actionRegistry/actions/xero/paymentCreate.diff.ts
4997
+ registerDiffResolver("qi/xero.payment.create", {
4998
+ resolver: async (inputs, _ctx) => {
4999
+ const results = [];
5000
+ const invoiceId = String(inputs.InvoiceID || "").trim();
5001
+ if (invoiceId) {
5002
+ results.push({
5003
+ key: "InvoiceID",
5004
+ label: "Invoice",
5005
+ before: null,
5006
+ after: invoiceId,
5007
+ changeType: "add"
5008
+ });
5009
+ }
5010
+ const accountId = String(inputs.AccountID || "").trim();
5011
+ if (accountId) {
5012
+ results.push({
5013
+ key: "AccountID",
5014
+ label: "Bank account",
5015
+ before: null,
5016
+ after: accountId,
5017
+ changeType: "add"
5018
+ });
5019
+ }
5020
+ const amountRaw = String(inputs.Amount ?? "").trim();
5021
+ if (amountRaw) {
5022
+ const n = Number(amountRaw);
5023
+ results.push({
5024
+ key: "Amount",
5025
+ label: "Amount",
5026
+ before: null,
5027
+ after: Number.isFinite(n) ? n.toFixed(2) : amountRaw,
5028
+ changeType: "add"
5029
+ });
5030
+ }
5031
+ if (inputs.Date) {
5032
+ results.push({ key: "Date", label: "Date", before: null, after: String(inputs.Date), changeType: "add" });
5033
+ }
5034
+ if (inputs.Reference) {
5035
+ results.push({ key: "Reference", label: "Reference (tx hash)", before: null, after: String(inputs.Reference), changeType: "add" });
5036
+ }
5037
+ if (inputs.tenant_id) {
5038
+ results.push({ key: "tenant_id", label: "Xero org", before: null, after: String(inputs.tenant_id), changeType: "add" });
5039
+ }
5040
+ return results;
5041
+ }
5042
+ });
5043
+
3831
5044
  // src/core/lib/actionRegistry/actions/walletFund.diff.ts
3832
5045
  registerDiffResolver("qi/wallet.fund", {
3833
5046
  resolver: async (inputs, _ctx) => {
@@ -5519,7 +6732,7 @@ function compileBaseUcanFlow(plan, registry) {
5519
6732
  if (!source) {
5520
6733
  throw new Error(`Trigger on block "${nodeId}" references unknown source block "${sourceId}".`);
5521
6734
  }
5522
- const declaredEvents = source.action.events || [];
6735
+ const declaredEvents = getEventsForBlock(source.action, source.cap.nb);
5523
6736
  if (declaredEvents.length === 0) {
5524
6737
  throw new Error(`Trigger on block "${nodeId}" references event "${eventName}" but source action "${source.action.type}" declares no events.`);
5525
6738
  }
@@ -5553,7 +6766,7 @@ function compileBaseUcanFlow(plan, registry) {
5553
6766
  if (!sourceEntry) {
5554
6767
  throw new Error(`Barrier trigger on block "${nodeId}" references unknown source block "${srcId}".`);
5555
6768
  }
5556
- const declaredEvents = sourceEntry.action.events || [];
6769
+ const declaredEvents = getEventsForBlock(sourceEntry.action, sourceEntry.cap.nb);
5557
6770
  const found = declaredEvents.find((e) => e.name === evtName);
5558
6771
  if (!found) {
5559
6772
  const declared = declaredEvents.map((e) => e.name).join(", ");
@@ -6308,12 +7521,15 @@ export {
6308
7521
  getAllActions,
6309
7522
  hasAction,
6310
7523
  getActionByCan,
7524
+ getEventsForBlock,
7525
+ getOutputSchemaForBlock,
6311
7526
  canToType,
6312
7527
  typeToCan,
6313
7528
  getAllCanMappings,
6314
7529
  buildServicesFromHandlers,
6315
7530
  getDiffResolver,
6316
7531
  hasDiffResolver,
7532
+ extractSurveyAnswerSchema,
6317
7533
  transformSurveyToCredentialSubject,
6318
7534
  buildVerifiableCredential,
6319
7535
  buildDomainCardLinkedResource,
@@ -6326,6 +7542,9 @@ export {
6326
7542
  didToMatrixUserId,
6327
7543
  findOrCreateDMRoom,
6328
7544
  sendDirectMessage,
7545
+ parseCalendarEventCreateInputs,
7546
+ serializeCalendarEventCreateInputs,
7547
+ parseAttendeesField,
6329
7548
  createUcanService,
6330
7549
  createUcanDelegationStore,
6331
7550
  createMemoryUcanDelegationStore,
@@ -6365,4 +7584,4 @@ export {
6365
7584
  readFlow,
6366
7585
  setupFlowFromBaseUcan
6367
7586
  };
6368
- //# sourceMappingURL=chunk-GSW6ADEY.mjs.map
7587
+ //# sourceMappingURL=chunk-OO42QPEG.mjs.map