@ixo/editor 5.19.0 → 5.20.0-experimental.1

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.
@@ -571,15 +571,68 @@ registerAction({
571
571
  });
572
572
 
573
573
  // src/core/lib/actionRegistry/actions/pod/listDomainFlows.ts
574
+ function normalizeSelectedTemplates(value) {
575
+ if (!Array.isArray(value)) return [];
576
+ return value.map((item) => {
577
+ if (!item || typeof item !== "object") return null;
578
+ const raw = item;
579
+ const protocolDid = String(raw.protocolDid || "").trim();
580
+ const sourceRoomId = String(raw.sourceRoomId || "").trim();
581
+ if (!protocolDid || !sourceRoomId) return null;
582
+ const sourceLabel = String(raw.sourceLabel || raw.label || sourceRoomId).trim();
583
+ return {
584
+ protocolDid,
585
+ protocolName: String(raw.protocolName || protocolDid).trim(),
586
+ sourceRoomId,
587
+ sourceLabel,
588
+ ...raw.path ? { path: String(raw.path) } : {}
589
+ };
590
+ }).filter((item) => item !== null);
591
+ }
574
592
  registerAction({
575
593
  type: "qi/pod.list-domain-flows",
576
594
  can: "pod/list-domain-flows",
577
595
  sideEffect: false,
578
596
  defaultRequiresConfirmation: false,
579
- outputSchema: [{ path: "selectedFlowDids", displayName: "Selected Flow DIDs", type: "array", description: "Array of selected flow template DIDs" }],
597
+ outputSchema: [
598
+ {
599
+ path: "selectedProtocolTemplates",
600
+ displayName: "Selected Protocol Templates",
601
+ type: "array",
602
+ description: "Selected protocol templates with protocol DID and source room ID"
603
+ },
604
+ { path: "selectedFlowDids", displayName: "Selected Flow Room IDs", type: "array", description: "Source Matrix room IDs for selected templates" },
605
+ { path: "flowTemplateConfig", displayName: "Flow Template Config", type: "object", description: "Event payload consumed by POD signing/import steps" }
606
+ ],
607
+ events: [
608
+ {
609
+ name: "configured",
610
+ displayName: "Flow Templates Configured",
611
+ description: "Fires when protocol flow templates are selected for the POD.",
612
+ payloadSchema: [
613
+ {
614
+ path: "flowTemplateConfig",
615
+ displayName: "Flow Template Config",
616
+ type: "object",
617
+ description: "Selected templates grouped by their protocol DID for later import"
618
+ }
619
+ ],
620
+ pendingDisplayFields: ["flowTemplateConfig.templateCount", "flowTemplateConfig.protocolCount"]
621
+ }
622
+ ],
580
623
  run: async (inputs) => {
581
- const selectedFlowDids = Array.isArray(inputs.selectedFlowDids) ? inputs.selectedFlowDids : [];
582
- return { output: { selectedFlowDids } };
624
+ const selectedProtocolTemplates = normalizeSelectedTemplates(inputs.selectedProtocolTemplates);
625
+ const selectedFlowDids = selectedProtocolTemplates.length > 0 ? selectedProtocolTemplates.map((template) => template.sourceRoomId) : Array.isArray(inputs.selectedFlowDids) ? inputs.selectedFlowDids : [];
626
+ const protocolCount = new Set(selectedProtocolTemplates.map((template) => template.protocolDid)).size;
627
+ const flowTemplateConfig = {
628
+ selectedTemplates: selectedProtocolTemplates,
629
+ templateCount: selectedProtocolTemplates.length,
630
+ protocolCount
631
+ };
632
+ return {
633
+ output: { selectedProtocolTemplates, selectedFlowDids, flowTemplateConfig },
634
+ events: [{ name: "configured", payload: { flowTemplateConfig } }]
635
+ };
583
636
  }
584
637
  });
585
638
 
@@ -2669,6 +2722,77 @@ function parseContextInput(value) {
2669
2722
  return key && val ? { key, val } : null;
2670
2723
  }).filter((entry) => entry !== null);
2671
2724
  }
