@ixo/editor 4.3.1 → 5.1.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.
@@ -15,7 +15,6 @@ var ACTION_TYPE_ALIASES = {
15
15
  "proposal.vote": "qi/proposal.vote",
16
16
  "protocol.select": "qi/protocol.select",
17
17
  "domain.sign": "qi/domain.sign",
18
- "domain.card-build": "qi/domain.card-build",
19
18
  "domain.card-preview": "qi/domain.card-preview",
20
19
  "credential.store": "qi/credential.store",
21
20
  // POD setup flow — camelCase orchestrator labels → canonical action types
@@ -62,7 +61,6 @@ var CAN_TO_TYPE = {
62
61
  "matrix/dm": "qi/matrix.dm",
63
62
  "proposal/create": "qi/proposal.create",
64
63
  "proposal/vote": "qi/proposal.vote",
65
- "domain/card-build": "qi/domain.card-build",
66
64
  "domain/card-preview": "qi/domain.card-preview",
67
65
  "domain/sign": "qi/domain.sign",
68
66
  "credential/store": "qi/credential.store",
@@ -77,7 +75,17 @@ var CAN_TO_TYPE = {
77
75
  "pod/entity-single-selection": "qi/pod.entity-single-selection",
78
76
  "pod/governance-config": "qi/pod.governance-config",
79
77
  "pod/member-multi-select": "qi/pod.member-multi-select",
80
- "pod/list-domain-flows": "qi/pod.list-domain-flows"
78
+ "pod/list-domain-flows": "qi/pod.list-domain-flows",
79
+ "wallet/generate": "qi/wallet.generate",
80
+ "wallet/fund": "qi/wallet.fund",
81
+ "iid/create": "qi/iid.create",
82
+ "matrix/register": "qi/matrix.register",
83
+ "entity/createOracle": "qi/entity.createOracle",
84
+ "sandbox/provision": "qi/sandbox.provision",
85
+ "oracle/contract": "qi/oracle.contract",
86
+ "oracle/storeSecrets": "qi/oracle.storeSecrets",
87
+ "oracle/storeConfig": "qi/oracle.storeConfig",
88
+ "oracle/configureOracle": "qi/oracle.configureOracle"
81
89
  };
82
90
  var TYPE_TO_CAN = Object.fromEntries(Object.entries(CAN_TO_TYPE).map(([can, type]) => [type, can]));
83
91
  function canToType(can) {
@@ -150,6 +158,30 @@ function buildServicesFromHandlers(handlers) {
150
158
  } : void 0,
151
159
  matrix: handlers?.storeMatrixCredential ? {
152
160
  storeCredential: async (params) => handlers.storeMatrixCredential(params)
161
+ } : void 0,
162
+ oracle: handlers?.generateWallet ? {
163
+ generateWallet: async () => handlers.generateWallet(),
164
+ fundWallet: async (params) => handlers.fundWallet(params),
165
+ createIidDocument: async (params) => handlers.createIidDocument(params),
166
+ registerMatrixAccount: async (params) => handlers.registerMatrixAccount(params),
167
+ createOracleEntity: async (params) => handlers.createOracleEntity(params),
168
+ contract: async (params) => handlers.oracleContract(params),
169
+ provisionSandbox: async (params) => handlers.provisionSandbox(params),
170
+ storeSecrets: async (params) => handlers.storeSecrets(params),
171
+ storeConfig: async (params) => handlers.storeOracleConfig(params),
172
+ validateMcpServer: handlers.validateMcpServer || (async () => ({ success: false, error: "Handler not configured" })),
173
+ encryptForOracle: async (params) => handlers.encryptSecretForOracle(params),
174
+ readStoredSecrets: async (params) => handlers.readStoredSecrets(params),
175
+ getNetworkConstants: async (params) => handlers.getNetworkConstants(params),
176
+ deploySetup: handlers.deploySetup || (async () => {
177
+ throw new Error("deploySetup handler not configured");
178
+ }),
179
+ deployStart: handlers.deployStart || (async () => {
180
+ throw new Error("deployStart handler not configured");
181
+ }),
182
+ updateOracleDomain: handlers.updateOracleDomain || (async () => {
183
+ throw new Error("updateOracleDomain handler not configured");
184
+ })
153
185
  } : void 0
154
186
  };
155
187
  }
@@ -308,12 +340,31 @@ registerAction({
308
340
  { path: "nftContractAddress", displayName: "NFT Contract Address", type: "string", description: "NFT staking contract address \u2014 nftStaking only" },
309
341
  { path: "tokenConfig", displayName: "Token Config", type: "object", description: "Token configuration \u2014 tokenStaking only" }
310
342
  ],
343
+ events: [
344
+ {
345
+ name: "configured",
346
+ displayName: "Members Configured",
347
+ description: "Fires when membership configuration is complete.",
348
+ payloadSchema: [
349
+ {
350
+ path: "memberConfig",
351
+ displayName: "Member Config",
352
+ type: "object",
353
+ description: "Full member configuration (members, multisigThreshold, nftContractAddress, or tokenConfig depending on group type)"
354
+ }
355
+ ],
356
+ pendingDisplayFields: ["memberConfig.memberCount"]
357
+ }
358
+ ],
311
359
  run: async (inputs) => {
312
360
  const groupType = inputs.groupType || "categorical";
313
361
  if (groupType === "nftStaking") {
314
362
  const addr = String(inputs.nftContractAddress || "").trim();
315
363
  if (!addr) throw new Error("NFT contract address is required for nftStaking groups");
316
- return { output: { nftContractAddress: addr } };
364
+ return {
365
+ output: { nftContractAddress: addr },
366
+ events: [{ name: "configured", payload: { memberConfig: { nftContractAddress: addr, memberCount: 0 } } }]
367
+ };
317
368
  }
318
369
  if (groupType === "tokenStaking") {
319
370
  const tokenConfig = inputs.tokenConfig;
@@ -326,7 +377,10 @@ registerAction({
326
377
  const supply = Number(tokenConfig.tokenSupply);
327
378
  if (isNaN(supply) || supply <= 0) throw new Error("Token supply must be a positive number");
328
379
  }
329
- return { output: { tokenConfig } };
380
+ return {
381
+ output: { tokenConfig },
382
+ events: [{ name: "configured", payload: { memberConfig: { tokenConfig, memberCount: 0 } } }]
383
+ };
330
384
  }
331
385
  const members = Array.isArray(inputs.members) ? inputs.members : [];
332
386
  if (members.length === 0) throw new Error("At least one member is required");
@@ -337,7 +391,10 @@ registerAction({
337
391
  const threshold = Number(inputs.multisigThreshold);
338
392
  if (!Number.isInteger(threshold) || threshold < 1) throw new Error("Multisig threshold must be a positive integer");
339
393
  if (threshold > members.length) throw new Error(`Threshold (${threshold}) cannot exceed member count (${members.length})`);
340
- return { output: { members, multisigThreshold: threshold } };
394
+ return {
395
+ output: { members, multisigThreshold: threshold },
396
+ events: [{ name: "configured", payload: { memberConfig: { members, multisigThreshold: threshold, memberCount: members.length } } }]
397
+ };
341
398
  }
342
399
  const hasAdmin = members.some((m) => String(m.role || "").toLowerCase() === "admin");
343
400
  if (!hasAdmin) throw new Error("At least one member must have the Admin role");
@@ -345,7 +402,10 @@ registerAction({
345
402
  const vp = Number(m.votingPower);
346
403
  if (!Number.isInteger(vp) || vp <= 0) throw new Error(`Voting power must be a positive integer (got ${m.votingPower} for ${m.did})`);
347
404
  }
348
- return { output: { members } };
405
+ return {
406
+ output: { members },
407
+ events: [{ name: "configured", payload: { memberConfig: { members, memberCount: members.length } } }]
408
+ };
349
409
  }
350
410
  });
351
411
 
@@ -361,6 +421,19 @@ registerAction({
361
421
  { path: "groupType", displayName: "Group Type", type: "string", description: "Governance group type (categorical | multisig | nftStaking | tokenStaking)" },
362
422
  { path: "governance", displayName: "Governance Settings", type: "object", description: "Cosmos group decision policy parameters" }
363
423
  ],
424
+ events: [
425
+ {
426
+ name: "configured",
427
+ displayName: "Governance Configured",
428
+ description: "Fires when governance group type and decision policy are configured.",
429
+ payloadSchema: [
430
+ { path: "governanceConfig.groupName", displayName: "Group Name", type: "string" },
431
+ { path: "governanceConfig.groupType", displayName: "Group Type", type: "string" },
432
+ { path: "governanceConfig.governance", displayName: "Governance Settings", type: "object" }
433
+ ],
434
+ pendingDisplayFields: ["governanceConfig.groupName", "governanceConfig.groupType"]
435
+ }
436
+ ],
364
437
  run: async (inputs) => {
365
438
  const groupName = String(inputs.groupName || "").trim();
366
439
  if (!groupName) throw new Error("groupName is required");
@@ -387,7 +460,10 @@ registerAction({
387
460
  throw new Error("threshold + vetoThreshold cannot exceed 1.0");
388
461
  }
389
462
  }
390
- return { output: { groupName, groupType, governance } };
463
+ return {
464
+ output: { groupName, groupType, governance },
465
+ events: [{ name: "configured", payload: { governanceConfig: { groupName, groupType, governance } } }]
466
+ };
391
467
  }
392
468
  });
393
469
 
@@ -2233,16 +2309,119 @@ var tempDomainCreatorSurvey = {
2233
2309
  };
2234
2310
 
2235
2311
  // src/core/lib/actionRegistry/actions/domainSign.ts
2312
+ function resolveEntityTypeFromSchema(schemaType) {
2313
+ const normalized = schemaType.replace(/^schema:/i, "").toLowerCase();
2314
+ const schemaToEntity = {
2315
+ organization: "dao",
2316
+ nonprofit: "dao",
2317
+ corporation: "dao",
2318
+ governmentorganization: "dao"
2319
+ };
2320
+ if (schemaToEntity[normalized]) return schemaToEntity[normalized];
2321
+ const allowed = /* @__PURE__ */ new Set(["dao", "protocol", "oracle", "investment", "project", "asset"]);
2322
+ return allowed.has(normalized) ? normalized : "dao";
2323
+ }
2324
+ function parseJsonInput(value) {
2325
+ if (!value) return null;
2326
+ if (typeof value === "object" && !Array.isArray(value)) return value;
2327
+ if (typeof value === "string") {
2328
+ try {
2329
+ const parsed = JSON.parse(value);
2330
+ return parsed && typeof parsed === "object" ? parsed : null;
2331
+ } catch {
2332
+ return null;
2333
+ }
2334
+ }
2335
+ return null;
2336
+ }
2337
+ function parseDuration(raw) {
2338
+ const seconds = parseInt(raw, 10) || 0;
2339
+ if (seconds % (7 * 86400) === 0) return { amount: seconds / (7 * 86400), unit: "weeks" };
2340
+ if (seconds % 86400 === 0) return { amount: seconds / 86400, unit: "days" };
2341
+ if (seconds % 3600 === 0) return { amount: seconds / 3600, unit: "hours" };
2342
+ if (seconds % 60 === 0) return { amount: seconds / 60, unit: "minutes" };
2343
+ return { amount: seconds, unit: "seconds" };
2344
+ }
2345
+ function buildGroupConfig(groupType, governance, memberConfig) {
2346
+ const duration = parseDuration(governance.votingPeriod || "604800s");
2347
+ const thresholdNum = parseFloat(governance.threshold || "0.51");
2348
+ const baseConfig = {
2349
+ proposalDurationAmount: duration.amount,
2350
+ proposalDurationUnit: duration.unit,
2351
+ proposalSubmissionPolicy: "members",
2352
+ voteSwitching: false
2353
+ };
2354
+ if (groupType === "multisig") {
2355
+ const members2 = memberConfig?.members || [];
2356
+ return {
2357
+ threshold: memberConfig?.multisigThreshold || 1,
2358
+ membershipCategory: [{ members: members2.map((m) => m.did) }]
2359
+ };
2360
+ }
2361
+ if (groupType === "nftStaking") {
2362
+ return {
2363
+ ...baseConfig,
2364
+ nftContractAddress: memberConfig?.nftContractAddress || "",
2365
+ unstakingDuration: governance.unstakingDuration ? parseDuration(governance.unstakingDuration) : void 0
2366
+ };
2367
+ }
2368
+ if (groupType === "tokenStaking") {
2369
+ const tokenConfig = memberConfig?.tokenConfig || {};
2370
+ return {
2371
+ ...baseConfig,
2372
+ passingThreshold: thresholdNum === 0.5 ? "majority" : "percentage",
2373
+ passingThresholdPercentage: Math.round(thresholdNum * 100),
2374
+ tokenName: tokenConfig.tokenName || "",
2375
+ tokenSymbol: tokenConfig.tokenSymbol || "",
2376
+ tokenSupply: tokenConfig.tokenSupply || 0,
2377
+ distributionCategory: tokenConfig.distributionCategory || [],
2378
+ unstakingDuration: governance.unstakingDuration ? parseDuration(governance.unstakingDuration) : void 0
2379
+ };
2380
+ }
2381
+ const members = memberConfig?.members || [];
2382
+ const byRole = /* @__PURE__ */ new Map();
2383
+ for (const m of members) {
2384
+ const role = m.role || "member";
2385
+ const existing = byRole.get(role);
2386
+ if (existing) {
2387
+ existing.dids.push(m.did);
2388
+ } else {
2389
+ byRole.set(role, { dids: [m.did], weight: m.votingPower || 1 });
2390
+ }
2391
+ }
2392
+ return {
2393
+ ...baseConfig,
2394
+ passingThreshold: thresholdNum === 0.5 ? "majority" : "percentage",
2395
+ passingThresholdPercentage: Math.round(thresholdNum * 100),
2396
+ membershipCategory: Array.from(byRole.values()).map(({ dids, weight }) => ({
2397
+ members: dids.map((did) => ({ memberAddress: did })),
2398
+ weightPerMember: weight
2399
+ }))
2400
+ };
2401
+ }
2236
2402
  registerAction({
2237
2403
  type: "qi/domain.sign",
2238
2404
  can: "domain/sign",
2239
2405
  sideEffect: true,
2240
2406
  defaultRequiresConfirmation: true,
2241
2407
  requiredCapability: "flow/block/execute",
2408
+ eligibleForEventTrigger: true,
2242
2409
  outputSchema: [
2243
2410
  { path: "entityDid", displayName: "Entity DID", type: "string", description: "The DID of the newly created domain entity" },
2244
2411
  { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The on-chain transaction hash for domain creation" }
2245
2412
  ],
2413
+ events: [
2414
+ {
2415
+ name: "created",
2416
+ displayName: "Domain Created",
2417
+ description: "Fires after the domain entity is successfully created on-chain.",
2418
+ payloadSchema: [
2419
+ { path: "entityDid", displayName: "Entity DID", type: "string", description: "DID of the newly created domain" },
2420
+ { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "On-chain transaction hash" }
2421
+ ],
2422
+ pendingDisplayFields: ["entityDid"]
2423
+ }
2424
+ ],
2246
2425
  run: async (inputs, ctx) => {
2247
2426
  const handlers = ctx.handlers;
2248
2427
  if (!handlers) {
@@ -2267,8 +2446,7 @@ registerAction({
2267
2446
  if (!domainCardData?.credentialSubject?.name) {
2268
2447
  throw new Error("domainCardData is missing or invalid (credentialSubject.name required)");
2269
2448
  }
2270
- const extractEntityType = (type) => type.replace(/^schema:/i, "").toLowerCase();
2271
- const entityType = String(inputs.entityType || "").trim() || (domainCardData.credentialSubject?.type?.[0] ? extractEntityType(domainCardData.credentialSubject.type[0]) : "dao");
2449
+ const entityType = String(inputs.entityType || "").trim() || (domainCardData.credentialSubject?.type?.[0] ? resolveEntityTypeFromSchema(domainCardData.credentialSubject.type[0]) : "dao");
2272
2450
  const issuerDid = handlers.getEntityDid?.() || handlers.getCurrentUser?.()?.address;
2273
2451
  if (!issuerDid) throw new Error("Unable to determine issuer DID");
2274
2452
  const entityDidPlaceholder = "did:ixo:entity:pending";
@@ -2313,18 +2491,27 @@ registerAction({
2313
2491
  serviceEndpoint: uploadResult.url,
2314
2492
  description: `Domain Card for ${domainCardData.credentialSubject?.name || "Domain"}`
2315
2493
  });
2316
- let linkedEntitiesData = [];
2317
- if (inputs.linkedEntities) {
2318
- if (typeof inputs.linkedEntities === "string") {
2319
- try {
2320
- linkedEntitiesData = JSON.parse(inputs.linkedEntities);
2321
- } catch {
2494
+ let governanceGroupLinkedEntities = [];
2495
+ const govConfig = parseJsonInput(inputs.governanceConfig);
2496
+ const memberConfig = parseJsonInput(inputs.memberConfig);
2497
+ if (govConfig && handlers.createGovernanceGroup) {
2498
+ const groupType = govConfig.groupType;
2499
+ const governance = govConfig.governance || {};
2500
+ const config = buildGroupConfig(groupType, governance, memberConfig);
2501
+ const groupResult = await handlers.createGovernanceGroup({
2502
+ groupType,
2503
+ name: govConfig.groupName || domainCardData.credentialSubject?.name || "Governance Group",
2504
+ config
2505
+ });
2506
+ governanceGroupLinkedEntities = [
2507
+ {
2508
+ id: groupResult.coreAddress,
2509
+ type: "group",
2510
+ relationship: "governs",
2511
+ service: ""
2322
2512
  }
2323
- } else if (Array.isArray(inputs.linkedEntities)) {
2324
- linkedEntitiesData = inputs.linkedEntities;
2325
- }
2513
+ ];
2326
2514
  }
2327
- const governanceGroupLinkedEntities = buildGovernanceGroupLinkedEntities(parseLinkedEntities(JSON.stringify(linkedEntitiesData)));
2328
2515
  const endDate = domainCardData.endDate || validUntil;
2329
2516
  const { entityDid: newEntityDid, transactionHash } = await handlers.createDomain({
2330
2517
  entityType,
@@ -2337,81 +2524,8 @@ registerAction({
2337
2524
  output: {
2338
2525
  entityDid: newEntityDid,
2339
2526
  transactionHash
2340
- }
2341
- };
2342
- }
2343
- });
2344
-
2345
- // src/core/lib/actionRegistry/actions/domainCardBuild.ts
2346
- registerAction({
2347
- type: "qi/domain.card-build",
2348
- can: "domain/card-build",
2349
- sideEffect: false,
2350
- defaultRequiresConfirmation: false,
2351
- outputSchema: [
2352
- {
2353
- path: "domainCardData",
2354
- displayName: "Domain Card Data",
2355
- type: "object",
2356
- description: "Unsigned domain card envelope { credentialSubject, validFrom, validUntil } ready for preview and signing"
2357
- },
2358
- { path: "unsignedCredential", displayName: "Unsigned Credential", type: "object", description: "The full unsigned W3C Verifiable Credential" },
2359
- { path: "credentialSubject", displayName: "Credential Subject", type: "object", description: "The ixo:DomainCard credentialSubject extracted from the survey" },
2360
- { path: "surveyData", displayName: "Survey Answers", type: "object", description: "Raw survey answers used to build the card" },
2361
- { path: "entityType", displayName: "Entity Type", type: "string", description: "Resolved entity type (from survey or configured default)" },
2362
- { path: "issuerDid", displayName: "Issuer DID", type: "string", description: "DID of the user or entity that built the card" }
2363
- ],
2364
- run: async (inputs, ctx) => {
2365
- const handlers = ctx.handlers;
2366
- if (!handlers) throw new Error("Handlers not available");
2367
- const configEntityType = String(inputs.entityType || "dao").trim();
2368
- let surveyData = {};
2369
- if (inputs.surveyData) {
2370
- if (typeof inputs.surveyData === "string") {
2371
- try {
2372
- surveyData = JSON.parse(inputs.surveyData);
2373
- } catch {
2374
- throw new Error("surveyData must be valid JSON");
2375
- }
2376
- } else if (typeof inputs.surveyData === "object" && !Array.isArray(inputs.surveyData)) {
2377
- surveyData = inputs.surveyData;
2378
- }
2379
- }
2380
- if (!surveyData || Object.keys(surveyData).length === 0) {
2381
- throw new Error("surveyData is required to build a domain card");
2382
- }
2383
- const issuerDid = handlers.getEntityDid?.() || handlers.getCurrentUser?.()?.address;
2384
- if (!issuerDid) throw new Error("Unable to determine issuer DID");
2385
- const entityDidPlaceholder = "did:ixo:entity:pending";
2386
- const credentialSubject = transformSurveyToCredentialSubject(surveyData, entityDidPlaceholder);
2387
- const validFrom = surveyData["schema:validFrom"] || (/* @__PURE__ */ new Date()).toISOString();
2388
- const validUntil = surveyData["schema:validUntil"] || (() => {
2389
- const d = new Date(validFrom);
2390
- d.setFullYear(d.getFullYear() + 5);
2391
- return d.toISOString();
2392
- })();
2393
- const unsignedCredential = buildVerifiableCredential({
2394
- entityDid: entityDidPlaceholder,
2395
- issuerDid,
2396
- credentialSubject,
2397
- validFrom,
2398
- validUntil
2399
- });
2400
- const resolvedEntityType = surveyData["type_2"] || surveyData["daoType"] || configEntityType;
2401
- const domainCardData = {
2402
- credentialSubject,
2403
- validFrom,
2404
- validUntil
2405
- };
2406
- return {
2407
- output: {
2408
- domainCardData,
2409
- unsignedCredential,
2410
- credentialSubject,
2411
- surveyData,
2412
- entityType: resolvedEntityType,
2413
- issuerDid
2414
- }
2527
+ },
2528
+ events: [{ name: "created", payload: { entityDid: newEntityDid, transactionHash } }]
2415
2529
  };
2416
2530
  }
2417
2531
  });
@@ -2433,6 +2547,18 @@ registerAction({
2433
2547
  { path: "status", displayName: "Status", type: "string", description: "pending | loading | ready | approved | error" },
2434
2548
  { path: "approvedAt", displayName: "Approved At", type: "number", description: "Timestamp when the user approved the preview" }
2435
2549
  ],
2550
+ events: [
2551
+ {
2552
+ name: "approved",
2553
+ displayName: "Preview Approved",
2554
+ description: "Fires when the user approves the oracle-enriched domain card preview.",
2555
+ payloadSchema: [
2556
+ { path: "domainCardData", displayName: "Domain Card Data", type: "object", description: "Approved domain card envelope ready for signing" },
2557
+ { path: "approvedAt", displayName: "Approved At", type: "number", description: "Timestamp of approval" }
2558
+ ],
2559
+ pendingDisplayFields: ["approvedAt"]
2560
+ }
2561
+ ],
2436
2562
  run: async (inputs) => {
2437
2563
  let domainCardData = inputs.domainCardData;
2438
2564
  if (typeof domainCardData === "string") {
@@ -2456,13 +2582,15 @@ registerAction({
2456
2582
  domainPreviewData = void 0;
2457
2583
  }
2458
2584
  }
2585
+ const now = Date.now();
2459
2586
  return {
2460
2587
  output: {
2461
2588
  domainCardData,
2462
2589
  domainPreviewData,
2463
2590
  status: "approved",
2464
- approvedAt: Date.now()
2465
- }
2591
+ approvedAt: now
2592
+ },
2593
+ events: [{ name: "approved", payload: { domainCardData, approvedAt: now } }]
2466
2594
  };
2467
2595
  }
2468
2596
  });
@@ -2608,6 +2736,853 @@ registerAction({
2608
2736
  }
2609
2737
  });
2610
2738
 
2739
+ // src/core/lib/actionRegistry/actions/walletGenerate.ts
2740
+ registerAction({
2741
+ type: "qi/wallet.generate",
2742
+ can: "wallet/generate",
2743
+ sideEffect: false,
2744
+ defaultRequiresConfirmation: false,
2745
+ outputSchema: [
2746
+ { path: "address", displayName: "Wallet Address", type: "string", description: "The generated IXO wallet address" },
2747
+ { path: "did", displayName: "DID", type: "string", description: "The DID derived from the wallet address" },
2748
+ { path: "pubKey", displayName: "Public Key", type: "string", description: "The secp256k1 public key (hex)" },
2749
+ { path: "mnemonic", displayName: "Mnemonic", type: "string", description: "The BIP39 mnemonic seed phrase" }
2750
+ ],
2751
+ run: async (_inputs, ctx) => {
2752
+ if (!ctx.services.oracle?.generateWallet) {
2753
+ throw new Error("oracle.generateWallet handler not available");
2754
+ }
2755
+ const result = await ctx.services.oracle.generateWallet();
2756
+ return { output: result };
2757
+ }
2758
+ });
2759
+
2760
+ // src/core/lib/actionRegistry/actions/walletFund.ts
2761
+ registerAction({
2762
+ type: "qi/wallet.fund",
2763
+ can: "wallet/fund",
2764
+ sideEffect: true,
2765
+ defaultRequiresConfirmation: true,
2766
+ outputSchema: [
2767
+ { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The funding transaction hash" }
2768
+ ],
2769
+ run: async (inputs, ctx) => {
2770
+ if (!ctx.services.oracle?.fundWallet) {
2771
+ throw new Error("oracle.fundWallet handler not available");
2772
+ }
2773
+ if (!inputs.address) throw new Error("address is required");
2774
+ const result = await ctx.services.oracle.fundWallet({
2775
+ address: inputs.address,
2776
+ amount: inputs.amount || 25e4
2777
+ });
2778
+ return { output: result };
2779
+ }
2780
+ });
2781
+
2782
+ // src/core/lib/actionRegistry/actions/walletGenerateAndFund.ts
2783
+ registerAction({
2784
+ type: "qi/wallet.generateAndFund",
2785
+ can: "wallet/generateAndFund",
2786
+ sideEffect: true,
2787
+ defaultRequiresConfirmation: false,
2788
+ outputSchema: [
2789
+ { path: "address", displayName: "Wallet Address", type: "string", description: "The generated IXO wallet address" },
2790
+ { path: "did", displayName: "DID", type: "string", description: "The DID derived from the wallet address" },
2791
+ { path: "pubKey", displayName: "Public Key", type: "string", description: "The secp256k1 public key (hex)" },
2792
+ { path: "mnemonic", displayName: "Mnemonic", type: "string", description: "The BIP39 mnemonic seed phrase" },
2793
+ { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The funding transaction hash" }
2794
+ ],
2795
+ run: async (inputs, ctx) => {
2796
+ if (!ctx.services.oracle?.generateWallet) {
2797
+ throw new Error("oracle.generateWallet handler not available");
2798
+ }
2799
+ if (!ctx.services.oracle?.fundWallet) {
2800
+ throw new Error("oracle.fundWallet handler not available");
2801
+ }
2802
+ const walletResult = await ctx.services.oracle.generateWallet();
2803
+ if (!walletResult?.address) {
2804
+ throw new Error("generateWallet did not return an address");
2805
+ }
2806
+ const fundResult = await ctx.services.oracle.fundWallet({
2807
+ address: walletResult.address,
2808
+ amount: inputs.amount || 25e4
2809
+ });
2810
+ if (!fundResult?.transactionHash) {
2811
+ throw new Error("fundWallet did not return a transactionHash");
2812
+ }
2813
+ return {
2814
+ output: {
2815
+ address: walletResult.address,
2816
+ did: walletResult.did,
2817
+ pubKey: walletResult.pubKey,
2818
+ mnemonic: walletResult.mnemonic,
2819
+ transactionHash: fundResult.transactionHash
2820
+ }
2821
+ };
2822
+ }
2823
+ });
2824
+
2825
+ // src/core/lib/actionRegistry/actions/iidCreate.ts
2826
+ registerAction({
2827
+ type: "qi/iid.create",
2828
+ can: "iid/create",
2829
+ sideEffect: true,
2830
+ defaultRequiresConfirmation: false,
2831
+ outputSchema: [
2832
+ { path: "did", displayName: "DID", type: "string", description: "The IID document DID" },
2833
+ { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The IID creation transaction hash" }
2834
+ ],
2835
+ run: async (inputs, ctx) => {
2836
+ if (!ctx.services.oracle?.createIidDocument) {
2837
+ throw new Error("oracle.createIidDocument handler not available");
2838
+ }
2839
+ if (!inputs.mnemonic) throw new Error("mnemonic is required");
2840
+ if (!inputs.did) throw new Error("did is required");
2841
+ if (!inputs.address) throw new Error("address is required");
2842
+ if (!inputs.pubKey) throw new Error("pubKey is required");
2843
+ const result = await ctx.services.oracle.createIidDocument({
2844
+ mnemonic: inputs.mnemonic,
2845
+ did: inputs.did,
2846
+ address: inputs.address,
2847
+ pubKey: inputs.pubKey
2848
+ });
2849
+ return { output: result };
2850
+ }
2851
+ });
2852
+
2853
+ // src/core/lib/actionRegistry/actions/matrixRegister.ts
2854
+ registerAction({
2855
+ type: "qi/matrix.register",
2856
+ can: "matrix/register",
2857
+ sideEffect: true,
2858
+ defaultRequiresConfirmation: false,
2859
+ outputSchema: [
2860
+ { path: "matrixUserId", displayName: "Matrix User ID", type: "string", description: "The Matrix user ID" },
2861
+ { path: "matrixAccessToken", displayName: "Matrix Access Token", type: "string", description: "The Matrix access token" },
2862
+ { path: "matrixRoomId", displayName: "Matrix Room ID", type: "string", description: "The Matrix room ID" },
2863
+ { path: "matrixDeviceId", displayName: "Matrix Device ID", type: "string", description: "The Matrix device ID" },
2864
+ { path: "matrixMnemonic", displayName: "Matrix Mnemonic", type: "string", description: "The Matrix account mnemonic" },
2865
+ { path: "matrixPassword", displayName: "Matrix Password", type: "string", description: "The Matrix account password" },
2866
+ { path: "matrixRecoveryPhrase", displayName: "Recovery Phrase", type: "string", description: "The Matrix recovery phrase" },
2867
+ { path: "matrixHomeServerUrl", displayName: "Matrix Homeserver", type: "string", description: "The Matrix homeserver URL" }
2868
+ ],
2869
+ run: async (inputs, ctx) => {
2870
+ if (!ctx.services.oracle?.registerMatrixAccount) {
2871
+ throw new Error("oracle.registerMatrixAccount handler not available");
2872
+ }
2873
+ if (!inputs.mnemonic) throw new Error("mnemonic is required");
2874
+ if (!inputs.address) throw new Error("address is required");
2875
+ if (!inputs.did) throw new Error("did is required");
2876
+ if (!inputs.pin) throw new Error("pin is required");
2877
+ if (!inputs.oracleName) throw new Error("oracleName is required");
2878
+ const result = await ctx.services.oracle.registerMatrixAccount({
2879
+ mnemonic: inputs.mnemonic,
2880
+ address: inputs.address,
2881
+ did: inputs.did,
2882
+ pin: inputs.pin,
2883
+ oracleName: inputs.oracleName,
2884
+ avatarUrl: inputs.avatarUrl
2885
+ });
2886
+ return { output: result };
2887
+ }
2888
+ });
2889
+
2890
+ // src/core/lib/actionRegistry/actions/identityCreate.ts
2891
+ registerAction({
2892
+ type: "qi/identity.create",
2893
+ can: "identity/create",
2894
+ sideEffect: true,
2895
+ defaultRequiresConfirmation: false,
2896
+ outputSchema: [
2897
+ { path: "did", displayName: "DID", type: "string", description: "The IID document DID" },
2898
+ { path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The IID creation transaction hash" },
2899
+ { path: "alreadyExisted", displayName: "Already Existed", type: "boolean", description: "Whether the IID document already existed" },
2900
+ { path: "matrixUserId", displayName: "Matrix User ID", type: "string", description: "The Matrix user ID" },
2901
+ { path: "matrixAccessToken", displayName: "Matrix Access Token", type: "string", description: "The Matrix access token" },
2902
+ { path: "matrixRoomId", displayName: "Matrix Room ID", type: "string", description: "The Matrix room ID" },
2903
+ { path: "matrixDeviceId", displayName: "Matrix Device ID", type: "string", description: "The Matrix device ID" },
2904
+ { path: "matrixMnemonic", displayName: "Matrix Mnemonic", type: "string", description: "The Matrix account mnemonic" },
2905
+ { path: "matrixPassword", displayName: "Matrix Password", type: "string", description: "The Matrix account password" },
2906
+ { path: "matrixRecoveryPhrase", displayName: "Recovery Phrase", type: "string", description: "The Matrix recovery phrase" },
2907
+ { path: "matrixHomeServerUrl", displayName: "Matrix Homeserver", type: "string", description: "The Matrix homeserver URL" }
2908
+ ],
2909
+ run: async (rawInputs, ctx) => {
2910
+ const form = (() => {
2911
+ const raw = rawInputs.formAnswers;
2912
+ if (typeof raw !== "string" || !raw) return {};
2913
+ try {
2914
+ const parsed = JSON.parse(raw);
2915
+ return parsed && typeof parsed === "object" ? parsed : {};
2916
+ } catch {
2917
+ return {};
2918
+ }
2919
+ })();
2920
+ const inputs = {
2921
+ // Rename: form.logoUrl → avatarUrl for the registerMatrixAccount handler
2922
+ avatarUrl: form.logoUrl,
2923
+ ...form,
2924
+ ...rawInputs
2925
+ };
2926
+ if (!ctx.services.oracle?.createIidDocument) {
2927
+ throw new Error("oracle.createIidDocument handler not available");
2928
+ }
2929
+ if (!ctx.services.oracle?.registerMatrixAccount) {
2930
+ throw new Error("oracle.registerMatrixAccount handler not available");
2931
+ }
2932
+ if (!inputs.mnemonic) throw new Error("mnemonic is required");
2933
+ if (!inputs.did) throw new Error("did is required");
2934
+ if (!inputs.address) throw new Error("address is required");
2935
+ if (!inputs.pubKey) throw new Error("pubKey is required");
2936
+ if (!inputs.pin) throw new Error("pin is required");
2937
+ if (!inputs.oracleName) throw new Error("oracleName is required");
2938
+ const iidResult = await ctx.services.oracle.createIidDocument({
2939
+ mnemonic: inputs.mnemonic,
2940
+ did: inputs.did,
2941
+ address: inputs.address,
2942
+ pubKey: inputs.pubKey
2943
+ });
2944
+ if (!iidResult?.transactionHash && !iidResult?.alreadyExisted) {
2945
+ throw new Error("IID creation returned success without a transaction hash or alreadyExisted flag");
2946
+ }
2947
+ const matrixResult = await ctx.services.oracle.registerMatrixAccount({
2948
+ mnemonic: inputs.mnemonic,
2949
+ address: inputs.address,
2950
+ did: inputs.did,
2951
+ pin: inputs.pin,
2952
+ oracleName: inputs.oracleName,
2953
+ avatarUrl: inputs.avatarUrl
2954
+ });
2955
+ if (!matrixResult?.matrixUserId) {
2956
+ throw new Error("Matrix registration returned success without a matrixUserId");
2957
+ }
2958
+ return {
2959
+ output: {
2960
+ did: iidResult.did || inputs.did,
2961
+ transactionHash: iidResult.transactionHash,
2962
+ alreadyExisted: iidResult.alreadyExisted || false,
2963
+ matrixUserId: matrixResult.matrixUserId,
2964
+ matrixAccessToken: matrixResult.matrixAccessToken,
2965
+ matrixRoomId: matrixResult.matrixRoomId,
2966
+ matrixDeviceId: matrixResult.matrixDeviceId,
2967
+ matrixMnemonic: matrixResult.matrixMnemonic,
2968
+ matrixPassword: matrixResult.matrixPassword,
2969
+ matrixRecoveryPhrase: matrixResult.matrixRecoveryPhrase,
2970
+ matrixHomeServerUrl: matrixResult.matrixHomeServerUrl
2971
+ }
2972
+ };
2973
+ }
2974
+ });
2975
+
2976
+ // src/core/lib/actionRegistry/actions/entityCreateOracle.ts
2977
+ registerAction({
2978
+ type: "qi/entity.createOracle",
2979
+ can: "entity/createOracle",
2980
+ sideEffect: true,
2981
+ defaultRequiresConfirmation: false,
2982
+ outputSchema: [
2983
+ { path: "entityDid", displayName: "Entity DID", type: "string", description: "The oracle entity DID" },
2984
+ { 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" }
2987
+ ],
2988
+ run: async (rawInputs, ctx) => {
2989
+ const form = (() => {
2990
+ const raw = rawInputs.formAnswers;
2991
+ if (typeof raw !== "string" || !raw) return {};
2992
+ try {
2993
+ const parsed = JSON.parse(raw);
2994
+ return parsed && typeof parsed === "object" ? parsed : {};
2995
+ } catch {
2996
+ return {};
2997
+ }
2998
+ })();
2999
+ const inputs = { ...form, ...rawInputs };
3000
+ if (!ctx.services.oracle?.createOracleEntity) {
3001
+ throw new Error("oracle.createOracleEntity handler not available");
3002
+ }
3003
+ if (!inputs.mnemonic) throw new Error("mnemonic is required");
3004
+ if (!inputs.address) throw new Error("address is required");
3005
+ if (!inputs.did) throw new Error("did is required");
3006
+ if (!inputs.pubKey) throw new Error("pubKey is required");
3007
+ if (!inputs.pin) throw new Error("pin is required");
3008
+ if (!inputs.matrixAccessToken) throw new Error("matrixAccessToken is required");
3009
+ if (!inputs.matrixRoomId) throw new Error("matrixRoomId is required");
3010
+ if (!inputs.oracleName) throw new Error("oracleName is required");
3011
+ if (!inputs.apiUrl) throw new Error("apiUrl is required");
3012
+ if (!inputs.price) throw new Error("price is required");
3013
+ const result = await ctx.services.oracle.createOracleEntity({
3014
+ mnemonic: inputs.mnemonic,
3015
+ address: inputs.address,
3016
+ did: inputs.did,
3017
+ pubKey: inputs.pubKey,
3018
+ pin: inputs.pin,
3019
+ matrixAccessToken: inputs.matrixAccessToken,
3020
+ matrixRoomId: inputs.matrixRoomId,
3021
+ oracleName: inputs.oracleName,
3022
+ orgName: inputs.orgName,
3023
+ description: inputs.description,
3024
+ location: inputs.location,
3025
+ logoUrl: inputs.logoUrl,
3026
+ coverImageUrl: inputs.coverImageUrl,
3027
+ apiUrl: inputs.apiUrl,
3028
+ price: inputs.price,
3029
+ llmModel: inputs.llmModel,
3030
+ opening: inputs.opening,
3031
+ communicationStyle: inputs.communicationStyle,
3032
+ capabilities: inputs.capabilities,
3033
+ mcpConfig: inputs.mcpConfig,
3034
+ parentProtocol: inputs.parentProtocol
3035
+ });
3036
+ return { output: result };
3037
+ }
3038
+ });
3039
+
3040
+ // src/core/lib/actionRegistry/actions/sandboxProvision.ts
3041
+ registerAction({
3042
+ type: "qi/sandbox.provision",
3043
+ can: "sandbox/provision",
3044
+ sideEffect: true,
3045
+ defaultRequiresConfirmation: false,
3046
+ outputSchema: [
3047
+ { path: "sandboxUrl", displayName: "Sandbox URL", type: "string", description: "The provisioned sandbox URL" },
3048
+ { path: "status", displayName: "Status", type: "string", description: "Provisioning status" }
3049
+ ],
3050
+ run: async (inputs, ctx) => {
3051
+ if (!ctx.services.oracle?.provisionSandbox) {
3052
+ throw new Error("oracle.provisionSandbox handler not available");
3053
+ }
3054
+ if (!inputs.entityDid) throw new Error("entityDid is required");
3055
+ if (!inputs.matrixRoomId) throw new Error("matrixRoomId is required");
3056
+ const result = await ctx.services.oracle.provisionSandbox({
3057
+ entityDid: inputs.entityDid,
3058
+ matrixRoomId: inputs.matrixRoomId
3059
+ });
3060
+ return { output: result };
3061
+ }
3062
+ });
3063
+
3064
+ // src/core/lib/actionRegistry/actions/oracleContract.ts
3065
+ registerAction({
3066
+ type: "qi/oracle.contract",
3067
+ can: "oracle/contract",
3068
+ sideEffect: true,
3069
+ defaultRequiresConfirmation: false,
3070
+ outputSchema: [
3071
+ { path: "userOracleRoomId", displayName: "User\u2194Oracle Room ID", type: "string", description: "Matrix room id of the user\u2194oracle DM room" },
3072
+ { path: "userOracleRoomAlias", displayName: "User\u2194Oracle Room Alias", type: "string", description: "Matrix alias of the user\u2194oracle DM room" }
3073
+ ],
3074
+ run: async (inputs, ctx) => {
3075
+ if (!ctx.services.oracle?.contract) {
3076
+ throw new Error("oracle.contract handler not available");
3077
+ }
3078
+ if (!inputs.oracleEntityDid) throw new Error("oracleEntityDid is required (from entity.createOracle output)");
3079
+ const result = await ctx.services.oracle.contract({
3080
+ oracleEntityDid: inputs.oracleEntityDid
3081
+ });
3082
+ return { output: result };
3083
+ }
3084
+ });
3085
+
3086
+ // 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
+ ];
3093
+ registerAction({
3094
+ type: "qi/oracle.storeSecrets",
3095
+ can: "oracle/storeSecrets",
3096
+ sideEffect: true,
3097
+ defaultRequiresConfirmation: false,
3098
+ outputSchema: [
3099
+ { path: "storedSecrets", displayName: "Stored Secrets", type: "array", description: "List of secret names stored" },
3100
+ { path: "roomId", displayName: "Room ID", type: "string", description: "Matrix room where secrets are stored" }
3101
+ ],
3102
+ run: async (inputs, ctx) => {
3103
+ if (!ctx.services.oracle?.storeSecrets) {
3104
+ throw new Error("oracle.storeSecrets handler not available");
3105
+ }
3106
+ if (!inputs.matrixRoomId) throw new Error("matrixRoomId is required (from oracle.contract output)");
3107
+ if (!inputs.publicKeyMultibase) throw new Error("publicKeyMultibase is required (from entity.createOracle output)");
3108
+ if (!inputs.verificationMethodId) throw new Error("verificationMethodId is required (from entity.createOracle output)");
3109
+ if (!inputs.matrixHomeServerUrl) throw new Error("matrixHomeServerUrl is required (from matrix.register output)");
3110
+ if (!inputs.matrixUsername) throw new Error("matrixUsername is required (from matrix.register output matrixUserId)");
3111
+ if (!inputs.matrixPassword) throw new Error("matrixPassword is required (from matrix.register output)");
3112
+ if (!inputs.mnemonic) throw new Error("mnemonic is required (from wallet.generate output)");
3113
+ if (!inputs.matrixRecoveryPhrase) throw new Error("matrixRecoveryPhrase is required (from matrix.register output)");
3114
+ if (!inputs.pin) throw new Error("pin is required (from form output)");
3115
+ if (!inputs.openRouterApiKeyJwe) {
3116
+ throw new Error("OPEN_ROUTER_API_KEY is required \u2014 enter it in the form before submitting");
3117
+ }
3118
+ const secrets = {
3119
+ SECP_MNEMONIC: inputs.mnemonic,
3120
+ MATRIX_ORACLE_ADMIN_PASSWORD: inputs.matrixPassword,
3121
+ MATRIX_RECOVERY_PHRASE: inputs.matrixRecoveryPhrase,
3122
+ MATRIX_VALUE_PIN: inputs.pin
3123
+ };
3124
+ const preEncryptedSecrets = {
3125
+ OPEN_ROUTER_API_KEY: inputs.openRouterApiKeyJwe
3126
+ };
3127
+ if (inputs.mcpAuthSecrets) {
3128
+ try {
3129
+ const mcpSecrets = typeof inputs.mcpAuthSecrets === "string" ? JSON.parse(inputs.mcpAuthSecrets) : inputs.mcpAuthSecrets;
3130
+ if (mcpSecrets && typeof mcpSecrets === "object") {
3131
+ Object.assign(preEncryptedSecrets, mcpSecrets);
3132
+ }
3133
+ } catch {
3134
+ console.warn("[oracle.storeSecrets] Failed to parse mcpAuthSecrets");
3135
+ }
3136
+ }
3137
+ for (const k of SENSITIVE_PLAINTEXT_KEYS) {
3138
+ if (!secrets[k]) throw new Error(`${k} is empty after assembly \u2014 refusing to call storeSecrets`);
3139
+ }
3140
+ const result = await ctx.services.oracle.storeSecrets({
3141
+ matrixRoomId: inputs.matrixRoomId,
3142
+ publicKeyMultibase: inputs.publicKeyMultibase,
3143
+ verificationMethodId: inputs.verificationMethodId,
3144
+ matrixHomeServerUrl: inputs.matrixHomeServerUrl,
3145
+ matrixUsername: inputs.matrixUsername,
3146
+ matrixPassword: inputs.matrixPassword,
3147
+ secrets,
3148
+ preEncryptedSecrets
3149
+ });
3150
+ return { output: result };
3151
+ }
3152
+ });
3153
+
3154
+ // src/core/lib/actionRegistry/actions/oracleStoreConfig.ts
3155
+ registerAction({
3156
+ type: "qi/oracle.storeConfig",
3157
+ can: "oracle/storeConfig",
3158
+ sideEffect: true,
3159
+ defaultRequiresConfirmation: false,
3160
+ outputSchema: [
3161
+ { path: "configStored", displayName: "Config Stored", type: "boolean", description: "Whether the oracle config was stored successfully" },
3162
+ { path: "roomId", displayName: "Room ID", type: "string", description: "The Matrix room ID where config was stored" }
3163
+ ],
3164
+ run: async (inputs, ctx) => {
3165
+ if (!ctx.services.oracle?.storeConfig) {
3166
+ throw new Error("oracle.storeConfig handler not available");
3167
+ }
3168
+ if (!inputs.matrixRoomId) throw new Error("matrixRoomId is required (from oracle.contract output)");
3169
+ if (!inputs.oracleName) throw new Error("oracleName is required");
3170
+ if (!inputs.entityDid) throw new Error("entityDid is required");
3171
+ const result = await ctx.services.oracle.storeConfig({
3172
+ matrixRoomId: inputs.matrixRoomId,
3173
+ config: {
3174
+ oracleName: inputs.oracleName,
3175
+ orgName: inputs.orgName || "",
3176
+ description: inputs.description || "",
3177
+ location: inputs.location || "",
3178
+ price: inputs.price ?? 0,
3179
+ apiUrl: inputs.apiUrl || "",
3180
+ entityDid: inputs.entityDid,
3181
+ logoUrl: inputs.logoUrl || "",
3182
+ llmModel: inputs.llmModel || "",
3183
+ opening: inputs.opening,
3184
+ communicationStyle: inputs.communicationStyle,
3185
+ capabilities: inputs.capabilities,
3186
+ skills: inputs.skills,
3187
+ mcpServers: inputs.mcpServers,
3188
+ matrixUserId: inputs.matrixUserId || "",
3189
+ matrixAccountRoomId: inputs.matrixAccountRoomId || "",
3190
+ oracleAddress: inputs.oracleAddress || "",
3191
+ oracleDid: inputs.oracleDid || ""
3192
+ }
3193
+ });
3194
+ return { output: result };
3195
+ }
3196
+ });
3197
+
3198
+ // 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
+ ];
3205
+ registerAction({
3206
+ type: "qi/oracle.storeSecretsAndConfig",
3207
+ can: "oracle/storeSecretsAndConfig",
3208
+ sideEffect: true,
3209
+ defaultRequiresConfirmation: false,
3210
+ outputSchema: [
3211
+ { path: "storedSecrets", displayName: "Stored Secrets", type: "array", description: "List of secret names stored" },
3212
+ { path: "roomId", displayName: "Room ID", type: "string", description: "Matrix room where data is stored" },
3213
+ { path: "configStored", displayName: "Config Stored", type: "boolean", description: "Whether oracle config was stored" }
3214
+ ],
3215
+ run: async (inputs, ctx) => {
3216
+ if (!ctx.services.oracle?.storeSecrets) throw new Error("oracle.storeSecrets handler not available");
3217
+ if (!ctx.services.oracle?.storeConfig) throw new Error("oracle.storeConfig handler not available");
3218
+ if (!inputs.matrixRoomId) throw new Error("matrixRoomId is required (from oracle.contract output)");
3219
+ if (!inputs.publicKeyMultibase) throw new Error("publicKeyMultibase is required (from entity.createOracle output)");
3220
+ if (!inputs.verificationMethodId) throw new Error("verificationMethodId is required (from entity.createOracle output)");
3221
+ if (!inputs.matrixHomeServerUrl) throw new Error("matrixHomeServerUrl is required (from matrix.register output)");
3222
+ if (!inputs.matrixUsername) throw new Error("matrixUsername is required (from matrix.register output matrixUserId)");
3223
+ if (!inputs.matrixPassword) throw new Error("matrixPassword is required (from matrix.register output)");
3224
+ if (!inputs.mnemonic) throw new Error("mnemonic is required (from wallet.generate output)");
3225
+ if (!inputs.matrixRecoveryPhrase) throw new Error("matrixRecoveryPhrase is required (from matrix.register output)");
3226
+ if (!inputs.pin) throw new Error("pin is required (from form output)");
3227
+ if (!inputs.openRouterApiKeyJwe) {
3228
+ throw new Error("OPEN_ROUTER_API_KEY is required \u2014 enter it in the form before submitting");
3229
+ }
3230
+ const secrets = {
3231
+ SECP_MNEMONIC: inputs.mnemonic,
3232
+ MATRIX_ORACLE_ADMIN_PASSWORD: inputs.matrixPassword,
3233
+ MATRIX_RECOVERY_PHRASE: inputs.matrixRecoveryPhrase,
3234
+ MATRIX_VALUE_PIN: inputs.pin
3235
+ };
3236
+ const preEncryptedSecrets = {
3237
+ OPEN_ROUTER_API_KEY: inputs.openRouterApiKeyJwe
3238
+ };
3239
+ if (inputs.mcpAuthSecrets) {
3240
+ try {
3241
+ const mcpSecrets = typeof inputs.mcpAuthSecrets === "string" ? JSON.parse(inputs.mcpAuthSecrets) : inputs.mcpAuthSecrets;
3242
+ if (mcpSecrets && typeof mcpSecrets === "object") {
3243
+ Object.assign(preEncryptedSecrets, mcpSecrets);
3244
+ }
3245
+ } catch {
3246
+ console.warn("[oracle.storeSecretsAndConfig] Failed to parse mcpAuthSecrets");
3247
+ }
3248
+ }
3249
+ for (const k of SENSITIVE_PLAINTEXT_KEYS2) {
3250
+ if (!secrets[k]) throw new Error(`${k} is empty after assembly \u2014 refusing to call storeSecrets`);
3251
+ }
3252
+ console.log("[oracle.storeSecretsAndConfig] Phase 1: storing secrets");
3253
+ const secretsResult = await ctx.services.oracle.storeSecrets({
3254
+ matrixRoomId: inputs.matrixRoomId,
3255
+ publicKeyMultibase: inputs.publicKeyMultibase,
3256
+ verificationMethodId: inputs.verificationMethodId,
3257
+ matrixHomeServerUrl: inputs.matrixHomeServerUrl,
3258
+ matrixUsername: inputs.matrixUsername,
3259
+ matrixPassword: inputs.matrixPassword,
3260
+ secrets,
3261
+ preEncryptedSecrets
3262
+ });
3263
+ if (!secretsResult.storedSecrets || secretsResult.storedSecrets.length === 0) {
3264
+ throw new Error("storeSecrets returned success without any storedSecrets");
3265
+ }
3266
+ if (!inputs.oracleName) throw new Error("oracleName is required");
3267
+ if (!inputs.entityDid) throw new Error("entityDid is required");
3268
+ console.log("[oracle.storeSecretsAndConfig] Phase 2: storing config");
3269
+ const configResult = await ctx.services.oracle.storeConfig({
3270
+ matrixRoomId: inputs.matrixRoomId,
3271
+ config: {
3272
+ oracleName: inputs.oracleName,
3273
+ orgName: inputs.orgName || "",
3274
+ description: inputs.description || "",
3275
+ location: inputs.location || "",
3276
+ price: inputs.price ?? 0,
3277
+ apiUrl: inputs.apiUrl || "",
3278
+ entityDid: inputs.entityDid,
3279
+ logoUrl: inputs.logoUrl || "",
3280
+ llmModel: inputs.llmModel || "",
3281
+ opening: inputs.opening,
3282
+ communicationStyle: inputs.communicationStyle,
3283
+ capabilities: inputs.capabilities,
3284
+ skills: inputs.skills,
3285
+ mcpServers: inputs.mcpServers,
3286
+ matrixUserId: inputs.matrixUserId || "",
3287
+ matrixAccountRoomId: inputs.matrixAccountRoomId || "",
3288
+ oracleAddress: inputs.oracleAddress || "",
3289
+ oracleDid: inputs.oracleDid || ""
3290
+ }
3291
+ });
3292
+ if (!configResult.configStored) {
3293
+ throw new Error("storeConfig returned success without configStored=true");
3294
+ }
3295
+ console.log("[oracle.storeSecretsAndConfig] Both phases complete");
3296
+ return {
3297
+ output: {
3298
+ storedSecrets: secretsResult.storedSecrets,
3299
+ roomId: secretsResult.roomId,
3300
+ configStored: configResult.configStored
3301
+ }
3302
+ };
3303
+ }
3304
+ });
3305
+
3306
+ // 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
+ ];
3313
+ registerAction({
3314
+ type: "qi/oracle.configureOracle",
3315
+ can: "oracle/configureOracle",
3316
+ sideEffect: true,
3317
+ defaultRequiresConfirmation: false,
3318
+ outputSchema: [
3319
+ { path: "userOracleRoomId", displayName: "User-Oracle Room ID", type: "string", description: "Matrix room id from contract phase" },
3320
+ { path: "userOracleRoomAlias", displayName: "User-Oracle Room Alias", type: "string", description: "Matrix alias from contract phase" },
3321
+ { path: "storedSecrets", displayName: "Stored Secrets", type: "array", description: "List of secret names stored" },
3322
+ { path: "roomId", displayName: "Room ID", type: "string", description: "Matrix room where data is stored" },
3323
+ { path: "configStored", displayName: "Config Stored", type: "boolean", description: "Whether oracle config was stored" },
3324
+ { path: "freshAccessToken", displayName: "Fresh Access Token", type: "string", description: "Fresh Matrix access token from mxLogin during secret storage" },
3325
+ { path: "openRouterApiKeyPlaintext", displayName: "OpenRouter API Key", type: "string", description: "Plaintext OpenRouter API key for deploy" }
3326
+ ],
3327
+ run: async (rawInputs, ctx) => {
3328
+ const form = (() => {
3329
+ const raw = rawInputs.formAnswers;
3330
+ if (typeof raw !== "string" || !raw) return {};
3331
+ try {
3332
+ const parsed = JSON.parse(raw);
3333
+ return parsed && typeof parsed === "object" ? parsed : {};
3334
+ } catch {
3335
+ return {};
3336
+ }
3337
+ })();
3338
+ const inputs = { ...form, ...rawInputs };
3339
+ if (!ctx.services.oracle?.contract) throw new Error("oracle.contract handler not available");
3340
+ if (!ctx.services.oracle?.storeSecrets) throw new Error("oracle.storeSecrets handler not available");
3341
+ if (!ctx.services.oracle?.storeConfig) throw new Error("oracle.storeConfig handler not available");
3342
+ if (!inputs.oracleEntityDid) throw new Error("oracleEntityDid is required (from entity.createOracle output)");
3343
+ if (!inputs.publicKeyMultibase) throw new Error("publicKeyMultibase is required (from entity.createOracle output)");
3344
+ if (!inputs.verificationMethodId) throw new Error("verificationMethodId is required (from entity.createOracle output)");
3345
+ if (!inputs.matrixHomeServerUrl) throw new Error("matrixHomeServerUrl is required (from matrix.register output)");
3346
+ if (!inputs.matrixUsername) throw new Error("matrixUsername is required (from matrix.register output matrixUserId)");
3347
+ if (!inputs.matrixPassword) throw new Error("matrixPassword is required (from matrix.register output)");
3348
+ if (!inputs.mnemonic) throw new Error("mnemonic is required (from wallet.generate output)");
3349
+ if (!inputs.matrixRecoveryPhrase) throw new Error("matrixRecoveryPhrase is required (from matrix.register output)");
3350
+ if (!inputs.pin) throw new Error("pin is required (from form output)");
3351
+ if (!inputs.openRouterApiKeyJwe) {
3352
+ throw new Error("OPEN_ROUTER_API_KEY is required \u2014 enter it in the form before submitting");
3353
+ }
3354
+ if (!inputs.oracleName) throw new Error("oracleName is required");
3355
+ if (!inputs.entityDid) throw new Error("entityDid is required");
3356
+ console.log("[oracle.configureOracle] Phase 1: contracting oracle");
3357
+ const contractResult = await ctx.services.oracle.contract({
3358
+ oracleEntityDid: inputs.oracleEntityDid
3359
+ });
3360
+ if (!contractResult.userOracleRoomId) {
3361
+ throw new Error("contract returned success without userOracleRoomId");
3362
+ }
3363
+ const matrixRoomId = contractResult.userOracleRoomId;
3364
+ const secrets = {
3365
+ SECP_MNEMONIC: inputs.mnemonic,
3366
+ MATRIX_ORACLE_ADMIN_PASSWORD: inputs.matrixPassword,
3367
+ MATRIX_RECOVERY_PHRASE: inputs.matrixRecoveryPhrase,
3368
+ MATRIX_VALUE_PIN: inputs.pin
3369
+ };
3370
+ const preEncryptedSecrets = {
3371
+ OPEN_ROUTER_API_KEY: inputs.openRouterApiKeyJwe
3372
+ };
3373
+ if (inputs.mcpAuthSecrets) {
3374
+ try {
3375
+ const mcpSecrets = typeof inputs.mcpAuthSecrets === "string" ? JSON.parse(inputs.mcpAuthSecrets) : inputs.mcpAuthSecrets;
3376
+ if (mcpSecrets && typeof mcpSecrets === "object") {
3377
+ Object.assign(preEncryptedSecrets, mcpSecrets);
3378
+ }
3379
+ } catch {
3380
+ console.warn("[oracle.configureOracle] Failed to parse mcpAuthSecrets");
3381
+ }
3382
+ }
3383
+ for (const k of SENSITIVE_PLAINTEXT_KEYS3) {
3384
+ if (!secrets[k]) throw new Error(`${k} is empty after assembly \u2014 refusing to call storeSecrets`);
3385
+ }
3386
+ console.log("[oracle.configureOracle] Phase 2: storing secrets");
3387
+ const secretsResult = await ctx.services.oracle.storeSecrets({
3388
+ matrixRoomId,
3389
+ publicKeyMultibase: inputs.publicKeyMultibase,
3390
+ verificationMethodId: inputs.verificationMethodId,
3391
+ matrixHomeServerUrl: inputs.matrixHomeServerUrl,
3392
+ matrixUsername: inputs.matrixUsername,
3393
+ matrixPassword: inputs.matrixPassword,
3394
+ secrets,
3395
+ preEncryptedSecrets
3396
+ });
3397
+ if (!secretsResult.storedSecrets || secretsResult.storedSecrets.length === 0) {
3398
+ throw new Error("storeSecrets returned success without any storedSecrets");
3399
+ }
3400
+ console.log("[oracle.configureOracle] Phase 3: storing config");
3401
+ const configResult = await ctx.services.oracle.storeConfig({
3402
+ matrixRoomId,
3403
+ config: {
3404
+ oracleName: inputs.oracleName,
3405
+ orgName: inputs.orgName || "",
3406
+ description: inputs.description || "",
3407
+ location: inputs.location || "",
3408
+ price: inputs.price ?? 0,
3409
+ apiUrl: inputs.apiUrl || "",
3410
+ entityDid: inputs.entityDid,
3411
+ logoUrl: inputs.logoUrl || "",
3412
+ llmModel: inputs.llmModel || "",
3413
+ opening: inputs.opening,
3414
+ communicationStyle: inputs.communicationStyle,
3415
+ capabilities: inputs.capabilities,
3416
+ skills: inputs.skills,
3417
+ mcpServers: inputs.mcpServers,
3418
+ matrixUserId: inputs.matrixUserId || "",
3419
+ matrixAccountRoomId: inputs.matrixAccountRoomId || "",
3420
+ oracleAddress: inputs.oracleAddress || "",
3421
+ oracleDid: inputs.oracleDid || ""
3422
+ }
3423
+ });
3424
+ if (!configResult.configStored) {
3425
+ throw new Error("storeConfig returned success without configStored=true");
3426
+ }
3427
+ console.log("[oracle.configureOracle] All 3 phases complete");
3428
+ return {
3429
+ output: {
3430
+ userOracleRoomId: contractResult.userOracleRoomId,
3431
+ userOracleRoomAlias: contractResult.userOracleRoomAlias,
3432
+ storedSecrets: secretsResult.storedSecrets,
3433
+ roomId: secretsResult.roomId,
3434
+ configStored: configResult.configStored,
3435
+ freshAccessToken: secretsResult.freshAccessToken || "",
3436
+ openRouterApiKeyPlaintext: inputs.openRouterApiKeyPlaintext || ""
3437
+ }
3438
+ };
3439
+ }
3440
+ });
3441
+
3442
+ // src/core/lib/actionRegistry/actions/oracleDeploySetup.ts
3443
+ registerAction({
3444
+ type: "qi/oracle.deploySetup",
3445
+ can: "oracle/deploySetup",
3446
+ sideEffect: true,
3447
+ defaultRequiresConfirmation: false,
3448
+ outputSchema: [
3449
+ { path: "setupComplete", displayName: "Setup Complete", type: "boolean", description: "Whether the oracle setup completed successfully" },
3450
+ { path: "stdout", displayName: "Build Output", type: "string", description: "Build stdout output" }
3451
+ ],
3452
+ run: async (inputs, ctx) => {
3453
+ if (!ctx.services.oracle?.deploySetup) {
3454
+ throw new Error("oracle.deploySetup handler not available");
3455
+ }
3456
+ if (!inputs.name) throw new Error("name is required (project name from form)");
3457
+ const config = typeof inputs.config === "string" ? JSON.parse(inputs.config) : inputs.config;
3458
+ if (!config || typeof config !== "object") throw new Error("config is required (oracle config object)");
3459
+ if (!inputs.roomId) throw new Error("roomId is required (from oracle contract/configure output)");
3460
+ const result = await ctx.services.oracle.deploySetup({ name: inputs.name, config, roomId: inputs.roomId });
3461
+ return { output: result };
3462
+ }
3463
+ });
3464
+
3465
+ // src/core/lib/actionRegistry/actions/oracleDeployStart.ts
3466
+ registerAction({
3467
+ type: "qi/oracle.deployStart",
3468
+ can: "oracle/deployStart",
3469
+ sideEffect: true,
3470
+ defaultRequiresConfirmation: false,
3471
+ outputSchema: [
3472
+ { path: "processId", displayName: "Process ID", type: "string", description: "Running process identifier" },
3473
+ { path: "status", displayName: "Status", type: "string", description: "Oracle process status" }
3474
+ ],
3475
+ run: async (inputs, ctx) => {
3476
+ if (!ctx.services.oracle?.deployStart) {
3477
+ throw new Error("oracle.deployStart handler not available");
3478
+ }
3479
+ if (!inputs.name) throw new Error("name is required (project name from form)");
3480
+ if (!inputs.entityDid) throw new Error("entityDid is required (from entity.createOracle output)");
3481
+ if (!inputs.roomId) throw new Error("roomId is required (from oracle contract/configure output)");
3482
+ const result = await ctx.services.oracle.deployStart({ name: inputs.name, entityDid: inputs.entityDid, roomId: inputs.roomId });
3483
+ return { output: result };
3484
+ }
3485
+ });
3486
+
3487
+ // src/core/lib/actionRegistry/actions/oracleDeploy.ts
3488
+ registerAction({
3489
+ type: "qi/oracle.deploy",
3490
+ can: "oracle/deploy",
3491
+ sideEffect: true,
3492
+ defaultRequiresConfirmation: false,
3493
+ outputSchema: [
3494
+ { path: "setupComplete", displayName: "Setup Complete", type: "boolean", description: "Whether the oracle setup completed successfully" },
3495
+ { path: "processId", displayName: "Process ID", type: "string", description: "Running process identifier" },
3496
+ { path: "status", displayName: "Status", type: "string", description: "Oracle process status" },
3497
+ { path: "deploymentUrl", displayName: "Deployment URL", type: "string", description: "The URL where the oracle was deployed" },
3498
+ { path: "domainUpdateTxHash", displayName: "Domain Update Tx", type: "string", description: "Transaction hash of the on-chain domain update" }
3499
+ ],
3500
+ run: async (rawInputs, ctx) => {
3501
+ const form = (() => {
3502
+ const raw = rawInputs.formAnswers;
3503
+ if (typeof raw !== "string" || !raw) return {};
3504
+ try {
3505
+ const parsed = JSON.parse(raw);
3506
+ return parsed && typeof parsed === "object" ? parsed : {};
3507
+ } catch {
3508
+ return {};
3509
+ }
3510
+ })();
3511
+ const inputs = {
3512
+ // Rename: form.projectName → name for deploy handler
3513
+ name: form.projectName,
3514
+ ...form,
3515
+ ...rawInputs
3516
+ };
3517
+ if (!ctx.services.oracle?.deploySetup) {
3518
+ throw new Error("oracle.deploySetup handler not available");
3519
+ }
3520
+ if (!ctx.services.oracle?.deployStart) {
3521
+ throw new Error("oracle.deployStart handler not available");
3522
+ }
3523
+ if (!inputs.name) throw new Error("name is required (project name from form)");
3524
+ if (!inputs.entityDid) throw new Error("entityDid is required (from entity.createOracle output)");
3525
+ if (!inputs.roomId) throw new Error("roomId is required (from oracle.configureOracle output)");
3526
+ const config = {
3527
+ oracleName: inputs.oracleName,
3528
+ orgName: inputs.orgName,
3529
+ description: inputs.description,
3530
+ location: inputs.location,
3531
+ price: inputs.price,
3532
+ apiUrl: inputs.apiUrl,
3533
+ logoUrl: inputs.logoUrl,
3534
+ llmModel: inputs.llmModel,
3535
+ opening: inputs.opening,
3536
+ communicationStyle: inputs.communicationStyle,
3537
+ capabilities: inputs.capabilities,
3538
+ skills: inputs.skills,
3539
+ mcpServers: inputs.mcpServers,
3540
+ entityDid: inputs.entityDid
3541
+ };
3542
+ const secrets = {
3543
+ mnemonic: inputs.mnemonic,
3544
+ matrixPassword: inputs.matrixPassword,
3545
+ matrixRecoveryPhrase: inputs.matrixRecoveryPhrase,
3546
+ pin: inputs.pin,
3547
+ matrixUsername: inputs.matrixUsername,
3548
+ matrixAccountRoomId: inputs.matrixAccountRoomId,
3549
+ freshAccessToken: inputs.freshAccessToken,
3550
+ openRouterApiKey: inputs.openRouterApiKey
3551
+ };
3552
+ const setupResult = await ctx.services.oracle.deploySetup({ name: inputs.name, config, roomId: inputs.roomId, secrets });
3553
+ if (!setupResult.setupComplete) {
3554
+ throw new Error(`Deploy setup failed: ${setupResult.stderr || "setupComplete was not true"}`);
3555
+ }
3556
+ const startResult = await ctx.services.oracle.deployStart({ name: inputs.name, entityDid: inputs.entityDid, roomId: inputs.roomId, secrets });
3557
+ if (!startResult.processId && startResult.status !== "running") {
3558
+ throw new Error("Deploy start failed: no processId returned and status is not running");
3559
+ }
3560
+ let deploymentUrl;
3561
+ let domainUpdateTxHash;
3562
+ if (startResult.url) {
3563
+ if (!ctx.services.oracle?.updateOracleDomain) {
3564
+ console.warn("[oracle.deploy] updateOracleDomain handler not available, skipping domain update");
3565
+ } else {
3566
+ const domainResult = await ctx.services.oracle.updateOracleDomain({
3567
+ entityDid: inputs.entityDid,
3568
+ newApiUrl: startResult.url
3569
+ });
3570
+ deploymentUrl = startResult.url;
3571
+ domainUpdateTxHash = domainResult.transactionHash;
3572
+ }
3573
+ }
3574
+ return {
3575
+ output: {
3576
+ setupComplete: setupResult.setupComplete,
3577
+ processId: startResult.processId,
3578
+ status: startResult.status,
3579
+ deploymentUrl,
3580
+ domainUpdateTxHash
3581
+ }
3582
+ };
3583
+ }
3584
+ });
3585
+
2611
3586
  // src/core/lib/actionRegistry/actions/bid/bid.diff.ts
2612
3587
  registerDiffResolver("bid", {
2613
3588
  resolver: async (inputs, _ctx) => {
@@ -2890,6 +3865,40 @@ registerDiffResolver("evaluateClaim", {
2890
3865
  }
2891
3866
  });
2892
3867
 
3868
+ // src/core/lib/actionRegistry/actions/walletFund.diff.ts
3869
+ registerDiffResolver("qi/wallet.fund", {
3870
+ resolver: async (inputs, _ctx) => {
3871
+ const address = String(inputs.address || "").trim();
3872
+ const amount = String(inputs.amount || "250000").trim();
3873
+ const network = String(inputs.network || "devnet").trim();
3874
+ const ixoAmount = (Number(amount) / 1e6).toFixed(6);
3875
+ return [
3876
+ {
3877
+ key: "recipient",
3878
+ label: "Recipient",
3879
+ before: "N/A",
3880
+ after: address || "Pending wallet generation",
3881
+ changeType: address ? "replace" : "unchanged"
3882
+ },
3883
+ {
3884
+ key: "amount",
3885
+ label: "Amount",
3886
+ before: "0 IXO",
3887
+ after: `${ixoAmount} IXO (${amount} uixo)`,
3888
+ changeType: "replace",
3889
+ severity: "info"
3890
+ },
3891
+ {
3892
+ key: "network",
3893
+ label: "Network",
3894
+ before: network,
3895
+ after: network,
3896
+ changeType: "unchanged"
3897
+ }
3898
+ ];
3899
+ }
3900
+ });
3901
+
2893
3902
  // src/core/services/ucanService.ts
2894
3903
  import {
2895
3904
  createDelegation as ucanCreateDelegation,
@@ -3920,6 +4929,65 @@ function removePendingInvocation(yDoc, listenerBlockId, pendingInvocationId) {
3920
4929
  inner.delete(pendingInvocationId);
3921
4930
  return true;
3922
4931
  }
4932
+ var BARRIER_STATE_MAP_KEY = "barrierState";
4933
+ function getBarrierStateMap(yDoc) {
4934
+ return yDoc.getMap(BARRIER_STATE_MAP_KEY);
4935
+ }
4936
+ function getOrCreateListenerBarrierMap(yDoc, listenerBlockId) {
4937
+ const outer = getBarrierStateMap(yDoc);
4938
+ let inner = outer.get(listenerBlockId);
4939
+ if (!inner) {
4940
+ inner = new Y.Map();
4941
+ outer.set(listenerBlockId, inner);
4942
+ }
4943
+ return inner;
4944
+ }
4945
+ function recordBarrierEvent(yDoc, listenerBlockId, entry) {
4946
+ let written = false;
4947
+ yDoc.transact(() => {
4948
+ const inner = getOrCreateListenerBarrierMap(yDoc, listenerBlockId);
4949
+ const key = `${entry.sourceBlockId}::${entry.eventName}`;
4950
+ const existing = inner.get(key);
4951
+ if (existing && typeof existing === "object" && existing.runId === entry.runId) return;
4952
+ inner.set(key, entry);
4953
+ written = true;
4954
+ });
4955
+ return written;
4956
+ }
4957
+ function readBarrierState(yDoc, listenerBlockId) {
4958
+ const outer = getBarrierStateMap(yDoc);
4959
+ const inner = outer.get(listenerBlockId);
4960
+ if (!inner) return [];
4961
+ const entries = [];
4962
+ inner.forEach((value) => {
4963
+ if (value && typeof value === "object") {
4964
+ entries.push(value);
4965
+ }
4966
+ });
4967
+ return entries;
4968
+ }
4969
+ function clearBarrierState(yDoc, listenerBlockId) {
4970
+ const outer = getBarrierStateMap(yDoc);
4971
+ yDoc.transact(() => {
4972
+ outer.delete(listenerBlockId);
4973
+ });
4974
+ }
4975
+ function computeBarrierInvocationId(entries, listenerBlockId) {
4976
+ const sorted = [...entries].sort((a, b) => a.sourceBlockId.localeCompare(b.sourceBlockId));
4977
+ const input = sorted.map((e) => `${e.sourceBlockId}:${e.runId}:${e.eventName}`).join("|") + `|>${listenerBlockId}`;
4978
+ return `bi-${fnv1a32(input)}`;
4979
+ }
4980
+ function mergeBarrierPayloads(entries) {
4981
+ const merged = {};
4982
+ for (const entry of entries) {
4983
+ if (entry.alias) {
4984
+ merged[entry.alias] = entry.payload;
4985
+ } else {
4986
+ Object.assign(merged, entry.payload);
4987
+ }
4988
+ }
4989
+ return merged;
4990
+ }
3923
4991
  function appendRunRecord(yDoc, blockId, details, userId) {
3924
4992
  const auditMap = yDoc.getMap("auditTrail");
3925
4993
  yDoc.transact(() => {
@@ -3992,21 +5060,41 @@ function replayFailedListenerRun(yDoc, failedRecord, listenerBlockId, originalPa
3992
5060
  }
3993
5061
 
3994
5062
  // src/core/lib/flowEngine/reconcile.ts
5063
+ var _reconcileRunning = false;
3995
5064
  function reconcilePendingInvocations(editor) {
5065
+ if (_reconcileRunning) return;
5066
+ _reconcileRunning = true;
5067
+ try {
5068
+ _reconcilePendingInvocationsInner(editor);
5069
+ } finally {
5070
+ _reconcileRunning = false;
5071
+ }
5072
+ }
5073
+ function _reconcilePendingInvocationsInner(editor) {
3996
5074
  const yDoc = editor._yDoc;
3997
5075
  if (!yDoc) return;
3998
5076
  const blocks = editor.document || [];
3999
5077
  if (blocks.length === 0) return;
4000
5078
  const listenersBySource = /* @__PURE__ */ new Map();
5079
+ const barrierListenersBySource = /* @__PURE__ */ new Map();
4001
5080
  for (const block of blocks) {
4002
5081
  const trigger = parseTrigger(block);
4003
- if (!trigger || trigger.type !== "block.event") continue;
4004
- if (!trigger.sourceBlockId || !trigger.eventName) continue;
4005
- const key = `${trigger.sourceBlockId}::${trigger.eventName}`;
4006
- if (!listenersBySource.has(key)) listenersBySource.set(key, []);
4007
- listenersBySource.get(key).push({ block, trigger });
5082
+ if (!trigger) continue;
5083
+ if (trigger.type === "block.event") {
5084
+ if (!trigger.sourceBlockId || !trigger.eventName) continue;
5085
+ const key = `${trigger.sourceBlockId}::${trigger.eventName}`;
5086
+ if (!listenersBySource.has(key)) listenersBySource.set(key, []);
5087
+ listenersBySource.get(key).push({ block, trigger });
5088
+ } else if (trigger.type === "block.event.all" && trigger.sources) {
5089
+ for (const source of trigger.sources) {
5090
+ if (!source.sourceBlockId || !source.eventName) continue;
5091
+ const key = `${source.sourceBlockId}::${source.eventName}`;
5092
+ if (!barrierListenersBySource.has(key)) barrierListenersBySource.set(key, []);
5093
+ barrierListenersBySource.get(key).push({ block, trigger, source });
5094
+ }
5095
+ }
4008
5096
  }
4009
- if (listenersBySource.size === 0) return;
5097
+ if (listenersBySource.size === 0 && barrierListenersBySource.size === 0) return;
4010
5098
  const runtimeMap = editor._yRuntime;
4011
5099
  const getNodeOutput = (nodeId) => {
4012
5100
  if (!runtimeMap) return void 0;
@@ -4025,14 +5113,21 @@ function reconcilePendingInvocations(editor) {
4025
5113
  break;
4026
5114
  }
4027
5115
  }
5116
+ for (const key of barrierListenersBySource.keys()) {
5117
+ if (key.startsWith(`${sourceBlockId}::`)) {
5118
+ hasAnyListener = true;
5119
+ break;
5120
+ }
5121
+ }
4028
5122
  if (!hasAnyListener) continue;
4029
5123
  const records = readRunRecords(yDoc, sourceBlockId);
4030
5124
  for (const record of records) {
4031
- processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput);
5125
+ processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput, runtimeMap);
5126
+ processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput, runtimeMap);
4032
5127
  }
4033
5128
  }
4034
5129
  }
4035
- function processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput) {
5130
+ function processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput, runtimeMap) {
4036
5131
  if (!Array.isArray(record.events)) return;
4037
5132
  record.events.forEach((event, eventIndex) => {
4038
5133
  if (!event?.name) return;
@@ -4049,8 +5144,7 @@ function processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNod
4049
5144
  eventName: event.name,
4050
5145
  eventIndex
4051
5146
  });
4052
- const assigneeDid = resolveAssignee(listenerBlock);
4053
- if (!assigneeDid) continue;
5147
+ const assigneeDid = resolveAssignee(listenerBlock) || "unassigned";
4054
5148
  const inputs = parseInputs(listenerBlock);
4055
5149
  const refSnapshots = snapshotInputRefs(inputs, getNodeOutput);
4056
5150
  const expiresAt = computeExpiry(listenerBlock, record.completedAt);
@@ -4067,9 +5161,77 @@ function processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNod
4067
5161
  expiresAt
4068
5162
  };
4069
5163
  queuePendingInvocation(yDoc, listenerBlockId, invocation);
5164
+ if (runtimeMap) {
5165
+ const prev = runtimeMap.get(listenerBlockId) || {};
5166
+ runtimeMap.set(listenerBlockId, {
5167
+ ...prev,
5168
+ pendingPayload: { ...event.payload, ...refSnapshots }
5169
+ });
5170
+ }
4070
5171
  }
4071
5172
  });
4072
5173
  }
5174
+ function processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput, runtimeMap) {
5175
+ if (!Array.isArray(record.events)) return;
5176
+ for (const event of record.events) {
5177
+ if (!event?.name) continue;
5178
+ const key = `${sourceBlockId}::${event.name}`;
5179
+ const listeners = barrierListenersBySource.get(key);
5180
+ if (!listeners || listeners.length === 0) continue;
5181
+ for (const { block: listenerBlock, trigger, source } of listeners) {
5182
+ const listenerBlockId = listenerBlock.id;
5183
+ if (!listenerBlockId) continue;
5184
+ const entry = {
5185
+ sourceBlockId,
5186
+ eventName: event.name,
5187
+ alias: source.alias,
5188
+ runId: record.runId,
5189
+ payload: event.payload || {},
5190
+ emittedAt: record.completedAt
5191
+ };
5192
+ recordBarrierEvent(yDoc, listenerBlockId, entry);
5193
+ if (runtimeMap) {
5194
+ const prev = runtimeMap.get(listenerBlockId) || {};
5195
+ const existing = prev.pendingPayload || {};
5196
+ runtimeMap.set(listenerBlockId, {
5197
+ ...prev,
5198
+ pendingPayload: { ...existing, ...event.payload || {} }
5199
+ });
5200
+ }
5201
+ const allSources = trigger.sources || [];
5202
+ const currentState = readBarrierState(yDoc, listenerBlockId);
5203
+ const allFired = allSources.length > 0 && allSources.every((s) => currentState.some((e) => e.sourceBlockId === s.sourceBlockId && e.eventName === s.eventName));
5204
+ if (!allFired) continue;
5205
+ const assigneeDid = resolveAssignee(listenerBlock) || "unassigned";
5206
+ const id = computeBarrierInvocationId(currentState, listenerBlockId);
5207
+ const mergedPayload = mergeBarrierPayloads(currentState);
5208
+ const inputs = parseInputs(listenerBlock);
5209
+ const refSnapshots = snapshotInputRefs(inputs, getNodeOutput);
5210
+ const expiresAt = computeExpiry(listenerBlock, record.completedAt);
5211
+ const invocation = {
5212
+ id,
5213
+ triggeringBlockId: sourceBlockId,
5214
+ sourceRunId: record.runId,
5215
+ eventName: `barrier:${allSources.map((s) => s.alias).join("+")}`,
5216
+ eventIndex: 0,
5217
+ payload: mergedPayload,
5218
+ refSnapshots,
5219
+ assigneeDid,
5220
+ emittedAt: record.completedAt,
5221
+ expiresAt
5222
+ };
5223
+ queuePendingInvocation(yDoc, listenerBlockId, invocation);
5224
+ clearBarrierState(yDoc, listenerBlockId);
5225
+ if (runtimeMap) {
5226
+ const prev = runtimeMap.get(listenerBlockId) || {};
5227
+ runtimeMap.set(listenerBlockId, {
5228
+ ...prev,
5229
+ pendingPayload: { ...mergedPayload, ...refSnapshots }
5230
+ });
5231
+ }
5232
+ }
5233
+ }
5234
+ }
4073
5235
  function parseTrigger(block) {
4074
5236
  const raw = block?.props?.trigger;
4075
5237
  if (!raw || typeof raw !== "string") return null;
@@ -4134,6 +5296,25 @@ function getActionForBlock(block) {
4134
5296
  return getAction(actionType);
4135
5297
  }
4136
5298
 
5299
+ // src/core/lib/flowEngine/emitEvents.ts
5300
+ function writeRunRecordAndReconcile(editor, blockId, output, events, actorDid) {
5301
+ const yDoc = editor._yDoc;
5302
+ if (!yDoc) return;
5303
+ if (events.length === 0) return;
5304
+ const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
5305
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5306
+ const details = {
5307
+ runId,
5308
+ output,
5309
+ events,
5310
+ startedAt: now,
5311
+ completedAt: now,
5312
+ actorDid
5313
+ };
5314
+ appendRunRecord(yDoc, blockId, details, actorDid);
5315
+ reconcilePendingInvocations(editor);
5316
+ }
5317
+
4137
5318
  // src/core/lib/flowEngine/migration.ts
4138
5319
  var MIGRATION_REGISTRY = {};
4139
5320
  function registerMigration(definition) {
@@ -4292,7 +5473,6 @@ var ICON_DEFAULTS = {
4292
5473
  "matrix/dm": "message-circle",
4293
5474
  "proposal/create": "scroll",
4294
5475
  "proposal/vote": "vote",
4295
- "domain/card-build": "file-text",
4296
5476
  "domain/card-preview": "id",
4297
5477
  "domain/sign": "feather",
4298
5478
  "credential/store": "shield",
@@ -4360,42 +5540,81 @@ function compileBaseUcanFlow(plan, registry) {
4360
5540
  const triggerEdges = [];
4361
5541
  for (const [nodeId, { cap, action }] of actionMap) {
4362
5542
  const trigger = cap.trigger;
4363
- if (!trigger || trigger.type !== "block.event") continue;
4364
- if (action.eligibleForEventTrigger !== true) {
4365
- throw new Error(
4366
- `Block "${nodeId}" is configured with a block.event trigger, but its action type "${action.type}" is not marked eligibleForEventTrigger. Set eligibleForEventTrigger: true on the action definition or change the trigger to manual.`
4367
- );
5543
+ if (!trigger) continue;
5544
+ if (trigger.type === "block.event") {
5545
+ if (action.eligibleForEventTrigger !== true) {
5546
+ throw new Error(
5547
+ `Block "${nodeId}" is configured with a block.event trigger, but its action type "${action.type}" is not marked eligibleForEventTrigger. Set eligibleForEventTrigger: true on the action definition or change the trigger to manual.`
5548
+ );
5549
+ }
5550
+ const sourceId = trigger.sourceBlockId?.trim();
5551
+ const eventName = trigger.eventName?.trim();
5552
+ if (!sourceId || !eventName) {
5553
+ throw new Error(`Block "${nodeId}" has a block.event trigger but is missing sourceBlockId or eventName.`);
5554
+ }
5555
+ const source = actionMap.get(sourceId);
5556
+ if (!source) {
5557
+ throw new Error(`Trigger on block "${nodeId}" references unknown source block "${sourceId}".`);
5558
+ }
5559
+ const declaredEvents = source.action.events || [];
5560
+ if (declaredEvents.length === 0) {
5561
+ throw new Error(`Trigger on block "${nodeId}" references event "${eventName}" but source action "${source.action.type}" declares no events.`);
5562
+ }
5563
+ const found = declaredEvents.find((e) => e.name === eventName);
5564
+ if (!found) {
5565
+ const declared = declaredEvents.map((e) => e.name).join(", ");
5566
+ throw new Error(`Trigger on block "${nodeId}" references unknown event "${eventName}" on action type "${source.action.type}". Declared events: [${declared}]`);
5567
+ }
5568
+ triggerEdges.push({
5569
+ id: `${sourceId}~event:${eventName}~>${nodeId}`,
5570
+ source: sourceId,
5571
+ target: nodeId,
5572
+ kind: "trigger"
5573
+ });
4368
5574
  }
4369
- const sourceId = trigger.sourceBlockId?.trim();
4370
- const eventName = trigger.eventName?.trim();
4371
- if (!sourceId || !eventName) {
4372
- throw new Error(`Block "${nodeId}" has a block.event trigger but is missing sourceBlockId or eventName.`);
4373
- }
4374
- const source = actionMap.get(sourceId);
4375
- if (!source) {
4376
- throw new Error(`Trigger on block "${nodeId}" references unknown source block "${sourceId}".`);
4377
- }
4378
- const declaredEvents = source.action.events || [];
4379
- if (declaredEvents.length === 0) {
4380
- throw new Error(`Trigger on block "${nodeId}" references event "${eventName}" but source action "${source.action.type}" declares no events.`);
4381
- }
4382
- const found = declaredEvents.find((e) => e.name === eventName);
4383
- if (!found) {
4384
- const declared = declaredEvents.map((e) => e.name).join(", ");
4385
- throw new Error(`Trigger on block "${nodeId}" references unknown event "${eventName}" on action type "${source.action.type}". Declared events: [${declared}]`);
4386
- }
4387
- triggerEdges.push({
4388
- id: `${sourceId}~event:${eventName}~>${nodeId}`,
4389
- source: sourceId,
4390
- target: nodeId,
4391
- kind: "trigger"
4392
- });
4393
- const refs = collectOutputRefs(cap.nb || {});
4394
- for (const ref of refs) {
4395
- const parsed = parseOutputRef2(ref);
4396
- if (!parsed) continue;
4397
- if (!actionMap.has(parsed.nodeId)) {
4398
- throw new Error(`Listener "${nodeId}" references output of unknown block "${parsed.nodeId}" via ref "${ref}".`);
5575
+ if (trigger.type === "block.event.all") {
5576
+ if (action.eligibleForEventTrigger !== true) {
5577
+ throw new Error(`Block "${nodeId}" is configured with a block.event.all trigger, but its action type "${action.type}" is not marked eligibleForEventTrigger.`);
5578
+ }
5579
+ const sources = trigger.sources || [];
5580
+ if (sources.length === 0) {
5581
+ throw new Error(`Block "${nodeId}" has a block.event.all trigger but no sources configured.`);
5582
+ }
5583
+ for (const src of sources) {
5584
+ const srcId = src.sourceBlockId?.trim();
5585
+ const evtName = src.eventName?.trim();
5586
+ if (!srcId || !evtName) {
5587
+ throw new Error(`Block "${nodeId}" has a block.event.all source with missing sourceBlockId or eventName.`);
5588
+ }
5589
+ const sourceEntry = actionMap.get(srcId);
5590
+ if (!sourceEntry) {
5591
+ throw new Error(`Barrier trigger on block "${nodeId}" references unknown source block "${srcId}".`);
5592
+ }
5593
+ const declaredEvents = sourceEntry.action.events || [];
5594
+ const found = declaredEvents.find((e) => e.name === evtName);
5595
+ if (!found) {
5596
+ const declared = declaredEvents.map((e) => e.name).join(", ");
5597
+ throw new Error(`Barrier trigger on block "${nodeId}" references unknown event "${evtName}" on action type "${sourceEntry.action.type}". Declared events: [${declared}]`);
5598
+ }
5599
+ if (!src.alias?.trim()) {
5600
+ throw new Error(`Barrier trigger on block "${nodeId}" has a source (${srcId}/${evtName}) with no alias.`);
5601
+ }
5602
+ triggerEdges.push({
5603
+ id: `${srcId}~event:${evtName}~>${nodeId}`,
5604
+ source: srcId,
5605
+ target: nodeId,
5606
+ kind: "trigger"
5607
+ });
5608
+ }
5609
+ }
5610
+ if (trigger.type === "block.event" || trigger.type === "block.event.all") {
5611
+ const refs = collectOutputRefs(cap.nb || {});
5612
+ for (const ref of refs) {
5613
+ const parsed = parseOutputRef2(ref);
5614
+ if (!parsed) continue;
5615
+ if (!actionMap.has(parsed.nodeId)) {
5616
+ throw new Error(`Listener "${nodeId}" references output of unknown block "${parsed.nodeId}" via ref "${ref}".`);
5617
+ }
4399
5618
  }
4400
5619
  }
4401
5620
  }
@@ -5139,6 +6358,7 @@ export {
5139
6358
  parseLinkedEntities,
5140
6359
  buildGovernanceGroupLinkedEntities,
5141
6360
  tempDomainCreatorSurvey,
6361
+ resolveEntityTypeFromSchema,
5142
6362
  getHomeserver,
5143
6363
  didToMatrixUserId,
5144
6364
  findOrCreateDMRoom,
@@ -5170,6 +6390,7 @@ export {
5170
6390
  replayFailedListenerRun,
5171
6391
  reconcilePendingInvocations,
5172
6392
  getActionForBlock,
6393
+ writeRunRecordAndReconcile,
5173
6394
  compileBaseUcanFlow,
5174
6395
  readCompiledFlowFromYDoc,
5175
6396
  mergeCompiledFlows,
@@ -5181,4 +6402,4 @@ export {
5181
6402
  readFlow,
5182
6403
  setupFlowFromBaseUcan
5183
6404
  };
5184
- //# sourceMappingURL=chunk-B7UIUYP5.mjs.map
6405
+ //# sourceMappingURL=chunk-75MWYZJ2.mjs.map