@ixo/editor 6.4.0 → 6.6.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.
@@ -917,6 +917,8 @@ registerAction({
917
917
  requiredCapability: "flow/block/execute",
918
918
  outputSchema: [
919
919
  { path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
920
+ { path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
921
+ { path: "proposalDescription", displayName: "Proposal Description", type: "string", description: "The long-form description voters see on-chain" },
920
922
  { path: "status", displayName: "Proposal Status", type: "string", description: "Current proposal status (open, passed, rejected, executed, etc.)" },
921
923
  { path: "proposalContractAddress", displayName: "Proposal Contract Address", type: "string", description: "The proposal module contract address" },
922
924
  { path: "coreAddress", displayName: "Core Address", type: "string", description: "The DAO core contract address" },
@@ -976,6 +978,8 @@ registerAction({
976
978
  return {
977
979
  output: {
978
980
  proposalId: String(proposalId),
981
+ proposalTitle: title,
982
+ proposalDescription: description,
979
983
  status: "open",
980
984
  proposalContractAddress: proposalContractAddress || "",
981
985
  coreAddress,
@@ -1003,6 +1007,8 @@ registerAction({
1003
1007
  requiredCapability: "flow/block/execute",
1004
1008
  outputSchema: [
1005
1009
  { path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
1010
+ { path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
1011
+ { path: "proposalDescription", displayName: "Proposal Description", type: "string", description: "The long-form description voters see on-chain" },
1006
1012
  { path: "status", displayName: "Proposal Status", type: "string", description: "Current proposal status (open, passed, rejected, executed, etc.)" },
1007
1013
  { path: "proposalContractAddress", displayName: "Proposal Contract Address", type: "string", description: "The proposal module contract address" },
1008
1014
  { path: "coreAddress", displayName: "Core Address", type: "string", description: "The DAO core contract address" },
@@ -1040,7 +1046,7 @@ registerAction({
1040
1046
  };
1041
1047
  const updateVotingConfigAction = { type: "UpdateVotingConfig", data };
1042
1048
  const title = String(inputs.title || "").trim() || "Update governance settings";
1043
- const description = String(inputs.description || "").trim() || "Updates the group's voting rules once the proposal passes.";
1049
+ const description = String(inputs.description || "").trim() || "Updates the POD's voting rules once the proposal passes.";
1044
1050
  const { preProposalContractAddress } = await handlers.getPreProposalContractAddress({ coreAddress });
1045
1051
  const { groupContractAddress } = await handlers.getGroupContractAddress({ coreAddress });
1046
1052
  const { proposalContractAddress } = await handlers.getProposalContractAddress({ coreAddress });
@@ -1058,6 +1064,8 @@ registerAction({
1058
1064
  return {
1059
1065
  output: {
1060
1066
  proposalId: String(proposalId),
1067
+ proposalTitle: title,
1068
+ proposalDescription: description,
1061
1069
  status: "open",
1062
1070
  proposalContractAddress: proposalContractAddress || "",
1063
1071
  coreAddress,
@@ -1067,6 +1075,121 @@ registerAction({
1067
1075
  }
1068
1076
  });
1069
1077
 
1078
+ // src/core/lib/actionRegistry/actions/governance/_shared.ts
1079
+ var STANDARD_OUTPUT_SCHEMA = [
1080
+ { path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
1081
+ { path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
1082
+ { path: "proposalDescription", displayName: "Proposal Description", type: "string", description: "The long-form description voters see on-chain" },
1083
+ { path: "status", displayName: "Proposal Status", type: "string", description: "Current proposal status (open, passed, rejected, executed, etc.)" },
1084
+ { path: "proposalContractAddress", displayName: "Proposal Contract Address", type: "string", description: "The proposal module contract address" },
1085
+ { path: "coreAddress", displayName: "Core Address", type: "string", description: "The DAO core contract address" },
1086
+ { path: "createdAt", displayName: "Created At", type: "string", description: "ISO timestamp of proposal creation" }
1087
+ ];
1088
+ function registerGovernanceProposalAction(spec) {
1089
+ registerAction({
1090
+ type: spec.type,
1091
+ can: spec.can,
1092
+ sideEffect: true,
1093
+ proof: { fields: ["proposalId"] },
1094
+ defaultRequiresConfirmation: true,
1095
+ requiredCapability: "flow/block/execute",
1096
+ outputSchema: [...STANDARD_OUTPUT_SCHEMA, ...spec.extraOutputSchema || []],
1097
+ run: async (inputs, ctx) => {
1098
+ const handlers = ctx.handlers;
1099
+ if (!handlers) {
1100
+ throw new Error("Handlers not available");
1101
+ }
1102
+ if (!handlers.getPreProposalContractAddress || !handlers.getGroupContractAddress || !handlers.getProposalContractAddress || !handlers.createProposal) {
1103
+ throw new Error("Governance proposal handlers not available");
1104
+ }
1105
+ const coreAddress = String(inputs.coreAddress || "").trim();
1106
+ if (!coreAddress) throw new Error("coreAddress is required");
1107
+ const actions2 = spec.buildActions(inputs);
1108
+ if (!actions2.length) throw new Error("The proposal must contain at least one action");
1109
+ const title = String(inputs.title || "").trim() || spec.defaultTitle(inputs);
1110
+ const description = String(inputs.description || "").trim() || (spec.defaultDescription ? spec.defaultDescription(inputs) : title);
1111
+ const { preProposalContractAddress } = await handlers.getPreProposalContractAddress({ coreAddress });
1112
+ const { groupContractAddress } = await handlers.getGroupContractAddress({ coreAddress });
1113
+ const { proposalContractAddress } = await handlers.getProposalContractAddress({ coreAddress });
1114
+ const proposalId = await handlers.createProposal({
1115
+ preProposalContractAddress,
1116
+ title,
1117
+ description,
1118
+ actions: actions2,
1119
+ coreAddress,
1120
+ groupContractAddress
1121
+ });
1122
+ if (proposalId === void 0 || proposalId === null || String(proposalId).trim() === "") {
1123
+ throw new Error("Proposal creation returned no proposal id. Check the handler logs.");
1124
+ }
1125
+ return {
1126
+ output: {
1127
+ proposalId: String(proposalId),
1128
+ proposalTitle: title,
1129
+ proposalDescription: description,
1130
+ status: "open",
1131
+ proposalContractAddress: proposalContractAddress || "",
1132
+ coreAddress,
1133
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1134
+ ...spec.buildExtraOutput ? spec.buildExtraOutput(inputs) : {}
1135
+ }
1136
+ };
1137
+ }
1138
+ });
1139
+ }
1140
+
1141
+ // src/core/lib/actionRegistry/actions/governance/submissionConfigProposal.ts
1142
+ var REFUND_POLICIES = ["always", "only_passed", "never"];
1143
+ registerGovernanceProposalAction({
1144
+ type: "qi/governance.submission-config-proposal",
1145
+ can: "governance/submission-config-proposal",
1146
+ buildActions: (inputs) => {
1147
+ const anyoneCanPropose = Boolean(inputs.anyoneCanPropose);
1148
+ const depositRequired = Boolean(inputs.depositRequired);
1149
+ let amount = "0";
1150
+ let refundPolicy = "always";
1151
+ if (depositRequired) {
1152
+ amount = String(inputs.depositAmount || "").trim();
1153
+ if (!/^\d+$/.test(amount) || BigInt(amount) <= 0n) {
1154
+ throw new Error("depositAmount must be a positive whole number in uixo base units");
1155
+ }
1156
+ const policy = String(inputs.depositRefundPolicy || "").trim();
1157
+ if (!REFUND_POLICIES.includes(policy)) {
1158
+ throw new Error("depositRefundPolicy must be one of 'always', 'only_passed' or 'never'");
1159
+ }
1160
+ refundPolicy = policy;
1161
+ }
1162
+ return [
1163
+ {
1164
+ type: "UpdatePreProposeConfig",
1165
+ data: {
1166
+ depositRequired,
1167
+ // Deposit token fixed to the chain's native IXO (uixo) for now.
1168
+ depositInfo: { amount, type: "native", denomOrAddress: "uixo", refundPolicy },
1169
+ anyoneCanPropose
1170
+ }
1171
+ }
1172
+ ];
1173
+ },
1174
+ defaultTitle: () => "Update proposal submission rules",
1175
+ defaultDescription: () => "Updates who may submit proposals and the required deposit once the proposal passes.",
1176
+ buildExtraOutput: (inputs) => {
1177
+ const depositRequired = Boolean(inputs.depositRequired);
1178
+ return {
1179
+ anyoneCanPropose: String(Boolean(inputs.anyoneCanPropose)),
1180
+ depositRequired: String(depositRequired),
1181
+ depositAmount: depositRequired ? String(inputs.depositAmount || "") : "0",
1182
+ depositRefundPolicy: depositRequired ? String(inputs.depositRefundPolicy || "") : "always"
1183
+ };
1184
+ },
1185
+ extraOutputSchema: [
1186
+ { path: "anyoneCanPropose", displayName: "Anyone Can Propose", type: "string", description: "Whether non-members may submit proposals after the update" },
1187
+ { path: "depositRequired", displayName: "Deposit Required", type: "string", description: "Whether a deposit is required to submit a proposal" },
1188
+ { path: "depositAmount", displayName: "Deposit Amount", type: "string", description: "Required deposit in uixo base units" },
1189
+ { path: "depositRefundPolicy", displayName: "Deposit Refund Policy", type: "string", description: "What happens to the deposit after voting ends" }
1190
+ ]
1191
+ });
1192
+
1070
1193
  // src/core/lib/actionRegistry/actions/governance/transactionSendFunds.ts
1071
1194
  registerAction({
1072
1195
  type: "qi/governance.transaction.send-funds",
@@ -1077,6 +1200,8 @@ registerAction({
1077
1200
  requiredCapability: "flow/block/execute",
1078
1201
  outputSchema: [
1079
1202
  { path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
1203
+ { path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
1204
+ { path: "proposalDescription", displayName: "Proposal Description", type: "string", description: "The long-form description voters see on-chain" },
1080
1205
  { path: "status", displayName: "Proposal Status", type: "string", description: "Current proposal status (open, passed, rejected, executed, etc.)" },
1081
1206
  { path: "proposalContractAddress", displayName: "Proposal Contract Address", type: "string", description: "The proposal module contract address" },
1082
1207
  { path: "coreAddress", displayName: "Core Address", type: "string", description: "The DAO core contract address" },
@@ -1109,7 +1234,7 @@ registerAction({
1109
1234
  const baseAmount = BigInt(Math.round(amount * 10 ** tokenInfo.exponent)).toString();
1110
1235
  const spendAction = { type: "Spend", data: { to: recipient, denom, amount: baseAmount } };
1111
1236
  const title = String(inputs.title || "").trim() || `Send ${amount} ${tokenInfo.symbol} to ${recipient}`;
1112
- const description = String(inputs.description || "").trim() || "Sends funds from the group treasury once the proposal passes.";
1237
+ const description = String(inputs.description || "").trim() || "Sends funds from the POD treasury once the proposal passes.";
1113
1238
  const { preProposalContractAddress } = await handlers.getPreProposalContractAddress({ coreAddress });
1114
1239
  const { groupContractAddress } = await handlers.getGroupContractAddress({ coreAddress });
1115
1240
  const { proposalContractAddress } = await handlers.getProposalContractAddress({ coreAddress });
@@ -1127,6 +1252,8 @@ registerAction({
1127
1252
  return {
1128
1253
  output: {
1129
1254
  proposalId: String(proposalId),
1255
+ proposalTitle: title,
1256
+ proposalDescription: description,
1130
1257
  status: "open",
1131
1258
  proposalContractAddress: proposalContractAddress || "",
1132
1259
  coreAddress,
@@ -1137,6 +1264,847 @@ registerAction({
1137
1264
  }
1138
1265
  });
1139
1266
 
1267
+ // src/core/lib/actionRegistry/actions/governance/transactionSendGroupToken.ts
1268
+ registerGovernanceProposalAction({
1269
+ type: "qi/governance.transaction.send-group-token",
1270
+ can: "governance.transaction/send-group-token",
1271
+ buildActions: (inputs) => {
1272
+ const contract = String(inputs.tokenContract || "").trim();
1273
+ if (!contract) throw new Error("tokenContract is required");
1274
+ const toAddress = String(inputs.recipient || "").trim();
1275
+ if (!toAddress) throw new Error("recipient is required");
1276
+ const amount = String(inputs.amount || "").trim();
1277
+ if (!/^\d+$/.test(amount) || BigInt(amount) <= 0n) {
1278
+ throw new Error("amount must be a positive whole number in the token\u2019s base units");
1279
+ }
1280
+ return [{ type: "SendGroupToken", data: { contract, toAddress, amount } }];
1281
+ },
1282
+ defaultTitle: (inputs) => `Send ${inputs.amount} group tokens to ${inputs.recipient}`,
1283
+ defaultDescription: () => "Transfers group tokens from the treasury once the proposal passes.",
1284
+ buildExtraOutput: (inputs) => ({ recipient: String(inputs.recipient || ""), tokenContract: String(inputs.tokenContract || "") }),
1285
+ extraOutputSchema: [
1286
+ { path: "recipient", displayName: "Recipient", type: "string", description: "Destination address of the transfer" },
1287
+ { path: "tokenContract", displayName: "Token Contract", type: "string", description: "The cw20 token contract transferred from" }
1288
+ ]
1289
+ });
1290
+
1291
+ // src/core/lib/actionRegistry/actions/governance/transactionMint.ts
1292
+ registerGovernanceProposalAction({
1293
+ type: "qi/governance.transaction.mint",
1294
+ can: "governance.transaction/mint",
1295
+ buildActions: (inputs) => {
1296
+ const to = String(inputs.recipient || "").trim();
1297
+ if (!to) throw new Error("recipient is required");
1298
+ const amount = String(inputs.amount ?? "").trim();
1299
+ if (!/^\d+$/.test(amount) || BigInt(amount) <= 0n) {
1300
+ throw new Error("amount must be a positive whole number in the token\u2019s base units");
1301
+ }
1302
+ return [{ type: "Mint", data: { to, amount } }];
1303
+ },
1304
+ defaultTitle: (inputs) => `Mint ${inputs.amount} governance tokens to ${inputs.recipient}`,
1305
+ defaultDescription: () => "Mints new governance tokens to the recipient once the proposal passes.",
1306
+ buildExtraOutput: (inputs) => ({ recipient: String(inputs.recipient || ""), amount: String(inputs.amount || "") }),
1307
+ extraOutputSchema: [
1308
+ { path: "recipient", displayName: "Recipient", type: "string", description: "Destination address the tokens are minted to" },
1309
+ { path: "amount", displayName: "Amount", type: "string", description: "Number of governance tokens minted" }
1310
+ ]
1311
+ });
1312
+
1313
+ // src/core/lib/actionRegistry/actions/governance/transactionPerformTokenSwap.ts
1314
+ registerGovernanceProposalAction({
1315
+ type: "qi/governance.transaction.perform-token-swap",
1316
+ can: "governance.transaction/perform-token-swap",
1317
+ buildActions: (inputs) => {
1318
+ const tokenSwapContractAddress = String(inputs.tokenSwapContractAddress || "").trim();
1319
+ if (!tokenSwapContractAddress) throw new Error("tokenSwapContractAddress is required");
1320
+ const type = String(inputs.selfPartyType || "").trim();
1321
+ if (type !== "native" && type !== "cw20") throw new Error("selfPartyType must be 'native' or 'cw20'");
1322
+ const denomOrAddress = String(inputs.selfPartyDenomOrAddress || "").trim();
1323
+ if (!denomOrAddress) throw new Error("selfPartyDenomOrAddress is required");
1324
+ const amount = String(inputs.selfPartyAmount || "").trim();
1325
+ if (!/^\d+$/.test(amount) || BigInt(amount) <= 0n) {
1326
+ throw new Error("selfPartyAmount must be a positive whole number in the token\u2019s base units");
1327
+ }
1328
+ return [
1329
+ {
1330
+ type: "PerformTokenSwap",
1331
+ data: { contractChosen: true, tokenSwapContractAddress, selfParty: { type, denomOrAddress, amount } }
1332
+ }
1333
+ ];
1334
+ },
1335
+ defaultTitle: (inputs) => `Fund token swap ${inputs.tokenSwapContractAddress}`,
1336
+ defaultDescription: () => "Sends the group\u2019s side of the token swap to the swap contract once the proposal passes.",
1337
+ buildExtraOutput: (inputs) => ({
1338
+ tokenSwapContractAddress: String(inputs.tokenSwapContractAddress || ""),
1339
+ selfPartyDenomOrAddress: String(inputs.selfPartyDenomOrAddress || ""),
1340
+ selfPartyAmount: String(inputs.selfPartyAmount || "")
1341
+ }),
1342
+ extraOutputSchema: [
1343
+ { path: "tokenSwapContractAddress", displayName: "Swap Contract", type: "string", description: "The cw-token-swap contract funded" },
1344
+ { path: "selfPartyDenomOrAddress", displayName: "Token Denom / Address", type: "string", description: "The native denom or cw20 address the group provides" },
1345
+ { path: "selfPartyAmount", displayName: "Amount", type: "string", description: "Amount provided, in the token\u2019s base units" }
1346
+ ]
1347
+ });
1348
+
1349
+ // src/core/lib/actionRegistry/actions/governance/transactionWithdrawTokenSwap.ts
1350
+ registerGovernanceProposalAction({
1351
+ type: "qi/governance.transaction.withdraw-token-swap",
1352
+ can: "governance.transaction/withdraw-token-swap",
1353
+ buildActions: (inputs) => {
1354
+ const tokenSwapContractAddress = String(inputs.tokenSwapContractAddress || "").trim();
1355
+ if (!tokenSwapContractAddress) throw new Error("tokenSwapContractAddress is required");
1356
+ return [{ type: "WithdrawTokenSwap", data: { contractChosen: true, tokenSwapContractAddress } }];
1357
+ },
1358
+ defaultTitle: (inputs) => `Withdraw from token swap ${inputs.tokenSwapContractAddress}`,
1359
+ defaultDescription: () => "Returns the group\u2019s funds from the token swap contract once the proposal passes.",
1360
+ buildExtraOutput: (inputs) => ({ tokenSwapContractAddress: String(inputs.tokenSwapContractAddress || "") }),
1361
+ extraOutputSchema: [{ path: "tokenSwapContractAddress", displayName: "Swap Contract", type: "string", description: "The cw-token-swap contract withdrawn from" }]
1362
+ });
1363
+
1364
+ // src/core/lib/actionRegistry/actions/governance/stakingStake.ts
1365
+ var STAKE_TYPES = ["delegate", "undelegate", "redelegate", "withdraw_delegator_reward"];
1366
+ var displayIxo = (amount) => {
1367
+ const base = Number(amount);
1368
+ return Number.isFinite(base) ? String(base / 1e6) : String(amount || "");
1369
+ };
1370
+ registerGovernanceProposalAction({
1371
+ type: "qi/governance.staking.stake",
1372
+ can: "governance.staking/stake",
1373
+ buildActions: (inputs) => {
1374
+ const stakeType = String(inputs.stakeType || "").trim();
1375
+ if (!STAKE_TYPES.includes(stakeType)) {
1376
+ throw new Error("stakeType must be delegate, undelegate, redelegate or withdraw_delegator_reward");
1377
+ }
1378
+ const validator = String(inputs.validator || "").trim();
1379
+ if (!validator) throw new Error("validator is required");
1380
+ const isRedelegate = stakeType === "redelegate";
1381
+ const toValidator = String(inputs.toValidator || "").trim();
1382
+ if (isRedelegate && !toValidator) throw new Error("toValidator is required for redelegations");
1383
+ let amount = "0";
1384
+ if (stakeType !== "withdraw_delegator_reward") {
1385
+ amount = String(inputs.amount || "").trim();
1386
+ if (!/^\d+$/.test(amount) || BigInt(amount) <= 0n) {
1387
+ throw new Error("amount must be a positive whole number of uixo (base units)");
1388
+ }
1389
+ }
1390
+ return [{ type: "Stake", data: { stakeType, validator, toValidator: isRedelegate ? toValidator : "", amount, denom: "uixo" } }];
1391
+ },
1392
+ // buildActions runs first, so the stake type is always a valid option here.
1393
+ defaultTitle: (inputs) => {
1394
+ switch (String(inputs.stakeType)) {
1395
+ case "undelegate":
1396
+ return `Unstake ${displayIxo(inputs.amount)} IXO from ${inputs.validator}`;
1397
+ case "redelegate":
1398
+ return `Restake ${displayIxo(inputs.amount)} IXO from ${inputs.validator} to ${inputs.toValidator}`;
1399
+ case "withdraw_delegator_reward":
1400
+ return `Claim staking rewards from ${inputs.validator}`;
1401
+ default:
1402
+ return `Stake ${displayIxo(inputs.amount)} IXO with ${inputs.validator}`;
1403
+ }
1404
+ },
1405
+ defaultDescription: () => "Executes the staking operation from the POD treasury once the proposal passes.",
1406
+ buildExtraOutput: (inputs) => ({
1407
+ stakeType: String(inputs.stakeType || ""),
1408
+ validator: String(inputs.validator || ""),
1409
+ amount: String(inputs.amount || "")
1410
+ }),
1411
+ extraOutputSchema: [
1412
+ { path: "stakeType", displayName: "Stake Type", type: "string", description: "The staking operation (delegate, undelegate, redelegate, withdraw_delegator_reward)" },
1413
+ { path: "validator", displayName: "Validator", type: "string", description: "The validator the operation targets (source for redelegations)" },
1414
+ { path: "amount", displayName: "Amount", type: "string", description: "The uixo amount staked, in base units" }
1415
+ ]
1416
+ });
1417
+
1418
+ // src/core/lib/actionRegistry/actions/governance/stakingStakeToGroup.ts
1419
+ registerGovernanceProposalAction({
1420
+ type: "qi/governance.staking.stake-to-group",
1421
+ can: "governance.staking/stake-to-group",
1422
+ buildActions: (inputs) => {
1423
+ const tokenContract = String(inputs.tokenContract || "").trim();
1424
+ if (!tokenContract) throw new Error("tokenContract is required");
1425
+ const stakingContract = String(inputs.stakingContract || "").trim();
1426
+ if (!stakingContract) throw new Error("stakingContract is required");
1427
+ const amount = String(inputs.amount || "").trim();
1428
+ if (!/^\d+$/.test(amount) || BigInt(amount) <= 0n) {
1429
+ throw new Error("amount must be a positive whole number in the token\u2019s base units");
1430
+ }
1431
+ return [{ type: "StakeToGroup", data: { tokenContract, stakingContract, amount } }];
1432
+ },
1433
+ defaultTitle: (inputs) => `Stake ${inputs.amount} tokens to the group`,
1434
+ defaultDescription: () => "Stakes treasury tokens into the group staking contract once the proposal passes.",
1435
+ buildExtraOutput: (inputs) => ({ tokenContract: String(inputs.tokenContract || ""), stakingContract: String(inputs.stakingContract || "") }),
1436
+ extraOutputSchema: [
1437
+ { path: "tokenContract", displayName: "Token Contract", type: "string", description: "The cw20 token contract the tokens are sent from" },
1438
+ { path: "stakingContract", displayName: "Staking Contract", type: "string", description: "The staking contract the tokens are staked into" }
1439
+ ]
1440
+ });
1441
+
1442
+ // src/core/lib/actionRegistry/actions/governance/authzGrant.ts
1443
+ registerGovernanceProposalAction({
1444
+ type: "qi/governance.authz.grant",
1445
+ can: "governance.authz/grant",
1446
+ buildActions: (inputs) => {
1447
+ const grantee = String(inputs.grantee || "").trim();
1448
+ if (!grantee) throw new Error("grantee is required");
1449
+ const msgTypeUrl = String(inputs.msgTypeUrl || "").trim();
1450
+ if (!msgTypeUrl) throw new Error("msgTypeUrl is required");
1451
+ if (!msgTypeUrl.startsWith("/")) {
1452
+ throw new Error('msgTypeUrl must be a fully-qualified message type URL starting with "/"');
1453
+ }
1454
+ return [{ type: "AuthzGrant", data: { typeUrl: "/cosmos.authz.v1beta1.MsgGrant", value: { grantee, msgTypeUrl } } }];
1455
+ },
1456
+ defaultTitle: (inputs) => `Grant ${inputs.msgTypeUrl} authorization to ${inputs.grantee}`,
1457
+ defaultDescription: () => "Authorizes the grantee to execute the message type on the group\u2019s behalf (1-year expiry) once the proposal passes.",
1458
+ buildExtraOutput: (inputs) => ({ grantee: String(inputs.grantee || ""), msgTypeUrl: String(inputs.msgTypeUrl || "") }),
1459
+ extraOutputSchema: [
1460
+ { path: "grantee", displayName: "Grantee", type: "string", description: "Address that receives the authorization" },
1461
+ { path: "msgTypeUrl", displayName: "Message Type URL", type: "string", description: "The message type the grantee may execute" }
1462
+ ]
1463
+ });
1464
+
1465
+ // src/core/lib/actionRegistry/actions/governance/authzRevoke.ts
1466
+ registerGovernanceProposalAction({
1467
+ type: "qi/governance.authz.revoke",
1468
+ can: "governance.authz/revoke",
1469
+ buildActions: (inputs) => {
1470
+ const grantee = String(inputs.grantee || "").trim();
1471
+ if (!grantee) throw new Error("grantee is required");
1472
+ const msgTypeUrl = String(inputs.msgTypeUrl || "").trim();
1473
+ if (!msgTypeUrl) throw new Error("msgTypeUrl is required");
1474
+ if (!msgTypeUrl.startsWith("/")) {
1475
+ throw new Error('msgTypeUrl must be a fully-qualified message type URL starting with "/"');
1476
+ }
1477
+ return [{ type: "AuthzRevoke", data: { typeUrl: "/cosmos.authz.v1beta1.MsgRevoke", value: { grantee, msgTypeUrl } } }];
1478
+ },
1479
+ defaultTitle: (inputs) => `Revoke ${inputs.msgTypeUrl} authorization from ${inputs.grantee}`,
1480
+ defaultDescription: () => "Revokes the grantee\u2019s authorization to execute the message type on the group\u2019s behalf once the proposal passes.",
1481
+ buildExtraOutput: (inputs) => ({ grantee: String(inputs.grantee || ""), msgTypeUrl: String(inputs.msgTypeUrl || "") }),
1482
+ extraOutputSchema: [
1483
+ { path: "grantee", displayName: "Grantee", type: "string", description: "Address whose authorization is revoked" },
1484
+ { path: "msgTypeUrl", displayName: "Message Type URL", type: "string", description: "The message type of the revoked grant" }
1485
+ ]
1486
+ });
1487
+
1488
+ // src/core/lib/actionRegistry/actions/governance/authzExec.ts
1489
+ var AUTHZ_EXEC_ACTION_TYPES = {
1490
+ delegate: "/cosmos.staking.v1beta1.MsgDelegate",
1491
+ undelegate: "/cosmos.staking.v1beta1.MsgUndelegate",
1492
+ redelegate: "/cosmos.staking.v1beta1.MsgBeginRedelegate",
1493
+ claimRewards: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward",
1494
+ custom: "custom"
1495
+ };
1496
+ var ACTION_LABELS = {
1497
+ [AUTHZ_EXEC_ACTION_TYPES.delegate]: "delegate",
1498
+ [AUTHZ_EXEC_ACTION_TYPES.undelegate]: "undelegate",
1499
+ [AUTHZ_EXEC_ACTION_TYPES.redelegate]: "redelegate",
1500
+ [AUTHZ_EXEC_ACTION_TYPES.claimRewards]: "claim-rewards",
1501
+ [AUTHZ_EXEC_ACTION_TYPES.custom]: "custom"
1502
+ };
1503
+ registerGovernanceProposalAction({
1504
+ type: "qi/governance.authz.exec",
1505
+ can: "governance.authz/exec",
1506
+ buildActions: (inputs) => {
1507
+ const actionType = String(inputs.authzExecActionType || "").trim();
1508
+ const known = Object.values(AUTHZ_EXEC_ACTION_TYPES);
1509
+ if (!known.includes(actionType)) {
1510
+ throw new Error(`authzExecActionType must be one of: ${known.join(", ")}`);
1511
+ }
1512
+ const delegator = String(inputs.delegatorAddress || "").trim();
1513
+ const validator = String(inputs.validatorAddress || "").trim();
1514
+ const validatorDst = String(inputs.validatorDstAddress || "").trim();
1515
+ const amount = String(inputs.amount || "").trim();
1516
+ const requireStakingFields = () => {
1517
+ if (!delegator) throw new Error("delegatorAddress is required");
1518
+ if (!validator) throw new Error("validatorAddress is required");
1519
+ if (!/^\d+$/.test(amount) || BigInt(amount) <= 0n) {
1520
+ throw new Error("amount must be a positive whole number in uixo base units");
1521
+ }
1522
+ };
1523
+ const data = {
1524
+ authzExecActionType: actionType,
1525
+ delegate: {},
1526
+ undelegate: {},
1527
+ redelegate: {},
1528
+ claimRewards: {},
1529
+ custom: ""
1530
+ };
1531
+ switch (actionType) {
1532
+ case AUTHZ_EXEC_ACTION_TYPES.delegate:
1533
+ requireStakingFields();
1534
+ data.delegate = { delegatorAddress: delegator, validatorAddress: validator, amount: { denom: "uixo", amount } };
1535
+ break;
1536
+ case AUTHZ_EXEC_ACTION_TYPES.undelegate:
1537
+ requireStakingFields();
1538
+ data.undelegate = { delegatorAddress: delegator, validatorAddress: validator, amount: { denom: "uixo", amount } };
1539
+ break;
1540
+ case AUTHZ_EXEC_ACTION_TYPES.redelegate:
1541
+ requireStakingFields();
1542
+ if (!validatorDst) throw new Error("validatorDstAddress is required");
1543
+ data.redelegate = { delegatorAddress: delegator, validatorSrcAddress: validator, validatorDstAddress: validatorDst, amount: { denom: "uixo", amount } };
1544
+ break;
1545
+ case AUTHZ_EXEC_ACTION_TYPES.claimRewards:
1546
+ if (!delegator) throw new Error("delegatorAddress is required");
1547
+ if (!validator) throw new Error("validatorAddress is required");
1548
+ data.claimRewards = { delegatorAddress: delegator, validatorAddress: validator };
1549
+ break;
1550
+ case AUTHZ_EXEC_ACTION_TYPES.custom: {
1551
+ const custom = String(inputs.custom || "").trim();
1552
+ if (!custom) throw new Error("custom is required");
1553
+ let parsed;
1554
+ try {
1555
+ parsed = JSON.parse(custom);
1556
+ } catch {
1557
+ throw new Error("custom must be valid JSON");
1558
+ }
1559
+ if (!Array.isArray(parsed) || parsed.length === 0) {
1560
+ throw new Error("custom must be a non-empty JSON array of encoded msgs");
1561
+ }
1562
+ data.custom = custom;
1563
+ break;
1564
+ }
1565
+ }
1566
+ return [{ type: "AuthzExec", data }];
1567
+ },
1568
+ defaultTitle: (inputs) => `Execute authorized ${ACTION_LABELS[String(inputs.authzExecActionType || "").trim()] || "authz"} action`,
1569
+ defaultDescription: () => "Executes a message the group was previously authorized to run once the proposal passes.",
1570
+ buildExtraOutput: (inputs) => ({
1571
+ authzExecActionType: String(inputs.authzExecActionType || ""),
1572
+ validatorAddress: String(inputs.validatorAddress || "")
1573
+ }),
1574
+ extraOutputSchema: [
1575
+ { path: "authzExecActionType", displayName: "Exec Action Type", type: "string", description: "The executed message type URL (or `custom`)" },
1576
+ { path: "validatorAddress", displayName: "Validator Address", type: "string", description: "The validator targeted by staking ops (empty for custom)" }
1577
+ ]
1578
+ });
1579
+
1580
+ // src/core/lib/actionRegistry/actions/governance/chainGovernanceVote.ts
1581
+ var VOTE_LABELS = { 1: "Yes", 2: "Abstain", 3: "No", 4: "No with veto" };
1582
+ registerGovernanceProposalAction({
1583
+ type: "qi/governance.chain-governance-vote",
1584
+ can: "governance/chain-governance-vote",
1585
+ buildActions: (inputs) => {
1586
+ const proposalId = String(inputs.proposalId || "").trim();
1587
+ if (!/^\d+$/.test(proposalId)) {
1588
+ throw new Error("proposalId must be a chain governance proposal number (digits only)");
1589
+ }
1590
+ const vote = Number(inputs.vote);
1591
+ if (!VOTE_LABELS[vote]) {
1592
+ throw new Error("vote must be 1 (Yes), 2 (Abstain), 3 (No) or 4 (No with veto)");
1593
+ }
1594
+ return [{ type: "GovernanceVote", data: { proposalId, vote } }];
1595
+ },
1596
+ // buildActions runs first, so the vote is always a valid option here.
1597
+ defaultTitle: (inputs) => `Vote ${VOTE_LABELS[Number(inputs.vote)]} on chain governance proposal #${inputs.proposalId}`,
1598
+ defaultDescription: () => "Casts the group's vote on the chain governance proposal once this proposal passes.",
1599
+ buildExtraOutput: (inputs) => ({
1600
+ chainProposalId: String(inputs.proposalId || ""),
1601
+ voteOption: VOTE_LABELS[Number(inputs.vote)] || String(inputs.vote || "")
1602
+ }),
1603
+ extraOutputSchema: [
1604
+ { path: "chainProposalId", displayName: "Chain Proposal ID", type: "string", description: "The chain governance proposal the POD votes on" },
1605
+ { path: "voteOption", displayName: "Vote Option", type: "string", description: "The vote cast (Yes, Abstain, No, No with veto)" }
1606
+ ]
1607
+ });
1608
+
1609
+ // src/core/lib/actionRegistry/actions/governance/daoJoin.ts
1610
+ registerGovernanceProposalAction({
1611
+ type: "qi/governance.dao.join",
1612
+ can: "governance.dao/join",
1613
+ buildActions: (inputs) => {
1614
+ const id = String(inputs.entityDid || "").trim();
1615
+ if (!id) throw new Error("entityDid is required");
1616
+ const address = String(inputs.memberId || "").trim();
1617
+ if (!address) throw new Error("memberId is required");
1618
+ const coreAddress = String(inputs.coreAddress || "").trim();
1619
+ return [{ type: "Join", data: { id, coreAddress, address } }];
1620
+ },
1621
+ defaultTitle: (inputs) => `Join entity ${inputs.entityDid}`,
1622
+ defaultDescription: (inputs) => `Links ${inputs.memberId} as a member of ${inputs.entityDid} once the proposal passes.`,
1623
+ buildExtraOutput: (inputs) => ({ entityDid: String(inputs.entityDid || ""), memberId: String(inputs.memberId || "") }),
1624
+ extraOutputSchema: [
1625
+ { path: "entityDid", displayName: "Entity DID", type: "string", description: "The entity the group joined" },
1626
+ { path: "memberId", displayName: "Member ID", type: "string", description: "The member DID/id recorded as the linked entity" }
1627
+ ]
1628
+ });
1629
+
1630
+ // src/core/lib/actionRegistry/actions/governance/daoUpdateInfo.ts
1631
+ registerGovernanceProposalAction({
1632
+ type: "qi/governance.dao.update-info",
1633
+ can: "governance.dao/update-info",
1634
+ buildActions: (inputs) => {
1635
+ const name = String(inputs.name || "").trim();
1636
+ if (!name) throw new Error("name is required");
1637
+ const daoDescription = String(inputs.daoDescription || "").trim();
1638
+ const imageUrl = String(inputs.imageUrl || "").trim();
1639
+ return [
1640
+ {
1641
+ type: "UpdateInfo",
1642
+ data: {
1643
+ name,
1644
+ description: daoDescription,
1645
+ automatically_add_cw20s: inputs.automaticallyAddCw20s === true || inputs.automaticallyAddCw20s === "true",
1646
+ automatically_add_cw721s: inputs.automaticallyAddCw721s === true || inputs.automaticallyAddCw721s === "true",
1647
+ image_url: imageUrl || null
1648
+ }
1649
+ }
1650
+ ];
1651
+ },
1652
+ defaultTitle: (inputs) => `Update DAO info to \u201C${inputs.name}\u201D`,
1653
+ defaultDescription: () => "Replaces the DAO name, description, image and auto-add settings once the proposal passes.",
1654
+ buildExtraOutput: (inputs) => ({ daoName: String(inputs.name || "") }),
1655
+ extraOutputSchema: [{ path: "daoName", displayName: "DAO Name", type: "string", description: "The DAO display name the proposal sets" }]
1656
+ });
1657
+
1658
+ // src/core/lib/actionRegistry/actions/governance/daoManageSubDaos.ts
1659
+ function normalizeAddresses(value) {
1660
+ return (Array.isArray(value) ? value : []).map((entry) => String(typeof entry === "string" ? entry : entry?.addr || entry?.address || "").trim());
1661
+ }
1662
+ registerGovernanceProposalAction({
1663
+ type: "qi/governance.dao.manage-subdaos",
1664
+ can: "governance.dao/manage-subdaos",
1665
+ buildActions: (inputs) => {
1666
+ const toAdd = normalizeAddresses(inputs.toAdd);
1667
+ const toRemove = normalizeAddresses(inputs.toRemove);
1668
+ if (toAdd.some((addr) => !addr) || toRemove.some((addr) => !addr)) {
1669
+ throw new Error("Every SubDAO entry needs an address");
1670
+ }
1671
+ if (toAdd.length + toRemove.length === 0) {
1672
+ throw new Error("Add or remove at least one SubDAO");
1673
+ }
1674
+ const all = [...toAdd, ...toRemove];
1675
+ if (new Set(all).size !== all.length) throw new Error("Duplicate SubDAO addresses are not allowed");
1676
+ return [
1677
+ {
1678
+ type: "ManageSubDaos",
1679
+ data: {
1680
+ toAdd: toAdd.map((addr) => ({ addr })),
1681
+ toRemove: toRemove.map((address) => ({ address }))
1682
+ }
1683
+ }
1684
+ ];
1685
+ },
1686
+ defaultTitle: (inputs) => {
1687
+ const added = normalizeAddresses(inputs.toAdd).length;
1688
+ const removed = normalizeAddresses(inputs.toRemove).length;
1689
+ return `Add ${added} and remove ${removed} SubDAOs`;
1690
+ },
1691
+ defaultDescription: () => "Updates the set of SubDAOs this DAO recognises once the proposal passes.",
1692
+ buildExtraOutput: (inputs) => ({
1693
+ addedCount: String(normalizeAddresses(inputs.toAdd).length),
1694
+ removedCount: String(normalizeAddresses(inputs.toRemove).length)
1695
+ }),
1696
+ extraOutputSchema: [
1697
+ { path: "addedCount", displayName: "SubDAOs Added", type: "string", description: "Number of SubDAOs the proposal recognises" },
1698
+ { path: "removedCount", displayName: "SubDAOs Removed", type: "string", description: "Number of SubDAOs the proposal removes" }
1699
+ ]
1700
+ });
1701
+
1702
+ // src/core/lib/actionRegistry/actions/governance/daoManageStorage.ts
1703
+ registerGovernanceProposalAction({
1704
+ type: "qi/governance.dao.manage-storage",
1705
+ can: "governance.dao/manage-storage",
1706
+ buildActions: (inputs) => {
1707
+ const setting = inputs.setting === true || inputs.setting === "true";
1708
+ const key = String(inputs.key || "").trim();
1709
+ if (!key) throw new Error("key is required");
1710
+ const value = String(inputs.value || "").trim();
1711
+ if (setting && !value) throw new Error("value is required when setting a storage item");
1712
+ return [{ type: "ManageStorageItems", data: { setting, key, value: setting ? value : "" } }];
1713
+ },
1714
+ defaultTitle: (inputs) => {
1715
+ const setting = inputs.setting === true || inputs.setting === "true";
1716
+ return setting ? `Set storage item \u201C${inputs.key}\u201D` : `Remove storage item \u201C${inputs.key}\u201D`;
1717
+ },
1718
+ defaultDescription: () => "Updates the DAO's on-chain key/value storage once the proposal passes.",
1719
+ buildExtraOutput: (inputs) => ({
1720
+ storageKey: String(inputs.key || ""),
1721
+ storageAction: inputs.setting === true || inputs.setting === "true" ? "set" : "remove"
1722
+ }),
1723
+ extraOutputSchema: [
1724
+ { path: "storageKey", displayName: "Storage Key", type: "string", description: "The storage key the proposal targets" },
1725
+ { path: "storageAction", displayName: "Storage Action", type: "string", description: "set | remove" }
1726
+ ]
1727
+ });
1728
+
1729
+ // src/core/lib/actionRegistry/actions/governance/daoAdminExec.ts
1730
+ registerGovernanceProposalAction({
1731
+ type: "qi/governance.dao.admin-exec",
1732
+ can: "governance.dao/admin-exec",
1733
+ buildActions: (inputs) => {
1734
+ const targetCoreAddress = String(inputs.targetCoreAddress || "").trim();
1735
+ if (!targetCoreAddress) throw new Error("targetCoreAddress is required");
1736
+ let msgs = inputs.msgs;
1737
+ if (typeof msgs === "string") {
1738
+ try {
1739
+ msgs = JSON.parse(msgs);
1740
+ } catch {
1741
+ throw new Error("msgs must be valid JSON");
1742
+ }
1743
+ }
1744
+ if (!Array.isArray(msgs) || msgs.length === 0) {
1745
+ throw new Error("msgs must be a non-empty JSON array of messages");
1746
+ }
1747
+ return [{ type: "DaoAdminExec", data: { coreAddress: targetCoreAddress, msgs } }];
1748
+ },
1749
+ defaultTitle: (inputs) => `Execute admin messages on ${inputs.targetCoreAddress}`,
1750
+ defaultDescription: () => "Executes the listed admin messages on the target SubDAO once the proposal passes.",
1751
+ buildExtraOutput: (inputs) => ({ targetCoreAddress: String(inputs.targetCoreAddress || "") }),
1752
+ extraOutputSchema: [{ path: "targetCoreAddress", displayName: "Target Core Address", type: "string", description: "The SubDAO core address the admin messages execute on" }]
1753
+ });
1754
+
1755
+ // src/core/lib/actionRegistry/actions/governance/daoAcceptToMarketplace.ts
1756
+ registerGovernanceProposalAction({
1757
+ type: "qi/governance.dao.accept-to-marketplace",
1758
+ can: "governance.dao/accept-to-marketplace",
1759
+ buildActions: (inputs) => {
1760
+ const did = String(inputs.did || "").trim();
1761
+ if (!did) throw new Error("did is required");
1762
+ const relayerNodeDid = String(inputs.relayerNodeDid || "").trim();
1763
+ if (!relayerNodeDid) throw new Error("relayerNodeDid is required");
1764
+ const relayerNodeAddress = String(inputs.relayerNodeAddress || "").trim();
1765
+ if (!relayerNodeAddress) throw new Error("relayerNodeAddress is required");
1766
+ return [{ type: "AcceptToMarketplace", data: { did, relayerNodeDid, relayerNodeAddress } }];
1767
+ },
1768
+ defaultTitle: (inputs) => `Accept ${inputs.did} to the marketplace`,
1769
+ defaultDescription: () => "Marks the entity as verified on the marketplace once the proposal passes.",
1770
+ buildExtraOutput: (inputs) => ({ entityDid: String(inputs.did || ""), relayerNodeDid: String(inputs.relayerNodeDid || "") }),
1771
+ extraOutputSchema: [
1772
+ { path: "entityDid", displayName: "Entity DID", type: "string", description: "The entity marked verified" },
1773
+ { path: "relayerNodeDid", displayName: "Relayer Node DID", type: "string", description: "The relayer node vouching for the entity" }
1774
+ ]
1775
+ });
1776
+
1777
+ // src/core/lib/actionRegistry/actions/governance/daoCreateEntity.ts
1778
+ var DEFAULT_TYPE_URL = "/ixo.entity.v1beta1.MsgCreateEntity";
1779
+ registerGovernanceProposalAction({
1780
+ type: "qi/governance.dao.create-entity",
1781
+ can: "governance.dao/create-entity",
1782
+ buildActions: (inputs) => {
1783
+ const typeUrl = String(inputs.typeUrl || "").trim() || DEFAULT_TYPE_URL;
1784
+ if (!typeUrl.startsWith("/")) throw new Error("typeUrl must be a fully-qualified message type url starting with \u201C/\u201D");
1785
+ let value = inputs.value;
1786
+ if (typeof value === "string") {
1787
+ try {
1788
+ value = JSON.parse(value);
1789
+ } catch {
1790
+ throw new Error("value must be valid JSON");
1791
+ }
1792
+ }
1793
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1794
+ throw new Error("value must be a JSON object with the message fields");
1795
+ }
1796
+ return [{ type: "CreateEntity", data: { typeUrl, value } }];
1797
+ },
1798
+ defaultTitle: () => "Create entity",
1799
+ defaultDescription: (inputs) => `Broadcasts a ${String(inputs.typeUrl || "").trim() || DEFAULT_TYPE_URL} message once the proposal passes.`,
1800
+ buildExtraOutput: (inputs) => ({ typeUrl: String(inputs.typeUrl || "").trim() || DEFAULT_TYPE_URL }),
1801
+ extraOutputSchema: [{ path: "typeUrl", displayName: "Message Type URL", type: "string", description: "The stargate message type the proposal broadcasts" }]
1802
+ });
1803
+
1804
+ // src/core/lib/actionRegistry/actions/governance/nftBurn.ts
1805
+ registerGovernanceProposalAction({
1806
+ type: "qi/governance.nft.burn",
1807
+ can: "governance.nft/burn",
1808
+ buildActions: (inputs) => {
1809
+ const collection = String(inputs.collection || "").trim();
1810
+ if (!collection) throw new Error("collection is required");
1811
+ const tokenId = String(inputs.tokenId || "").trim();
1812
+ if (!tokenId) throw new Error("tokenId is required");
1813
+ return [{ type: "BurnNft", data: { collection, tokenId } }];
1814
+ },
1815
+ defaultTitle: (inputs) => `Burn NFT ${inputs.tokenId} from ${inputs.collection}`,
1816
+ defaultDescription: () => "Permanently burns the NFT from the treasury once the proposal passes.",
1817
+ buildExtraOutput: (inputs) => ({ collection: String(inputs.collection || ""), tokenId: String(inputs.tokenId || "") }),
1818
+ extraOutputSchema: [
1819
+ { path: "collection", displayName: "Collection", type: "string", description: "The cw721 collection contract the token is burned from" },
1820
+ { path: "tokenId", displayName: "Token ID", type: "string", description: "The id of the burned token" }
1821
+ ]
1822
+ });
1823
+
1824
+ // src/core/lib/actionRegistry/actions/governance/nftTransfer.ts
1825
+ registerGovernanceProposalAction({
1826
+ type: "qi/governance.nft.transfer",
1827
+ can: "governance.nft/transfer",
1828
+ buildActions: (inputs) => {
1829
+ const collection = String(inputs.collection || "").trim();
1830
+ if (!collection) throw new Error("collection is required");
1831
+ const tokenId = String(inputs.tokenId || "").trim();
1832
+ if (!tokenId) throw new Error("tokenId is required");
1833
+ const recipient = String(inputs.recipient || "").trim();
1834
+ if (!recipient) throw new Error("recipient is required");
1835
+ const executeSmartContract = inputs.executeSmartContract === true || inputs.executeSmartContract === "true";
1836
+ let smartContractMsg = "";
1837
+ if (executeSmartContract) {
1838
+ const raw = String(inputs.smartContractMsg || "").trim();
1839
+ if (!raw) throw new Error("smartContractMsg is required when executeSmartContract is enabled");
1840
+ try {
1841
+ smartContractMsg = JSON.parse(raw);
1842
+ } catch {
1843
+ throw new Error("smartContractMsg must be valid JSON");
1844
+ }
1845
+ }
1846
+ return [{ type: "TransferNft", data: { collection, tokenId, recipient, executeSmartContract, smartContractMsg } }];
1847
+ },
1848
+ defaultTitle: (inputs) => `Transfer NFT ${inputs.tokenId} to ${inputs.recipient}`,
1849
+ defaultDescription: () => "Transfers the NFT from the treasury once the proposal passes.",
1850
+ buildExtraOutput: (inputs) => ({
1851
+ collection: String(inputs.collection || ""),
1852
+ tokenId: String(inputs.tokenId || ""),
1853
+ recipient: String(inputs.recipient || "")
1854
+ }),
1855
+ extraOutputSchema: [
1856
+ { path: "collection", displayName: "Collection", type: "string", description: "The cw721 collection contract the token is transferred from" },
1857
+ { path: "tokenId", displayName: "Token ID", type: "string", description: "The id of the transferred token" },
1858
+ { path: "recipient", displayName: "Recipient", type: "string", description: "Destination address of the transfer" }
1859
+ ]
1860
+ });
1861
+
1862
+ // src/core/lib/actionRegistry/actions/governance/nftManageCollections.ts
1863
+ registerGovernanceProposalAction({
1864
+ type: "qi/governance.nft.manage-collections",
1865
+ can: "governance.nft/manage-collections",
1866
+ buildActions: (inputs) => {
1867
+ const address = String(inputs.address || "").trim();
1868
+ if (!address) throw new Error("address is required");
1869
+ const adding = inputs.adding === true || inputs.adding === "true";
1870
+ return [{ type: "ManageCw721", data: { adding, address } }];
1871
+ },
1872
+ defaultTitle: (inputs) => {
1873
+ const adding = inputs.adding === true || inputs.adding === "true";
1874
+ return `${adding ? "Track" : "Untrack"} NFT collection ${inputs.address}`;
1875
+ },
1876
+ defaultDescription: () => "Updates the NFT collections tracked in the treasury once the proposal passes.",
1877
+ buildExtraOutput: (inputs) => ({
1878
+ adding: String(inputs.adding === true || inputs.adding === "true"),
1879
+ address: String(inputs.address || "")
1880
+ }),
1881
+ extraOutputSchema: [
1882
+ { path: "adding", displayName: "Adding", type: "string", description: "Whether the collection is tracked (true) or untracked (false)" },
1883
+ { path: "address", displayName: "Collection Address", type: "string", description: "The cw721 collection contract tracked or untracked" }
1884
+ ]
1885
+ });
1886
+
1887
+ // src/core/lib/actionRegistry/actions/governance/contractExecute.ts
1888
+ registerGovernanceProposalAction({
1889
+ type: "qi/governance.contract.execute",
1890
+ can: "governance.contract/execute",
1891
+ buildActions: (inputs) => {
1892
+ const address = String(inputs.address || "").trim();
1893
+ if (!address) throw new Error("address is required");
1894
+ const message = String(inputs.message || "").trim();
1895
+ if (!message) throw new Error("message is required");
1896
+ try {
1897
+ JSON.parse(message);
1898
+ } catch {
1899
+ throw new Error("message must be a valid JSON object");
1900
+ }
1901
+ const rawFunds = Array.isArray(inputs.funds) ? inputs.funds : [];
1902
+ const funds = rawFunds.map((fund) => {
1903
+ const denom = String(fund?.denom || "").trim();
1904
+ if (!denom) throw new Error("every attached fund needs a denom");
1905
+ const amount = String(fund?.amount || "").trim();
1906
+ if (!/^\d+$/.test(amount) || BigInt(amount) <= 0n) {
1907
+ throw new Error("every attached fund amount must be a positive whole number in base units");
1908
+ }
1909
+ return { denom, amount };
1910
+ });
1911
+ return [{ type: "Execute", data: { address, message, funds } }];
1912
+ },
1913
+ defaultTitle: (inputs) => `Execute a message on contract ${inputs.address}`,
1914
+ defaultDescription: () => "Executes the message on the smart contract as the DAO once the proposal passes.",
1915
+ buildExtraOutput: (inputs) => ({ contractAddress: String(inputs.address || "") }),
1916
+ extraOutputSchema: [{ path: "contractAddress", displayName: "Contract Address", type: "string", description: "The smart contract the message executes on" }]
1917
+ });
1918
+
1919
+ // src/core/lib/actionRegistry/actions/governance/contractInstantiate.ts
1920
+ registerGovernanceProposalAction({
1921
+ type: "qi/governance.contract.instantiate",
1922
+ can: "governance.contract/instantiate",
1923
+ buildActions: (inputs) => {
1924
+ const codeId = Number(inputs.codeId);
1925
+ if (!Number.isInteger(codeId) || codeId <= 0) throw new Error("codeId must be a positive whole number");
1926
+ const label = String(inputs.label || "").trim();
1927
+ if (!label) throw new Error("label is required");
1928
+ const admin = String(inputs.admin || "").trim() || String(inputs.coreAddress || "").trim();
1929
+ if (!admin) throw new Error("admin is required (defaults to the group core address)");
1930
+ const message = String(inputs.message || "").trim();
1931
+ if (!message) throw new Error("message is required");
1932
+ try {
1933
+ JSON.parse(message);
1934
+ } catch {
1935
+ throw new Error("message must be a valid JSON object");
1936
+ }
1937
+ const rawFunds = Array.isArray(inputs.funds) ? inputs.funds : [];
1938
+ const funds = rawFunds.map((fund) => {
1939
+ const denom = String(fund?.denom || "").trim();
1940
+ if (!denom) throw new Error("every attached fund needs a denom");
1941
+ const amount = String(fund?.amount || "").trim();
1942
+ if (!/^\d+$/.test(amount) || BigInt(amount) <= 0n) {
1943
+ throw new Error("every attached fund amount must be a positive whole number in base units");
1944
+ }
1945
+ return { denom, amount };
1946
+ });
1947
+ return [{ type: "Instantiate", data: { admin, codeId, label, message, funds } }];
1948
+ },
1949
+ defaultTitle: (inputs) => `Instantiate code ${inputs.codeId} as \u201C${inputs.label}\u201D`,
1950
+ defaultDescription: () => "Instantiates a new smart contract from the code id once the proposal passes.",
1951
+ buildExtraOutput: (inputs) => ({ codeId: String(inputs.codeId || ""), contractLabel: String(inputs.label || "") }),
1952
+ extraOutputSchema: [
1953
+ { path: "codeId", displayName: "Code ID", type: "string", description: "The wasm code id instantiated" },
1954
+ { path: "contractLabel", displayName: "Contract Label", type: "string", description: "The label stored on the new contract" }
1955
+ ]
1956
+ });
1957
+
1958
+ // src/core/lib/actionRegistry/actions/governance/contractMigrate.ts
1959
+ registerGovernanceProposalAction({
1960
+ type: "qi/governance.contract.migrate",
1961
+ can: "governance.contract/migrate",
1962
+ buildActions: (inputs) => {
1963
+ const contract = String(inputs.contract || "").trim();
1964
+ if (!contract) throw new Error("contract is required");
1965
+ const codeId = Number(inputs.codeId);
1966
+ if (!Number.isInteger(codeId) || codeId <= 0) throw new Error("codeId must be a positive whole number");
1967
+ const msg = String(inputs.msg || "").trim();
1968
+ if (!msg) throw new Error("msg is required (use {} for contracts whose migrate entry point takes no data)");
1969
+ try {
1970
+ JSON.parse(msg);
1971
+ } catch {
1972
+ throw new Error("msg must be a valid JSON object");
1973
+ }
1974
+ return [{ type: "Migrate", data: { contract, codeId, msg } }];
1975
+ },
1976
+ defaultTitle: (inputs) => `Migrate contract ${inputs.contract} to code ${inputs.codeId}`,
1977
+ defaultDescription: () => "Migrates the smart contract to the new code id once the proposal passes.",
1978
+ buildExtraOutput: (inputs) => ({ contractAddress: String(inputs.contract || ""), codeId: String(inputs.codeId || "") }),
1979
+ extraOutputSchema: [
1980
+ { path: "contractAddress", displayName: "Contract Address", type: "string", description: "The smart contract migrated" },
1981
+ { path: "codeId", displayName: "Code ID", type: "string", description: "The wasm code id migrated to" }
1982
+ ]
1983
+ });
1984
+
1985
+ // src/core/lib/actionRegistry/actions/governance/contractUpdateAdmin.ts
1986
+ registerGovernanceProposalAction({
1987
+ type: "qi/governance.contract.update-admin",
1988
+ can: "governance.contract/update-admin",
1989
+ buildActions: (inputs) => {
1990
+ const contract = String(inputs.contract || "").trim();
1991
+ if (!contract) throw new Error("contract is required");
1992
+ const newAdmin = String(inputs.newAdmin || "").trim();
1993
+ if (!newAdmin) throw new Error("newAdmin is required");
1994
+ return [{ type: "UpdateAdmin", data: { contract, newAdmin } }];
1995
+ },
1996
+ defaultTitle: (inputs) => `Update admin of ${inputs.contract} to ${inputs.newAdmin}`,
1997
+ defaultDescription: () => "Transfers admin rights over the smart contract once the proposal passes.",
1998
+ buildExtraOutput: (inputs) => ({ contractAddress: String(inputs.contract || ""), newAdmin: String(inputs.newAdmin || "") }),
1999
+ extraOutputSchema: [
2000
+ { path: "contractAddress", displayName: "Contract Address", type: "string", description: "The smart contract whose admin changed" },
2001
+ { path: "newAdmin", displayName: "New Admin", type: "string", description: "The address that becomes the contract admin" }
2002
+ ]
2003
+ });
2004
+
2005
+ // src/core/lib/actionRegistry/actions/governance/contractManageCw20.ts
2006
+ registerGovernanceProposalAction({
2007
+ type: "qi/governance.contract.manage-cw20",
2008
+ can: "governance.contract/manage-cw20",
2009
+ buildActions: (inputs) => {
2010
+ const rawAdding = inputs.adding;
2011
+ const adding = rawAdding === true || rawAdding === "true" ? true : rawAdding === false || rawAdding === "false" ? false : null;
2012
+ if (adding === null) throw new Error("adding must be true (track the token) or false (untrack it)");
2013
+ const address = String(inputs.address || "").trim();
2014
+ if (!address) throw new Error("address is required");
2015
+ return [{ type: "ManageCw20", data: { adding, address } }];
2016
+ },
2017
+ defaultTitle: (inputs) => {
2018
+ const adding = inputs.adding === true || inputs.adding === "true";
2019
+ return `${adding ? "Track" : "Untrack"} cw20 token ${inputs.address} in the treasury`;
2020
+ },
2021
+ defaultDescription: () => "Updates the treasury\u2019s cw20 token list once the proposal passes.",
2022
+ buildExtraOutput: (inputs) => ({
2023
+ tokenContract: String(inputs.address || ""),
2024
+ adding: inputs.adding === true || inputs.adding === "true" ? "true" : "false"
2025
+ }),
2026
+ extraOutputSchema: [
2027
+ { path: "tokenContract", displayName: "Token Contract", type: "string", description: "The cw20 token contract tracked or untracked" },
2028
+ { path: "adding", displayName: "Tracking", type: "string", description: "'true' when the token was tracked, 'false' when untracked" }
2029
+ ]
2030
+ });
2031
+
2032
+ // src/core/lib/actionRegistry/actions/governance/validatorActions.ts
2033
+ var VALIDATOR_ACTION_LABELS = {
2034
+ "/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission": "Withdraw validator commission",
2035
+ "/cosmos.staking.v1beta1.MsgCreateValidator": "Create validator",
2036
+ "/cosmos.staking.v1beta1.MsgEditValidator": "Edit validator",
2037
+ "/cosmos.slashing.v1beta1.MsgUnjail": "Unjail validator"
2038
+ };
2039
+ registerGovernanceProposalAction({
2040
+ type: "qi/governance.validator.actions",
2041
+ can: "governance.validator/actions",
2042
+ buildActions: (inputs) => {
2043
+ const validatorActionType = String(inputs.validatorActionType || "").trim();
2044
+ if (!VALIDATOR_ACTION_LABELS[validatorActionType]) {
2045
+ throw new Error("validatorActionType must be one of the supported typeUrls (MsgWithdrawValidatorCommission, MsgCreateValidator, MsgEditValidator, MsgUnjail)");
2046
+ }
2047
+ const isCreate = validatorActionType === "/cosmos.staking.v1beta1.MsgCreateValidator";
2048
+ const isEdit = validatorActionType === "/cosmos.staking.v1beta1.MsgEditValidator";
2049
+ let createMsg = "";
2050
+ if (isCreate) {
2051
+ createMsg = String(inputs.createMsg || "").trim();
2052
+ if (!createMsg) throw new Error("createMsg is required for MsgCreateValidator");
2053
+ try {
2054
+ JSON.parse(createMsg);
2055
+ } catch {
2056
+ throw new Error("createMsg must be valid JSON");
2057
+ }
2058
+ }
2059
+ let editMsg = "";
2060
+ if (isEdit) {
2061
+ editMsg = String(inputs.editMsg || "").trim();
2062
+ if (!editMsg) throw new Error("editMsg is required for MsgEditValidator");
2063
+ try {
2064
+ JSON.parse(editMsg);
2065
+ } catch {
2066
+ throw new Error("editMsg must be valid JSON");
2067
+ }
2068
+ }
2069
+ return [{ type: "ValidatorActions", data: { validatorActionType, createMsg, editMsg } }];
2070
+ },
2071
+ // buildActions runs first, so the action type is always a valid option here.
2072
+ defaultTitle: (inputs) => `Validator action: ${VALIDATOR_ACTION_LABELS[String(inputs.validatorActionType)] || inputs.validatorActionType}`,
2073
+ defaultDescription: () => "Executes the validator operation with the POD's validator account once the proposal passes.",
2074
+ buildExtraOutput: (inputs) => ({
2075
+ validatorActionType: String(inputs.validatorActionType || ""),
2076
+ validatorActionLabel: VALIDATOR_ACTION_LABELS[String(inputs.validatorActionType)] || ""
2077
+ }),
2078
+ extraOutputSchema: [
2079
+ { path: "validatorActionType", displayName: "Validator Action Type", type: "string", description: "The full cosmos typeUrl of the validator operation" },
2080
+ { path: "validatorActionLabel", displayName: "Validator Action", type: "string", description: "Human-readable name of the validator operation" }
2081
+ ]
2082
+ });
2083
+
2084
+ // src/core/lib/actionRegistry/actions/governance/customMessage.ts
2085
+ registerGovernanceProposalAction({
2086
+ type: "qi/governance.custom-message",
2087
+ can: "governance/custom-message",
2088
+ buildActions: (inputs) => {
2089
+ const message = String(inputs.message || "").trim();
2090
+ if (!message) throw new Error("message is required");
2091
+ let parsed;
2092
+ try {
2093
+ parsed = JSON.parse(message);
2094
+ } catch (err) {
2095
+ throw new Error(`message must be valid JSON: ${err instanceof Error ? err.message : String(err)}`);
2096
+ }
2097
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2098
+ throw new Error('message must be a JSON object \u2014 a single cosmos msg like {"bank":\u2026}, {"wasm":\u2026} or {"stargate":\u2026}');
2099
+ }
2100
+ return [{ type: "Custom", data: { message } }];
2101
+ },
2102
+ defaultTitle: () => "Execute custom message",
2103
+ defaultDescription: () => "Executes the raw cosmos message exactly as written once the proposal passes.",
2104
+ buildExtraOutput: (inputs) => ({ message: String(inputs.message || "") }),
2105
+ extraOutputSchema: [{ path: "message", displayName: "Message", type: "string", description: "The raw JSON cosmos message the proposal executes" }]
2106
+ });
2107
+
1140
2108
  // src/core/lib/actionRegistry/actions/httpRequest.ts
1141
2109
  registerAction({
1142
2110
  type: "qi/http.request",
@@ -1407,6 +2375,60 @@ registerAction({
1407
2375
  }
1408
2376
  });
1409
2377
 
2378
+ // src/core/lib/actionRegistry/actions/_shared/groupExecution.ts
2379
+ var GROUP_PROPOSAL_READBACK_KIND = "group-proposal";
2380
+ function isGroupExecution(inputs) {
2381
+ return inputs?.actAsGroup === true || inputs?.actAsGroup === "true";
2382
+ }
2383
+ function requireGroupExecutionParams(inputs) {
2384
+ const coreAddress = String(inputs.groupCoreAddress || "").trim();
2385
+ if (!coreAddress) throw new Error("groupCoreAddress is required when acting as a POD");
2386
+ const title = String(inputs.proposalTitle || "").trim();
2387
+ if (!title) throw new Error("proposalTitle is required when acting as a POD \u2014 voters must see what they are approving");
2388
+ const description = String(inputs.proposalDescription || "").trim();
2389
+ if (!description) throw new Error("proposalDescription is required when acting as a POD \u2014 voters must see what they are approving");
2390
+ return { coreAddress, title, description };
2391
+ }
2392
+ async function proposeGroupExecution(ctx, params) {
2393
+ const handlers = ctx.handlers;
2394
+ if (!handlers?.createGroupExecutionProposal) {
2395
+ throw new Error("Group execution is not available: the host does not implement createGroupExecutionProposal");
2396
+ }
2397
+ const { coreAddress, title, description, msgs, expectedOutput, finalize, finalizeParams, completionEvent } = params;
2398
+ const { proposalId, proposalContractAddress } = await handlers.createGroupExecutionProposal({
2399
+ coreAddress,
2400
+ title,
2401
+ description,
2402
+ msgs
2403
+ });
2404
+ if (proposalId === void 0 || proposalId === null || String(proposalId).trim() === "") {
2405
+ throw new Error("POD proposal creation returned no proposal id. Check the handler logs.");
2406
+ }
2407
+ return {
2408
+ output: {
2409
+ ...expectedOutput,
2410
+ groupExecution: true,
2411
+ groupCoreAddress: coreAddress,
2412
+ groupProposalId: String(proposalId),
2413
+ groupProposalContractAddress: String(proposalContractAddress || ""),
2414
+ proposalTitle: title,
2415
+ proposalDescription: description
2416
+ },
2417
+ completion: {
2418
+ state: "awaiting_readback",
2419
+ readBack: {
2420
+ kind: GROUP_PROPOSAL_READBACK_KIND,
2421
+ proposalId: String(proposalId),
2422
+ proposalContractAddress: String(proposalContractAddress || ""),
2423
+ coreAddress,
2424
+ expectedOutput,
2425
+ ...completionEvent ? { completionEvent } : {},
2426
+ ...finalize ? { finalize, finalizeParams: finalizeParams || {} } : {}
2427
+ }
2428
+ }
2429
+ };
2430
+ }
2431
+
1410
2432
  // src/core/lib/actionRegistry/actions/bid/bid.ts
1411
2433
  function normalizeBidRole(role) {
1412
2434
  const normalized = String(role || "").trim().toLowerCase();
@@ -1460,6 +2482,29 @@ registerAction({
1460
2482
  throw new Error("surveyAnswers must be an object");
1461
2483
  }
1462
2484
  const chainRole = normalizeBidRole(roleInput);
2485
+ if (isGroupExecution(inputs)) {
2486
+ const { coreAddress, title, description } = requireGroupExecutionParams(inputs);
2487
+ return proposeGroupExecution(ctx, {
2488
+ coreAddress,
2489
+ title,
2490
+ description,
2491
+ msgs: [],
2492
+ expectedOutput: {
2493
+ bidId: "",
2494
+ collectionId,
2495
+ role: roleInput,
2496
+ submitterDid: coreAddress,
2497
+ deedDid
2498
+ },
2499
+ finalize: "bid.submit",
2500
+ finalizeParams: {
2501
+ collectionId,
2502
+ role: chainRole,
2503
+ surveyAnswers,
2504
+ entityDid: deedDid || void 0
2505
+ }
2506
+ });
2507
+ }
1463
2508
  const submission = await service.submitBid({
1464
2509
  collectionId,
1465
2510
  role: chainRole,
@@ -1552,6 +2597,71 @@ registerAction({
1552
2597
  if (!role) throw new Error("role is required");
1553
2598
  if (!applicantDid) throw new Error("applicantDid is required");
1554
2599
  if (!applicantAddress) throw new Error("applicantAddress is required");
2600
+ if (isGroupExecution(inputs)) {
2601
+ const { coreAddress, title, description } = requireGroupExecutionParams(inputs);
2602
+ const expectedOutput = {
2603
+ bidId,
2604
+ decision,
2605
+ status: decision === "approve" ? "approved" : "rejected",
2606
+ evaluatedByDid: coreAddress,
2607
+ evaluatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2608
+ reason: decision === "reject" ? String(inputs.reason || "") : "",
2609
+ collectionId,
2610
+ role,
2611
+ deedDid,
2612
+ applicantDid,
2613
+ applicantAddress
2614
+ };
2615
+ if (decision === "approve") {
2616
+ const adminAddress = String(inputs.adminAddress || "").trim();
2617
+ if (!adminAddress) throw new Error("adminAddress is required when decision is approve");
2618
+ if (!isServiceAgentRole(role) && !isEvaluationAgentRole(role)) {
2619
+ throw new Error("Invalid role for evaluation. Expected service_agent or evaluation_agent");
2620
+ }
2621
+ const groupHandlers = ctx.handlers;
2622
+ if (typeof groupHandlers?.prepareGroupAgentApplicationApproval !== "function") {
2623
+ throw new Error("Acting as a POD is not available: the host does not implement prepareGroupAgentApplicationApproval");
2624
+ }
2625
+ let maxAmounts = inputs.maxAmounts;
2626
+ if (typeof maxAmounts === "string" && maxAmounts.trim()) {
2627
+ try {
2628
+ maxAmounts = JSON.parse(maxAmounts);
2629
+ } catch {
2630
+ throw new Error("maxAmounts must be valid JSON when provided");
2631
+ }
2632
+ }
2633
+ const prepared = await groupHandlers.prepareGroupAgentApplicationApproval({
2634
+ groupAddress: coreAddress,
2635
+ deedDid,
2636
+ collectionId,
2637
+ adminAddress,
2638
+ role: isServiceAgentRole(role) ? "service_agent" : "evaluation_agent",
2639
+ applicantAddress,
2640
+ agentQuota: isServiceAgentRole(role) ? 30 : 10,
2641
+ maxAmounts: Array.isArray(maxAmounts) ? maxAmounts : void 0
2642
+ });
2643
+ return proposeGroupExecution(ctx, {
2644
+ coreAddress,
2645
+ title,
2646
+ description,
2647
+ msgs: prepared.msgs || [],
2648
+ expectedOutput,
2649
+ finalize: "bid.approve",
2650
+ finalizeParams: { bidId, collectionId, did: applicantDid, entityDid: deedDid }
2651
+ });
2652
+ }
2653
+ const reason = String(inputs.reason || "").trim();
2654
+ if (!reason) throw new Error("reason is required when decision is reject");
2655
+ return proposeGroupExecution(ctx, {
2656
+ coreAddress,
2657
+ title,
2658
+ description,
2659
+ msgs: [],
2660
+ expectedOutput,
2661
+ finalize: "bid.reject",
2662
+ finalizeParams: { bidId, collectionId, did: deedDid, reason, entityDid: deedDid }
2663
+ });
2664
+ }
1555
2665
  if (decision === "approve") {
1556
2666
  const adminAddress = String(inputs.adminAddress || "").trim();
1557
2667
  if (!adminAddress) {
@@ -1820,6 +2930,40 @@ registerAction({
1820
2930
  if (!pin) {
1821
2931
  throw new Error("PIN is required to submit claim");
1822
2932
  }
2933
+ if (isGroupExecution(inputs)) {
2934
+ const { coreAddress, title, description } = requireGroupExecutionParams(inputs);
2935
+ const handlers = ctx.handlers;
2936
+ if (typeof handlers?.prepareGroupClaimSubmission !== "function") {
2937
+ throw new Error("Acting as a POD is not available: the host does not implement prepareGroupClaimSubmission");
2938
+ }
2939
+ const prepared = await handlers.prepareGroupClaimSubmission({
2940
+ surveyData: surveyAnswers,
2941
+ deedDid,
2942
+ entityDid: deedDid,
2943
+ collectionId,
2944
+ adminAddress,
2945
+ pin,
2946
+ groupAddress: coreAddress
2947
+ });
2948
+ const claimId2 = String(prepared?.claimId || "").trim();
2949
+ if (!claimId2) throw new Error("prepareGroupClaimSubmission returned no claim identifier");
2950
+ return proposeGroupExecution(ctx, {
2951
+ coreAddress,
2952
+ title,
2953
+ description,
2954
+ msgs: prepared.msgs || [],
2955
+ expectedOutput: {
2956
+ claimId: claimId2,
2957
+ transactionHash: "",
2958
+ collectionId,
2959
+ deedDid,
2960
+ submittedByDid: coreAddress,
2961
+ submittedAt: (/* @__PURE__ */ new Date()).toISOString(),
2962
+ surveyAnswers
2963
+ },
2964
+ completionEvent: "submitted"
2965
+ });
2966
+ }
1823
2967
  const result = await service.submitClaim({
1824
2968
  surveyData: surveyAnswers,
1825
2969
  deedDid,
@@ -2524,7 +3668,8 @@ registerAction({
2524
3668
  if (!deedDid) throw new Error("deedDid is required");
2525
3669
  if (!adminAddress) throw new Error("adminAddress is required");
2526
3670
  const handlers = ctx.handlers;
2527
- const actorAddress = String(ctx.actorDid || service.getCurrentUser?.()?.address || "").trim();
3671
+ const groupMode = isGroupExecution(inputs);
3672
+ const actorAddress = groupMode ? String(inputs.groupCoreAddress || "").trim() : String(ctx.actorDid || service.getCurrentUser?.()?.address || "").trim();
2528
3673
  if (!actorAddress) {
2529
3674
  throw new Error("Unable to resolve actor address for evaluator authorization");
2530
3675
  }
@@ -2598,6 +3743,54 @@ registerAction({
2598
3743
  });
2599
3744
  verificationProof = String(udid?.url || udid?.cid || "");
2600
3745
  }
3746
+ if (groupMode) {
3747
+ const { coreAddress, title, description } = requireGroupExecutionParams(inputs);
3748
+ const groupHandlers = ctx.handlers;
3749
+ if (typeof groupHandlers?.prepareGroupClaimEvaluation !== "function") {
3750
+ throw new Error("Acting as a POD is not available: the host does not implement prepareGroupClaimEvaluation");
3751
+ }
3752
+ const prepared = await groupHandlers.prepareGroupClaimEvaluation({
3753
+ groupAddress: coreAddress,
3754
+ deedDid,
3755
+ claimId,
3756
+ collectionId,
3757
+ adminAddress,
3758
+ status: toStatus(decision),
3759
+ verificationProof,
3760
+ amount: normalizedCoin
3761
+ });
3762
+ let expectedSurveyAnswers = {};
3763
+ try {
3764
+ const getClaimData = groupHandlers?.getClaimData;
3765
+ if (typeof getClaimData === "function") {
3766
+ const claim = await getClaimData(collectionId, claimId, { entityDid: deedDid });
3767
+ const candidate = claim?.credentialSubject ?? claim?.surveyAnswers ?? claim;
3768
+ if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
3769
+ expectedSurveyAnswers = candidate;
3770
+ }
3771
+ }
3772
+ } catch {
3773
+ }
3774
+ return proposeGroupExecution(ctx, {
3775
+ coreAddress,
3776
+ title,
3777
+ description,
3778
+ msgs: prepared.msgs || [],
3779
+ expectedOutput: {
3780
+ claimId,
3781
+ decision,
3782
+ status: decision === "approve" ? "approved" : "rejected",
3783
+ verificationProof,
3784
+ transactionHash: "",
3785
+ collectionId,
3786
+ deedDid,
3787
+ evaluatedByDid: coreAddress,
3788
+ evaluatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3789
+ surveyAnswers: expectedSurveyAnswers
3790
+ },
3791
+ completionEvent: "evaluated"
3792
+ });
3793
+ }
2601
3794
  const currentUser = service.getCurrentUser();
2602
3795
  const granteeAddress = String(inputs.granteeAddress || currentUser?.address || "").trim();
2603
3796
  if (!granteeAddress) {
@@ -6000,6 +7193,32 @@ registerAction({
6000
7193
  payments: inputs.payments,
6001
7194
  intents: inputs.intents
6002
7195
  };
7196
+ if (isGroupExecution(inputs)) {
7197
+ const { coreAddress, title, description } = requireGroupExecutionParams(inputs);
7198
+ const groupHandlers = ctx.handlers;
7199
+ if (typeof groupHandlers?.prepareGroupCollectionLifecycle !== "function") {
7200
+ throw new Error("Acting as a POD is not available: the host does not implement prepareGroupCollectionLifecycle");
7201
+ }
7202
+ const prepared = await groupHandlers.prepareGroupCollectionLifecycle({
7203
+ groupAddress: coreAddress,
7204
+ operation: "create",
7205
+ params: createParams
7206
+ });
7207
+ return proposeGroupExecution(ctx, {
7208
+ coreAddress,
7209
+ title,
7210
+ description,
7211
+ msgs: prepared.msgs || [],
7212
+ expectedOutput: { collectionId: "", entity, protocol, state: inputs.state, startDate: inputs.startDate, endDate: inputs.endDate, quota, transactionHash: "" },
7213
+ finalize: "collection.create-resolve",
7214
+ finalizeParams: {
7215
+ entityDid: entity,
7216
+ protocolDid: protocol,
7217
+ priorCollectionIds: Array.isArray(prepared.priorCollectionIds) ? prepared.priorCollectionIds : []
7218
+ },
7219
+ completionEvent: COLLECTION_CREATED_EVENT_NAME
7220
+ });
7221
+ }
6003
7222
  const result = await service.create(createParams);
6004
7223
  const transactionHash2 = String(result?.transactionHash || "").trim();
6005
7224
  const collectionId2 = String(result?.collectionId || "").trim();
@@ -6042,6 +7261,27 @@ registerAction({
6042
7261
  if (!adminAddress) {
6043
7262
  throw new Error(`${writeOp}: could not resolve admin address for collection "${collectionId}" (chain state has no admin). Cannot authorise the update.`);
6044
7263
  }
7264
+ if (isGroupExecution(inputs)) {
7265
+ const { coreAddress, title, description } = requireGroupExecutionParams(inputs);
7266
+ const groupHandlers = ctx.handlers;
7267
+ if (typeof groupHandlers?.prepareGroupCollectionLifecycle !== "function") {
7268
+ throw new Error("Acting as a POD is not available: the host does not implement prepareGroupCollectionLifecycle");
7269
+ }
7270
+ const prepared = await groupHandlers.prepareGroupCollectionLifecycle({
7271
+ groupAddress: coreAddress,
7272
+ operation: writeOp,
7273
+ params: { ...inputs, collectionId, adminAddress }
7274
+ });
7275
+ return proposeGroupExecution(ctx, {
7276
+ coreAddress,
7277
+ title,
7278
+ description,
7279
+ msgs: prepared.msgs || [],
7280
+ expectedOutput: { ...current, collectionId, transactionHash: "" },
7281
+ finalize: "collection.refresh",
7282
+ finalizeParams: { collectionId }
7283
+ });
7284
+ }
6045
7285
  let transactionHash = "";
6046
7286
  switch (writeOp) {
6047
7287
  case "updateState": {
@@ -6201,6 +7441,53 @@ registerAction({
6201
7441
  if (!adminAddress) throw new Error(`${operation}: adminAddress (entity admin account) is required`);
6202
7442
  const role = normalizeRole(inputs.role);
6203
7443
  const deedDid = String(inputs.deedDid || "").trim() || void 0;
7444
+ if (isGroupExecution(inputs) && (operation === "add" || operation === "revoke")) {
7445
+ const { coreAddress, title, description } = requireGroupExecutionParams(inputs);
7446
+ const groupHandlers = ctx.handlers;
7447
+ if (typeof groupHandlers?.prepareGroupCollectionUserChange !== "function") {
7448
+ throw new Error("Acting as a POD is not available: the host does not implement prepareGroupCollectionUserChange");
7449
+ }
7450
+ if (!deedDid) throw new Error(`${operation}: deedDid is required when acting as a POD`);
7451
+ const grantees = [];
7452
+ if (operation === "add" && inputs.granteeKind === "group-members") {
7453
+ const members = Array.isArray(inputs.members) ? inputs.members.filter((m) => !!String(m?.address || "").trim()) : [];
7454
+ if (members.length === 0) throw new Error("add (group-members): no members resolved to grant to.");
7455
+ grantees.push(...members.map((m) => String(m.address).trim()));
7456
+ } else {
7457
+ const granteeAddress2 = String(inputs.granteeAddress || "").trim();
7458
+ if (!granteeAddress2) throw new Error(`${operation}: granteeAddress is required`);
7459
+ grantees.push(granteeAddress2);
7460
+ }
7461
+ const msgs = [];
7462
+ for (const granteeAddress2 of grantees) {
7463
+ const prepared = await groupHandlers.prepareGroupCollectionUserChange({
7464
+ groupAddress: coreAddress,
7465
+ operation,
7466
+ entityDid: deedDid,
7467
+ collectionId,
7468
+ adminAddress,
7469
+ role,
7470
+ granteeAddress: granteeAddress2,
7471
+ quota: inputs.agentQuota !== void 0 && inputs.agentQuota !== null ? Number(inputs.agentQuota) : void 0,
7472
+ maxAmount: Array.isArray(inputs.maxAmount) ? inputs.maxAmount : void 0
7473
+ });
7474
+ msgs.push(...prepared.msgs || []);
7475
+ }
7476
+ return proposeGroupExecution(ctx, {
7477
+ coreAddress,
7478
+ title,
7479
+ description,
7480
+ msgs,
7481
+ expectedOutput: {
7482
+ transactionHash: "",
7483
+ transactionHashes: [],
7484
+ grantedCount: operation === "add" ? grantees.length : 0,
7485
+ role,
7486
+ collectionId,
7487
+ granteeAddress: grantees.length === 1 ? grantees[0] : void 0
7488
+ }
7489
+ });
7490
+ }
6204
7491
  if (operation === "revoke") {
6205
7492
  const granteeAddress2 = String(inputs.granteeAddress || "").trim();
6206
7493
  if (!granteeAddress2) throw new Error("revoke: granteeAddress is required");
@@ -10619,6 +11906,42 @@ import * as Y3 from "yjs";
10619
11906
 
10620
11907
  // src/core/lib/flowEngine/readBackReconciler.ts
10621
11908
  import * as Y2 from "yjs";
11909
+ function isYDoc(value) {
11910
+ return value instanceof Y2.Doc;
11911
+ }
11912
+ function getYDoc(editorOrYDoc) {
11913
+ if (!editorOrYDoc) return void 0;
11914
+ if (isYDoc(editorOrYDoc)) return editorOrYDoc;
11915
+ return editorOrYDoc._yDoc;
11916
+ }
11917
+ function getEditor(editorOrYDoc) {
11918
+ return editorOrYDoc && !isYDoc(editorOrYDoc) ? editorOrYDoc : void 0;
11919
+ }
11920
+ function getRuntime(editorOrYDoc, runtime) {
11921
+ if (runtime) return runtime;
11922
+ const yDoc = getYDoc(editorOrYDoc);
11923
+ if (yDoc) return createYDocRuntimeManager(yDoc);
11924
+ return createRuntimeStateManager(!isYDoc(editorOrYDoc) ? editorOrYDoc : void 0);
11925
+ }
11926
+ function getReconcileEditor(params, yDoc, editor) {
11927
+ if (editor) return editor;
11928
+ if (!yDoc || !params.document) return void 0;
11929
+ return {
11930
+ _yDoc: yDoc,
11931
+ _yRuntime: yDoc.getMap("runtime"),
11932
+ document: params.document
11933
+ };
11934
+ }
11935
+ function makeRunId(now) {
11936
+ return `readback-${now()}-${Math.random().toString(36).slice(2, 8)}`;
11937
+ }
11938
+ function asOutput(value) {
11939
+ return value && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
11940
+ }
11941
+ function errorRecord(error) {
11942
+ if (!error) return void 0;
11943
+ return typeof error === "string" ? { message: error } : { message: error.message, code: error.code };
11944
+ }
10622
11945
  function normalizeActionReadBackMetadata(params) {
10623
11946
  const base = params.readBack && typeof params.readBack === "object" && !Array.isArray(params.readBack) ? { ...params.readBack } : {};
10624
11947
  const kind = typeof base.kind === "string" && base.kind.trim() ? base.kind.trim() : params.actionType;
@@ -10643,14 +11966,221 @@ function normalizeActionReadBackMetadata(params) {
10643
11966
  }
10644
11967
  return normalized;
10645
11968
  }
11969
+ function writeReconciliationRunRecord(params) {
11970
+ if (!params.yDoc) return void 0;
11971
+ const completedAt = params.now();
11972
+ const runId = makeRunId(params.now);
11973
+ const pendingInvocation = params.readBack.pendingInvocation;
11974
+ const details = {
11975
+ runId,
11976
+ output: params.output,
11977
+ events: params.events,
11978
+ startedAt: params.readBack.requestedAt || new Date(completedAt).toISOString(),
11979
+ completedAt: new Date(completedAt).toISOString(),
11980
+ actorDid: params.actorDid,
11981
+ invocationCid: params.readBack.invocationCid,
11982
+ capabilityId: params.readBack.capabilityId,
11983
+ error: params.error,
11984
+ readBack: params.readBack,
11985
+ reconciled: true
11986
+ };
11987
+ if (pendingInvocation) {
11988
+ details.fromPendingInvocationId = pendingInvocation.id;
11989
+ details.triggeredBy = {
11990
+ sourceBlockId: pendingInvocation.triggeringBlockId,
11991
+ eventName: pendingInvocation.eventName
11992
+ };
11993
+ details.sourceRunId = pendingInvocation.sourceRunId;
11994
+ }
11995
+ appendRunRecord(params.yDoc, params.blockId, details, params.actorDid);
11996
+ if (params.editor && params.events.length > 0) {
11997
+ reconcilePendingInvocations(params.editor);
11998
+ }
11999
+ return runId;
12000
+ }
12001
+ async function reconcileActionReadBack(params) {
12002
+ const now = params.now || Date.now;
12003
+ const yDoc = getYDoc(params.editorOrYDoc);
12004
+ const editor = getEditor(params.editorOrYDoc);
12005
+ const runtime = getRuntime(params.editorOrYDoc, params.runtime);
12006
+ const current = runtime.get(params.blockId);
12007
+ const output = asOutput(current.output);
12008
+ const readBack = current.readBack;
12009
+ if (current.state !== "awaiting_readback" || !readBack?.kind) {
12010
+ return {
12011
+ success: false,
12012
+ blockId: params.blockId,
12013
+ state: current.state === "failed" ? "failed" : "pending",
12014
+ output,
12015
+ events: [],
12016
+ error: `Block "${params.blockId}" is not awaiting read-back`,
12017
+ pendingInvocationRemoved: false,
12018
+ readBack
12019
+ };
12020
+ }
12021
+ const resolver = params.resolver || params.resolvers?.[readBack.kind];
12022
+ if (!resolver) {
12023
+ return {
12024
+ success: false,
12025
+ blockId: params.blockId,
12026
+ state: "pending",
12027
+ output,
12028
+ events: [],
12029
+ error: `No read-back resolver registered for "${readBack.kind}"`,
12030
+ pendingInvocationRemoved: false,
12031
+ readBack
12032
+ };
12033
+ }
12034
+ const resolution = await resolver({
12035
+ blockId: params.blockId,
12036
+ runtime: current,
12037
+ output,
12038
+ readBack
12039
+ });
12040
+ const checkedAt = now();
12041
+ const checkedIso = new Date(checkedAt).toISOString();
12042
+ const nextReadBack = {
12043
+ ...readBack,
12044
+ ...resolution.readBack || {},
12045
+ status: resolution.state,
12046
+ lastCheckedAt: checkedIso
12047
+ };
12048
+ if (resolution.state === "pending") {
12049
+ runtime.update(params.blockId, {
12050
+ readBack: nextReadBack
12051
+ });
12052
+ return {
12053
+ success: true,
12054
+ blockId: params.blockId,
12055
+ state: "pending",
12056
+ output,
12057
+ events: resolution.events || [],
12058
+ pendingInvocationRemoved: false,
12059
+ readBack: nextReadBack
12060
+ };
12061
+ }
12062
+ const events = resolution.events || [];
12063
+ const finalOutput = {
12064
+ ...output,
12065
+ ...resolution.output || {}
12066
+ };
12067
+ const actorDid = params.actorDid || readBack.actorDid || current.executedByDid || "system:readback";
12068
+ if (resolution.state === "failed") {
12069
+ const error = errorRecord(resolution.error) || { message: "External read-back failed" };
12070
+ const failedReadBack = {
12071
+ ...nextReadBack,
12072
+ terminalAt: checkedIso
12073
+ };
12074
+ runtime.update(params.blockId, {
12075
+ state: "failed",
12076
+ output: finalOutput,
12077
+ error: { ...error, at: checkedAt },
12078
+ readBack: failedReadBack
12079
+ });
12080
+ const runId2 = writeReconciliationRunRecord({
12081
+ yDoc,
12082
+ editor: getReconcileEditor(params, yDoc, editor),
12083
+ blockId: params.blockId,
12084
+ actorDid,
12085
+ output: finalOutput,
12086
+ events,
12087
+ readBack: failedReadBack,
12088
+ error,
12089
+ now
12090
+ });
12091
+ return {
12092
+ success: false,
12093
+ blockId: params.blockId,
12094
+ state: "failed",
12095
+ output: finalOutput,
12096
+ events,
12097
+ error: error.message,
12098
+ runId: runId2,
12099
+ pendingInvocationRemoved: false,
12100
+ readBack: failedReadBack
12101
+ };
12102
+ }
12103
+ const readBackActionType = readBack.actionType || readBack.kind;
12104
+ const actionDef = readBackActionType ? getAction(readBackActionType) : void 0;
12105
+ if (actionDef) {
12106
+ const proofCheck = validateActionProof(actionDef, finalOutput || {});
12107
+ if (!proofCheck.valid) {
12108
+ const message = proofCheck.reason || "Read-back resolved completed without proof of execution.";
12109
+ const proofFailedReadBack = {
12110
+ ...nextReadBack,
12111
+ terminalAt: checkedIso
12112
+ };
12113
+ runtime.update(params.blockId, {
12114
+ state: actionDef.sideEffect ? "needs_verification" : "failed",
12115
+ output: finalOutput,
12116
+ error: { message, code: PROOF_MISSING_CODE, at: checkedAt },
12117
+ readBack: proofFailedReadBack
12118
+ });
12119
+ const failedRunId = writeReconciliationRunRecord({
12120
+ yDoc,
12121
+ editor: getReconcileEditor(params, yDoc, editor),
12122
+ blockId: params.blockId,
12123
+ actorDid,
12124
+ output: finalOutput,
12125
+ events,
12126
+ readBack: proofFailedReadBack,
12127
+ error: { message, code: PROOF_MISSING_CODE },
12128
+ now
12129
+ });
12130
+ return {
12131
+ success: false,
12132
+ blockId: params.blockId,
12133
+ state: "failed",
12134
+ output: finalOutput,
12135
+ events,
12136
+ error: message,
12137
+ runId: failedRunId,
12138
+ pendingInvocationRemoved: false,
12139
+ readBack: proofFailedReadBack
12140
+ };
12141
+ }
12142
+ }
12143
+ const completedReadBack = {
12144
+ ...nextReadBack,
12145
+ terminalAt: checkedIso
12146
+ };
12147
+ runtime.update(params.blockId, {
12148
+ state: "completed",
12149
+ output: finalOutput,
12150
+ executedAt: checkedAt,
12151
+ error: void 0,
12152
+ readBack: completedReadBack
12153
+ });
12154
+ const runId = writeReconciliationRunRecord({
12155
+ yDoc,
12156
+ editor: getReconcileEditor(params, yDoc, editor),
12157
+ blockId: params.blockId,
12158
+ actorDid,
12159
+ output: finalOutput,
12160
+ events,
12161
+ readBack: completedReadBack,
12162
+ now
12163
+ });
12164
+ const pendingInvocationRemoved = Boolean(yDoc && completedReadBack.pendingInvocation?.id) && removePendingInvocation(yDoc, params.blockId, completedReadBack.pendingInvocation.id);
12165
+ return {
12166
+ success: true,
12167
+ blockId: params.blockId,
12168
+ state: "completed",
12169
+ output: finalOutput,
12170
+ events,
12171
+ runId,
12172
+ pendingInvocationRemoved,
12173
+ readBack: completedReadBack
12174
+ };
12175
+ }
10646
12176
 
10647
12177
  // src/core/lib/flowEngine/actionExecutor.ts
10648
- function isYDoc(value) {
12178
+ function isYDoc2(value) {
10649
12179
  return value instanceof Y3.Doc;
10650
12180
  }
10651
- function getYDoc(editorOrYDoc) {
12181
+ function getYDoc2(editorOrYDoc) {
10652
12182
  if (!editorOrYDoc) return void 0;
10653
- if (isYDoc(editorOrYDoc)) return editorOrYDoc;
12183
+ if (isYDoc2(editorOrYDoc)) return editorOrYDoc;
10654
12184
  return editorOrYDoc._yDoc;
10655
12185
  }
10656
12186
  function parseInputs2(value) {
@@ -10665,20 +12195,20 @@ function parseInputs2(value) {
10665
12195
  return {};
10666
12196
  }
10667
12197
  }
10668
- function getRuntime(editorOrYDoc, runtime) {
12198
+ function getRuntime2(editorOrYDoc, runtime) {
10669
12199
  if (runtime) return runtime;
10670
- const yDoc = getYDoc(editorOrYDoc);
12200
+ const yDoc = getYDoc2(editorOrYDoc);
10671
12201
  if (yDoc) return createYDocRuntimeManager(yDoc);
10672
- return createRuntimeStateManager(!isYDoc(editorOrYDoc) ? editorOrYDoc : void 0);
12202
+ return createRuntimeStateManager(!isYDoc2(editorOrYDoc) ? editorOrYDoc : void 0);
10673
12203
  }
10674
- function getEditor(editorOrYDoc) {
10675
- return editorOrYDoc && !isYDoc(editorOrYDoc) ? editorOrYDoc : void 0;
12204
+ function getEditor2(editorOrYDoc) {
12205
+ return editorOrYDoc && !isYDoc2(editorOrYDoc) ? editorOrYDoc : void 0;
10676
12206
  }
10677
12207
  function findBlock(params) {
10678
12208
  if (params.block) return params.block;
10679
12209
  const blockId = params.blockId;
10680
12210
  if (!blockId) return void 0;
10681
- const editor = getEditor(params.editorOrYDoc);
12211
+ const editor = getEditor2(params.editorOrYDoc);
10682
12212
  return (params.document || editor?.document || []).find((block) => block?.id === blockId);
10683
12213
  }
10684
12214
  function getFlowMetadata(editor) {
@@ -10712,8 +12242,8 @@ function getNodeOutput(runtime, nodeId) {
10712
12242
  }
10713
12243
  function buildActionRunInputs(params) {
10714
12244
  const blockId = params.blockId || params.block?.id;
10715
- const yDoc = getYDoc(params.editorOrYDoc);
10716
- const runtime = getRuntime(params.editorOrYDoc, params.runtime);
12245
+ const yDoc = getYDoc2(params.editorOrYDoc);
12246
+ const runtime = getRuntime2(params.editorOrYDoc, params.runtime);
10717
12247
  const savedInputs = parseInputs2(params.savedInputs ?? params.block?.props?.inputs);
10718
12248
  const pendingInvocation = getPendingInvocation(yDoc, blockId, params.pendingInvocationId);
10719
12249
  const triggerContext = pendingInvocation ? {
@@ -10752,10 +12282,10 @@ function updateRuntimeFailure(runtime, blockId, message, now) {
10752
12282
  error: { message, at: now() }
10753
12283
  });
10754
12284
  }
10755
- function makeRunId(now) {
12285
+ function makeRunId2(now) {
10756
12286
  return `run-${now()}-${Math.random().toString(36).slice(2, 8)}`;
10757
12287
  }
10758
- function getReconcileEditor(params, yDoc, editor) {
12288
+ function getReconcileEditor2(params, yDoc, editor) {
10759
12289
  if (editor) return editor;
10760
12290
  if (!yDoc || !params.document) return void 0;
10761
12291
  return {
@@ -10766,7 +12296,7 @@ function getReconcileEditor(params, yDoc, editor) {
10766
12296
  }
10767
12297
  function persistEvents(params) {
10768
12298
  if (!params.yDoc || params.events.length === 0) return void 0;
10769
- const runId = makeRunId(params.now);
12299
+ const runId = makeRunId2(params.now);
10770
12300
  const details = {
10771
12301
  runId,
10772
12302
  output: params.output,
@@ -10798,9 +12328,9 @@ function cleanupCompletedPendingInvocation(yDoc, blockId, pendingInvocation) {
10798
12328
  async function executeActionBlock(params) {
10799
12329
  const block = findBlock(params);
10800
12330
  const blockId = params.blockId || block?.id;
10801
- const editor = getEditor(params.editorOrYDoc);
10802
- const yDoc = getYDoc(params.editorOrYDoc);
10803
- const runtime = getRuntime(params.editorOrYDoc, params.runtime);
12331
+ const editor = getEditor2(params.editorOrYDoc);
12332
+ const yDoc = getYDoc2(params.editorOrYDoc);
12333
+ const runtime = getRuntime2(params.editorOrYDoc, params.runtime);
10804
12334
  const now = params.now || Date.now;
10805
12335
  if (!block || !blockId) {
10806
12336
  return buildFailureResult({
@@ -10960,7 +12490,7 @@ async function executeActionBlock(params) {
10960
12490
  });
10961
12491
  const runId = persistEvents({
10962
12492
  yDoc,
10963
- editor: getReconcileEditor(params, yDoc, editor),
12493
+ editor: getReconcileEditor2(params, yDoc, editor),
10964
12494
  blockId,
10965
12495
  actorDid: params.actorDid,
10966
12496
  output,
@@ -12719,7 +14249,7 @@ var BLOCKER_DIAGNOSIS_VERSION = 2;
12719
14249
  function getBlocks(context) {
12720
14250
  return context.blocks || context.editor?.document || [];
12721
14251
  }
12722
- function getRuntime2(yDoc, nodeId) {
14252
+ function getRuntime3(yDoc, nodeId) {
12723
14253
  const runtime = yDoc.getMap("runtime");
12724
14254
  const value = runtime.get(nodeId);
12725
14255
  return value && typeof value === "object" ? value : {};
@@ -12929,7 +14459,7 @@ function planRalphLoopCommands(context, options = {}) {
12929
14459
  const nodeId = getBlockId(block);
12930
14460
  if (!nodeId) continue;
12931
14461
  const pendingInvocationCount = readPendingInvocations(context.yDoc, nodeId).length;
12932
- const runtime = getRuntime2(context.yDoc, nodeId);
14462
+ const runtime = getRuntime3(context.yDoc, nodeId);
12933
14463
  const actionType = getBlockActionType(block);
12934
14464
  const completionVerification = verifyCompletion({
12935
14465
  runtime,
@@ -13623,6 +15153,7 @@ export {
13623
15153
  executeNode,
13624
15154
  PROOF_MISSING_CODE,
13625
15155
  validateActionProof,
15156
+ reconcileActionReadBack,
13626
15157
  buildActionRunInputs,
13627
15158
  executeActionBlock,
13628
15159
  verifyCompletion,
@@ -13683,4 +15214,4 @@ export {
13683
15214
  executeQueuedFlowAgentCoreCommands,
13684
15215
  FlowAgentService
13685
15216
  };
13686
- //# sourceMappingURL=chunk-ABOIPKAB.js.map
15217
+ //# sourceMappingURL=chunk-EULL3QTC.js.map