2725
+ function getSelectedProtocolTemplates(flowTemplateConfig) {
2726
+ const raw = flowTemplateConfig?.selectedTemplates;
2727
+ if (!Array.isArray(raw)) return [];
2728
+ return raw.map((item) => {
2729
+ if (!item || typeof item !== "object") return null;
2730
+ const record = item;
2731
+ const protocolDid = String(record.protocolDid || "").trim();
2732
+ const sourceRoomId = String(record.sourceRoomId || "").trim();
2733
+ if (!protocolDid || !sourceRoomId) return null;
2734
+ const sourceLabel = String(record.sourceLabel || record.label || "").trim();
2735
+ return {
2736
+ protocolDid,
2737
+ sourceRoomId,
2738
+ ...sourceLabel ? { sourceLabel } : {}
2739
+ };
2740
+ }).filter((item) => item !== null);
2741
+ }
2742
+ function groupTemplatesByProtocol(templates) {
2743
+ const grouped = /* @__PURE__ */ new Map();
2744
+ for (const template of templates) {
2745
+ const existing = grouped.get(template.protocolDid) || [];
2746
+ existing.push({
2747
+ sourceRoomId: template.sourceRoomId,
2748
+ ...template.sourceLabel ? { sourceLabel: template.sourceLabel } : {}
2749
+ });
2750
+ grouped.set(template.protocolDid, existing);
2751
+ }
2752
+ return grouped;
2753
+ }
2754
+ var DOMAIN_SIGN_CACHE_KEY = "domainSign";
2755
+ function getDomainSignCheckpointKey(inputs, ctx) {
2756
+ return String(ctx.pendingInvocation?.id || inputs.pendingInvocationId || inputs.invocationId || "manual");
2757
+ }
2758
+ function readDomainSignCheckpoint(ctx, key) {
2759
+ const cache = ctx.runtime?.get?.(ctx.nodeId)?.cache;
2760
+ const bucket = cache?.[DOMAIN_SIGN_CACHE_KEY];
2761
+ if (!bucket || typeof bucket !== "object") return void 0;
2762
+ const checkpoint = bucket[key];
2763
+ return checkpoint && typeof checkpoint === "object" ? checkpoint : void 0;
2764
+ }
2765
+ function writeDomainSignCheckpoint(ctx, key, checkpoint) {
2766
+ if (!ctx.runtime?.get || !ctx.runtime?.update || !ctx.nodeId) return;
2767
+ const current = ctx.runtime.get(ctx.nodeId) || {};
2768
+ const cache = current.cache && typeof current.cache === "object" ? current.cache : {};
2769
+ const bucket = cache[DOMAIN_SIGN_CACHE_KEY] && typeof cache[DOMAIN_SIGN_CACHE_KEY] === "object" ? cache[DOMAIN_SIGN_CACHE_KEY] : {};
2770
+ ctx.runtime.update(ctx.nodeId, {
2771
+ cache: {
2772
+ ...cache,
2773
+ [DOMAIN_SIGN_CACHE_KEY]: {
2774
+ ...bucket,
2775
+ [key]: checkpoint
2776
+ }
2777
+ }
2778
+ });
2779
+ }
2780
+ function buildCompletedDomainSignResult(output) {
2781
+ return {
2782
+ output,
2783
+ events: [
2784
+ {
2785
+ name: "created",
2786
+ payload: {
2787
+ entityDid: output.entityDid,
2788
+ governanceGroupCoreAddress: output.governanceGroupCoreAddress || "",
2789
+ transactionHash: output.transactionHash || "",
2790
+ linkedResourceTransactionHash: output.linkedResourceTransactionHash || ""
2791
+ }
2792
+ }
2793
+ ]
2794
+ };
2795
+ }
2672
2796
  function parseDuration(raw) {
2673
2797
  const seconds = parseInt(raw, 10) || 0;
2674
2798
  if (seconds % (7 * 86400) === 0) return { amount: seconds / (7 * 86400), unit: "weeks" };
@@ -2755,6 +2879,24 @@ registerAction({
2755
2879
  displayName: "Linked Resource Transaction Hash",
2756
2880
  type: "string",
2757
2881
  description: "The on-chain transaction hash for adding the domain card linked resource"
2882
+ },
2883
+ {
2884
+ path: "flowTemplateConfig",
2885
+ displayName: "Flow Template Config",
2886
+ type: "object",
2887
+ description: "Selected protocol templates captured before POD creation"
2888
+ },
2889
+ {
2890
+ path: "domainSpaces",
2891
+ displayName: "Domain Spaces",
2892
+ type: "object",
2893
+ description: "Matrix space structure sourced for the new domain"
2894
+ },
2895
+ {
2896
+ path: "protocolTemplateImports",
2897
+ displayName: "Protocol Template Imports",
2898
+ type: "array",
2899
+ description: "Import results for selected protocol templates"
2758
2900
  }
2759
2901
  ],
2760
2902
  events: [
@@ -2782,16 +2924,7 @@ registerAction({
2782
2924
  }
2783
2925
  ],
2784
2926
  run: async (inputs, ctx) => {
2785
- const handlers = ctx.handlers;
2786
- if (!handlers) {
2787
- throw new Error("Handlers not available");
2788
- }
2789
- if (!handlers.requestPin) throw new Error("requestPin handler not available");
2790
- if (!handlers.signCredential) throw new Error("signCredential handler not implemented");
2791
- if (!handlers.publicFileUpload) throw new Error("publicFileUpload handler not available");
2792
- if (!handlers.createDomain) throw new Error("createDomain handler not implemented");
2793
- if (!handlers.createAddLinkedResourceMessage) throw new Error("createAddLinkedResourceMessage handler not implemented");
2794
- if (!handlers.executeTransaction) throw new Error("executeTransaction handler not implemented");
2927
+ const handlers = ctx.handlers || {};
2795
2928
  let domainCardData;
2796
2929
  if (typeof inputs.domainCardData === "string") {
2797
2930
  try {
@@ -2814,99 +2947,238 @@ registerAction({
2814
2947
  d.setFullYear(d.getFullYear() + 100);
2815
2948
  return d.toISOString();
2816
2949
  })();
2817
- let governanceGroupLinkedEntities = [];
2818
- let governanceGroupCoreAddress = "";
2819
2950
  const govConfig = parseJsonInput(inputs.governanceConfig);
2820
2951
  const memberConfig = parseJsonInput(inputs.memberConfig);
2952
+ const flowTemplateConfig = parseJsonInput(inputs.flowTemplateConfig);
2821
2953
  const context = parseContextInput(inputs.context);
2822
- if (govConfig && handlers.createGovernanceGroup) {
2823
- const groupType = govConfig.groupType;
2824
- const governance = govConfig.governance || {};
2825
- const config = buildGroupConfig(groupType, governance, memberConfig);
2826
- const groupResult = await handlers.createGovernanceGroup({
2827
- groupType,
2828
- name: govConfig.groupName || domainCardData.credentialSubject?.name || "Governance Group",
2829
- config
2830
- });
2831
- governanceGroupCoreAddress = groupResult.coreAddress;
2832
- governanceGroupLinkedEntities = [
2833
- {
2834
- id: `{id}#${governanceGroupCoreAddress}`,
2835
- type: "group",
2836
- relationship: "governs",
2837
- service: ""
2954
+ const selectedProtocolTemplates = getSelectedProtocolTemplates(flowTemplateConfig);
2955
+ const checkpointKey = getDomainSignCheckpointKey(inputs, ctx);
2956
+ let checkpoint = readDomainSignCheckpoint(ctx, checkpointKey) || {
2957
+ invocationId: checkpointKey,
2958
+ status: "running",
2959
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2960
+ completedSteps: {}
2961
+ };
2962
+ if (checkpoint.status === "completed" && checkpoint.output) {
2963
+ return buildCompletedDomainSignResult(checkpoint.output);
2964
+ }
2965
+ if (!ctx.handlers) {
2966
+ throw new Error("Handlers not available");
2967
+ }
2968
+ if (typeof handlers.requestPin !== "function") throw new Error("requestPin handler not available");
2969
+ if (typeof handlers.signCredential !== "function") throw new Error("signCredential handler not implemented");
2970
+ if (typeof handlers.publicFileUpload !== "function") throw new Error("publicFileUpload handler not available");
2971
+ if (typeof handlers.createDomain !== "function") throw new Error("createDomain handler not implemented");
2972
+ if (typeof handlers.createAddLinkedResourceMessage !== "function") throw new Error("createAddLinkedResourceMessage handler not implemented");
2973
+ if (typeof handlers.executeTransaction !== "function") throw new Error("executeTransaction handler not implemented");
2974
+ const saveCheckpoint = (updates) => {
2975
+ checkpoint = {
2976
+ ...checkpoint,
2977
+ ...updates,
2978
+ invocationId: checkpointKey,
2979
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2980
+ completedSteps: {
2981
+ ...checkpoint.completedSteps || {},
2982
+ ...updates.completedSteps || {}
2838
2983
  }
2839
- ];
2840
- }
2841
- const endDate = domainCardData.endDate || validUntil;
2842
- const { entityDid: newEntityDid, transactionHash } = await handlers.createDomain({
2843
- entityType,
2844
- context: context.length > 0 ? context : void 0,
2845
- linkedResource: [],
2846
- linkedEntity: governanceGroupLinkedEntities.length > 0 ? governanceGroupLinkedEntities : void 0,
2847
- startDate: validFrom,
2848
- endDate
2849
- });
2850
- const issuerDid = handlers.getEntityDid?.() || handlers.getCurrentUser?.()?.address;
2851
- if (!issuerDid) throw new Error("Unable to determine issuer DID");
2852
- const credentialSubject = {
2853
- ...domainCardData.credentialSubject,
2854
- id: newEntityDid
2984
+ };
2985
+ writeDomainSignCheckpoint(ctx, checkpointKey, checkpoint);
2986
+ return checkpoint;
2855
2987
  };
2856
- const unsignedCredential = buildVerifiableCredential({
2857
- entityDid: newEntityDid,
2858
- issuerDid,
2859
- credentialSubject,
2860
- validFrom,
2861
- validUntil
2862
- });
2863
- const pin = await handlers.requestPin({
2864
- title: "Sign Domain Card",
2865
- description: "Enter your PIN to sign the Domain Card credential",
2866
- submitText: "Sign"
2867
- });
2868
- const { signedCredential } = await handlers.signCredential({
2869
- issuerDid,
2870
- issuerType: "user",
2871
- credential: unsignedCredential,
2872
- pin
2873
- });
2874
- const credentialBlob = new Blob([JSON.stringify(signedCredential, null, 2)], {
2875
- type: "application/json"
2876
- });
2877
- const credentialFile = new File([credentialBlob], "domainCard.json", {
2878
- type: "application/json"
2879
- });
2880
- const uploadResult = await handlers.publicFileUpload(credentialFile);
2881
- const domainCardLinkedResource = buildDomainCardLinkedResource({
2882
- entityDid: newEntityDid,
2883
- cid: uploadResult.cid,
2884
- serviceEndpoint: uploadResult.url,
2885
- description: `Domain Card for ${domainCardData.credentialSubject?.name || "Domain"}`
2886
- });
2887
- const addLinkedResourceMessage = await handlers.createAddLinkedResourceMessage({
2888
- entityDid: newEntityDid,
2889
- linkedResource: domainCardLinkedResource
2890
- });
2891
- const addLinkedResourceResult = await handlers.executeTransaction({
2892
- messages: [addLinkedResourceMessage],
2893
- memo: ""
2988
+ saveCheckpoint({
2989
+ status: "running",
2990
+ error: void 0,
2991
+ flowTemplateConfig
2894
2992
  });
2895
- const linkedResourceTransactionHash = String(addLinkedResourceResult?.transactionHash || "");
2896
- if (!linkedResourceTransactionHash) {
2897
- throw new Error("Linked resource transaction completed but no transaction hash received");
2898
- }
2899
- return {
2900
- output: {
2993
+ try {
2994
+ let governanceGroupLinkedEntities = checkpoint.governanceGroupLinkedEntities || [];
2995
+ let governanceGroupCoreAddress = checkpoint.governanceGroupCoreAddress || "";
2996
+ if (govConfig && handlers.createGovernanceGroup && !checkpoint.completedSteps?.createGovernanceGroup) {
2997
+ const groupType = govConfig.groupType;
2998
+ const governance = govConfig.governance || {};
2999
+ const config = buildGroupConfig(groupType, governance, memberConfig);
3000
+ const groupResult = await handlers.createGovernanceGroup({
3001
+ groupType,
3002
+ name: govConfig.groupName || domainCardData.credentialSubject?.name || "Governance Group",
3003
+ config
3004
+ });
3005
+ governanceGroupCoreAddress = groupResult.coreAddress;
3006
+ governanceGroupLinkedEntities = [
3007
+ {
3008
+ id: `{id}#${governanceGroupCoreAddress}`,
3009
+ type: "group",
3010
+ relationship: "governs",
3011
+ service: ""
3012
+ }
3013
+ ];
3014
+ saveCheckpoint({
3015
+ governanceGroupCoreAddress,
3016
+ governanceGroupLinkedEntities,
3017
+ completedSteps: { createGovernanceGroup: true }
3018
+ });
3019
+ } else if (!govConfig) {
3020
+ saveCheckpoint({ completedSteps: { createGovernanceGroup: true } });
3021
+ }
3022
+ const issuerDid = handlers.getEntityDid?.() || handlers.getCurrentUser?.()?.address;
3023
+ if (!issuerDid) throw new Error("Unable to determine issuer DID");
3024
+ const signAndUploadDomainCard = async (entityDid) => {
3025
+ const credentialSubject = {
3026
+ ...domainCardData.credentialSubject,
3027
+ id: entityDid
3028
+ };
3029
+ const unsignedCredential = buildVerifiableCredential({
3030
+ entityDid,
3031
+ issuerDid,
3032
+ credentialSubject,
3033
+ validFrom,
3034
+ validUntil
3035
+ });
3036
+ const pin = await handlers.requestPin({
3037
+ title: "Sign Domain Card",
3038
+ description: "Enter your PIN to sign the Domain Card credential",
3039
+ submitText: "Sign"
3040
+ });
3041
+ const { signedCredential } = await handlers.signCredential({
3042
+ issuerDid,
3043
+ issuerType: "user",
3044
+ credential: unsignedCredential,
3045
+ pin
3046
+ });
3047
+ const credentialBlob = new Blob([JSON.stringify(signedCredential, null, 2)], {
3048
+ type: "application/json"
3049
+ });
3050
+ const credentialFile = new File([credentialBlob], "domainCard.json", {
3051
+ type: "application/json"
3052
+ });
3053
+ const uploadResult = await handlers.publicFileUpload(credentialFile);
3054
+ return {
3055
+ ...buildDomainCardLinkedResource({
3056
+ entityDid,
3057
+ cid: uploadResult.cid,
3058
+ serviceEndpoint: uploadResult.url,
3059
+ description: `Domain Card for ${domainCardData.credentialSubject?.name || "Domain"}`
3060
+ }),
3061
+ id: "{id}#dmn"
3062
+ };
3063
+ };
3064
+ const endDate = domainCardData.endDate || validUntil;
3065
+ let newEntityDid = checkpoint.entityDid || "";
3066
+ let transactionHash = checkpoint.transactionHash || "";
3067
+ let linkedResourceTransactionHash = checkpoint.linkedResourceTransactionHash || "";
3068
+ let domainCardLinkedResource = checkpoint.domainCardLinkedResource;
3069
+ if (!checkpoint.completedSteps?.createDomain || !newEntityDid || !transactionHash) {
3070
+ const createResult = await handlers.createDomain({
3071
+ entityType,
3072
+ context: context.length > 0 ? context : void 0,
3073
+ linkedResource: [],
3074
+ linkedEntity: governanceGroupLinkedEntities.length > 0 ? governanceGroupLinkedEntities : void 0,
3075
+ startDate: validFrom,
3076
+ endDate
3077
+ });
3078
+ newEntityDid = createResult.entityDid;
3079
+ transactionHash = createResult.transactionHash;
3080
+ saveCheckpoint({
3081
+ entityDid: newEntityDid,
3082
+ transactionHash,
3083
+ completedSteps: { createDomain: true }
3084
+ });
3085
+ }
3086
+ if (!checkpoint.completedSteps?.signAndUploadDomainCard || !domainCardLinkedResource) {
3087
+ domainCardLinkedResource = await signAndUploadDomainCard(newEntityDid);
3088
+ saveCheckpoint({
3089
+ domainCardLinkedResource,
3090
+ completedSteps: { signAndUploadDomainCard: true }
3091
+ });
3092
+ }
3093
+ if (!checkpoint.completedSteps?.addLinkedResource || !linkedResourceTransactionHash) {
3094
+ const addLinkedResourceMessage = await handlers.createAddLinkedResourceMessage({
3095
+ entityDid: newEntityDid,
3096
+ linkedResource: domainCardLinkedResource
3097
+ });
3098
+ const addLinkedResourceResult = await handlers.executeTransaction({
3099
+ messages: [addLinkedResourceMessage],
3100
+ memo: ""
3101
+ });
3102
+ linkedResourceTransactionHash = String(addLinkedResourceResult?.transactionHash || "");
3103
+ if (!linkedResourceTransactionHash) {
3104
+ throw new Error("Linked resource transaction completed but no transaction hash received");
3105
+ }
3106
+ saveCheckpoint({
3107
+ linkedResourceTransactionHash,
3108
+ completedSteps: { addLinkedResource: true }
3109
+ });
3110
+ }
3111
+ let domainSpaces = checkpoint.domainSpaces;
3112
+ let protocolTemplateImports = checkpoint.protocolTemplateImports || [];
3113
+ if (handlers.sourceDomainSpaces && (!checkpoint.completedSteps?.sourceDomainSpaces || !domainSpaces)) {
3114
+ domainSpaces = await handlers.sourceDomainSpaces({ entityDid: newEntityDid });
3115
+ if (!domainSpaces?.success) {
3116
+ throw new Error("Domain spaces could not be sourced for the newly created domain");
3117
+ }
3118
+ saveCheckpoint({
3119
+ domainSpaces,
3120
+ completedSteps: { sourceDomainSpaces: true }
3121
+ });
3122
+ } else if (!handlers.sourceDomainSpaces) {
3123
+ saveCheckpoint({ completedSteps: { sourceDomainSpaces: true } });
3124
+ }
3125
+ if (selectedProtocolTemplates.length > 0 && !checkpoint.completedSteps?.importTemplates) {
3126
+ if (!handlers.sourceDomainSpaces) {
3127
+ throw new Error("sourceDomainSpaces handler is required to import selected protocol templates");
3128
+ }
3129
+ if (!handlers.importProtocolTemplatesToSpace) {
3130
+ throw new Error("importProtocolTemplatesToSpace handler is required to import selected protocol templates");
3131
+ }
3132
+ const targetFlowsSubspaceId = domainSpaces?.subspaces?.["domain flows"]?.space_id;
3133
+ if (!targetFlowsSubspaceId) {
3134
+ throw new Error("Domain flows subspace was not returned by sourceDomainSpaces");
3135
+ }
3136
+ const groupedTemplates = groupTemplatesByProtocol(selectedProtocolTemplates);
3137
+ protocolTemplateImports = await Promise.all(
3138
+ Array.from(groupedTemplates.entries()).map(async ([protocolDid, templates]) => {
3139
+ const result = await handlers.importProtocolTemplatesToSpace({
3140
+ protocolDid,
3141
+ entityDid: newEntityDid,
3142
+ targetFlowsSubspaceId,
3143
+ templates
3144
+ });
3145
+ return { protocolDid, imported: result.imported };
3146
+ })
3147
+ );
3148
+ saveCheckpoint({
3149
+ protocolTemplateImports,
3150
+ completedSteps: { importTemplates: true }
3151
+ });
3152
+ } else if (selectedProtocolTemplates.length === 0) {
3153
+ saveCheckpoint({
3154
+ protocolTemplateImports,
3155
+ completedSteps: { importTemplates: true }
3156
+ });
3157
+ }
3158
+ const output = {
2901
3159
  entityDid: newEntityDid,
2902
3160
  governanceGroupCoreAddress,
2903
3161
  linkedEntities: governanceGroupLinkedEntities,
2904
3162
  linkedResources: [domainCardLinkedResource],
3163
+ flowTemplateConfig,
3164
+ domainSpaces,
3165
+ protocolTemplateImports,
2905
3166
  transactionHash,
2906
3167
  linkedResourceTransactionHash
2907
- },
2908
- events: [{ name: "created", payload: { entityDid: newEntityDid, governanceGroupCoreAddress, transactionHash, linkedResourceTransactionHash } }]
2909
- };
3168
+ };
3169
+ saveCheckpoint({
3170
+ status: "completed",
3171
+ output
3172
+ });
3173
+ return buildCompletedDomainSignResult(output);
3174
+ } catch (error) {
3175
+ const message = error instanceof Error ? error.message : "Domain sign failed";
3176
+ saveCheckpoint({
3177
+ status: "failed",
3178
+ error: message
3179
+ });
3180
+ throw error;
3181
+ }
2910
3182
  }
2911
3183
  });
2912
3184
 
@@ -3078,9 +3350,7 @@ registerAction({
3078
3350
  }
3079
3351
  const verb = String(inputs.verb || "").trim().toLowerCase();
3080
3352
  if (verb !== "propose" && verb !== "execute" && verb !== "check") {
3081
- throw new Error(
3082
- 'verb is required and must be one of: "propose", "execute", "check"'
3083
- );
3353
+ throw new Error('verb is required and must be one of: "propose", "execute", "check"');
3084
3354
  }
3085
3355
  const paymentBlockId = String(inputs.paymentBlockId || "").trim();
3086
3356
  if (!paymentBlockId) {
@@ -3108,6 +3378,15 @@ registerAction({
3108
3378
  verb,
3109
3379
  rowCount: rowIds.length,
3110
3380
  paymentBlockId
3381
+ },
3382
+ completion: {
3383
+ state: "awaiting_readback",
3384
+ readBack: {
3385
+ kind: "paymentRows",
3386
+ paymentBlockId,
3387
+ rowIds,
3388
+ verb
3389
+ }
3111
3390
  }
3112
3391
  };
3113
3392
  }
@@ -5275,6 +5554,7 @@ function toSupportedDID(did) {
5275
5554
  }
5276
5555
  function normalizeDid(did) {
5277
5556
  if (did.startsWith("did:")) return did;
5557
+ if (did.startsWith("ixo1")) return `did:ixo:${did}`;
5278
5558
  if (!did.startsWith("@did-")) return did;
5279
5559
  const [localpart] = did.split(":");
5280
5560
  const parts = localpart.split("-");
@@ -5363,7 +5643,7 @@ var createUcanService = (config) => {
5363
5643
  const capabilities = [{ can: "flow/*", with: uri }];
5364
5644
  if (handlers.createSignerSession && handlers.createDelegationWithSession) {
5365
5645
  const session = await handlers.createSignerSession({
5366
- did: ownerDid,
5646
+ did: normalizedOwnerDid,
5367
5647
  didType: issuerType,
5368
5648
  entityRoomId,
5369
5649
  pin,
@@ -5371,15 +5651,15 @@ var createUcanService = (config) => {
5371
5651
  });
5372
5652
  const result = await handlers.createDelegationWithSession({
5373
5653
  sessionId: session.sessionId,
5374
- audience: ownerDid,
5654
+ audience: normalizedOwnerDid,
5375
5655
  capabilities,
5376
5656
  proofs: []
5377
5657
  });
5378
5658
  const storedDelegation2 = {
5379
5659
  cid: result.cid,
5380
5660
  delegation: result.delegation,
5381
- issuerDid: ownerDid,
5382
- audienceDid: ownerDid,
5661
+ issuerDid: normalizedOwnerDid,
5662
+ audienceDid: normalizedOwnerDid,
5383
5663
  capabilities,
5384
5664
  createdAt: Date.now(),
5385
5665
  format: "car",
@@ -5420,7 +5700,7 @@ var createUcanService = (config) => {
5420
5700
  const proofCids = proofs;
5421
5701
  if (handlers.createSignerSession && handlers.createDelegationWithSession) {
5422
5702
  const session = await handlers.createSignerSession({
5423
- did: issuerDid,
5703
+ did: normalizedIssuerDid,
5424
5704
  didType: issuerType,
5425
5705
  entityRoomId,
5426
5706
  pin,
@@ -5429,7 +5709,7 @@ var createUcanService = (config) => {
5429
5709
  const proofCars = await getProofCars(proofCids);
5430
5710
  const result = await handlers.createDelegationWithSession({
5431
5711
  sessionId: session.sessionId,
5432
- audience,
5712
+ audience: normalizedAudienceDid,
5433
5713
  capabilities,
5434
5714
  proofs: proofCars,
5435
5715
  expiration: expiration ? Math.floor(expiration / 1e3) : void 0
@@ -5437,8 +5717,8 @@ var createUcanService = (config) => {
5437
5717
  const storedDelegation2 = {
5438
5718
  cid: result.cid,
5439
5719
  delegation: result.delegation,
5440
- issuerDid,
5441
- audienceDid: audience,
5720
+ issuerDid: normalizedIssuerDid,
5721
+ audienceDid: normalizedAudienceDid,
5442
5722
  capabilities,
5443
5723
  expiration,
5444
5724
  createdAt: Date.now(),
@@ -5491,7 +5771,7 @@ var createUcanService = (config) => {
5491
5771
  const delegations = delegationStore.getByAudience(audienceDid);
5492
5772
  if (delegations.length === 0) {
5493
5773
  const root = delegationStore.getRoot();
5494
- if (root && root.audienceDid === audienceDid) {
5774
+ if (root && normalizeDid(root.audienceDid) === normalizeDid(audienceDid)) {
5495
5775
  return { found: true, proofCids: [root.cid] };
5496
5776
  }
5497
5777
  return { found: false, error: "No delegations found for actor" };
@@ -5547,7 +5827,7 @@ var createUcanService = (config) => {
5547
5827
  const isFlowScoped = capability.can === "flow/*" || capability.can.startsWith("flow/");
5548
5828
  if (isFlowScoped) {
5549
5829
  const root = proofChain[proofChain.length - 1];
5550
- if (root.issuerDid !== flowOwnerDid) {
5830
+ if (normalizeDid(root.issuerDid) !== normalizeDid(flowOwnerDid)) {
5551
5831
  return { valid: false, error: `Root issuer ${root.issuerDid} is not flow owner ${flowOwnerDid}` };
5552
5832
  }
5553
5833
  }
@@ -5555,7 +5835,8 @@ var createUcanService = (config) => {
5555
5835
  };
5556
5836
  const createAndValidateInvocation = async (params, _flowId, _blockId) => {
5557
5837
  const { invokerDid, invokerType, entityRoomId, capability, proofs, pin } = params;
5558
- const validation = await validateDelegationChain(invokerDid, capability);
5838
+ const normalizedInvokerDid = normalizeDid(invokerDid);
5839
+ const validation = await validateDelegationChain(normalizedInvokerDid, capability);
5559
5840
  if (!validation.valid) {
5560
5841
  return {
5561
5842
  cid: "",
@@ -5564,11 +5845,11 @@ var createUcanService = (config) => {
5564
5845
  error: validation.error
5565
5846
  };
5566
5847
  }
5567
- const audience = params.audience ?? flowOwnerDid;
5848
+ const audience = normalizeDid(params.audience ?? flowOwnerDid);
5568
5849
  if (handlers.createSignerSession && handlers.createInvocationWithSession) {
5569
5850
  try {
5570
5851
  const session = await handlers.createSignerSession({
5571
- did: invokerDid,
5852
+ did: normalizedInvokerDid,
5572
5853
  didType: invokerType,
5573
5854
  entityRoomId,
5574
5855
  pin,
@@ -6083,6 +6364,9 @@ var createRuntimeStateManager = (editor) => {
6083
6364
  }
6084
6365
  return createMemoryManager();
6085
6366
  };
6367
+ var createYDocRuntimeManager = (yDoc) => {
6368
+ return createYMapManager(yDoc.getMap("runtime"));
6369
+ };
6086
6370
  function clearRuntimeForTemplateClone(yDoc) {
6087
6371
  const runtime = yDoc.getMap("runtime");
6088
6372
  const invocations = yDoc.getMap("invocations");
@@ -6100,6 +6384,66 @@ function clearRuntimeForTemplateClone(yDoc) {
6100
6384
  });
6101
6385
  }
6102
6386
 
6387
+ // src/core/types/baseUcan.ts
6388
+ function isRuntimeRef(value) {
6389
+ return typeof value === "object" && value !== null && "$ref" in value && typeof value.$ref === "string";
6390
+ }
6391
+
6392
+ // src/core/lib/flowCompiler/resolveRefs.ts
6393
+ function resolveRuntimeRefs(nb, getNodeOutput2, triggerContext) {
6394
+ return resolveValue(nb, getNodeOutput2, triggerContext);
6395
+ }
6396
+ function resolveValue(value, getNodeOutput2, triggerContext) {
6397
+ if (isRuntimeRef(value)) {
6398
+ return resolveRef(value.$ref, getNodeOutput2, triggerContext);
6399
+ }
6400
+ if (Array.isArray(value)) {
6401
+ return value.map((item) => resolveValue(item, getNodeOutput2, triggerContext));
6402
+ }
6403
+ if (typeof value === "object" && value !== null) {
6404
+ const result = {};
6405
+ for (const [key, val] of Object.entries(value)) {
6406
+ result[key] = resolveValue(val, getNodeOutput2, triggerContext);
6407
+ }
6408
+ return result;
6409
+ }
6410
+ return value;
6411
+ }
6412
+ function resolveRef(ref, getNodeOutput2, triggerContext) {
6413
+ if (ref.startsWith("trigger.payload.")) {
6414
+ if (!triggerContext) {
6415
+ throw new Error(`Trigger ref "${ref}" used outside of a listener invocation context. trigger.payload.* refs are only valid on block.event-triggered blocks.`);
6416
+ }
6417
+ const fieldPath2 = ref.slice("trigger.payload.".length);
6418
+ return getNestedValue(triggerContext.payload, fieldPath2);
6419
+ }
6420
+ if (triggerContext && Object.prototype.hasOwnProperty.call(triggerContext.refSnapshots, ref)) {
6421
+ return triggerContext.refSnapshots[ref];
6422
+ }
6423
+ const outputIndex = ref.indexOf(".output.");
6424
+ if (outputIndex === -1) {
6425
+ throw new Error(`Invalid runtime reference "${ref}". Expected format: "nodeId.output.fieldPath" or "trigger.payload.fieldPath"`);
6426
+ }
6427
+ const nodeId = ref.slice(0, outputIndex);
6428
+ const fieldPath = ref.slice(outputIndex + ".output.".length);
6429
+ const output = getNodeOutput2(nodeId);
6430
+ if (!output) {
6431
+ return void 0;
6432
+ }
6433
+ return getNestedValue(output, fieldPath);
6434
+ }
6435
+ function getNestedValue(obj, path) {
6436
+ const parts = path.split(".");
6437
+ let current = obj;
6438
+ for (const part of parts) {
6439
+ if (current == null || typeof current !== "object") {
6440
+ return void 0;
6441
+ }
6442
+ current = current[part];
6443
+ }
6444
+ return current;
6445
+ }
6446
+
6103
6447
  // src/core/lib/flowEngine/versionManifest.ts
6104
6448
  var VERSION_MANIFEST = {
6105
6449
  "0.3": {
@@ -6310,11 +6654,6 @@ var executeNode = async ({ node, actorDid, actorType, entityRoomId, context, act
6310
6654
  }
6311
6655
  };
6312
6656
 
6313
- // src/core/types/baseUcan.ts
6314
- function isRuntimeRef(value) {
6315
- return typeof value === "object" && value !== null && "$ref" in value && typeof value.$ref === "string";
6316
- }
6317
-
6318
6657
  // src/core/lib/flowEngine/triggers.ts
6319
6658
  import * as Y from "yjs";
6320
6659
  var RUN_RECORD_AUDIT_TYPE = "block.run";
@@ -6323,15 +6662,15 @@ function computePendingInvocationId(args) {
6323
6662
  const input = `${sourceBlockId}:${sourceRunId}:${listenerBlockId}:${eventName}:${eventIndex}`;
6324
6663
  return `pi-${fnv1a32(input)}`;
6325
6664
  }
6326
- function snapshotInputRefs(inputs, getNodeOutput) {
6665
+ function snapshotInputRefs(inputs, getNodeOutput2) {
6327
6666
  const snapshots = {};
6328
6667
  walkRefs(inputs, (ref) => {
6329
6668
  if (ref.$ref.startsWith("trigger.")) return;
6330
6669
  const parsed = parseOutputRef(ref.$ref);
6331
6670
  if (!parsed) return;
6332
- const output = getNodeOutput(parsed.nodeId);
6671
+ const output = getNodeOutput2(parsed.nodeId);
6333
6672
  if (!output) return;
6334
- snapshots[ref.$ref] = getNestedValue(output, parsed.fieldPath);
6673
+ snapshots[ref.$ref] = getNestedValue2(output, parsed.fieldPath);
6335
6674
  });
6336
6675
  return snapshots;
6337
6676
  }
@@ -6356,7 +6695,7 @@ function parseOutputRef(ref) {
6356
6695
  fieldPath: ref.slice(outputIndex + ".output.".length)
6357
6696
  };
6358
6697
  }
6359
- function getNestedValue(obj, path) {
6698
+ function getNestedValue2(obj, path) {
6360
6699
  const parts = path.split(".");
6361
6700
  let current = obj;
6362
6701
  for (const part of parts) {
@@ -6584,7 +6923,7 @@ function _reconcilePendingInvocationsInner(editor) {
6584
6923
  }
6585
6924
  if (listenersBySource.size === 0 && barrierListenersBySource.size === 0) return;
6586
6925
  const runtimeMap = editor._yRuntime;
6587
- const getNodeOutput = (nodeId) => {
6926
+ const getNodeOutput2 = (nodeId) => {
6588
6927
  if (!runtimeMap) return void 0;
6589
6928
  const state = runtimeMap.get(nodeId);
6590
6929
  if (!state || typeof state !== "object") return void 0;
@@ -6610,12 +6949,12 @@ function _reconcilePendingInvocationsInner(editor) {
6610
6949
  if (!hasAnyListener) continue;
6611
6950
  const records = readRunRecords(yDoc, sourceBlockId);
6612
6951
  for (const record of records) {
6613
- processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput, runtimeMap);
6614
- processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput, runtimeMap);
6952
+ processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput2, runtimeMap);
6953
+ processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput2, runtimeMap);
6615
6954
  }
6616
6955
  }
6617
6956
  }
6618
- function processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput, runtimeMap) {
6957
+ function processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput2, runtimeMap) {
6619
6958
  if (!Array.isArray(record.events)) return;
6620
6959
  record.events.forEach((event, eventIndex) => {
6621
6960
  if (!event?.name) return;
@@ -6634,7 +6973,7 @@ function processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNod
6634
6973
  });
6635
6974
  const assigneeDid = resolveAssignee(listenerBlock) || "unassigned";
6636
6975
  const inputs = parseInputs(listenerBlock);
6637
- const refSnapshots = snapshotInputRefs(inputs, getNodeOutput);
6976
+ const refSnapshots = snapshotInputRefs(inputs, getNodeOutput2);
6638
6977
  const expiresAt = computeExpiry(listenerBlock, record.completedAt);
6639
6978
  const invocation = {
6640
6979
  id,
@@ -6659,7 +6998,7 @@ function processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNod
6659
6998
  }
6660
6999
  });
6661
7000
  }
6662
- function processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput, runtimeMap) {
7001
+ function processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput2, runtimeMap) {
6663
7002
  if (!Array.isArray(record.events)) return;
6664
7003
  for (const event of record.events) {
6665
7004
  if (!event?.name) continue;
@@ -6694,7 +7033,7 @@ function processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBy
6694
7033
  const id = computeBarrierInvocationId(currentState, listenerBlockId);
6695
7034
  const mergedPayload = mergeBarrierPayloads(currentState);
6696
7035
  const inputs = parseInputs(listenerBlock);
6697
- const refSnapshots = snapshotInputRefs(inputs, getNodeOutput);
7036
+ const refSnapshots = snapshotInputRefs(inputs, getNodeOutput2);
6698
7037
  const expiresAt = computeExpiry(listenerBlock, record.completedAt);
6699
7038
  const invocation = {
6700
7039
  id,
@@ -6804,6 +7143,553 @@ function writeRunRecordAndReconcile(editor, blockId, output, events, actorDid, d
6804
7143
  reconcilePendingInvocations(editor);
6805
7144
  }
6806
7145
 
7146
+ // src/core/lib/flowEngine/actionExecutor.ts
7147
+ import * as Y3 from "yjs";
7148
+
7149
+ // src/core/lib/flowEngine/readBackReconciler.ts
7150
+ import * as Y2 from "yjs";
7151
+ function isYDoc(value) {
7152
+ return value instanceof Y2.Doc;
7153
+ }
7154
+ function getYDoc(editorOrYDoc) {
7155
+ if (!editorOrYDoc) return void 0;
7156
+ if (isYDoc(editorOrYDoc)) return editorOrYDoc;
7157
+ return editorOrYDoc._yDoc;
7158
+ }
7159
+ function getEditor(editorOrYDoc) {
7160
+ return editorOrYDoc && !isYDoc(editorOrYDoc) ? editorOrYDoc : void 0;
7161
+ }
7162
+ function getRuntime(editorOrYDoc, runtime) {
7163
+ if (runtime) return runtime;
7164
+ const yDoc = getYDoc(editorOrYDoc);
7165
+ if (yDoc) return createYDocRuntimeManager(yDoc);
7166
+ return createRuntimeStateManager(!isYDoc(editorOrYDoc) ? editorOrYDoc : void 0);
7167
+ }
7168
+ function getReconcileEditor(params, yDoc, editor) {
7169
+ if (editor) return editor;
7170
+ if (!yDoc || !params.document) return void 0;
7171
+ return {
7172
+ _yDoc: yDoc,
7173
+ _yRuntime: yDoc.getMap("runtime"),
7174
+ document: params.document
7175
+ };
7176
+ }
7177
+ function makeRunId(now) {
7178
+ return `readback-${now()}-${Math.random().toString(36).slice(2, 8)}`;
7179
+ }
7180
+ function asOutput(value) {
7181
+ return value && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
7182
+ }
7183
+ function errorRecord(error) {
7184
+ if (!error) return void 0;
7185
+ return typeof error === "string" ? { message: error } : { message: error.message, code: error.code };
7186
+ }
7187
+ function normalizeActionReadBackMetadata(params) {
7188
+ const base = params.readBack && typeof params.readBack === "object" && !Array.isArray(params.readBack) ? { ...params.readBack } : {};
7189
+ const kind = typeof base.kind === "string" && base.kind.trim() ? base.kind.trim() : params.actionType;
7190
+ const normalized = {
7191
+ ...base,
7192
+ kind,
7193
+ status: "pending",
7194
+ requestedAt: typeof base.requestedAt === "string" ? base.requestedAt : new Date(params.requestedAt).toISOString(),
7195
+ blockId: params.blockId,
7196
+ actionType: params.actionType,
7197
+ actorDid: params.actorDid,
7198
+ invocationCid: params.invocationCid,
7199
+ capabilityId: params.capabilityId
7200
+ };
7201
+ if (params.pendingInvocation) {
7202
+ normalized.pendingInvocation = {
7203
+ id: params.pendingInvocation.id,
7204
+ triggeringBlockId: params.pendingInvocation.triggeringBlockId,
7205
+ eventName: params.pendingInvocation.eventName,
7206
+ sourceRunId: params.pendingInvocation.sourceRunId
7207
+ };
7208
+ }
7209
+ return normalized;
7210
+ }
7211
+ function writeReconciliationRunRecord(params) {
7212
+ if (!params.yDoc) return void 0;
7213
+ const completedAt = params.now();
7214
+ const runId = makeRunId(params.now);
7215
+ const pendingInvocation = params.readBack.pendingInvocation;
7216
+ const details = {
7217
+ runId,
7218
+ output: params.output,
7219
+ events: params.events,
7220
+ startedAt: params.readBack.requestedAt || new Date(completedAt).toISOString(),
7221
+ completedAt: new Date(completedAt).toISOString(),
7222
+ actorDid: params.actorDid,
7223
+ invocationCid: params.readBack.invocationCid,
7224
+ capabilityId: params.readBack.capabilityId,
7225
+ error: params.error,
7226
+ readBack: params.readBack,
7227
+ reconciled: true
7228
+ };
7229
+ if (pendingInvocation) {
7230
+ details.fromPendingInvocationId = pendingInvocation.id;
7231
+ details.triggeredBy = {
7232
+ sourceBlockId: pendingInvocation.triggeringBlockId,
7233
+ eventName: pendingInvocation.eventName
7234
+ };
7235
+ details.sourceRunId = pendingInvocation.sourceRunId;
7236
+ }
7237
+ appendRunRecord(params.yDoc, params.blockId, details, params.actorDid);
7238
+ if (params.editor && params.events.length > 0) {
7239
+ reconcilePendingInvocations(params.editor);
7240
+ }
7241
+ return runId;
7242
+ }
7243
+ async function reconcileActionReadBack(params) {
7244
+ const now = params.now || Date.now;
7245
+ const yDoc = getYDoc(params.editorOrYDoc);
7246
+ const editor = getEditor(params.editorOrYDoc);
7247
+ const runtime = getRuntime(params.editorOrYDoc, params.runtime);
7248
+ const current = runtime.get(params.blockId);
7249
+ const output = asOutput(current.output);
7250
+ const readBack = current.readBack;
7251
+ if (current.state !== "awaiting_readback" || !readBack?.kind) {
7252
+ return {
7253
+ success: false,
7254
+ blockId: params.blockId,
7255
+ state: current.state === "failed" ? "failed" : "pending",
7256
+ output,
7257
+ events: [],
7258
+ error: `Block "${params.blockId}" is not awaiting read-back`,
7259
+ pendingInvocationRemoved: false,
7260
+ readBack
7261
+ };
7262
+ }
7263
+ const resolver = params.resolver || params.resolvers?.[readBack.kind];
7264
+ if (!resolver) {
7265
+ return {
7266
+ success: false,
7267
+ blockId: params.blockId,
7268
+ state: "pending",
7269
+ output,
7270
+ events: [],
7271
+ error: `No read-back resolver registered for "${readBack.kind}"`,
7272
+ pendingInvocationRemoved: false,
7273
+ readBack
7274
+ };
7275
+ }
7276
+ const resolution = await resolver({
7277
+ blockId: params.blockId,
7278
+ runtime: current,
7279
+ output,
7280
+ readBack
7281
+ });
7282
+ const checkedAt = now();
7283
+ const checkedIso = new Date(checkedAt).toISOString();
7284
+ const nextReadBack = {
7285
+ ...readBack,
7286
+ ...resolution.readBack || {},
7287
+ status: resolution.state,
7288
+ lastCheckedAt: checkedIso
7289
+ };
7290
+ if (resolution.state === "pending") {
7291
+ runtime.update(params.blockId, {
7292
+ readBack: nextReadBack
7293
+ });
7294
+ return {
7295
+ success: true,
7296
+ blockId: params.blockId,
7297
+ state: "pending",
7298
+ output,
7299
+ events: resolution.events || [],
7300
+ pendingInvocationRemoved: false,
7301
+ readBack: nextReadBack
7302
+ };
7303
+ }
7304
+ const events = resolution.events || [];
7305
+ const finalOutput = {
7306
+ ...output,
7307
+ ...resolution.output || {}
7308
+ };
7309
+ const actorDid = params.actorDid || readBack.actorDid || current.executedByDid || "system:readback";
7310
+ if (resolution.state === "failed") {
7311
+ const error = errorRecord(resolution.error) || { message: "External read-back failed" };
7312
+ const failedReadBack = {
7313
+ ...nextReadBack,
7314
+ terminalAt: checkedIso
7315
+ };
7316
+ runtime.update(params.blockId, {
7317
+ state: "failed",
7318
+ output: finalOutput,
7319
+ error: { ...error, at: checkedAt },
7320
+ readBack: failedReadBack
7321
+ });
7322
+ const runId2 = writeReconciliationRunRecord({
7323
+ yDoc,
7324
+ editor: getReconcileEditor(params, yDoc, editor),
7325
+ blockId: params.blockId,
7326
+ actorDid,
7327
+ output: finalOutput,
7328
+ events,
7329
+ readBack: failedReadBack,
7330
+ error,
7331
+ now
7332
+ });
7333
+ return {
7334
+ success: false,
7335
+ blockId: params.blockId,
7336
+ state: "failed",
7337
+ output: finalOutput,
7338
+ events,
7339
+ error: error.message,
7340
+ runId: runId2,
7341
+ pendingInvocationRemoved: false,
7342
+ readBack: failedReadBack
7343
+ };
7344
+ }
7345
+ const completedReadBack = {
7346
+ ...nextReadBack,
7347
+ terminalAt: checkedIso
7348
+ };
7349
+ runtime.update(params.blockId, {
7350
+ state: "completed",
7351
+ output: finalOutput,
7352
+ executedAt: checkedAt,
7353
+ error: void 0,
7354
+ readBack: completedReadBack
7355
+ });
7356
+ const runId = writeReconciliationRunRecord({
7357
+ yDoc,
7358
+ editor: getReconcileEditor(params, yDoc, editor),
7359
+ blockId: params.blockId,
7360
+ actorDid,
7361
+ output: finalOutput,
7362
+ events,
7363
+ readBack: completedReadBack,
7364
+ now
7365
+ });
7366
+ const pendingInvocationRemoved = Boolean(yDoc && completedReadBack.pendingInvocation?.id) && removePendingInvocation(yDoc, params.blockId, completedReadBack.pendingInvocation.id);
7367
+ return {
7368
+ success: true,
7369
+ blockId: params.blockId,
7370
+ state: "completed",
7371
+ output: finalOutput,
7372
+ events,
7373
+ runId,
7374
+ pendingInvocationRemoved,
7375
+ readBack: completedReadBack
7376
+ };
7377
+ }
7378
+
7379
+ // src/core/lib/flowEngine/actionExecutor.ts
7380
+ function isYDoc2(value) {
7381
+ return value instanceof Y3.Doc;
7382
+ }
7383
+ function getYDoc2(editorOrYDoc) {
7384
+ if (!editorOrYDoc) return void 0;
7385
+ if (isYDoc2(editorOrYDoc)) return editorOrYDoc;
7386
+ return editorOrYDoc._yDoc;
7387
+ }
7388
+ function parseInputs2(value) {
7389
+ if (value == null || value === "") return {};
7390
+ if (typeof value === "object" && !Array.isArray(value)) return { ...value };
7391
+ if (typeof value !== "string") return {};
7392
+ try {
7393
+ const parsed = JSON.parse(value);
7394
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
7395
+ } catch {
7396
+ return {};
7397
+ }
7398
+ }
7399
+ function getRuntime2(editorOrYDoc, runtime) {
7400
+ if (runtime) return runtime;
7401
+ const yDoc = getYDoc2(editorOrYDoc);
7402
+ if (yDoc) return createYDocRuntimeManager(yDoc);
7403
+ return createRuntimeStateManager(!isYDoc2(editorOrYDoc) ? editorOrYDoc : void 0);
7404
+ }
7405
+ function getEditor2(editorOrYDoc) {
7406
+ return editorOrYDoc && !isYDoc2(editorOrYDoc) ? editorOrYDoc : void 0;
7407
+ }
7408
+ function findBlock(params) {
7409
+ if (params.block) return params.block;
7410
+ const blockId = params.blockId;
7411
+ if (!blockId) return void 0;
7412
+ const editor = getEditor2(params.editorOrYDoc);
7413
+ return (params.document || editor?.document || []).find((block) => block?.id === blockId);
7414
+ }
7415
+ function getFlowMetadata(editor) {
7416
+ const metadata = editor?.getFlowMetadata?.();
7417
+ const flowId = metadata?.doc_id || "";
7418
+ return {
7419
+ flowId,
7420
+ flowUri: flowId ? `ixo:flow:${flowId}` : "",
7421
+ flowOwnerDid: editor?.getFlowOwnerDid?.() || metadata?.flowOwnerDid || "",
7422
+ schemaVersion: metadata?.schema_version || "0.3"
7423
+ };
7424
+ }
7425
+ function getPendingInvocation(yDoc, blockId, pendingInvocationId) {
7426
+ if (!pendingInvocationId) return void 0;
7427
+ if (!yDoc) {
7428
+ throw new Error(`Cannot load pending invocation "${pendingInvocationId}" without a Y.Doc`);
7429
+ }
7430
+ if (!blockId) {
7431
+ throw new Error(`Cannot load pending invocation "${pendingInvocationId}" without a block id`);
7432
+ }
7433
+ const pending = readPendingInvocations(yDoc, blockId).find((invocation) => invocation.id === pendingInvocationId);
7434
+ if (!pending) {
7435
+ throw new Error(`Pending invocation "${pendingInvocationId}" was not found for block "${blockId}"`);
7436
+ }
7437
+ return pending;
7438
+ }
7439
+ function getNodeOutput(runtime, nodeId) {
7440
+ const state = runtime.get(nodeId);
7441
+ const output = state.output;
7442
+ return output && typeof output === "object" && !Array.isArray(output) ? output : void 0;
7443
+ }
7444
+ function buildActionRunInputs(params) {
7445
+ const blockId = params.blockId || params.block?.id;
7446
+ const yDoc = getYDoc2(params.editorOrYDoc);
7447
+ const runtime = getRuntime2(params.editorOrYDoc, params.runtime);
7448
+ const savedInputs = parseInputs2(params.savedInputs ?? params.block?.props?.inputs);
7449
+ const pendingInvocation = getPendingInvocation(yDoc, blockId, params.pendingInvocationId);
7450
+ const triggerContext = pendingInvocation ? {
7451
+ payload: pendingInvocation.payload || {},
7452
+ refSnapshots: pendingInvocation.refSnapshots || {}
7453
+ } : void 0;
7454
+ const mergedInputs = {
7455
+ ...savedInputs,
7456
+ ...pendingInvocation?.payload || {},
7457
+ ...pendingInvocation?.refSnapshots || {},
7458
+ ...params.runtimeInputs || {}
7459
+ };
7460
+ return {
7461
+ inputs: resolveRuntimeRefs(mergedInputs, (nodeId) => getNodeOutput(runtime, nodeId), triggerContext),
7462
+ savedInputs,
7463
+ mergedInputs,
7464
+ pendingInvocation,
7465
+ triggerContext
7466
+ };
7467
+ }
7468
+ function buildFailureResult(params) {
7469
+ return {
7470
+ success: false,
7471
+ stage: params.stage,
7472
+ blockId: params.blockId,
7473
+ actionType: params.actionType,
7474
+ events: [],
7475
+ error: params.error,
7476
+ completionState: "failed",
7477
+ pendingInvocation: params.pendingInvocation
7478
+ };
7479
+ }
7480
+ function updateRuntimeFailure(runtime, blockId, message, now) {
7481
+ runtime.update(blockId, {
7482
+ state: "failed",
7483
+ error: { message, at: now() }
7484
+ });
7485
+ }
7486
+ function makeRunId2(now) {
7487
+ return `run-${now()}-${Math.random().toString(36).slice(2, 8)}`;
7488
+ }
7489
+ function getReconcileEditor2(params, yDoc, editor) {
7490
+ if (editor) return editor;
7491
+ if (!yDoc || !params.document) return void 0;
7492
+ return {
7493
+ _yDoc: yDoc,
7494
+ _yRuntime: yDoc.getMap("runtime"),
7495
+ document: params.document
7496
+ };
7497
+ }
7498
+ function persistEvents(params) {
7499
+ if (!params.yDoc || params.events.length === 0) return void 0;
7500
+ const runId = makeRunId2(params.now);
7501
+ const details = {
7502
+ runId,
7503
+ output: params.output,
7504
+ events: params.events,
7505
+ startedAt: new Date(params.startedAt).toISOString(),
7506
+ completedAt: new Date(params.completedAt).toISOString(),
7507
+ actorDid: params.actorDid,
7508
+ invocationCid: params.invocationCid,
7509
+ capabilityId: params.capabilityId
7510
+ };
7511
+ if (params.pendingInvocation) {
7512
+ details.fromPendingInvocationId = params.pendingInvocation.id;
7513
+ details.triggeredBy = {
7514
+ sourceBlockId: params.pendingInvocation.triggeringBlockId,
7515
+ eventName: params.pendingInvocation.eventName
7516
+ };
7517
+ details.sourceRunId = params.pendingInvocation.sourceRunId;
7518
+ }
7519
+ appendRunRecord(params.yDoc, params.blockId, details, params.actorDid);
7520
+ if (params.editor) {
7521
+ reconcilePendingInvocations(params.editor);
7522
+ }
7523
+ return runId;
7524
+ }
7525
+ function cleanupCompletedPendingInvocation(yDoc, blockId, pendingInvocation) {
7526
+ if (!yDoc || !pendingInvocation) return false;
7527
+ return removePendingInvocation(yDoc, blockId, pendingInvocation.id);
7528
+ }
7529
+ async function executeActionBlock(params) {
7530
+ const block = findBlock(params);
7531
+ const blockId = params.blockId || block?.id;
7532
+ const editor = getEditor2(params.editorOrYDoc);
7533
+ const yDoc = getYDoc2(params.editorOrYDoc);
7534
+ const runtime = getRuntime2(params.editorOrYDoc, params.runtime);
7535
+ const now = params.now || Date.now;
7536
+ if (!block || !blockId) {
7537
+ return buildFailureResult({
7538
+ blockId: blockId || "",
7539
+ stage: "input",
7540
+ error: blockId ? `Block "${blockId}" was not found` : "executeActionBlock requires a blockId or block"
7541
+ });
7542
+ }
7543
+ const actionType = typeof block.props?.actionType === "string" ? block.props.actionType : void 0;
7544
+ if (!actionType) {
7545
+ updateRuntimeFailure(runtime, blockId, "Action block is missing props.actionType", now);
7546
+ return buildFailureResult({
7547
+ blockId,
7548
+ stage: "input",
7549
+ error: "Action block is missing props.actionType"
7550
+ });
7551
+ }
7552
+ const action = getAction(actionType);
7553
+ if (!action) {
7554
+ const message = `No registered action found for ${actionType}`;
7555
+ updateRuntimeFailure(runtime, blockId, message, now);
7556
+ return buildFailureResult({ blockId, actionType, stage: "action_lookup", error: message });
7557
+ }
7558
+ let inputBuild;
7559
+ try {
7560
+ inputBuild = buildActionRunInputs({ ...params, block, blockId, runtime });
7561
+ } catch (error) {
7562
+ const message = error instanceof Error ? error.message : "Failed to build action inputs";
7563
+ updateRuntimeFailure(runtime, blockId, message, now);
7564
+ return buildFailureResult({ blockId, actionType, stage: "input", error: message });
7565
+ }
7566
+ const flowNode = buildFlowNodeFromBlock(block);
7567
+ const metadata = getFlowMetadata(editor);
7568
+ const flowId = params.flowId || metadata.flowId || blockId;
7569
+ const flowUri = params.flowUri || metadata.flowUri || `ixo:flow:${flowId}`;
7570
+ const flowOwnerDid = params.flowOwnerDid || metadata.flowOwnerDid;
7571
+ const schemaVersion = params.schemaVersion || metadata.schemaVersion;
7572
+ const events = [];
7573
+ let completionState = "completed";
7574
+ let readBack;
7575
+ let rawReadBack;
7576
+ let requestedAwaitingReadBack = false;
7577
+ const startedAt = now();
7578
+ runtime.update(blockId, {
7579
+ state: "running",
7580
+ output: void 0,
7581
+ error: void 0,
7582
+ executedByDid: params.actorDid
7583
+ });
7584
+ const outcome = await executeNode({
7585
+ node: flowNode,
7586
+ actorDid: params.actorDid,
7587
+ actorType: params.actorType,
7588
+ entityRoomId: params.entityRoomId,
7589
+ context: {
7590
+ runtime,
7591
+ ucanService: params.ucanService || editor?.getUcanService?.() || void 0,
7592
+ invocationStore: params.invocationStore || editor?._invocationStore,
7593
+ flowUri,
7594
+ flowId,
7595
+ flowOwnerDid,
7596
+ schemaVersion,
7597
+ now
7598
+ },
7599
+ action: async () => {
7600
+ const result = await action.run(inputBuild.inputs, {
7601
+ actorDid: params.actorDid,
7602
+ flowId,
7603
+ flowUri,
7604
+ nodeId: blockId,
7605
+ flowNode,
7606
+ runtime,
7607
+ services: params.services || {},
7608
+ handlers: params.handlers,
7609
+ editor,
7610
+ pendingInvocation: inputBuild.pendingInvocation
7611
+ });
7612
+ if (result.events?.length) events.push(...result.events);
7613
+ if (result.completion?.state === "awaiting_readback") {
7614
+ requestedAwaitingReadBack = true;
7615
+ rawReadBack = result.completion.readBack;
7616
+ }
7617
+ return {
7618
+ payload: result.output,
7619
+ submittedByDid: params.actorDid || void 0
7620
+ };
7621
+ },
7622
+ pin: params.pin || ""
7623
+ });
7624
+ if (!outcome.success) {
7625
+ const message = outcome.error || "Action execution failed";
7626
+ updateRuntimeFailure(runtime, blockId, message, now);
7627
+ return {
7628
+ ...buildFailureResult({
7629
+ blockId,
7630
+ actionType,
7631
+ stage: outcome.stage,
7632
+ error: message,
7633
+ pendingInvocation: inputBuild.pendingInvocation
7634
+ }),
7635
+ invocationCid: outcome.invocationCid,
7636
+ capabilityId: outcome.capabilityId
7637
+ };
7638
+ }
7639
+ const output = outcome.result?.payload || {};
7640
+ const completedAt = now();
7641
+ if (requestedAwaitingReadBack) {
7642
+ completionState = "awaiting_readback";
7643
+ readBack = normalizeActionReadBackMetadata({
7644
+ readBack: rawReadBack,
7645
+ blockId,
7646
+ actionType,
7647
+ actorDid: params.actorDid,
7648
+ invocationCid: outcome.invocationCid,
7649
+ capabilityId: outcome.capabilityId,
7650
+ requestedAt: startedAt,
7651
+ pendingInvocation: inputBuild.pendingInvocation
7652
+ });
7653
+ }
7654
+ runtime.update(blockId, {
7655
+ state: completionState,
7656
+ output,
7657
+ executedByDid: params.actorDid,
7658
+ executedAt: completedAt,
7659
+ lastInvocationCid: outcome.invocationCid || outcome.capabilityId,
7660
+ readBack
7661
+ });
7662
+ const runId = persistEvents({
7663
+ yDoc,
7664
+ editor: getReconcileEditor2(params, yDoc, editor),
7665
+ blockId,
7666
+ actorDid: params.actorDid,
7667
+ output,
7668
+ events,
7669
+ invocationCid: outcome.invocationCid,
7670
+ capabilityId: outcome.capabilityId,
7671
+ pendingInvocation: inputBuild.pendingInvocation,
7672
+ startedAt,
7673
+ completedAt,
7674
+ now
7675
+ });
7676
+ const pendingInvocationRemoved = completionState === "completed" ? cleanupCompletedPendingInvocation(yDoc, blockId, inputBuild.pendingInvocation) : false;
7677
+ return {
7678
+ success: true,
7679
+ stage: outcome.stage,
7680
+ blockId,
7681
+ actionType,
7682
+ output,
7683
+ events,
7684
+ invocationCid: outcome.invocationCid,
7685
+ capabilityId: outcome.capabilityId,
7686
+ runId,
7687
+ pendingInvocationRemoved,
7688
+ completionState,
7689
+ pendingInvocation: inputBuild.pendingInvocation
7690
+ };
7691
+ }
7692
+
6807
7693
  // src/core/lib/flowEngine/migration.ts
6808
7694
  var MIGRATION_REGISTRY = {};
6809
7695
  function registerMigration(definition) {
@@ -7251,13 +8137,13 @@ function detectTriggerCycles(triggerEdges) {
7251
8137
  }
7252
8138
 
7253
8139
  // src/core/lib/flowCompiler/readFlow.ts
7254
- import * as Y2 from "yjs";
8140
+ import * as Y4 from "yjs";
7255
8141
  function readCompiledFlowFromYDoc(yDoc) {
7256
8142
  const flowMeta = yDoc.getMap("qi.flow.meta");
7257
8143
  const flowNodes = yDoc.getMap("qi.flow.nodes");
7258
8144
  const nodes = {};
7259
8145
  flowNodes.forEach((value, nodeId) => {
7260
- if (value instanceof Y2.Map) {
8146
+ if (value instanceof Y4.Map) {
7261
8147
  nodes[nodeId] = yMapToFlowNode(value);
7262
8148
  }
7263
8149
  });
@@ -7277,7 +8163,7 @@ function readCompiledFlowFromYDoc(yDoc) {
7277
8163
  const flowEdges = yDoc.getMap("qi.flow.edges");
7278
8164
  const edges = [];
7279
8165
  flowEdges.forEach((value) => {
7280
- if (value instanceof Y2.Map) {
8166
+ if (value instanceof Y4.Map) {
7281
8167
  edges.push(yMapToEdge(value));
7282
8168
  }
7283
8169
  });
@@ -7458,11 +8344,11 @@ function nodeToCapability(node) {
7458
8344
  }
7459
8345
 
7460
8346
  // src/core/lib/flowCompiler/setup.ts
7461
- import * as Y5 from "yjs";
8347
+ import * as Y7 from "yjs";
7462
8348
  import { MatrixProvider } from "@ixo/matrix-crdt";
7463
8349
 
7464
8350
  // src/core/lib/flowCompiler/hydrate.ts
7465
- import * as Y3 from "yjs";
8351
+ import * as Y5 from "yjs";
7466
8352
  function hydrateYDocFromCompiledFlow(yDoc, compiled) {
7467
8353
  yDoc.transact(() => {
7468
8354
  const flowMeta = yDoc.getMap("qi.flow.meta");
@@ -7477,7 +8363,7 @@ function hydrateYDocFromCompiledFlow(yDoc, compiled) {
7477
8363
  }
7478
8364
  const flowEdges = yDoc.getMap("qi.flow.edges");
7479
8365
  for (const edge of compiled.edges) {
7480
- const yEdge = new Y3.Map();
8366
+ const yEdge = new Y5.Map();
7481
8367
  yEdge.set("id", edge.id);
7482
8368
  yEdge.set("source", edge.source);
7483
8369
  yEdge.set("target", edge.target);
@@ -7531,7 +8417,7 @@ function hydrateYDocFromMergeResult(yDoc, mergeResult) {
7531
8417
  const flowEdges = yDoc.getMap("qi.flow.edges");
7532
8418
  flowEdges.forEach((_, key) => flowEdges.delete(key));
7533
8419
  for (const edge of merged.edges) {
7534
- const yEdge = new Y3.Map();
8420
+ const yEdge = new Y5.Map();
7535
8421
  yEdge.set("id", edge.id);
7536
8422
  yEdge.set("source", edge.source);
7537
8423
  yEdge.set("target", edge.target);
@@ -7566,7 +8452,7 @@ function initializeRuntimeForNodes(runtimeMap, compiled, nodeIds) {
7566
8452
  }
7567
8453
  }
7568
8454
  function createYMapFromNode(node) {
7569
- const yNode = new Y3.Map();
8455
+ const yNode = new Y5.Map();
7570
8456
  yNode.set("id", node.id);
7571
8457
  yNode.set("blockId", node.blockId);
7572
8458
  yNode.set("can", node.can);
@@ -7581,7 +8467,7 @@ function createYMapFromNode(node) {
7581
8467
  }
7582
8468
 
7583
8469
  // src/core/lib/flowCompiler/documentFragment.ts
7584
- import * as Y4 from "yjs";
8470
+ import * as Y6 from "yjs";
7585
8471
  function writeCompiledBlocksToFragment(fragment, blocks) {
7586
8472
  const documentBlockGroup = getOrCreateDocumentBlockGroup(fragment);
7587
8473
  for (const block of blocks) {
@@ -7600,7 +8486,7 @@ function replaceBlockInFragment(fragment, block) {
7600
8486
  if (!blockGroup) return false;
7601
8487
  for (let i = 0; i < blockGroup.length; i++) {
7602
8488
  const container = blockGroup.get(i);
7603
- if (container instanceof Y4.XmlElement && container.getAttribute("id") === block.id) {
8489
+ if (container instanceof Y6.XmlElement && container.getAttribute("id") === block.id) {
7604
8490
  blockGroup.delete(i, 1);
7605
8491
  const newContainer = createBlockContainer(block);
7606
8492
  blockGroup.insert(i, [newContainer]);
@@ -7634,12 +8520,12 @@ function applyMergeResultToFragment(fragment, mergeResult) {
7634
8520
  }
7635
8521
  }
7636
8522
  function createBlockContainer(block) {
7637
- const blockContainer = new Y4.XmlElement("blockContainer");
8523
+ const blockContainer = new Y6.XmlElement("blockContainer");
7638
8524
  const { backgroundColor: rawBackgroundColor, textColor: rawTextColor, ...contentProps } = block.props;
7639
8525
  blockContainer.setAttribute("id", block.id);
7640
8526
  blockContainer.setAttribute("textColor", rawTextColor || "default");
7641
8527
  blockContainer.setAttribute("backgroundColor", rawBackgroundColor || "default");
7642
- const blockContent = new Y4.XmlElement(block.type);
8528
+ const blockContent = new Y6.XmlElement(block.type);
7643
8529
  for (const [key, value] of Object.entries(contentProps)) {
7644
8530
  if (value !== "") {
7645
8531
  blockContent.setAttribute(key, value);
@@ -7654,13 +8540,13 @@ function appendBlockToGroup(blockGroup, block) {
7654
8540
  }
7655
8541
  function getOrCreateDocumentBlockGroup(fragment) {
7656
8542
  if (fragment.length === 0) {
7657
- const blockGroup = new Y4.XmlElement("blockGroup");
8543
+ const blockGroup = new Y6.XmlElement("blockGroup");
7658
8544
  fragment.insert(0, [blockGroup]);
7659
8545
  return blockGroup;
7660
8546
  }
7661
8547
  if (fragment.length === 1) {
7662
8548
  const rootNode = fragment.get(0);
7663
- if (rootNode instanceof Y4.XmlElement && rootNode.nodeName === "blockGroup") {
8549
+ if (rootNode instanceof Y6.XmlElement && rootNode.nodeName === "blockGroup") {
7664
8550
  return rootNode;
7665
8551
  }
7666
8552
  }
@@ -7670,7 +8556,7 @@ function getExistingBlockGroup(fragment) {
7670
8556
  if (fragment.length === 0) return null;
7671
8557
  if (fragment.length === 1) {
7672
8558
  const rootNode = fragment.get(0);
7673
- if (rootNode instanceof Y4.XmlElement && rootNode.nodeName === "blockGroup") {
8559
+ if (rootNode instanceof Y6.XmlElement && rootNode.nodeName === "blockGroup") {
7674
8560
  return rootNode;
7675
8561
  }
7676
8562
  }
@@ -7773,7 +8659,7 @@ async function setupFlowFromBaseUcan(options) {
7773
8659
  };
7774
8660
  }
7775
8661
  async function connectToRoom(roomId, matrixClient) {
7776
- const yDoc = new Y5.Doc();
8662
+ const yDoc = new Y7.Doc();
7777
8663
  const client = matrixClient;
7778
8664
  client.canSupportVoip = false;
7779
8665
  client.clientOpts = { lazyLoadMembers: true };
@@ -7828,7 +8714,7 @@ function setRootMetadata(yDoc, root, plan, creatorDid, docId) {
7828
8714
  }
7829
8715
 
7830
8716
  // src/core/lib/flowAgent/store.ts
7831
- import * as Y6 from "yjs";
8717
+ import * as Y8 from "yjs";
7832
8718
 
7833
8719
  // src/core/lib/flowAgent/utils.ts
7834
8720
  function stableStringify(value) {
@@ -8009,7 +8895,7 @@ function appendAgentLedgerEvent(yDoc, event) {
8009
8895
  const auditMap = yDoc.getMap("auditTrail");
8010
8896
  let arr = auditMap.get(FLOW_AGENT_AUDIT_SCOPE);
8011
8897
  if (!arr) {
8012
- arr = new Y6.Array();
8898
+ arr = new Y8.Array();
8013
8899
  auditMap.set(FLOW_AGENT_AUDIT_SCOPE, arr);
8014
8900
  }
8015
8901
  const fullEvent = {
@@ -8273,12 +9159,88 @@ function snapshotNode(block, runtime, now, pendingInvocationCount = 0) {
8273
9159
  return snapshot;
8274
9160
  }
8275
9161
 
9162
+ // src/core/lib/flowAgent/actionExecutionPolicy.ts
9163
+ var RUNTIME_INPUT_REQUIRED_ACTIONS = /* @__PURE__ */ new Set(["qi/form.submit", "qi/human.form.submit", "qi/human.checkbox.set", "qi/protocol.select"]);
9164
+ function isRecord(value) {
9165
+ return value != null && typeof value === "object" && !Array.isArray(value);
9166
+ }
9167
+ function parseStringList(value) {
9168
+ const parsed = parseJsonProp(value, []);
9169
+ if (!Array.isArray(parsed)) return [];
9170
+ return parsed.filter((item) => typeof item === "string" && item.length > 0);
9171
+ }
9172
+ function normalizeMode(value) {
9173
+ if (value === "saved-input" || value === "saved_input" || value === "savedInput") return "saved-input";
9174
+ if (value === "runtime-input-required" || value === "runtime_input_required" || value === "runtimeInputRequired") return "runtime-input-required";
9175
+ if (value === "human-only" || value === "human_only" || value === "humanOnly") return "human-only";
9176
+ return void 0;
9177
+ }
9178
+ function parseExecutionConfig(value) {
9179
+ if (value == null || value === "") return {};
9180
+ if (typeof value === "string") {
9181
+ const directMode = normalizeMode(value);
9182
+ if (directMode) return { mode: directMode };
9183
+ }
9184
+ const parsed = parseJsonProp(value, {});
9185
+ if (!isRecord(parsed)) return {};
9186
+ return {
9187
+ mode: normalizeMode(parsed.mode),
9188
+ requiredRuntimeInputs: parseStringList(parsed.requiredRuntimeInputs)
9189
+ };
9190
+ }
9191
+ function inferDefaultMode(actionType, requiredRuntimeInputs) {
9192
+ if (requiredRuntimeInputs.length > 0) return "runtime-input-required";
9193
+ if (actionType && RUNTIME_INPUT_REQUIRED_ACTIONS.has(actionType)) return "runtime-input-required";
9194
+ return "saved-input";
9195
+ }
9196
+ function getFlowAgentActionExecutionPolicy(block) {
9197
+ const props = getBlockProps(block);
9198
+ const actionType = getBlockActionType(block);
9199
+ const config = parseExecutionConfig(props.flowAgentExecution || props.flowAgentExecutionMode || props.agentExecutionMode);
9200
+ const requiredRuntimeInputs = [
9201
+ ...config.requiredRuntimeInputs || [],
9202
+ ...parseStringList(props.requiredRuntimeInputs || props.runtimeRequiredInputs || props.flowAgentRequiredRuntimeInputs)
9203
+ ];
9204
+ return {
9205
+ mode: config.mode || inferDefaultMode(actionType, requiredRuntimeInputs),
9206
+ requiredRuntimeInputs: Array.from(new Set(requiredRuntimeInputs))
9207
+ };
9208
+ }
9209
+ function getCommandRuntimeInputs(payload) {
9210
+ return isRecord(payload.runtimeInputs) ? payload.runtimeInputs : void 0;
9211
+ }
9212
+ function validateFlowAgentExecuteActionPayload(block, payload) {
9213
+ const policy = getFlowAgentActionExecutionPolicy(block);
9214
+ if (policy.mode === "human-only") {
9215
+ return {
9216
+ valid: false,
9217
+ mode: policy.mode,
9218
+ missingRuntimeInputs: [],
9219
+ reason: "Action requires human execution and cannot be run by the Flow Agent"
9220
+ };
9221
+ }
9222
+ if (policy.mode !== "runtime-input-required") {
9223
+ return { valid: true, mode: policy.mode, missingRuntimeInputs: [] };
9224
+ }
9225
+ const runtimeInputs = getCommandRuntimeInputs(payload);
9226
+ const missingRuntimeInputs = policy.requiredRuntimeInputs.length > 0 ? policy.requiredRuntimeInputs.filter((key) => runtimeInputs?.[key] == null || runtimeInputs[key] === "") : runtimeInputs ? [] : ["runtimeInputs"];
9227
+ if (missingRuntimeInputs.length > 0) {
9228
+ return {
9229
+ valid: false,
9230
+ mode: policy.mode,
9231
+ missingRuntimeInputs,
9232
+ reason: policy.requiredRuntimeInputs.length > 0 ? `Missing required runtimeInputs: ${missingRuntimeInputs.join(", ")}` : "Action requires runtimeInputs before the Flow Agent can execute it"
9233
+ };
9234
+ }
9235
+ return { valid: true, mode: policy.mode, missingRuntimeInputs: [] };
9236
+ }
9237
+
8276
9238
  // src/core/lib/flowAgent/orchestrator.ts
8277
9239
  var BLOCKER_DIAGNOSIS_VERSION = 2;
8278
9240
  function getBlocks(context) {
8279
9241
  return context.blocks || context.editor?.document || [];
8280
9242
  }
8281
- function getRuntime(yDoc, nodeId) {
9243
+ function getRuntime3(yDoc, nodeId) {
8282
9244
  const runtime = yDoc.getMap("runtime");
8283
9245
  const value = runtime.get(nodeId);
8284
9246
  return value && typeof value === "object" ? value : {};
@@ -8373,6 +9335,11 @@ function queueIfAuthorized(context, options, type, nodeId, reason, payload) {
8373
9335
  }
8374
9336
  return queueAgentCommand(context.yDoc, command).created ? command : null;
8375
9337
  }
9338
+ function statusForResult(result) {
9339
+ if (result.status) return result.status;
9340
+ if (!result.success) return "failed";
9341
+ return result.confirmed === false ? "awaiting_readback" : "confirmed";
9342
+ }
8376
9343
  function planForSnapshot(context, options, block, snapshot) {
8377
9344
  const queued = [];
8378
9345
  const actionType = getBlockActionType(block);
@@ -8432,8 +9399,25 @@ function planForSnapshot(context, options, block, snapshot) {
8432
9399
  return queued;
8433
9400
  }
8434
9401
  if (actionType && canQueueCommand("execute_action", snapshot.nodeId, context, options)) {
9402
+ const pendingInvocation = readPendingInvocations(context.yDoc, snapshot.nodeId)[0];
9403
+ const payload = {
9404
+ actionType,
9405
+ ...pendingInvocation ? { pendingInvocationId: pendingInvocation.id } : {}
9406
+ };
9407
+ const actionValidation = validateFlowAgentExecuteActionPayload(block, payload);
9408
+ if (!actionValidation.valid) {
9409
+ const commandType = actionValidation.mode === "human-only" ? "notify_actor" : "diagnose_blocker";
9410
+ const command2 = queueIfAuthorized(context, options, commandType, snapshot.nodeId, actionValidation.reason || "Action node cannot be executed by Flow Agent yet", {
9411
+ cause: actionValidation.mode === "runtime-input-required" ? "missing_input" : "unknown",
9412
+ requiredActionType: actionType,
9413
+ requiredRuntimeInputs: actionValidation.missingRuntimeInputs,
9414
+ severity: actionValidation.mode === "human-only" ? "attention" : void 0
9415
+ });
9416
+ if (command2) queued.push(command2);
9417
+ return queued;
9418
+ }
8435
9419
  const command = queueIfAuthorized(context, options, "execute_action", snapshot.nodeId, "Action node is pending and executable", {
8436
- actionType
9420
+ ...payload
8437
9421
  });
8438
9422
  if (command) queued.push(command);
8439
9423
  return queued;
@@ -8455,7 +9439,7 @@ function planRalphLoopCommands(context, options = {}) {
8455
9439
  const nodeId = getBlockId(block);
8456
9440
  if (!nodeId) continue;
8457
9441
  const pendingInvocationCount = readPendingInvocations(context.yDoc, nodeId).length;
8458
- const snapshot = snapshotNode(block, getRuntime(context.yDoc, nodeId), now, pendingInvocationCount);
9442
+ const snapshot = snapshotNode(block, getRuntime3(context.yDoc, nodeId), now, pendingInvocationCount);
8459
9443
  snapshots.push(snapshot);
8460
9444
  queuedCommands.push(...planForSnapshot(context, options, block, snapshot));
8461
9445
  }
@@ -8538,7 +9522,7 @@ async function executeQueuedAgentCommands(context, options = {}) {
8538
9522
  continue;
8539
9523
  }
8540
9524
  updateAgentCommand(context.yDoc, command.id, {
8541
- status: result.success ? "confirmed" : "failed",
9525
+ status: statusForResult(result),
8542
9526
  error: result.error,
8543
9527
  updatedAt: context.now?.() || Date.now()
8544
9528
  });
@@ -8587,7 +9571,7 @@ function safeAppendAgentLedgerEvent2(yDoc, event) {
8587
9571
  throw error;
8588
9572
  }
8589
9573
  }
8590
- function createYDocRuntimeManager(yDoc) {
9574
+ function createYDocRuntimeManager2(yDoc) {
8591
9575
  const runtime = yDoc.getMap("runtime");
8592
9576
  return {
8593
9577
  get: (nodeId) => {
@@ -8618,19 +9602,16 @@ function clearRuntimeBlocker(yDoc, nodeId, updates) {
8618
9602
  });
8619
9603
  }
8620
9604
  function findContextBlock(context, nodeId) {
8621
- const block = (context.blocks || []).find((entry) => getBlockId(entry) === nodeId);
9605
+ const block = (context.blocks || context.editor?.document || []).find((entry) => getBlockId(entry) === nodeId);
8622
9606
  return block && typeof block === "object" ? block : null;
8623
9607
  }
8624
- function parseInputs2(value) {
8625
- return parseJsonProp(value, {});
8626
- }
8627
9608
  function getXmlAttribute(element, key) {
8628
9609
  return element.getAttribute(key);
8629
9610
  }
8630
9611
  function setXmlAttribute(element, key, value) {
8631
9612
  element.setAttribute(key, value);
8632
9613
  }
8633
- function isRecord(value) {
9614
+ function isRecord2(value) {
8634
9615
  return value != null && typeof value === "object" && !Array.isArray(value);
8635
9616
  }
8636
9617
  function isYXmlContainerLike(value) {
@@ -8643,13 +9624,13 @@ function getElementBlockId(element) {
8643
9624
  const directId = getXmlAttribute(element, "id");
8644
9625
  if (typeof directId === "string" && directId.length > 0) return directId;
8645
9626
  const attrs = getXmlAttribute(element, "attrs");
8646
- const attrsId = isRecord(attrs) ? attrs.id : void 0;
9627
+ const attrsId = isRecord2(attrs) ? attrs.id : void 0;
8647
9628
  return typeof attrsId === "string" && attrsId.length > 0 ? attrsId : void 0;
8648
9629
  }
8649
9630
  function updateXmlElementProps(element, blockId, propsPatch) {
8650
9631
  const attrs = getXmlAttribute(element, "attrs");
8651
- if (isRecord(attrs)) {
8652
- const existingProps = isRecord(attrs.props) ? attrs.props : {};
9632
+ if (isRecord2(attrs)) {
9633
+ const existingProps = isRecord2(attrs.props) ? attrs.props : {};
8653
9634
  setXmlAttribute(element, "attrs", {
8654
9635
  ...attrs,
8655
9636
  id: typeof attrs.id === "string" ? attrs.id : blockId,
@@ -8706,7 +9687,7 @@ function assignActorInYDoc(yDoc, command, context) {
8706
9687
  if (!updatedDocument) {
8707
9688
  throw new Error(`Block ${command.nodeId} was not found for assignment`);
8708
9689
  }
8709
- const runtime = createYDocRuntimeManager(yDoc);
9690
+ const runtime = createYDocRuntimeManager2(yDoc);
8710
9691
  runtime.update(command.nodeId, {
8711
9692
  assignedActorDid: targetActorDid,
8712
9693
  assignedByDid: context.actor.did,
@@ -8751,76 +9732,69 @@ async function executeActionCommand(command, context, options) {
8751
9732
  if (!actionType) {
8752
9733
  return { commandId: command.id, success: false, error: "execute_action command is missing payload.actionType" };
8753
9734
  }
8754
- const action = getAction(actionType);
8755
- if (!action) {
8756
- return { commandId: command.id, success: false, error: `No registered action found for ${actionType}` };
8757
- }
8758
9735
  const block = findContextBlock(context, command.nodeId);
8759
9736
  if (!block) {
8760
9737
  return { commandId: command.id, success: false, error: `Block ${command.nodeId} was not found in runtime context` };
8761
9738
  }
8762
9739
  const props = getBlockProps(block);
8763
- const inputs = parseInputs2(props.inputs);
8764
- const runtime = createYDocRuntimeManager(context.yDoc);
8765
- const flowNode = buildFlowNodeFromBlock(block);
8766
- const outcome = await executeNode({
8767
- node: flowNode,
9740
+ if (typeof props.actionType === "string" && props.actionType !== actionType) {
9741
+ return {
9742
+ commandId: command.id,
9743
+ success: false,
9744
+ error: `execute_action payload.actionType ${actionType} does not match block actionType ${props.actionType}`
9745
+ };
9746
+ }
9747
+ const runtimeInputs = command.payload.runtimeInputs;
9748
+ if (runtimeInputs != null && !isRecord2(runtimeInputs)) {
9749
+ return { commandId: command.id, success: false, error: "execute_action payload.runtimeInputs must be an object when provided" };
9750
+ }
9751
+ const inputValidation = validateFlowAgentExecuteActionPayload(block, command.payload);
9752
+ if (!inputValidation.valid) {
9753
+ return {
9754
+ commandId: command.id,
9755
+ success: false,
9756
+ confirmed: false,
9757
+ status: inputValidation.mode === "human-only" ? "skipped" : "failed",
9758
+ error: inputValidation.reason
9759
+ };
9760
+ }
9761
+ const pendingInvocationId = typeof command.payload.pendingInvocationId === "string" ? command.payload.pendingInvocationId : void 0;
9762
+ const outcome = await executeActionBlock({
9763
+ editorOrYDoc: context.editor || context.yDoc,
9764
+ block,
9765
+ blockId: command.nodeId,
9766
+ document: context.blocks || context.editor?.document || [],
8768
9767
  actorDid: context.actor.did,
8769
9768
  actorType: options.actorType,
8770
9769
  entityRoomId: options.entityRoomId,
8771
- context: {
8772
- runtime,
8773
- ucanService: options.ucanService,
8774
- invocationStore: options.invocationStore,
8775
- flowUri: context.flowUri,
8776
- flowId: context.flowId,
8777
- flowOwnerDid: "",
8778
- schemaVersion: options.schemaVersion || DEFAULT_SCHEMA_VERSION,
8779
- now: options.now || context.now
8780
- },
8781
- action: async () => {
8782
- const result = await action.run(inputs, {
8783
- actorDid: context.actor.did,
8784
- flowId: context.flowId,
8785
- flowUri: context.flowUri,
8786
- nodeId: command.nodeId,
8787
- flowNode,
8788
- runtime,
8789
- services: options.actionServices || {}
8790
- });
8791
- return { payload: result.output };
8792
- },
8793
- pin: options.pin
9770
+ ucanService: options.ucanService,
9771
+ invocationStore: options.invocationStore,
9772
+ services: options.actionServices || {},
9773
+ handlers: options.actionHandlers,
9774
+ runtimeInputs: getCommandRuntimeInputs(command.payload),
9775
+ pendingInvocationId,
9776
+ pin: options.pin,
9777
+ flowUri: context.flowUri,
9778
+ flowId: context.flowId,
9779
+ flowOwnerDid: options.flowOwnerDid || "",
9780
+ schemaVersion: options.schemaVersion || DEFAULT_SCHEMA_VERSION,
9781
+ now: options.now || context.now
8794
9782
  });
8795
- if (outcome.success && outcome.result) {
8796
- runtime.update(command.nodeId, {
8797
- state: "completed",
8798
- output: outcome.result.payload || {},
8799
- executedByDid: context.actor.did,
8800
- executedAt: options.now?.() || context.now?.() || Date.now(),
8801
- lastInvocationCid: outcome.invocationCid || outcome.capabilityId,
8802
- lastCommandId: command.id
8803
- });
8804
- } else if (!outcome.success) {
8805
- runtime.update(command.nodeId, {
8806
- state: "failed",
8807
- error: {
8808
- message: outcome.error || "Unknown execution error",
8809
- at: options.now?.() || context.now?.() || Date.now(),
8810
- commandId: command.id
8811
- },
8812
- lastCommandId: command.id
8813
- });
8814
- }
8815
9783
  return {
8816
9784
  commandId: command.id,
8817
9785
  success: outcome.success,
8818
- confirmed: outcome.success,
9786
+ confirmed: outcome.success && outcome.completionState === "completed",
8819
9787
  output: {
8820
- ...outcome.result?.payload && typeof outcome.result.payload === "object" ? outcome.result.payload : {},
8821
- ...outcome.invocationCid ? { invocationCid: outcome.invocationCid } : {}
9788
+ ...outcome.output && typeof outcome.output === "object" ? outcome.output : {},
9789
+ ...outcome.invocationCid ? { invocationCid: outcome.invocationCid } : {},
9790
+ ...outcome.capabilityId ? { capabilityId: outcome.capabilityId } : {},
9791
+ ...outcome.runId ? { runId: outcome.runId } : {},
9792
+ ...outcome.events.length > 0 ? { events: outcome.events } : {},
9793
+ ...pendingInvocationId ? { pendingInvocationId, pendingInvocationRemoved: outcome.pendingInvocationRemoved } : {},
9794
+ completionState: outcome.completionState
8822
9795
  },
8823
- error: outcome.error
9796
+ error: outcome.error,
9797
+ completionState: outcome.completionState
8824
9798
  };
8825
9799
  }
8826
9800
  function getRecoverableBlockerReason(runtime) {
@@ -8915,6 +9889,11 @@ async function callConfiguredExecutor(command, context, options) {
8915
9889
  return void 0;
8916
9890
  }
8917
9891
  }
9892
+ function statusForResult2(result) {
9893
+ if (result.status) return result.status;
9894
+ if (!result.success) return "failed";
9895
+ return result.confirmed === false ? "awaiting_readback" : "confirmed";
9896
+ }
8918
9897
  async function executeQueuedFlowAgentCoreCommands(context, options) {
8919
9898
  const now = options.now?.() || context.now?.() || Date.now();
8920
9899
  const { leases } = getFlowAgentMaps(context.yDoc);
@@ -8968,7 +9947,7 @@ async function executeQueuedFlowAgentCoreCommands(context, options) {
8968
9947
  continue;
8969
9948
  }
8970
9949
  updateAgentCommand(context.yDoc, command.id, {
8971
- status: normalized.success ? "confirmed" : "failed",
9950
+ status: statusForResult2(normalized),
8972
9951
  error: normalized.error,
8973
9952
  updatedAt: options.now?.() || context.now?.() || Date.now()
8974
9953
  });
@@ -9085,9 +10064,10 @@ export {
9085
10064
  buildFlowNodeFromBlock,
9086
10065
  createRuntimeStateManager,
9087
10066
  clearRuntimeForTemplateClone,
10067
+ isRuntimeRef,
10068
+ resolveRuntimeRefs,
9088
10069
  isAuthorized,
9089
10070
  executeNode,
9090
- isRuntimeRef,
9091
10071
  RUN_RECORD_AUDIT_TYPE,
9092
10072
  computePendingInvocationId,
9093
10073
  snapshotInputRefs,
@@ -9102,6 +10082,9 @@ export {
9102
10082
  replayFailedListenerRun,
9103
10083
  reconcilePendingInvocations,
9104
10084
  getActionForBlock,
10085
+ reconcileActionReadBack,
10086
+ buildActionRunInputs,
10087
+ executeActionBlock,
9105
10088
  writeRunRecordAndReconcile,
9106
10089
  compileBaseUcanFlow,
9107
10090
  readCompiledFlowFromYDoc,
@@ -9143,4 +10126,4 @@ export {
9143
10126
  executeQueuedFlowAgentCoreCommands,
9144
10127
  FlowAgentService
9145
10128
  };
9146
- //# sourceMappingURL=chunk-67477PYU.mjs.map
10129
+ //# sourceMappingURL=chunk-XGQRYGA3.mjs.map