@ixo/editor 5.39.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action-manifest.json +5276 -0
- package/dist/{chunk-YWZJYSOP.js → chunk-5TJK6JKV.js} +1306 -786
- package/dist/chunk-5TJK6JKV.js.map +1 -0
- package/dist/{chunk-JXTHVI23.js → chunk-EOMC6ZVX.js} +2 -2
- package/dist/{chunk-RDM56DB7.js → chunk-LMYTTXOQ.js} +2417 -2367
- package/dist/chunk-LMYTTXOQ.js.map +1 -0
- package/dist/core/index.d.ts +194 -251
- package/dist/core/index.js +26 -2
- package/dist/core/index.js.map +1 -1
- package/dist/{graphql-client-jOgmcMNg.d.ts → graphql-client-s4ig1o1G.d.ts} +2 -2
- package/dist/{index-B4Qe8xbv.d.ts → index-DL8Yh3Xu.d.ts} +11 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/mantine/index.d.ts +3 -3
- package/dist/mantine/index.js +2 -2
- package/dist/{store-B-2A-Tlv.d.ts → store-C_KCYNzv.d.ts} +911 -567
- package/package.json +3 -2
- package/dist/chunk-RDM56DB7.js.map +0 -1
- package/dist/chunk-YWZJYSOP.js.map +0 -1
- /package/dist/{chunk-JXTHVI23.js.map → chunk-EOMC6ZVX.js.map} +0 -0
|
@@ -1,5 +1,20 @@
|
|
|
1
|
+
// src/core/lib/warnOnce.ts
|
|
2
|
+
var seen = /* @__PURE__ */ new Set();
|
|
3
|
+
function warnOnce(key, message) {
|
|
4
|
+
if (seen.has(key)) return;
|
|
5
|
+
seen.add(key);
|
|
6
|
+
console.warn(message);
|
|
7
|
+
}
|
|
8
|
+
|
|
1
9
|
// src/core/lib/actionRegistry/registry.ts
|
|
2
10
|
var actions = /* @__PURE__ */ new Map();
|
|
11
|
+
var STEP_COMPLETED_EVENT_NAME = "step.completed";
|
|
12
|
+
var STEP_COMPLETED_EVENT = {
|
|
13
|
+
name: STEP_COMPLETED_EVENT_NAME,
|
|
14
|
+
displayName: "Completed",
|
|
15
|
+
description: "Fires when this step is completed.",
|
|
16
|
+
payloadSchema: []
|
|
17
|
+
};
|
|
3
18
|
var ACTION_TYPE_ALIASES = {
|
|
4
19
|
bid: "qi/bid.submit",
|
|
5
20
|
claim: "qi/claim.submit",
|
|
@@ -40,6 +55,13 @@ function getAction(type) {
|
|
|
40
55
|
function getAllActions() {
|
|
41
56
|
return Array.from(actions.values());
|
|
42
57
|
}
|
|
58
|
+
function isRepeatableAction(type) {
|
|
59
|
+
if (!type) return false;
|
|
60
|
+
return getAction(type)?.cardinality === "many";
|
|
61
|
+
}
|
|
62
|
+
function getAliasEntries() {
|
|
63
|
+
return Array.from(aliases.entries());
|
|
64
|
+
}
|
|
43
65
|
function hasAction(type) {
|
|
44
66
|
return actions.has(resolveActionType(type));
|
|
45
67
|
}
|
|
@@ -51,28 +73,49 @@ function getActionByCan(can) {
|
|
|
51
73
|
}
|
|
52
74
|
function getEventsForBlock(action, inputs) {
|
|
53
75
|
if (!action) return [];
|
|
76
|
+
let events;
|
|
54
77
|
if (action.getDynamicEvents) {
|
|
55
78
|
const parsed = normalizeInputs(inputs);
|
|
56
79
|
try {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
80
|
+
const dynamic = action.getDynamicEvents(parsed) || [];
|
|
81
|
+
events = action.dynamicResolutionMode === "replace" ? dynamic : mergeByKey(action.events || [], dynamic, (event) => event.name);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
warnOnce(
|
|
84
|
+
`dynamic-events:${action.type}`,
|
|
85
|
+
`[flow-config] action ${action.type}: getDynamicEvents threw (${error instanceof Error ? error.message : String(error)}); no events resolved`
|
|
86
|
+
);
|
|
87
|
+
events = [];
|
|
60
88
|
}
|
|
89
|
+
} else {
|
|
90
|
+
events = action.events || [];
|
|
91
|
+
}
|
|
92
|
+
if (events.some((event) => event.name === STEP_COMPLETED_EVENT_NAME)) {
|
|
93
|
+
return events;
|
|
61
94
|
}
|
|
62
|
-
return
|
|
95
|
+
return [...events, STEP_COMPLETED_EVENT];
|
|
63
96
|
}
|
|
64
97
|
function getOutputSchemaForBlock(action, inputs) {
|
|
65
98
|
if (!action) return [];
|
|
66
99
|
if (action.getDynamicOutputSchema) {
|
|
67
100
|
const parsed = normalizeInputs(inputs);
|
|
68
101
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
102
|
+
const dynamic = action.getDynamicOutputSchema(parsed) || [];
|
|
103
|
+
if (action.dynamicResolutionMode === "replace") return dynamic;
|
|
104
|
+
return mergeByKey(action.outputSchema || [], dynamic, (field) => field.path);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
warnOnce(
|
|
107
|
+
`dynamic-output-schema:${action.type}`,
|
|
108
|
+
`[flow-config] action ${action.type}: getDynamicOutputSchema threw (${error instanceof Error ? error.message : String(error)}); using static schema`
|
|
109
|
+
);
|
|
71
110
|
return action.outputSchema || [];
|
|
72
111
|
}
|
|
73
112
|
}
|
|
74
113
|
return action.outputSchema || [];
|
|
75
114
|
}
|
|
115
|
+
function mergeByKey(baseline, dynamic, keyOf) {
|
|
116
|
+
const dynamicKeys = new Set(dynamic.map(keyOf));
|
|
117
|
+
return [...baseline.filter((item) => !dynamicKeys.has(keyOf(item))), ...dynamic];
|
|
118
|
+
}
|
|
76
119
|
function normalizeInputs(inputs) {
|
|
77
120
|
if (!inputs) return {};
|
|
78
121
|
if (typeof inputs === "object") return inputs;
|
|
@@ -81,12 +124,56 @@ function normalizeInputs(inputs) {
|
|
|
81
124
|
const parsed = JSON.parse(inputs || "{}");
|
|
82
125
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
83
126
|
} catch {
|
|
127
|
+
warnOnce(`inputs-normalize:${inputs}`, `[flow-config] block inputs are not valid JSON; treated as empty`);
|
|
84
128
|
return {};
|
|
85
129
|
}
|
|
86
130
|
}
|
|
87
131
|
return {};
|
|
88
132
|
}
|
|
89
133
|
|
|
134
|
+
// src/core/lib/actionRegistry/manifest.ts
|
|
135
|
+
function serializeProof(action) {
|
|
136
|
+
const proof = action.proof;
|
|
137
|
+
if (proof === "none" || proof === void 0) return { kind: "none" };
|
|
138
|
+
if ("validate" in proof) return { kind: "custom" };
|
|
139
|
+
return { kind: "fields", fields: [...proof.fields] };
|
|
140
|
+
}
|
|
141
|
+
function generateActionManifest() {
|
|
142
|
+
const aliasEntries = getAliasEntries();
|
|
143
|
+
const aliasesByType = /* @__PURE__ */ new Map();
|
|
144
|
+
for (const [alias, canonical] of aliasEntries) {
|
|
145
|
+
const list = aliasesByType.get(canonical) || [];
|
|
146
|
+
list.push(alias);
|
|
147
|
+
aliasesByType.set(canonical, list);
|
|
148
|
+
}
|
|
149
|
+
const actions2 = getAllActions().map((action) => {
|
|
150
|
+
const entry = {
|
|
151
|
+
type: action.type,
|
|
152
|
+
aliases: (aliasesByType.get(action.type) || []).sort(),
|
|
153
|
+
sideEffect: action.sideEffect,
|
|
154
|
+
defaultRequiresConfirmation: action.defaultRequiresConfirmation,
|
|
155
|
+
proof: serializeProof(action),
|
|
156
|
+
hasDynamicEvents: !!action.getDynamicEvents,
|
|
157
|
+
hasDynamicOutputSchema: !!action.getDynamicOutputSchema,
|
|
158
|
+
eligibleForEventTrigger: !!action.eligibleForEventTrigger
|
|
159
|
+
};
|
|
160
|
+
if (action.can) entry.can = action.can;
|
|
161
|
+
if (action.requiredCapability) entry.requiredCapability = action.requiredCapability;
|
|
162
|
+
if (action.inputSchema) entry.inputSchema = action.inputSchema;
|
|
163
|
+
if (action.outputSchema) entry.outputSchema = action.outputSchema;
|
|
164
|
+
if (action.events) {
|
|
165
|
+
entry.events = action.events.map((event) => ({
|
|
166
|
+
name: event.name,
|
|
167
|
+
displayName: event.displayName,
|
|
168
|
+
description: event.description,
|
|
169
|
+
payloadSchema: event.payloadSchema
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
return entry;
|
|
173
|
+
}).sort((a, b) => a.type.localeCompare(b.type));
|
|
174
|
+
return { manifestVersion: "1", actions: actions2 };
|
|
175
|
+
}
|
|
176
|
+
|
|
90
177
|
// src/core/lib/actionRegistry/canMapping.ts
|
|
91
178
|
var CAN_TO_TYPE = {
|
|
92
179
|
"bid/submit": "qi/bid.submit",
|
|
@@ -154,7 +241,37 @@ function getAllCanMappings() {
|
|
|
154
241
|
}
|
|
155
242
|
|
|
156
243
|
// src/core/lib/actionRegistry/adapters.ts
|
|
244
|
+
var SERVICE_GROUP_REQUIRED_HANDLERS = {
|
|
245
|
+
bid: ["submitBid", "approveBid", "rejectBid", "approveServiceAgentApplication", "approveEvaluatorApplication"],
|
|
246
|
+
claim: ["requestPin", "submitClaim", "evaluateClaim", "getCurrentUser"],
|
|
247
|
+
collection: [
|
|
248
|
+
"collection.get",
|
|
249
|
+
"collection.create",
|
|
250
|
+
"collection.updateState",
|
|
251
|
+
"collection.updateDates",
|
|
252
|
+
"collection.updateQuota",
|
|
253
|
+
"collection.updatePayments",
|
|
254
|
+
"collection.updateIntents"
|
|
255
|
+
],
|
|
256
|
+
collectionUsers: ["collectionUsers.grant", "collectionUsers.revoke", "collectionUsers.list", "collectionUsers.classifyAddress", "collectionUsers.enumerateMembers"],
|
|
257
|
+
carbon: ["carbon.loadBatches", "carbon.harvest", "carbon.retire"]
|
|
258
|
+
};
|
|
259
|
+
function getHandlerAtPath(handlers, path) {
|
|
260
|
+
return path.split(".").reduce((acc, key) => acc == null ? void 0 : acc[key], handlers);
|
|
261
|
+
}
|
|
262
|
+
function warnPartialServiceGroups(handlers) {
|
|
263
|
+
if (!handlers) return;
|
|
264
|
+
for (const [group, keys] of Object.entries(SERVICE_GROUP_REQUIRED_HANDLERS)) {
|
|
265
|
+
const missing = keys.filter((key) => !getHandlerAtPath(handlers, key));
|
|
266
|
+
if (missing.length > 0 && missing.length < keys.length) {
|
|
267
|
+
console.warn(
|
|
268
|
+
`[buildServicesFromHandlers] services.${group} is DISABLED: handlers provide ${keys.length - missing.length}/${keys.length} required methods; missing: ${missing.join(", ")}`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
157
273
|
function buildServicesFromHandlers(handlers) {
|
|
274
|
+
warnPartialServiceGroups(handlers);
|
|
158
275
|
return {
|
|
159
276
|
http: {
|
|
160
277
|
request: async (params) => {
|
|
@@ -362,6 +479,7 @@ registerAction({
|
|
|
362
479
|
type: "qi/pod.domain-indexer-lookup",
|
|
363
480
|
can: "pod/domain-indexer-lookup",
|
|
364
481
|
sideEffect: false,
|
|
482
|
+
proof: "none",
|
|
365
483
|
defaultRequiresConfirmation: false,
|
|
366
484
|
inputSchema: {
|
|
367
485
|
type: "object",
|
|
@@ -401,6 +519,7 @@ registerAction({
|
|
|
401
519
|
type: "qi/pod.domain-single-selection",
|
|
402
520
|
can: "pod/domain-single-selection",
|
|
403
521
|
sideEffect: false,
|
|
522
|
+
proof: "none",
|
|
404
523
|
defaultRequiresConfirmation: false,
|
|
405
524
|
inputSchema: {
|
|
406
525
|
type: "object",
|
|
@@ -455,6 +574,7 @@ registerAction({
|
|
|
455
574
|
type: "qi/pod.entity-single-selection",
|
|
456
575
|
can: "pod/entity-single-selection",
|
|
457
576
|
sideEffect: false,
|
|
577
|
+
proof: "none",
|
|
458
578
|
defaultRequiresConfirmation: false,
|
|
459
579
|
inputSchema: {
|
|
460
580
|
type: "object",
|
|
@@ -513,6 +633,7 @@ registerAction({
|
|
|
513
633
|
type: "qi/pod.member-multi-select",
|
|
514
634
|
can: "pod/member-multi-select",
|
|
515
635
|
sideEffect: false,
|
|
636
|
+
proof: "none",
|
|
516
637
|
defaultRequiresConfirmation: false,
|
|
517
638
|
inputSchema: {
|
|
518
639
|
type: "object",
|
|
@@ -606,6 +727,7 @@ registerAction({
|
|
|
606
727
|
type: "qi/pod.governance-config",
|
|
607
728
|
can: "pod/governance-config",
|
|
608
729
|
sideEffect: false,
|
|
730
|
+
proof: "none",
|
|
609
731
|
defaultRequiresConfirmation: false,
|
|
610
732
|
inputSchema: {
|
|
611
733
|
type: "object",
|
|
@@ -690,6 +812,7 @@ registerAction({
|
|
|
690
812
|
type: "qi/pod.list-domain-flows",
|
|
691
813
|
can: "pod/list-domain-flows",
|
|
692
814
|
sideEffect: false,
|
|
815
|
+
proof: "none",
|
|
693
816
|
defaultRequiresConfirmation: false,
|
|
694
817
|
inputSchema: {
|
|
695
818
|
type: "object",
|
|
@@ -779,6 +902,7 @@ registerAction({
|
|
|
779
902
|
type: "qi/governance.member-proposal",
|
|
780
903
|
can: "governance/member-proposal",
|
|
781
904
|
sideEffect: true,
|
|
905
|
+
proof: { fields: ["proposalId"] },
|
|
782
906
|
defaultRequiresConfirmation: true,
|
|
783
907
|
requiredCapability: "flow/block/execute",
|
|
784
908
|
outputSchema: [
|
|
@@ -794,6 +918,9 @@ registerAction({
|
|
|
794
918
|
if (!handlers) {
|
|
795
919
|
throw new Error("Handlers not available");
|
|
796
920
|
}
|
|
921
|
+
if (!handlers.getPreProposalContractAddress || !handlers.getGroupContractAddress || !handlers.getProposalContractAddress || !handlers.createProposal) {
|
|
922
|
+
throw new Error("Governance proposal handlers not available");
|
|
923
|
+
}
|
|
797
924
|
const coreAddress = String(inputs.coreAddress || "").trim();
|
|
798
925
|
if (!coreAddress) throw new Error("coreAddress is required");
|
|
799
926
|
const operation = String(inputs.operation || "").trim();
|
|
@@ -861,6 +988,7 @@ registerAction({
|
|
|
861
988
|
type: "qi/governance.settings-proposal",
|
|
862
989
|
can: "governance/settings-proposal",
|
|
863
990
|
sideEffect: true,
|
|
991
|
+
proof: { fields: ["proposalId"] },
|
|
864
992
|
defaultRequiresConfirmation: true,
|
|
865
993
|
requiredCapability: "flow/block/execute",
|
|
866
994
|
outputSchema: [
|
|
@@ -875,6 +1003,9 @@ registerAction({
|
|
|
875
1003
|
if (!handlers) {
|
|
876
1004
|
throw new Error("Handlers not available");
|
|
877
1005
|
}
|
|
1006
|
+
if (!handlers.getPreProposalContractAddress || !handlers.getGroupContractAddress || !handlers.getProposalContractAddress || !handlers.createProposal) {
|
|
1007
|
+
throw new Error("Governance proposal handlers not available");
|
|
1008
|
+
}
|
|
878
1009
|
const coreAddress = String(inputs.coreAddress || "").trim();
|
|
879
1010
|
if (!coreAddress) throw new Error("coreAddress is required");
|
|
880
1011
|
const votingPeriodHours = Number(inputs.votingPeriodHours);
|
|
@@ -937,6 +1068,7 @@ registerAction({
|
|
|
937
1068
|
type: "qi/governance.transaction.send-funds",
|
|
938
1069
|
can: "governance.transaction/send-funds",
|
|
939
1070
|
sideEffect: true,
|
|
1071
|
+
proof: { fields: ["proposalId"] },
|
|
940
1072
|
defaultRequiresConfirmation: true,
|
|
941
1073
|
requiredCapability: "flow/block/execute",
|
|
942
1074
|
outputSchema: [
|
|
@@ -952,6 +1084,9 @@ registerAction({
|
|
|
952
1084
|
if (!handlers) {
|
|
953
1085
|
throw new Error("Handlers not available");
|
|
954
1086
|
}
|
|
1087
|
+
if (!handlers.getPreProposalContractAddress || !handlers.getGroupContractAddress || !handlers.getProposalContractAddress || !handlers.createProposal) {
|
|
1088
|
+
throw new Error("Governance proposal handlers not available");
|
|
1089
|
+
}
|
|
955
1090
|
const coreAddress = String(inputs.coreAddress || "").trim();
|
|
956
1091
|
if (!coreAddress) throw new Error("coreAddress is required");
|
|
957
1092
|
const recipient = String(inputs.recipient || "").trim();
|
|
@@ -995,6 +1130,7 @@ registerAction({
|
|
|
995
1130
|
type: "qi/http.request",
|
|
996
1131
|
can: "http/request",
|
|
997
1132
|
sideEffect: false,
|
|
1133
|
+
proof: { fields: ["status"] },
|
|
998
1134
|
defaultRequiresConfirmation: false,
|
|
999
1135
|
// HTTP request can be triggered as a listener — a human assignee is DM'd
|
|
1000
1136
|
// to invoke it when the upstream event fires. See §3.6 of the
|
|
@@ -1055,6 +1191,7 @@ registerAction({
|
|
|
1055
1191
|
type: "qi/email.send",
|
|
1056
1192
|
can: "email/send",
|
|
1057
1193
|
sideEffect: true,
|
|
1194
|
+
proof: { fields: ["messageId"] },
|
|
1058
1195
|
defaultRequiresConfirmation: true,
|
|
1059
1196
|
requiredCapability: "email/send",
|
|
1060
1197
|
// Email send is autonomous-enough to be triggered by another block's event.
|
|
@@ -1120,6 +1257,7 @@ registerAction({
|
|
|
1120
1257
|
type: "qi/human.checkbox.set",
|
|
1121
1258
|
can: "human/checkbox",
|
|
1122
1259
|
sideEffect: true,
|
|
1260
|
+
proof: "none",
|
|
1123
1261
|
defaultRequiresConfirmation: false,
|
|
1124
1262
|
requiredCapability: "flow/execute",
|
|
1125
1263
|
inputSchema: {
|
|
@@ -1161,6 +1299,7 @@ function registerFormSubmitAction(type, can) {
|
|
|
1161
1299
|
type,
|
|
1162
1300
|
can,
|
|
1163
1301
|
sideEffect: true,
|
|
1302
|
+
proof: "none",
|
|
1164
1303
|
defaultRequiresConfirmation: false,
|
|
1165
1304
|
requiredCapability: "flow/execute",
|
|
1166
1305
|
// Additive: makes the form an event SOURCE so downstream blocks can
|
|
@@ -1210,6 +1349,7 @@ registerAction({
|
|
|
1210
1349
|
type: "qi/notification.push",
|
|
1211
1350
|
can: "notification/push",
|
|
1212
1351
|
sideEffect: true,
|
|
1352
|
+
proof: { fields: ["messageId"] },
|
|
1213
1353
|
defaultRequiresConfirmation: true,
|
|
1214
1354
|
requiredCapability: "notify/send",
|
|
1215
1355
|
inputSchema: {
|
|
@@ -1266,6 +1406,7 @@ registerAction({
|
|
|
1266
1406
|
type: "qi/bid.submit",
|
|
1267
1407
|
can: "bid/submit",
|
|
1268
1408
|
sideEffect: true,
|
|
1409
|
+
proof: { fields: ["bidId"] },
|
|
1269
1410
|
defaultRequiresConfirmation: true,
|
|
1270
1411
|
requiredCapability: "flow/block/execute",
|
|
1271
1412
|
inputSchema: {
|
|
@@ -1349,6 +1490,7 @@ registerAction({
|
|
|
1349
1490
|
type: "qi/bid.evaluate",
|
|
1350
1491
|
can: "bid/evaluate",
|
|
1351
1492
|
sideEffect: true,
|
|
1493
|
+
proof: { fields: ["bidId"] },
|
|
1352
1494
|
defaultRequiresConfirmation: true,
|
|
1353
1495
|
requiredCapability: "flow/block/execute",
|
|
1354
1496
|
outputSchema: [
|
|
@@ -1495,7 +1637,7 @@ function extractSurveyAnswerSchema(survey) {
|
|
|
1495
1637
|
if (!survey || typeof survey !== "object") return [];
|
|
1496
1638
|
const json = survey;
|
|
1497
1639
|
const collected = [];
|
|
1498
|
-
const
|
|
1640
|
+
const seen2 = /* @__PURE__ */ new Set();
|
|
1499
1641
|
const walk = (elements) => {
|
|
1500
1642
|
if (!Array.isArray(elements)) return;
|
|
1501
1643
|
for (const el of elements) {
|
|
@@ -1506,8 +1648,8 @@ function extractSurveyAnswerSchema(survey) {
|
|
|
1506
1648
|
}
|
|
1507
1649
|
const name = typeof el.name === "string" ? el.name.trim() : "";
|
|
1508
1650
|
if (!name) continue;
|
|
1509
|
-
if (
|
|
1510
|
-
|
|
1651
|
+
if (seen2.has(name)) continue;
|
|
1652
|
+
seen2.add(name);
|
|
1511
1653
|
const rawType = typeof el.type === "string" ? el.type : "";
|
|
1512
1654
|
const mapped = TYPE_MAP[rawType] ?? "string";
|
|
1513
1655
|
let itemSchema;
|
|
@@ -1577,6 +1719,13 @@ registerAction({
|
|
|
1577
1719
|
type: "qi/claim.submit",
|
|
1578
1720
|
can: "claim/submit",
|
|
1579
1721
|
sideEffect: true,
|
|
1722
|
+
// Dynamic resolvers predate merge-by-default and intentionally control
|
|
1723
|
+
// the full vocabulary (e.g. returning [] to hide the static baseline).
|
|
1724
|
+
dynamicResolutionMode: "replace",
|
|
1725
|
+
proof: { fields: ["claimId"] },
|
|
1726
|
+
// Repeatable: a collection accepts many claims over the flow's life (e.g. a
|
|
1727
|
+
// monthly submission). Never latches to a terminal Done. See cardinality.
|
|
1728
|
+
cardinality: "many",
|
|
1580
1729
|
defaultRequiresConfirmation: true,
|
|
1581
1730
|
requiredCapability: "flow/block/execute",
|
|
1582
1731
|
eligibleForEventTrigger: true,
|
|
@@ -1947,7 +2096,7 @@ function serializeXeroPaymentCreateInputs(inputs) {
|
|
|
1947
2096
|
return JSON.stringify(inputs);
|
|
1948
2097
|
}
|
|
1949
2098
|
|
|
1950
|
-
// src/
|
|
2099
|
+
// src/core/lib/flowEngine/referenceResolver.ts
|
|
1951
2100
|
var REFERENCE_REGEX = /\{\{([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_.:]+)\}\}/g;
|
|
1952
2101
|
function parseReferences(input) {
|
|
1953
2102
|
const references = [];
|
|
@@ -1969,68 +2118,78 @@ function getNestedValue(obj, path) {
|
|
|
1969
2118
|
return current?.[key];
|
|
1970
2119
|
}, obj);
|
|
1971
2120
|
}
|
|
1972
|
-
function
|
|
2121
|
+
function resolveSingleReferenceDetailed(blockId, propPath, editorDocument, yRuntime, scope) {
|
|
1973
2122
|
if (scope && Object.prototype.hasOwnProperty.call(scope, blockId)) {
|
|
1974
2123
|
const root = scope[blockId];
|
|
1975
|
-
if (root == null) return void 0;
|
|
1976
|
-
|
|
2124
|
+
if (root == null) return { value: void 0, reason: "missing-value" };
|
|
2125
|
+
const value2 = getNestedValue(root, propPath);
|
|
2126
|
+
return { value: value2, reason: value2 === void 0 ? "missing-value" : void 0 };
|
|
1977
2127
|
}
|
|
1978
2128
|
if (!editorDocument || !Array.isArray(editorDocument)) {
|
|
1979
|
-
return void 0;
|
|
2129
|
+
return { value: void 0, reason: "unknown-block" };
|
|
1980
2130
|
}
|
|
1981
2131
|
const block = editorDocument.find((b) => b.id === blockId);
|
|
1982
2132
|
if (!block) {
|
|
1983
|
-
return void 0;
|
|
2133
|
+
return { value: void 0, reason: "unknown-block" };
|
|
1984
2134
|
}
|
|
1985
2135
|
if (propPath.startsWith("output.")) {
|
|
1986
|
-
if (!yRuntime) return void 0;
|
|
2136
|
+
if (!yRuntime) return { value: void 0, reason: "missing-value" };
|
|
1987
2137
|
const runtimeState = yRuntime.get(blockId);
|
|
1988
|
-
if (!runtimeState?.output) return void 0;
|
|
2138
|
+
if (!runtimeState?.output) return { value: void 0, reason: "missing-value" };
|
|
1989
2139
|
const innerPath = propPath.substring("output.".length);
|
|
1990
2140
|
const direct = getNestedValue(runtimeState.output, innerPath);
|
|
1991
|
-
if (direct !== void 0) return direct;
|
|
2141
|
+
if (direct !== void 0) return { value: direct };
|
|
1992
2142
|
if (runtimeState.output.data !== void 0) {
|
|
1993
|
-
|
|
2143
|
+
const value2 = getNestedValue(runtimeState.output.data, innerPath);
|
|
2144
|
+
return { value: value2, reason: value2 === void 0 ? "missing-value" : void 0 };
|
|
1994
2145
|
}
|
|
1995
2146
|
if (runtimeState.output.http?.data !== void 0) {
|
|
1996
|
-
|
|
2147
|
+
const value2 = getNestedValue(runtimeState.output.http.data, innerPath);
|
|
2148
|
+
return { value: value2, reason: value2 === void 0 ? "missing-value" : void 0 };
|
|
1997
2149
|
}
|
|
1998
|
-
return void 0;
|
|
2150
|
+
return { value: void 0, reason: "missing-value" };
|
|
1999
2151
|
}
|
|
2000
2152
|
if (propPath.startsWith("response.")) {
|
|
2001
2153
|
const responseData = block.props.response;
|
|
2002
2154
|
if (!responseData) {
|
|
2003
|
-
return void 0;
|
|
2155
|
+
return { value: void 0, reason: "missing-value" };
|
|
2004
2156
|
}
|
|
2005
2157
|
try {
|
|
2006
2158
|
const parsedResponse = typeof responseData === "string" ? JSON.parse(responseData) : responseData;
|
|
2007
2159
|
const innerPath = propPath.substring("response.".length);
|
|
2008
2160
|
const value2 = getNestedValue(parsedResponse, innerPath);
|
|
2009
|
-
return value2;
|
|
2010
|
-
} catch
|
|
2011
|
-
|
|
2161
|
+
return { value: value2, reason: value2 === void 0 ? "missing-value" : void 0 };
|
|
2162
|
+
} catch {
|
|
2163
|
+
warnOnce(`ref-response-parse:${blockId}`, `[flow-config] block ${blockId}: props.response is not valid JSON; {{${blockId}.${propPath}}} cannot resolve`);
|
|
2164
|
+
return { value: void 0, reason: "response-parse-error" };
|
|
2012
2165
|
}
|
|
2013
2166
|
}
|
|
2014
2167
|
const value = getNestedValue(block.props, propPath);
|
|
2015
|
-
return value;
|
|
2168
|
+
return { value, reason: value === void 0 ? "missing-value" : void 0 };
|
|
2016
2169
|
}
|
|
2017
|
-
function
|
|
2018
|
-
|
|
2170
|
+
function resolveSingleReference(blockId, propPath, editorDocument, yRuntime, scope) {
|
|
2171
|
+
return resolveSingleReferenceDetailed(blockId, propPath, editorDocument, yRuntime, scope).value;
|
|
2172
|
+
}
|
|
2173
|
+
function resolveReferencesDetailed(input, editorDocument, options = {}) {
|
|
2174
|
+
const { fallback = "", stringifyObjects = true, yRuntime, scope, warnContext } = options;
|
|
2175
|
+
const unresolved = [];
|
|
2019
2176
|
if (input == null) {
|
|
2020
|
-
return "";
|
|
2177
|
+
return { value: "", unresolved };
|
|
2021
2178
|
}
|
|
2022
2179
|
const inputStr = String(input);
|
|
2023
2180
|
const references = parseReferences(inputStr);
|
|
2024
2181
|
if (references.length === 0) {
|
|
2025
|
-
return inputStr;
|
|
2182
|
+
return { value: inputStr, unresolved };
|
|
2026
2183
|
}
|
|
2027
2184
|
let result = inputStr;
|
|
2028
2185
|
for (let i = references.length - 1; i >= 0; i--) {
|
|
2029
2186
|
const ref = references[i];
|
|
2030
|
-
const
|
|
2187
|
+
const resolution = resolveSingleReferenceDetailed(ref.blockId, ref.propPath, editorDocument, yRuntime, scope);
|
|
2188
|
+
const resolvedValue = resolution.value;
|
|
2031
2189
|
let replacementStr;
|
|
2032
2190
|
if (resolvedValue === void 0 || resolvedValue === null) {
|
|
2033
2191
|
replacementStr = fallback;
|
|
2192
|
+
unresolved.push({ ref: ref.fullMatch, blockId: ref.blockId, propPath: ref.propPath, reason: resolution.reason || "missing-value" });
|
|
2034
2193
|
} else if (typeof resolvedValue === "object") {
|
|
2035
2194
|
replacementStr = stringifyObjects ? JSON.stringify(resolvedValue) : fallback;
|
|
2036
2195
|
} else {
|
|
@@ -2038,7 +2197,15 @@ function resolveReferences(input, editorDocument, options = {}) {
|
|
|
2038
2197
|
}
|
|
2039
2198
|
result = result.substring(0, ref.startIndex) + replacementStr + result.substring(ref.endIndex);
|
|
2040
2199
|
}
|
|
2041
|
-
|
|
2200
|
+
if (warnContext && unresolved.length > 0) {
|
|
2201
|
+
for (const entry of unresolved) {
|
|
2202
|
+
warnOnce(`ref-unresolved:${warnContext}:${entry.ref}`, `[flow-config] ${warnContext}: reference ${entry.ref} did not resolve (${entry.reason}); using fallback '${fallback}'`);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
return { value: result, unresolved };
|
|
2206
|
+
}
|
|
2207
|
+
function resolveReferences(input, editorDocument, options = {}) {
|
|
2208
|
+
return resolveReferencesDetailed(input, editorDocument, options).value;
|
|
2042
2209
|
}
|
|
2043
2210
|
function hasReferences(input) {
|
|
2044
2211
|
if (input == null) return false;
|
|
@@ -2241,6 +2408,13 @@ registerAction({
|
|
|
2241
2408
|
type: "qi/claim.evaluate",
|
|
2242
2409
|
can: "claim/evaluate",
|
|
2243
2410
|
sideEffect: true,
|
|
2411
|
+
// Dynamic resolvers predate merge-by-default and intentionally control
|
|
2412
|
+
// the full vocabulary (e.g. returning [] to hide the static baseline).
|
|
2413
|
+
dynamicResolutionMode: "replace",
|
|
2414
|
+
proof: { fields: ["claimId"] },
|
|
2415
|
+
// Repeatable: an evaluator processes many claims over the flow's life. Never
|
|
2416
|
+
// latches to a terminal Done. See cardinality.
|
|
2417
|
+
cardinality: "many",
|
|
2244
2418
|
defaultRequiresConfirmation: true,
|
|
2245
2419
|
requiredCapability: "flow/block/execute",
|
|
2246
2420
|
// Static fallback — used until a collection is picked and the survey
|
|
@@ -2534,6 +2708,7 @@ registerAction({
|
|
|
2534
2708
|
type: "qi/proposal.create",
|
|
2535
2709
|
can: "proposal/create",
|
|
2536
2710
|
sideEffect: true,
|
|
2711
|
+
proof: { fields: ["proposalId"] },
|
|
2537
2712
|
defaultRequiresConfirmation: true,
|
|
2538
2713
|
requiredCapability: "flow/block/execute",
|
|
2539
2714
|
inputSchema: {
|
|
@@ -2558,6 +2733,9 @@ registerAction({
|
|
|
2558
2733
|
if (!handlers) {
|
|
2559
2734
|
throw new Error("Handlers not available");
|
|
2560
2735
|
}
|
|
2736
|
+
if (!handlers.getPreProposalContractAddress || !handlers.getGroupContractAddress || !handlers.getProposalContractAddress || !handlers.createProposal) {
|
|
2737
|
+
throw new Error("Governance proposal handlers not available");
|
|
2738
|
+
}
|
|
2561
2739
|
const coreAddress = String(inputs.coreAddress || "").trim();
|
|
2562
2740
|
const title = String(inputs.title || "").trim();
|
|
2563
2741
|
const description = String(inputs.description || "").trim();
|
|
@@ -2611,6 +2789,7 @@ registerAction({
|
|
|
2611
2789
|
type: "qi/proposal.vote",
|
|
2612
2790
|
can: "proposal/vote",
|
|
2613
2791
|
sideEffect: true,
|
|
2792
|
+
proof: { fields: ["votedAt"] },
|
|
2614
2793
|
defaultRequiresConfirmation: true,
|
|
2615
2794
|
requiredCapability: "flow/block/execute",
|
|
2616
2795
|
inputSchema: {
|
|
@@ -2634,6 +2813,9 @@ registerAction({
|
|
|
2634
2813
|
if (!handlers) {
|
|
2635
2814
|
throw new Error("Handlers not available");
|
|
2636
2815
|
}
|
|
2816
|
+
if (!handlers.vote) {
|
|
2817
|
+
throw new Error("vote handler not available");
|
|
2818
|
+
}
|
|
2637
2819
|
const proposalId = Number(inputs.proposalId);
|
|
2638
2820
|
const vote = String(inputs.vote || "").trim();
|
|
2639
2821
|
const rationale = String(inputs.rationale || "").trim();
|
|
@@ -2667,6 +2849,7 @@ registerAction({
|
|
|
2667
2849
|
type: "qi/protocol.select",
|
|
2668
2850
|
can: "protocol/select",
|
|
2669
2851
|
sideEffect: false,
|
|
2852
|
+
proof: "none",
|
|
2670
2853
|
defaultRequiresConfirmation: false,
|
|
2671
2854
|
inputSchema: {
|
|
2672
2855
|
type: "object",
|
|
@@ -3946,6 +4129,7 @@ registerAction({
|
|
|
3946
4129
|
type: "qi/domain.sign",
|
|
3947
4130
|
can: "domain/sign",
|
|
3948
4131
|
sideEffect: true,
|
|
4132
|
+
proof: { fields: ["entityDid"] },
|
|
3949
4133
|
defaultRequiresConfirmation: true,
|
|
3950
4134
|
requiredCapability: "flow/block/execute",
|
|
3951
4135
|
eligibleForEventTrigger: true,
|
|
@@ -4096,6 +4280,7 @@ registerAction({
|
|
|
4096
4280
|
if (typeof handlers.createDomain !== "function") throw new Error("createDomain handler not implemented");
|
|
4097
4281
|
if (typeof handlers.createAddLinkedResourceMessage !== "function") throw new Error("createAddLinkedResourceMessage handler not implemented");
|
|
4098
4282
|
if (typeof handlers.executeTransaction !== "function") throw new Error("executeTransaction handler not implemented");
|
|
4283
|
+
const { requestPin, signCredential, publicFileUpload } = handlers;
|
|
4099
4284
|
const saveCheckpoint = (updates) => {
|
|
4100
4285
|
checkpoint = {
|
|
4101
4286
|
...checkpoint,
|
|
@@ -4160,14 +4345,14 @@ registerAction({
|
|
|
4160
4345
|
});
|
|
4161
4346
|
let signedCredential;
|
|
4162
4347
|
for (let attempt = 1; attempt <= MAX_PIN_ATTEMPTS; attempt++) {
|
|
4163
|
-
const pin = await
|
|
4348
|
+
const pin = await requestPin({
|
|
4164
4349
|
title: "Sign Domain Card",
|
|
4165
4350
|
description: attempt === 1 ? "Enter your PIN to sign the Domain Card credential" : "Incorrect PIN. Re-enter your PIN to sign the Domain Card credential.",
|
|
4166
4351
|
submitText: "Sign"
|
|
4167
4352
|
});
|
|
4168
4353
|
if (!pin) throw new Error("PIN entry cancelled");
|
|
4169
4354
|
try {
|
|
4170
|
-
({ signedCredential } = await
|
|
4355
|
+
({ signedCredential } = await signCredential({
|
|
4171
4356
|
issuerDid,
|
|
4172
4357
|
issuerType: "user",
|
|
4173
4358
|
credential: unsignedCredential,
|
|
@@ -4186,7 +4371,7 @@ registerAction({
|
|
|
4186
4371
|
const credentialFile = new File([credentialBlob], "domainCard.json", {
|
|
4187
4372
|
type: "application/json"
|
|
4188
4373
|
});
|
|
4189
|
-
const uploadResult = await
|
|
4374
|
+
const uploadResult = await publicFileUpload(credentialFile);
|
|
4190
4375
|
return {
|
|
4191
4376
|
...buildDomainCardLinkedResource({
|
|
4192
4377
|
entityDid,
|
|
@@ -4360,6 +4545,7 @@ registerAction({
|
|
|
4360
4545
|
type: "qi/domain.card-preview",
|
|
4361
4546
|
can: "domain/card-preview",
|
|
4362
4547
|
sideEffect: false,
|
|
4548
|
+
proof: "none",
|
|
4363
4549
|
defaultRequiresConfirmation: false,
|
|
4364
4550
|
inputSchema: {
|
|
4365
4551
|
type: "object",
|
|
@@ -4439,6 +4625,7 @@ registerAction({
|
|
|
4439
4625
|
type: "oracle",
|
|
4440
4626
|
can: "oracle/query",
|
|
4441
4627
|
sideEffect: false,
|
|
4628
|
+
proof: "none",
|
|
4442
4629
|
defaultRequiresConfirmation: false,
|
|
4443
4630
|
inputSchema: {
|
|
4444
4631
|
type: "object",
|
|
@@ -4466,6 +4653,7 @@ registerAction({
|
|
|
4466
4653
|
type: "qi/credential.store",
|
|
4467
4654
|
can: "credential/store",
|
|
4468
4655
|
sideEffect: true,
|
|
4656
|
+
proof: { fields: ["storedAt"] },
|
|
4469
4657
|
defaultRequiresConfirmation: true,
|
|
4470
4658
|
requiredCapability: "flow/execute",
|
|
4471
4659
|
inputSchema: {
|
|
@@ -4527,6 +4715,7 @@ registerAction({
|
|
|
4527
4715
|
type: "qi/payment.execute",
|
|
4528
4716
|
can: "payment/execute",
|
|
4529
4717
|
sideEffect: true,
|
|
4718
|
+
proof: { fields: ["paymentBlockId"] },
|
|
4530
4719
|
defaultRequiresConfirmation: true,
|
|
4531
4720
|
requiredCapability: "flow/block/execute",
|
|
4532
4721
|
inputSchema: {
|
|
@@ -4611,6 +4800,7 @@ registerAction({
|
|
|
4611
4800
|
type: "qi/matrix.dm",
|
|
4612
4801
|
can: "matrix/dm",
|
|
4613
4802
|
sideEffect: true,
|
|
4803
|
+
proof: { fields: ["roomId"] },
|
|
4614
4804
|
defaultRequiresConfirmation: false,
|
|
4615
4805
|
inputSchema: {
|
|
4616
4806
|
type: "object",
|
|
@@ -4648,6 +4838,7 @@ registerAction({
|
|
|
4648
4838
|
type: "qi/wallet.generate",
|
|
4649
4839
|
can: "wallet/generate",
|
|
4650
4840
|
sideEffect: false,
|
|
4841
|
+
proof: { fields: ["address"] },
|
|
4651
4842
|
defaultRequiresConfirmation: false,
|
|
4652
4843
|
inputSchema: {
|
|
4653
4844
|
type: "object",
|
|
@@ -4674,6 +4865,7 @@ registerAction({
|
|
|
4674
4865
|
type: "qi/wallet.fund",
|
|
4675
4866
|
can: "wallet/fund",
|
|
4676
4867
|
sideEffect: true,
|
|
4868
|
+
proof: { fields: ["transactionHash"] },
|
|
4677
4869
|
defaultRequiresConfirmation: true,
|
|
4678
4870
|
inputSchema: {
|
|
4679
4871
|
type: "object",
|
|
@@ -4702,6 +4894,7 @@ registerAction({
|
|
|
4702
4894
|
type: "qi/wallet.generateAndFund",
|
|
4703
4895
|
can: "wallet/generateAndFund",
|
|
4704
4896
|
sideEffect: true,
|
|
4897
|
+
proof: { fields: ["transactionHash"] },
|
|
4705
4898
|
defaultRequiresConfirmation: false,
|
|
4706
4899
|
inputSchema: {
|
|
4707
4900
|
type: "object",
|
|
@@ -4752,6 +4945,7 @@ registerAction({
|
|
|
4752
4945
|
type: "qi/iid.create",
|
|
4753
4946
|
can: "iid/create",
|
|
4754
4947
|
sideEffect: true,
|
|
4948
|
+
proof: { fields: ["transactionHash", "alreadyExisted"] },
|
|
4755
4949
|
defaultRequiresConfirmation: false,
|
|
4756
4950
|
inputSchema: {
|
|
4757
4951
|
type: "object",
|
|
@@ -4790,6 +4984,7 @@ registerAction({
|
|
|
4790
4984
|
type: "qi/matrix.register",
|
|
4791
4985
|
can: "matrix/register",
|
|
4792
4986
|
sideEffect: true,
|
|
4987
|
+
proof: { fields: ["matrixUserId"] },
|
|
4793
4988
|
defaultRequiresConfirmation: false,
|
|
4794
4989
|
inputSchema: {
|
|
4795
4990
|
type: "object",
|
|
@@ -4839,6 +5034,8 @@ registerAction({
|
|
|
4839
5034
|
type: "qi/identity.create",
|
|
4840
5035
|
can: "identity/create",
|
|
4841
5036
|
sideEffect: true,
|
|
5037
|
+
// Dual-phase proof: on-chain IID (transactionHash or alreadyExisted) AND matrix registration.
|
|
5038
|
+
proof: { validate: (output) => !!(output.transactionHash || output.alreadyExisted) && !!output.matrixUserId },
|
|
4842
5039
|
defaultRequiresConfirmation: false,
|
|
4843
5040
|
inputSchema: {
|
|
4844
5041
|
type: "object",
|
|
@@ -4939,6 +5136,7 @@ registerAction({
|
|
|
4939
5136
|
type: "qi/entity.createOracle",
|
|
4940
5137
|
can: "entity/createOracle",
|
|
4941
5138
|
sideEffect: true,
|
|
5139
|
+
proof: { fields: ["entityDid"] },
|
|
4942
5140
|
defaultRequiresConfirmation: false,
|
|
4943
5141
|
inputSchema: {
|
|
4944
5142
|
type: "object",
|
|
@@ -5041,6 +5239,7 @@ registerAction({
|
|
|
5041
5239
|
type: "qi/sandbox.provision",
|
|
5042
5240
|
can: "sandbox/provision",
|
|
5043
5241
|
sideEffect: true,
|
|
5242
|
+
proof: { fields: ["status", "sandboxUrl"] },
|
|
5044
5243
|
defaultRequiresConfirmation: false,
|
|
5045
5244
|
inputSchema: {
|
|
5046
5245
|
type: "object",
|
|
@@ -5073,6 +5272,7 @@ registerAction({
|
|
|
5073
5272
|
type: "qi/oracle.contract",
|
|
5074
5273
|
can: "oracle/contract",
|
|
5075
5274
|
sideEffect: true,
|
|
5275
|
+
proof: { fields: ["userOracleRoomId"] },
|
|
5076
5276
|
defaultRequiresConfirmation: false,
|
|
5077
5277
|
inputSchema: {
|
|
5078
5278
|
type: "object",
|
|
@@ -5103,6 +5303,7 @@ registerAction({
|
|
|
5103
5303
|
type: "qi/oracle.storeSecrets",
|
|
5104
5304
|
can: "oracle/storeSecrets",
|
|
5105
5305
|
sideEffect: true,
|
|
5306
|
+
proof: { fields: ["storedSecrets"] },
|
|
5106
5307
|
defaultRequiresConfirmation: false,
|
|
5107
5308
|
inputSchema: {
|
|
5108
5309
|
type: "object",
|
|
@@ -5193,6 +5394,7 @@ registerAction({
|
|
|
5193
5394
|
type: "qi/oracle.storeConfig",
|
|
5194
5395
|
can: "oracle/storeConfig",
|
|
5195
5396
|
sideEffect: true,
|
|
5397
|
+
proof: { fields: ["configStored"] },
|
|
5196
5398
|
defaultRequiresConfirmation: false,
|
|
5197
5399
|
inputSchema: {
|
|
5198
5400
|
type: "object",
|
|
@@ -5263,6 +5465,7 @@ registerAction({
|
|
|
5263
5465
|
type: "qi/oracle.storeSecretsAndConfig",
|
|
5264
5466
|
can: "oracle/storeSecretsAndConfig",
|
|
5265
5467
|
sideEffect: true,
|
|
5468
|
+
proof: { fields: ["storedSecrets"] },
|
|
5266
5469
|
defaultRequiresConfirmation: false,
|
|
5267
5470
|
inputSchema: {
|
|
5268
5471
|
type: "object",
|
|
@@ -5414,6 +5617,7 @@ registerAction({
|
|
|
5414
5617
|
type: "qi/oracle.configureOracle",
|
|
5415
5618
|
can: "oracle/configureOracle",
|
|
5416
5619
|
sideEffect: true,
|
|
5620
|
+
proof: { fields: ["configStored"] },
|
|
5417
5621
|
defaultRequiresConfirmation: false,
|
|
5418
5622
|
inputSchema: {
|
|
5419
5623
|
type: "object",
|
|
@@ -5594,6 +5798,7 @@ registerAction({
|
|
|
5594
5798
|
type: "qi/oracle.deploySetup",
|
|
5595
5799
|
can: "oracle/deploySetup",
|
|
5596
5800
|
sideEffect: true,
|
|
5801
|
+
proof: { fields: ["setupComplete"] },
|
|
5597
5802
|
defaultRequiresConfirmation: false,
|
|
5598
5803
|
inputSchema: {
|
|
5599
5804
|
type: "object",
|
|
@@ -5628,6 +5833,7 @@ registerAction({
|
|
|
5628
5833
|
type: "qi/oracle.deployStart",
|
|
5629
5834
|
can: "oracle/deployStart",
|
|
5630
5835
|
sideEffect: true,
|
|
5836
|
+
proof: { fields: ["processId", "status"] },
|
|
5631
5837
|
defaultRequiresConfirmation: false,
|
|
5632
5838
|
inputSchema: {
|
|
5633
5839
|
type: "object",
|
|
@@ -5661,6 +5867,7 @@ registerAction({
|
|
|
5661
5867
|
type: "qi/oracle.deploy",
|
|
5662
5868
|
can: "oracle/deploy",
|
|
5663
5869
|
sideEffect: true,
|
|
5870
|
+
proof: { fields: ["processId", "status"] },
|
|
5664
5871
|
defaultRequiresConfirmation: false,
|
|
5665
5872
|
inputSchema: {
|
|
5666
5873
|
type: "object",
|
|
@@ -5796,6 +6003,10 @@ registerAction({
|
|
|
5796
6003
|
type: COLLECTION_LIFECYCLE_ACTION_TYPE,
|
|
5797
6004
|
can: "collection/lifecycle",
|
|
5798
6005
|
sideEffect: true,
|
|
6006
|
+
// Dynamic resolvers predate merge-by-default and intentionally control
|
|
6007
|
+
// the full vocabulary (e.g. returning [] to hide the static baseline).
|
|
6008
|
+
dynamicResolutionMode: "replace",
|
|
6009
|
+
proof: { fields: ["collectionId"] },
|
|
5799
6010
|
defaultRequiresConfirmation: true,
|
|
5800
6011
|
requiredCapability: "flow/block/execute",
|
|
5801
6012
|
outputSchema: COLLECTION_LIFECYCLE_OUTPUT_SCHEMA,
|
|
@@ -5992,6 +6203,13 @@ registerAction({
|
|
|
5992
6203
|
type: COLLECTION_USERS_ACTION_TYPE,
|
|
5993
6204
|
can: "collection/users",
|
|
5994
6205
|
sideEffect: true,
|
|
6206
|
+
// Dynamic resolvers predate merge-by-default and intentionally control
|
|
6207
|
+
// the full vocabulary (e.g. returning [] to hide the static baseline).
|
|
6208
|
+
dynamicResolutionMode: "replace",
|
|
6209
|
+
proof: { fields: ["transactionHash", "collectionId"] },
|
|
6210
|
+
// Repeatable: members are granted/revoked many times over the flow's life.
|
|
6211
|
+
// Never latches to a terminal Done. See cardinality.
|
|
6212
|
+
cardinality: "many",
|
|
5995
6213
|
defaultRequiresConfirmation: true,
|
|
5996
6214
|
requiredCapability: "flow/block/execute",
|
|
5997
6215
|
outputSchema: COLLECTION_USERS_OUTPUT_SCHEMA,
|
|
@@ -6155,6 +6373,7 @@ registerAction({
|
|
|
6155
6373
|
type: "qi/carbon.loadBatches",
|
|
6156
6374
|
can: "carbon/load",
|
|
6157
6375
|
sideEffect: false,
|
|
6376
|
+
proof: "none",
|
|
6158
6377
|
defaultRequiresConfirmation: false,
|
|
6159
6378
|
inputSchema: {
|
|
6160
6379
|
type: "object",
|
|
@@ -6181,6 +6400,7 @@ registerAction({
|
|
|
6181
6400
|
type: "qi/carbon.harvest",
|
|
6182
6401
|
can: "carbon/harvest",
|
|
6183
6402
|
sideEffect: true,
|
|
6403
|
+
proof: { fields: ["transactionHash"] },
|
|
6184
6404
|
defaultRequiresConfirmation: true,
|
|
6185
6405
|
inputSchema: {
|
|
6186
6406
|
type: "object",
|
|
@@ -6220,6 +6440,7 @@ registerAction({
|
|
|
6220
6440
|
type: "qi/carbon.retire",
|
|
6221
6441
|
can: "carbon/retire",
|
|
6222
6442
|
sideEffect: true,
|
|
6443
|
+
proof: { fields: ["transactionHash"] },
|
|
6223
6444
|
defaultRequiresConfirmation: true,
|
|
6224
6445
|
inputSchema: {
|
|
6225
6446
|
type: "object",
|
|
@@ -6281,6 +6502,7 @@ registerAction({
|
|
|
6281
6502
|
type: "qi/entity.transfer",
|
|
6282
6503
|
can: "entity/transfer",
|
|
6283
6504
|
sideEffect: true,
|
|
6505
|
+
proof: { fields: ["transactionHash"] },
|
|
6284
6506
|
defaultRequiresConfirmation: true,
|
|
6285
6507
|
outputSchema: ENTITY_TRANSFER_OUTPUT,
|
|
6286
6508
|
inputSchema: {
|
|
@@ -6426,6 +6648,8 @@ registerAction({
|
|
|
6426
6648
|
type: "qi/gmail.email.send",
|
|
6427
6649
|
can: "gmail.email/send",
|
|
6428
6650
|
sideEffect: true,
|
|
6651
|
+
// Proof of execution: Gmail returns the sent message id. Matches qi/email.send.
|
|
6652
|
+
proof: { fields: ["messageId"] },
|
|
6429
6653
|
defaultRequiresConfirmation: true,
|
|
6430
6654
|
requiredCapability: "flow/block/execute",
|
|
6431
6655
|
// Can be wired to another block's event (e.g. form submitted → send email).
|
|
@@ -6498,6 +6722,11 @@ registerAction({
|
|
|
6498
6722
|
type: "qi/outlook.email.send",
|
|
6499
6723
|
can: "outlook.email/send",
|
|
6500
6724
|
sideEffect: true,
|
|
6725
|
+
// Outlook's send tool often returns no message id (see run() below), so there
|
|
6726
|
+
// is no reliable output field to prove execution — a successful tool call
|
|
6727
|
+
// (run() throws on failure) is the signal. Declared 'none' rather than
|
|
6728
|
+
// requiring messageId, which would push every id-less send to needs_verification.
|
|
6729
|
+
proof: "none",
|
|
6501
6730
|
defaultRequiresConfirmation: true,
|
|
6502
6731
|
requiredCapability: "flow/block/execute",
|
|
6503
6732
|
// Can be wired to another block's event (e.g. form submitted → send email).
|
|
@@ -6570,6 +6799,8 @@ registerAction({
|
|
|
6570
6799
|
type: "qi/slack.message.send",
|
|
6571
6800
|
can: "slack.message/send",
|
|
6572
6801
|
sideEffect: true,
|
|
6802
|
+
// Proof of execution: Slack returns the posted message timestamp (ts).
|
|
6803
|
+
proof: { fields: ["messageTs"] },
|
|
6573
6804
|
defaultRequiresConfirmation: true,
|
|
6574
6805
|
requiredCapability: "flow/block/execute",
|
|
6575
6806
|
// Can be wired to another block's event (e.g. form submitted → post message).
|
|
@@ -6645,6 +6876,8 @@ registerAction({
|
|
|
6645
6876
|
type: "qi/googlecalendar.event.create",
|
|
6646
6877
|
can: "googlecalendar.event/create",
|
|
6647
6878
|
sideEffect: true,
|
|
6879
|
+
// Proof of execution: the created event's id. Matches qi/calendar.event.create.
|
|
6880
|
+
proof: { fields: ["eventId"] },
|
|
6648
6881
|
defaultRequiresConfirmation: true,
|
|
6649
6882
|
requiredCapability: "flow/block/execute",
|
|
6650
6883
|
eligibleForEventTrigger: true,
|
|
@@ -6741,6 +6974,7 @@ registerAction({
|
|
|
6741
6974
|
type: "qi/calendar.event.create",
|
|
6742
6975
|
can: "calendar.event/create",
|
|
6743
6976
|
sideEffect: true,
|
|
6977
|
+
proof: { fields: ["eventId"] },
|
|
6744
6978
|
defaultRequiresConfirmation: true,
|
|
6745
6979
|
requiredCapability: "flow/block/execute",
|
|
6746
6980
|
eligibleForEventTrigger: true,
|
|
@@ -6848,6 +7082,7 @@ registerAction({
|
|
|
6848
7082
|
type: "qi/calendar.event.update",
|
|
6849
7083
|
can: "calendar.event/update",
|
|
6850
7084
|
sideEffect: true,
|
|
7085
|
+
proof: { fields: ["eventId"] },
|
|
6851
7086
|
defaultRequiresConfirmation: true,
|
|
6852
7087
|
requiredCapability: "flow/block/execute",
|
|
6853
7088
|
eligibleForEventTrigger: true,
|
|
@@ -6957,6 +7192,7 @@ registerAction({
|
|
|
6957
7192
|
type: "qi/calendar.event.list",
|
|
6958
7193
|
can: "calendar.event/list",
|
|
6959
7194
|
sideEffect: false,
|
|
7195
|
+
proof: "none",
|
|
6960
7196
|
defaultRequiresConfirmation: false,
|
|
6961
7197
|
requiredCapability: "flow/block/execute",
|
|
6962
7198
|
eligibleForEventTrigger: false,
|
|
@@ -7038,6 +7274,7 @@ registerAction({
|
|
|
7038
7274
|
type: "qi/xero.contact.create",
|
|
7039
7275
|
can: "xero.contact/create",
|
|
7040
7276
|
sideEffect: true,
|
|
7277
|
+
proof: { fields: ["contactId"] },
|
|
7041
7278
|
defaultRequiresConfirmation: true,
|
|
7042
7279
|
requiredCapability: "flow/block/execute",
|
|
7043
7280
|
eligibleForEventTrigger: true,
|
|
@@ -7136,6 +7373,10 @@ registerAction({
|
|
|
7136
7373
|
type: "qi/xero.invoice.create",
|
|
7137
7374
|
can: "xero.invoice/create",
|
|
7138
7375
|
sideEffect: true,
|
|
7376
|
+
proof: { fields: ["invoiceId"] },
|
|
7377
|
+
// Repeatable: many invoices are created over the flow's life. Never latches
|
|
7378
|
+
// to a terminal Done. See cardinality.
|
|
7379
|
+
cardinality: "many",
|
|
7139
7380
|
defaultRequiresConfirmation: true,
|
|
7140
7381
|
requiredCapability: "flow/block/execute",
|
|
7141
7382
|
eligibleForEventTrigger: true,
|
|
@@ -7241,6 +7482,7 @@ registerAction({
|
|
|
7241
7482
|
type: "qi/xero.invoice.list",
|
|
7242
7483
|
can: "xero.invoice/list",
|
|
7243
7484
|
sideEffect: false,
|
|
7485
|
+
proof: "none",
|
|
7244
7486
|
defaultRequiresConfirmation: false,
|
|
7245
7487
|
requiredCapability: "flow/block/execute",
|
|
7246
7488
|
eligibleForEventTrigger: false,
|
|
@@ -7321,6 +7563,10 @@ registerAction({
|
|
|
7321
7563
|
type: "qi/xero.payment.create",
|
|
7322
7564
|
can: "xero.payment/create",
|
|
7323
7565
|
sideEffect: true,
|
|
7566
|
+
proof: { fields: ["paymentId"] },
|
|
7567
|
+
// Repeatable: many payments are created over the flow's life. Never latches
|
|
7568
|
+
// to a terminal Done. See cardinality.
|
|
7569
|
+
cardinality: "many",
|
|
7324
7570
|
defaultRequiresConfirmation: true,
|
|
7325
7571
|
requiredCapability: "flow/block/execute",
|
|
7326
7572
|
eligibleForEventTrigger: true,
|
|
@@ -9031,175 +9277,285 @@ var createUcanService = (config) => {
|
|
|
9031
9277
|
};
|
|
9032
9278
|
};
|
|
9033
9279
|
|
|
9034
|
-
// src/core/lib/flowEngine/utils.ts
|
|
9035
|
-
var buildAuthzFromProps = (props) => {
|
|
9036
|
-
const linkedClaimCollectionId = typeof props.linkedClaimCollectionId === "string" ? props.linkedClaimCollectionId.trim() : "";
|
|
9037
|
-
const authz = {};
|
|
9038
|
-
if (linkedClaimCollectionId) {
|
|
9039
|
-
authz.linkedClaim = { collectionId: linkedClaimCollectionId };
|
|
9040
|
-
}
|
|
9041
|
-
return authz;
|
|
9042
|
-
};
|
|
9043
|
-
var buildFlowNodeFromBlock = (block) => {
|
|
9044
|
-
const base = {
|
|
9045
|
-
id: block.id,
|
|
9046
|
-
type: block.type,
|
|
9047
|
-
props: block.props || {}
|
|
9048
|
-
};
|
|
9049
|
-
const authz = buildAuthzFromProps(block.props || {});
|
|
9050
|
-
return {
|
|
9051
|
-
...base,
|
|
9052
|
-
...authz
|
|
9053
|
-
};
|
|
9054
|
-
};
|
|
9055
|
-
|
|
9056
|
-
// src/core/lib/flowEngine/runtime.ts
|
|
9057
|
-
var XERO_WORK_ITEMS_MAP_NAME = "xeroWorkItems";
|
|
9058
|
-
var XERO_CONNECTION_MAP_NAME = "xeroConnection";
|
|
9059
|
-
var ensureStateObject = (value) => {
|
|
9060
|
-
if (!value || typeof value !== "object") {
|
|
9061
|
-
return {};
|
|
9062
|
-
}
|
|
9063
|
-
return { ...value };
|
|
9064
|
-
};
|
|
9065
|
-
var createYMapManager = (map) => {
|
|
9066
|
-
return {
|
|
9067
|
-
get: (nodeId) => {
|
|
9068
|
-
const stored = map.get(nodeId);
|
|
9069
|
-
return ensureStateObject(stored);
|
|
9070
|
-
},
|
|
9071
|
-
update: (nodeId, updates) => {
|
|
9072
|
-
const current = ensureStateObject(map.get(nodeId));
|
|
9073
|
-
map.set(nodeId, { ...current, ...updates });
|
|
9074
|
-
}
|
|
9075
|
-
};
|
|
9076
|
-
};
|
|
9077
|
-
var createMemoryManager = () => {
|
|
9078
|
-
const memory = /* @__PURE__ */ new Map();
|
|
9079
|
-
return {
|
|
9080
|
-
get: (nodeId) => ensureStateObject(memory.get(nodeId)),
|
|
9081
|
-
update: (nodeId, updates) => {
|
|
9082
|
-
const current = ensureStateObject(memory.get(nodeId));
|
|
9083
|
-
memory.set(nodeId, { ...current, ...updates });
|
|
9084
|
-
}
|
|
9085
|
-
};
|
|
9086
|
-
};
|
|
9087
|
-
var createRuntimeStateManager = (editor) => {
|
|
9088
|
-
if (editor?._yRuntime) {
|
|
9089
|
-
return createYMapManager(editor._yRuntime);
|
|
9090
|
-
}
|
|
9091
|
-
return createMemoryManager();
|
|
9092
|
-
};
|
|
9093
|
-
var createYDocRuntimeManager = (yDoc) => {
|
|
9094
|
-
return createYMapManager(yDoc.getMap("runtime"));
|
|
9095
|
-
};
|
|
9096
|
-
function clearRuntimeForTemplateClone(yDoc) {
|
|
9097
|
-
const runtime = yDoc.getMap("runtime");
|
|
9098
|
-
const invocations = yDoc.getMap("invocations");
|
|
9099
|
-
const pendingInvocations = yDoc.getMap("pendingInvocations");
|
|
9100
|
-
const agentOutbox = yDoc.getMap("agentOutbox");
|
|
9101
|
-
const agentLeases = yDoc.getMap("agentLeases");
|
|
9102
|
-
const auditTrail = yDoc.getMap("auditTrail");
|
|
9103
|
-
const xeroWorkItems = yDoc.getMap(XERO_WORK_ITEMS_MAP_NAME);
|
|
9104
|
-
const xeroConnection = yDoc.getMap(XERO_CONNECTION_MAP_NAME);
|
|
9105
|
-
yDoc.transact(() => {
|
|
9106
|
-
runtime.forEach((_, key) => runtime.delete(key));
|
|
9107
|
-
invocations.forEach((_, key) => invocations.delete(key));
|
|
9108
|
-
pendingInvocations.forEach((_, key) => pendingInvocations.delete(key));
|
|
9109
|
-
agentOutbox.forEach((_, key) => agentOutbox.delete(key));
|
|
9110
|
-
agentLeases.forEach((_, key) => agentLeases.delete(key));
|
|
9111
|
-
auditTrail.forEach((_, key) => auditTrail.delete(key));
|
|
9112
|
-
xeroWorkItems.forEach((_, key) => xeroWorkItems.delete(key));
|
|
9113
|
-
xeroConnection.forEach((_, key) => xeroConnection.delete(key));
|
|
9114
|
-
});
|
|
9115
|
-
}
|
|
9116
|
-
|
|
9117
9280
|
// src/core/types/baseUcan.ts
|
|
9118
9281
|
function isRuntimeRef(value) {
|
|
9119
9282
|
return typeof value === "object" && value !== null && "$ref" in value && typeof value.$ref === "string";
|
|
9120
9283
|
}
|
|
9121
9284
|
|
|
9122
|
-
// src/core/lib/
|
|
9123
|
-
|
|
9124
|
-
|
|
9285
|
+
// src/core/lib/flowEngine/triggers.ts
|
|
9286
|
+
import * as Y from "yjs";
|
|
9287
|
+
var RUN_RECORD_AUDIT_TYPE = "block.run";
|
|
9288
|
+
function computePendingInvocationId(args) {
|
|
9289
|
+
const { sourceBlockId, sourceRunId, listenerBlockId, eventName, eventIndex } = args;
|
|
9290
|
+
const input = `${sourceBlockId}:${sourceRunId}:${listenerBlockId}:${eventName}:${eventIndex}`;
|
|
9291
|
+
return `pi-${fnv1a32(input)}`;
|
|
9125
9292
|
}
|
|
9126
|
-
function
|
|
9293
|
+
function snapshotInputRefs(inputs, getNodeOutput2) {
|
|
9294
|
+
const snapshots = {};
|
|
9295
|
+
walkRefs(inputs, (ref) => {
|
|
9296
|
+
if (ref.$ref.startsWith("trigger.")) return;
|
|
9297
|
+
const parsed = parseOutputRef(ref.$ref);
|
|
9298
|
+
if (!parsed) return;
|
|
9299
|
+
const output = getNodeOutput2(parsed.nodeId);
|
|
9300
|
+
if (!output) return;
|
|
9301
|
+
snapshots[ref.$ref] = getNestedValue(output, parsed.fieldPath);
|
|
9302
|
+
});
|
|
9303
|
+
return snapshots;
|
|
9304
|
+
}
|
|
9305
|
+
function walkRefs(value, visit) {
|
|
9127
9306
|
if (isRuntimeRef(value)) {
|
|
9128
|
-
|
|
9307
|
+
visit(value);
|
|
9308
|
+
return;
|
|
9129
9309
|
}
|
|
9130
9310
|
if (Array.isArray(value)) {
|
|
9131
|
-
|
|
9311
|
+
for (const item of value) walkRefs(item, visit);
|
|
9312
|
+
return;
|
|
9132
9313
|
}
|
|
9133
9314
|
if (typeof value === "object" && value !== null) {
|
|
9134
|
-
const
|
|
9135
|
-
for (const [key, val] of Object.entries(value)) {
|
|
9136
|
-
result[key] = resolveValue(val, getNodeOutput2, triggerContext);
|
|
9137
|
-
}
|
|
9138
|
-
return result;
|
|
9315
|
+
for (const v of Object.values(value)) walkRefs(v, visit);
|
|
9139
9316
|
}
|
|
9140
|
-
return value;
|
|
9141
9317
|
}
|
|
9142
|
-
function
|
|
9143
|
-
if (ref.startsWith("trigger.payload.")) {
|
|
9144
|
-
if (!triggerContext) {
|
|
9145
|
-
throw new Error(`Trigger ref "${ref}" used outside of a listener invocation context. trigger.payload.* refs are only valid on block.event-triggered blocks.`);
|
|
9146
|
-
}
|
|
9147
|
-
const fieldPath2 = ref.slice("trigger.payload.".length);
|
|
9148
|
-
return getNestedValue2(triggerContext.payload, fieldPath2);
|
|
9149
|
-
}
|
|
9150
|
-
if (triggerContext && Object.prototype.hasOwnProperty.call(triggerContext.refSnapshots, ref)) {
|
|
9151
|
-
return triggerContext.refSnapshots[ref];
|
|
9152
|
-
}
|
|
9318
|
+
function parseOutputRef(ref) {
|
|
9153
9319
|
const outputIndex = ref.indexOf(".output.");
|
|
9154
|
-
if (outputIndex === -1)
|
|
9155
|
-
|
|
9320
|
+
if (outputIndex === -1) return null;
|
|
9321
|
+
return {
|
|
9322
|
+
nodeId: ref.slice(0, outputIndex),
|
|
9323
|
+
fieldPath: ref.slice(outputIndex + ".output.".length)
|
|
9324
|
+
};
|
|
9325
|
+
}
|
|
9326
|
+
function fnv1a32(input) {
|
|
9327
|
+
let hash = 2166136261;
|
|
9328
|
+
for (let i = 0; i < input.length; i++) {
|
|
9329
|
+
hash ^= input.charCodeAt(i);
|
|
9330
|
+
hash = Math.imul(hash, 16777619);
|
|
9156
9331
|
}
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9332
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
9333
|
+
}
|
|
9334
|
+
var PENDING_INVOCATIONS_MAP_KEY = "pendingInvocations";
|
|
9335
|
+
function getPendingInvocationsMap(yDoc) {
|
|
9336
|
+
return yDoc.getMap(PENDING_INVOCATIONS_MAP_KEY);
|
|
9337
|
+
}
|
|
9338
|
+
function getOrCreateBlockPendingMap(yDoc, blockId) {
|
|
9339
|
+
const outer = getPendingInvocationsMap(yDoc);
|
|
9340
|
+
let inner = outer.get(blockId);
|
|
9341
|
+
if (!inner) {
|
|
9342
|
+
inner = new Y.Map();
|
|
9343
|
+
outer.set(blockId, inner);
|
|
9162
9344
|
}
|
|
9163
|
-
return
|
|
9345
|
+
return inner;
|
|
9164
9346
|
}
|
|
9165
|
-
function
|
|
9166
|
-
const
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9347
|
+
function readPendingInvocations(yDoc, blockId) {
|
|
9348
|
+
const outer = getPendingInvocationsMap(yDoc);
|
|
9349
|
+
const inner = outer.get(blockId);
|
|
9350
|
+
if (!inner) return [];
|
|
9351
|
+
const items = [];
|
|
9352
|
+
inner.forEach((value) => {
|
|
9353
|
+
if (value && typeof value === "object") {
|
|
9354
|
+
items.push(value);
|
|
9171
9355
|
}
|
|
9172
|
-
|
|
9356
|
+
});
|
|
9357
|
+
items.sort((a, b) => a.emittedAt.localeCompare(b.emittedAt));
|
|
9358
|
+
return items;
|
|
9359
|
+
}
|
|
9360
|
+
function queuePendingInvocation(yDoc, listenerBlockId, invocation) {
|
|
9361
|
+
let created = false;
|
|
9362
|
+
yDoc.transact(() => {
|
|
9363
|
+
const inner = getOrCreateBlockPendingMap(yDoc, listenerBlockId);
|
|
9364
|
+
if (inner.has(invocation.id)) return;
|
|
9365
|
+
inner.set(invocation.id, invocation);
|
|
9366
|
+
created = true;
|
|
9367
|
+
});
|
|
9368
|
+
return created;
|
|
9369
|
+
}
|
|
9370
|
+
function removePendingInvocation(yDoc, listenerBlockId, pendingInvocationId) {
|
|
9371
|
+
const outer = getPendingInvocationsMap(yDoc);
|
|
9372
|
+
const inner = outer.get(listenerBlockId);
|
|
9373
|
+
if (!inner) return false;
|
|
9374
|
+
if (!inner.has(pendingInvocationId)) return false;
|
|
9375
|
+
inner.delete(pendingInvocationId);
|
|
9376
|
+
return true;
|
|
9377
|
+
}
|
|
9378
|
+
function readConsumedPendingInvocationIds(yDoc, listenerBlockId) {
|
|
9379
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
9380
|
+
for (const record of readRunRecords(yDoc, listenerBlockId)) {
|
|
9381
|
+
if (record.fromPendingInvocationId) consumed.add(record.fromPendingInvocationId);
|
|
9173
9382
|
}
|
|
9174
|
-
return
|
|
9383
|
+
return consumed;
|
|
9175
9384
|
}
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9385
|
+
function sweepExpiredPendingInvocations(yDoc, now = Date.now()) {
|
|
9386
|
+
const outer = getPendingInvocationsMap(yDoc);
|
|
9387
|
+
let removed = 0;
|
|
9388
|
+
outer.forEach((inner, listenerBlockId) => {
|
|
9389
|
+
const innerMap = inner;
|
|
9390
|
+
if (!innerMap || typeof innerMap.forEach !== "function") return;
|
|
9391
|
+
const expiredIds = [];
|
|
9392
|
+
innerMap.forEach((value, id) => {
|
|
9393
|
+
const expiresAt = value?.expiresAt;
|
|
9394
|
+
if (expiresAt && Date.parse(expiresAt) < now) expiredIds.push(id);
|
|
9395
|
+
});
|
|
9396
|
+
for (const id of expiredIds) {
|
|
9397
|
+
innerMap.delete(id);
|
|
9398
|
+
removed += 1;
|
|
9399
|
+
}
|
|
9400
|
+
void listenerBlockId;
|
|
9401
|
+
});
|
|
9402
|
+
return removed;
|
|
9403
|
+
}
|
|
9404
|
+
var BARRIER_STATE_MAP_KEY = "barrierState";
|
|
9405
|
+
function getBarrierStateMap(yDoc) {
|
|
9406
|
+
return yDoc.getMap(BARRIER_STATE_MAP_KEY);
|
|
9407
|
+
}
|
|
9408
|
+
function getOrCreateListenerBarrierMap(yDoc, listenerBlockId) {
|
|
9409
|
+
const outer = getBarrierStateMap(yDoc);
|
|
9410
|
+
let inner = outer.get(listenerBlockId);
|
|
9411
|
+
if (!inner) {
|
|
9412
|
+
inner = new Y.Map();
|
|
9413
|
+
outer.set(listenerBlockId, inner);
|
|
9414
|
+
}
|
|
9415
|
+
return inner;
|
|
9416
|
+
}
|
|
9417
|
+
function recordBarrierEvent(yDoc, listenerBlockId, entry) {
|
|
9418
|
+
let written = false;
|
|
9419
|
+
yDoc.transact(() => {
|
|
9420
|
+
const inner = getOrCreateListenerBarrierMap(yDoc, listenerBlockId);
|
|
9421
|
+
const key = `${entry.sourceBlockId}::${entry.eventName}`;
|
|
9422
|
+
const existing = inner.get(key);
|
|
9423
|
+
if (existing && typeof existing === "object" && existing.runId === entry.runId) return;
|
|
9424
|
+
inner.set(key, entry);
|
|
9425
|
+
written = true;
|
|
9426
|
+
});
|
|
9427
|
+
return written;
|
|
9428
|
+
}
|
|
9429
|
+
function readBarrierState(yDoc, listenerBlockId) {
|
|
9430
|
+
const outer = getBarrierStateMap(yDoc);
|
|
9431
|
+
const inner = outer.get(listenerBlockId);
|
|
9432
|
+
if (!inner) return [];
|
|
9433
|
+
const entries = [];
|
|
9434
|
+
inner.forEach((value) => {
|
|
9435
|
+
if (value && typeof value === "object") {
|
|
9436
|
+
entries.push(value);
|
|
9437
|
+
}
|
|
9438
|
+
});
|
|
9439
|
+
return entries;
|
|
9440
|
+
}
|
|
9441
|
+
function clearBarrierState(yDoc, listenerBlockId) {
|
|
9442
|
+
const outer = getBarrierStateMap(yDoc);
|
|
9443
|
+
yDoc.transact(() => {
|
|
9444
|
+
outer.delete(listenerBlockId);
|
|
9445
|
+
});
|
|
9446
|
+
}
|
|
9447
|
+
function computeBarrierInvocationId(entries, listenerBlockId) {
|
|
9448
|
+
const sorted = [...entries].sort((a, b) => a.sourceBlockId.localeCompare(b.sourceBlockId));
|
|
9449
|
+
const input = sorted.map((e) => `${e.sourceBlockId}:${e.runId}:${e.eventName}`).join("|") + `|>${listenerBlockId}`;
|
|
9450
|
+
return `bi-${fnv1a32(input)}`;
|
|
9451
|
+
}
|
|
9452
|
+
function mergeBarrierPayloads(entries) {
|
|
9453
|
+
const merged = {};
|
|
9454
|
+
for (const entry of entries) {
|
|
9455
|
+
if (entry.alias) {
|
|
9456
|
+
merged[entry.alias] = entry.payload;
|
|
9457
|
+
} else {
|
|
9458
|
+
Object.assign(merged, entry.payload);
|
|
9459
|
+
}
|
|
9460
|
+
}
|
|
9461
|
+
return merged;
|
|
9462
|
+
}
|
|
9463
|
+
function appendRunRecord(yDoc, blockId, details, userId) {
|
|
9464
|
+
const auditMap = yDoc.getMap("auditTrail");
|
|
9465
|
+
yDoc.transact(() => {
|
|
9466
|
+
let arr = auditMap.get(blockId);
|
|
9467
|
+
if (!arr) {
|
|
9468
|
+
arr = new Y.Array();
|
|
9469
|
+
auditMap.set(blockId, arr);
|
|
9470
|
+
}
|
|
9471
|
+
const event = {
|
|
9472
|
+
id: `${details.runId}`,
|
|
9473
|
+
blockId,
|
|
9474
|
+
type: RUN_RECORD_AUDIT_TYPE,
|
|
9475
|
+
details,
|
|
9476
|
+
message: void 0,
|
|
9477
|
+
meta: {
|
|
9478
|
+
timestamp: details.completedAt,
|
|
9479
|
+
userId,
|
|
9480
|
+
editable: false
|
|
9481
|
+
}
|
|
9482
|
+
};
|
|
9483
|
+
arr.push([event]);
|
|
9484
|
+
});
|
|
9485
|
+
}
|
|
9486
|
+
function readRunRecords(yDoc, blockId) {
|
|
9487
|
+
const auditMap = yDoc.getMap("auditTrail");
|
|
9488
|
+
const arr = auditMap.get(blockId);
|
|
9489
|
+
if (!arr) return [];
|
|
9490
|
+
const records = [];
|
|
9491
|
+
arr.forEach((entry) => {
|
|
9492
|
+
if (!entry || typeof entry !== "object") return;
|
|
9493
|
+
const e = entry;
|
|
9494
|
+
if (e.type === RUN_RECORD_AUDIT_TYPE && e.details) {
|
|
9495
|
+
records.push(e.details);
|
|
9496
|
+
}
|
|
9497
|
+
});
|
|
9498
|
+
return records;
|
|
9499
|
+
}
|
|
9500
|
+
function findFailedListenersForSourceRun(yDoc, sourceBlockId, sourceRunId, listenerBlockIds) {
|
|
9501
|
+
const failures = [];
|
|
9502
|
+
for (const listenerBlockId of listenerBlockIds) {
|
|
9503
|
+
const records = readRunRecords(yDoc, listenerBlockId);
|
|
9504
|
+
for (const record of records) {
|
|
9505
|
+
if (!record.error) continue;
|
|
9506
|
+
if (record.triggeredBy?.sourceBlockId !== sourceBlockId) continue;
|
|
9507
|
+
if (record.fromPendingInvocationId == null) continue;
|
|
9508
|
+
const sourceRunIdField = record.sourceRunId;
|
|
9509
|
+
if (sourceRunIdField && sourceRunIdField !== sourceRunId) continue;
|
|
9510
|
+
failures.push({ listenerBlockId, record });
|
|
9511
|
+
}
|
|
9512
|
+
}
|
|
9513
|
+
return failures;
|
|
9514
|
+
}
|
|
9515
|
+
function replayFailedListenerRun(yDoc, failedRecord, listenerBlockId, originalPayload, originalRefSnapshots, assigneeDid) {
|
|
9516
|
+
if (!failedRecord.triggeredBy) return false;
|
|
9517
|
+
const replayId = `${failedRecord.runId}:replay-${Date.now().toString(36)}`;
|
|
9518
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9519
|
+
const replay = {
|
|
9520
|
+
id: replayId,
|
|
9521
|
+
triggeringBlockId: failedRecord.triggeredBy.sourceBlockId,
|
|
9522
|
+
sourceRunId: failedRecord.sourceRunId || failedRecord.runId,
|
|
9523
|
+
eventName: failedRecord.triggeredBy.eventName,
|
|
9524
|
+
eventIndex: 0,
|
|
9525
|
+
payload: originalPayload,
|
|
9526
|
+
refSnapshots: originalRefSnapshots,
|
|
9527
|
+
assigneeDid,
|
|
9528
|
+
emittedAt: now,
|
|
9529
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString()
|
|
9530
|
+
};
|
|
9531
|
+
return queuePendingInvocation(yDoc, listenerBlockId, replay);
|
|
9532
|
+
}
|
|
9533
|
+
|
|
9534
|
+
// src/core/lib/flowEngine/versionManifest.ts
|
|
9535
|
+
var VERSION_MANIFEST = {
|
|
9536
|
+
"0.3": {
|
|
9537
|
+
version: "0.3",
|
|
9538
|
+
label: "Legacy",
|
|
9539
|
+
ucanRequired: false,
|
|
9540
|
+
delegationRootRequired: false,
|
|
9541
|
+
whitelistOnlyAllowed: true,
|
|
9542
|
+
unrestrictedAllowed: true,
|
|
9543
|
+
executionPath: "legacy",
|
|
9544
|
+
authorizationFn: "v1",
|
|
9545
|
+
allowedAuthModes: ["anyone", "actors", "capability"],
|
|
9546
|
+
ui: {
|
|
9547
|
+
showDelegationPanel: false,
|
|
9548
|
+
showWhitelistConfig: true,
|
|
9549
|
+
showAnyoneConfig: true,
|
|
9550
|
+
showMigrationBanner: true,
|
|
9551
|
+
requirePinForExecution: false
|
|
9552
|
+
},
|
|
9196
9553
|
description: "Legacy version. UCAN optional, whitelist-only authorization accepted."
|
|
9197
9554
|
},
|
|
9198
9555
|
"1.0.0": {
|
|
9199
9556
|
version: "1.0.0",
|
|
9200
9557
|
label: "UCAN Required",
|
|
9201
|
-
|
|
9202
|
-
ucanRequired: false,
|
|
9558
|
+
ucanRequired: true,
|
|
9203
9559
|
delegationRootRequired: true,
|
|
9204
9560
|
whitelistOnlyAllowed: false,
|
|
9205
9561
|
unrestrictedAllowed: false,
|
|
@@ -9217,6 +9573,7 @@ var VERSION_MANIFEST = {
|
|
|
9217
9573
|
}
|
|
9218
9574
|
};
|
|
9219
9575
|
var LATEST_VERSION = "1.0.0";
|
|
9576
|
+
var MIGRATION_PATH = ["0.3", "1.0.0"];
|
|
9220
9577
|
function getVersionPolicy(version) {
|
|
9221
9578
|
const policy = VERSION_MANIFEST[version];
|
|
9222
9579
|
if (!policy) {
|
|
@@ -9224,635 +9581,673 @@ function getVersionPolicy(version) {
|
|
|
9224
9581
|
}
|
|
9225
9582
|
return policy;
|
|
9226
9583
|
}
|
|
9584
|
+
function isVersionAtLeast(current, minimum) {
|
|
9585
|
+
const currentIdx = MIGRATION_PATH.indexOf(current);
|
|
9586
|
+
const minimumIdx = MIGRATION_PATH.indexOf(minimum);
|
|
9587
|
+
const effectiveCurrent = currentIdx === -1 ? MIGRATION_PATH.length : currentIdx;
|
|
9588
|
+
return effectiveCurrent >= minimumIdx;
|
|
9589
|
+
}
|
|
9227
9590
|
|
|
9228
|
-
// src/core/lib/flowEngine/
|
|
9229
|
-
var
|
|
9230
|
-
|
|
9231
|
-
if (
|
|
9232
|
-
|
|
9591
|
+
// src/core/lib/flowEngine/reconcile.ts
|
|
9592
|
+
var _reconcileRunning = false;
|
|
9593
|
+
function reconcilePendingInvocations(editor) {
|
|
9594
|
+
if (_reconcileRunning) return;
|
|
9595
|
+
_reconcileRunning = true;
|
|
9596
|
+
try {
|
|
9597
|
+
_reconcilePendingInvocationsInner(editor);
|
|
9598
|
+
} finally {
|
|
9599
|
+
_reconcileRunning = false;
|
|
9233
9600
|
}
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9601
|
+
}
|
|
9602
|
+
function _reconcilePendingInvocationsInner(editor) {
|
|
9603
|
+
const yDoc = editor._yDoc;
|
|
9604
|
+
if (!yDoc) return;
|
|
9605
|
+
const blocks = editor.document || [];
|
|
9606
|
+
if (blocks.length === 0) return;
|
|
9607
|
+
const listenersBySource = /* @__PURE__ */ new Map();
|
|
9608
|
+
const barrierListenersBySource = /* @__PURE__ */ new Map();
|
|
9609
|
+
for (const block of blocks) {
|
|
9610
|
+
const trigger = parseTrigger(block);
|
|
9611
|
+
if (!trigger) continue;
|
|
9612
|
+
if (trigger.type === "block.event") {
|
|
9613
|
+
if (!trigger.sourceBlockId || !trigger.eventName) continue;
|
|
9614
|
+
const key = `${trigger.sourceBlockId}::${trigger.eventName}`;
|
|
9615
|
+
if (!listenersBySource.has(key)) listenersBySource.set(key, []);
|
|
9616
|
+
listenersBySource.get(key).push({ block, trigger });
|
|
9617
|
+
} else if (trigger.type === "block.event.all" && trigger.sources) {
|
|
9618
|
+
for (const source of trigger.sources) {
|
|
9619
|
+
if (!source.sourceBlockId || !source.eventName) continue;
|
|
9620
|
+
const key = `${source.sourceBlockId}::${source.eventName}`;
|
|
9621
|
+
if (!barrierListenersBySource.has(key)) barrierListenersBySource.set(key, []);
|
|
9622
|
+
barrierListenersBySource.get(key).push({ block, trigger, source });
|
|
9623
|
+
}
|
|
9237
9624
|
}
|
|
9238
|
-
return {
|
|
9239
|
-
authorized: false,
|
|
9240
|
-
reason: "UCAN service is not configured. This flow version requires UCAN authorization."
|
|
9241
|
-
};
|
|
9242
|
-
}
|
|
9243
|
-
const capability = {
|
|
9244
|
-
can: "flow/block/execute",
|
|
9245
|
-
with: `${flowUri}:${blockId}`
|
|
9246
|
-
};
|
|
9247
|
-
const result = await ucanService.validateDelegationChain(actorDid, capability);
|
|
9248
|
-
if (!result.valid) {
|
|
9249
|
-
return {
|
|
9250
|
-
authorized: false,
|
|
9251
|
-
reason: result.error || "No valid capability chain found"
|
|
9252
|
-
};
|
|
9253
9625
|
}
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9259
|
-
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9626
|
+
if (listenersBySource.size === 0 && barrierListenersBySource.size === 0) return;
|
|
9627
|
+
const now = Date.now();
|
|
9628
|
+
sweepExpiredPendingInvocations(yDoc, now);
|
|
9629
|
+
const consumedByListener = /* @__PURE__ */ new Map();
|
|
9630
|
+
const isConsumed = (listenerBlockId, pendingId) => {
|
|
9631
|
+
let consumed = consumedByListener.get(listenerBlockId);
|
|
9632
|
+
if (!consumed) {
|
|
9633
|
+
consumed = readConsumedPendingInvocationIds(yDoc, listenerBlockId);
|
|
9634
|
+
consumedByListener.set(listenerBlockId, consumed);
|
|
9635
|
+
}
|
|
9636
|
+
return consumed.has(pendingId);
|
|
9637
|
+
};
|
|
9638
|
+
const schemaVersionRaw = yDoc.getMap("root").get("schema_version");
|
|
9639
|
+
const ucanRequired = typeof schemaVersionRaw === "string" ? !!getVersionPolicy(schemaVersionRaw)?.ucanRequired : false;
|
|
9640
|
+
const invocationsMap = yDoc.getMap("invocations");
|
|
9641
|
+
const runtimeMap = editor._yRuntime;
|
|
9642
|
+
const getNodeOutput2 = (nodeId) => {
|
|
9643
|
+
if (!runtimeMap) return void 0;
|
|
9644
|
+
const state = runtimeMap.get(nodeId);
|
|
9645
|
+
if (!state || typeof state !== "object") return void 0;
|
|
9646
|
+
const out = state.output;
|
|
9647
|
+
return out && typeof out === "object" ? out : void 0;
|
|
9269
9648
|
};
|
|
9270
|
-
|
|
9271
|
-
|
|
9649
|
+
for (const block of blocks) {
|
|
9650
|
+
const sourceBlockId = block?.id;
|
|
9651
|
+
if (!sourceBlockId) continue;
|
|
9652
|
+
let hasAnyListener = false;
|
|
9653
|
+
for (const key of listenersBySource.keys()) {
|
|
9654
|
+
if (key.startsWith(`${sourceBlockId}::`)) {
|
|
9655
|
+
hasAnyListener = true;
|
|
9656
|
+
break;
|
|
9657
|
+
}
|
|
9658
|
+
}
|
|
9659
|
+
for (const key of barrierListenersBySource.keys()) {
|
|
9660
|
+
if (key.startsWith(`${sourceBlockId}::`)) {
|
|
9661
|
+
hasAnyListener = true;
|
|
9662
|
+
break;
|
|
9663
|
+
}
|
|
9664
|
+
}
|
|
9665
|
+
if (!hasAnyListener) continue;
|
|
9666
|
+
const records = readRunRecords(yDoc, sourceBlockId);
|
|
9667
|
+
for (const record of records) {
|
|
9668
|
+
if (!isTrustedRunRecord(record, ucanRequired, invocationsMap)) continue;
|
|
9669
|
+
processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput2, runtimeMap, isConsumed, now);
|
|
9670
|
+
processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput2, runtimeMap, isConsumed, now);
|
|
9671
|
+
}
|
|
9272
9672
|
}
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
} catch (error) {
|
|
9314
|
-
const message = error instanceof Error ? error.message : "Failed to create invocation";
|
|
9315
|
-
return { success: false, stage: "authorization", error: message };
|
|
9316
|
-
}
|
|
9317
|
-
}
|
|
9318
|
-
try {
|
|
9319
|
-
const result = await action();
|
|
9320
|
-
if (node.linkedClaim && !result.claimId) {
|
|
9321
|
-
if (invocationStore && invocationCid && invocationData) {
|
|
9322
|
-
const storedInvocation = {
|
|
9323
|
-
cid: invocationCid,
|
|
9324
|
-
invocation: invocationData,
|
|
9325
|
-
invokerDid: actorDid,
|
|
9326
|
-
capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
|
|
9327
|
-
executedAt: now ? now() : Date.now(),
|
|
9328
|
-
flowId,
|
|
9329
|
-
blockId: node.id,
|
|
9330
|
-
result: "failure",
|
|
9331
|
-
error: "Execution did not return a claimId for linked claim requirement.",
|
|
9332
|
-
proofCids: auth.proofCids || []
|
|
9333
|
-
};
|
|
9334
|
-
invocationStore.add(storedInvocation);
|
|
9335
|
-
}
|
|
9336
|
-
return {
|
|
9337
|
-
success: false,
|
|
9338
|
-
stage: "claim",
|
|
9339
|
-
error: "Execution did not return a claimId for linked claim requirement.",
|
|
9340
|
-
invocationCid
|
|
9673
|
+
}
|
|
9674
|
+
function isTrustedRunRecord(record, ucanRequired, invocationsMap) {
|
|
9675
|
+
if (!ucanRequired) return true;
|
|
9676
|
+
if (!record.invocationCid) return false;
|
|
9677
|
+
return invocationsMap.get(record.invocationCid) !== void 0;
|
|
9678
|
+
}
|
|
9679
|
+
function processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput2, runtimeMap, isConsumed, now) {
|
|
9680
|
+
if (!Array.isArray(record.events)) return;
|
|
9681
|
+
record.events.forEach((event, eventIndex) => {
|
|
9682
|
+
if (!event?.name) return;
|
|
9683
|
+
const key = `${sourceBlockId}::${event.name}`;
|
|
9684
|
+
const listeners = listenersBySource.get(key);
|
|
9685
|
+
if (!listeners || listeners.length === 0) return;
|
|
9686
|
+
for (const { block: listenerBlock } of listeners) {
|
|
9687
|
+
const listenerBlockId = listenerBlock.id;
|
|
9688
|
+
if (!listenerBlockId) continue;
|
|
9689
|
+
const id = computePendingInvocationId({
|
|
9690
|
+
sourceBlockId,
|
|
9691
|
+
sourceRunId: record.runId,
|
|
9692
|
+
listenerBlockId,
|
|
9693
|
+
eventName: event.name,
|
|
9694
|
+
eventIndex
|
|
9695
|
+
});
|
|
9696
|
+
if (isConsumed(listenerBlockId, id)) continue;
|
|
9697
|
+
const assigneeDid = resolveAssignee(listenerBlock) || "unassigned";
|
|
9698
|
+
const inputs = parseInputs(listenerBlock);
|
|
9699
|
+
const refSnapshots = snapshotInputRefs(inputs, getNodeOutput2);
|
|
9700
|
+
const expiresAt = computeExpiry(listenerBlock, record.completedAt);
|
|
9701
|
+
if (expiresAt && Date.parse(expiresAt) < now) continue;
|
|
9702
|
+
const invocation = {
|
|
9703
|
+
id,
|
|
9704
|
+
triggeringBlockId: sourceBlockId,
|
|
9705
|
+
sourceRunId: record.runId,
|
|
9706
|
+
eventName: event.name,
|
|
9707
|
+
eventIndex,
|
|
9708
|
+
payload: event.payload,
|
|
9709
|
+
refSnapshots,
|
|
9710
|
+
assigneeDid,
|
|
9711
|
+
emittedAt: record.completedAt,
|
|
9712
|
+
expiresAt
|
|
9341
9713
|
};
|
|
9714
|
+
queuePendingInvocation(yDoc, listenerBlockId, invocation);
|
|
9715
|
+
if (runtimeMap) {
|
|
9716
|
+
const prev = runtimeMap.get(listenerBlockId) || {};
|
|
9717
|
+
runtimeMap.set(listenerBlockId, {
|
|
9718
|
+
...prev,
|
|
9719
|
+
pendingPayload: { ...event.payload, ...refSnapshots }
|
|
9720
|
+
});
|
|
9721
|
+
}
|
|
9342
9722
|
}
|
|
9343
|
-
|
|
9344
|
-
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9353
|
-
|
|
9354
|
-
|
|
9723
|
+
});
|
|
9724
|
+
}
|
|
9725
|
+
function processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput2, runtimeMap, isConsumed, now) {
|
|
9726
|
+
if (!Array.isArray(record.events)) return;
|
|
9727
|
+
for (const event of record.events) {
|
|
9728
|
+
if (!event?.name) continue;
|
|
9729
|
+
const key = `${sourceBlockId}::${event.name}`;
|
|
9730
|
+
const listeners = barrierListenersBySource.get(key);
|
|
9731
|
+
if (!listeners || listeners.length === 0) continue;
|
|
9732
|
+
for (const { block: listenerBlock, trigger, source } of listeners) {
|
|
9733
|
+
const listenerBlockId = listenerBlock.id;
|
|
9734
|
+
if (!listenerBlockId) continue;
|
|
9735
|
+
const entry = {
|
|
9736
|
+
sourceBlockId,
|
|
9737
|
+
eventName: event.name,
|
|
9738
|
+
alias: source.alias,
|
|
9739
|
+
runId: record.runId,
|
|
9740
|
+
payload: event.payload || {},
|
|
9741
|
+
emittedAt: record.completedAt
|
|
9355
9742
|
};
|
|
9356
|
-
|
|
9357
|
-
|
|
9358
|
-
|
|
9359
|
-
|
|
9360
|
-
|
|
9361
|
-
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
const
|
|
9370
|
-
|
|
9371
|
-
|
|
9372
|
-
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9743
|
+
recordBarrierEvent(yDoc, listenerBlockId, entry);
|
|
9744
|
+
if (runtimeMap) {
|
|
9745
|
+
const prev = runtimeMap.get(listenerBlockId) || {};
|
|
9746
|
+
const existing = prev.pendingPayload || {};
|
|
9747
|
+
runtimeMap.set(listenerBlockId, {
|
|
9748
|
+
...prev,
|
|
9749
|
+
pendingPayload: { ...existing, ...event.payload || {} }
|
|
9750
|
+
});
|
|
9751
|
+
}
|
|
9752
|
+
const allSources = trigger.sources || [];
|
|
9753
|
+
const currentState = readBarrierState(yDoc, listenerBlockId);
|
|
9754
|
+
const requiredSources = allSources.filter((s) => s.optional !== true);
|
|
9755
|
+
const gatingSources = requiredSources.length > 0 ? requiredSources : allSources;
|
|
9756
|
+
const allFired = gatingSources.length > 0 && gatingSources.every((s) => currentState.some((e) => e.sourceBlockId === s.sourceBlockId && e.eventName === s.eventName));
|
|
9757
|
+
if (!allFired) continue;
|
|
9758
|
+
const assigneeDid = resolveAssignee(listenerBlock) || "unassigned";
|
|
9759
|
+
const id = computeBarrierInvocationId(currentState, listenerBlockId);
|
|
9760
|
+
if (isConsumed(listenerBlockId, id)) continue;
|
|
9761
|
+
const mergedPayload = mergeBarrierPayloads(currentState);
|
|
9762
|
+
const inputs = parseInputs(listenerBlock);
|
|
9763
|
+
const refSnapshots = snapshotInputRefs(inputs, getNodeOutput2);
|
|
9764
|
+
const expiresAt = computeExpiry(listenerBlock, record.completedAt);
|
|
9765
|
+
if (expiresAt && Date.parse(expiresAt) < now) continue;
|
|
9766
|
+
const invocation = {
|
|
9767
|
+
id,
|
|
9768
|
+
triggeringBlockId: sourceBlockId,
|
|
9769
|
+
sourceRunId: record.runId,
|
|
9770
|
+
eventName: `barrier:${allSources.map((s) => s.alias).join("+")}`,
|
|
9771
|
+
eventIndex: 0,
|
|
9772
|
+
payload: mergedPayload,
|
|
9773
|
+
refSnapshots,
|
|
9774
|
+
assigneeDid,
|
|
9775
|
+
emittedAt: record.completedAt,
|
|
9776
|
+
expiresAt
|
|
9380
9777
|
};
|
|
9381
|
-
|
|
9778
|
+
queuePendingInvocation(yDoc, listenerBlockId, invocation);
|
|
9779
|
+
clearBarrierState(yDoc, listenerBlockId);
|
|
9780
|
+
if (runtimeMap) {
|
|
9781
|
+
const prev = runtimeMap.get(listenerBlockId) || {};
|
|
9782
|
+
runtimeMap.set(listenerBlockId, {
|
|
9783
|
+
...prev,
|
|
9784
|
+
pendingPayload: { ...mergedPayload, ...refSnapshots }
|
|
9785
|
+
});
|
|
9786
|
+
}
|
|
9382
9787
|
}
|
|
9383
|
-
return { success: false, stage: "action", error: message, invocationCid };
|
|
9384
9788
|
}
|
|
9385
|
-
};
|
|
9386
|
-
|
|
9387
|
-
// src/core/lib/flowEngine/triggers.ts
|
|
9388
|
-
import * as Y from "yjs";
|
|
9389
|
-
var RUN_RECORD_AUDIT_TYPE = "block.run";
|
|
9390
|
-
function computePendingInvocationId(args) {
|
|
9391
|
-
const { sourceBlockId, sourceRunId, listenerBlockId, eventName, eventIndex } = args;
|
|
9392
|
-
const input = `${sourceBlockId}:${sourceRunId}:${listenerBlockId}:${eventName}:${eventIndex}`;
|
|
9393
|
-
return `pi-${fnv1a32(input)}`;
|
|
9394
|
-
}
|
|
9395
|
-
function snapshotInputRefs(inputs, getNodeOutput2) {
|
|
9396
|
-
const snapshots = {};
|
|
9397
|
-
walkRefs(inputs, (ref) => {
|
|
9398
|
-
if (ref.$ref.startsWith("trigger.")) return;
|
|
9399
|
-
const parsed = parseOutputRef(ref.$ref);
|
|
9400
|
-
if (!parsed) return;
|
|
9401
|
-
const output = getNodeOutput2(parsed.nodeId);
|
|
9402
|
-
if (!output) return;
|
|
9403
|
-
snapshots[ref.$ref] = getNestedValue3(output, parsed.fieldPath);
|
|
9404
|
-
});
|
|
9405
|
-
return snapshots;
|
|
9406
|
-
}
|
|
9407
|
-
function walkRefs(value, visit) {
|
|
9408
|
-
if (isRuntimeRef(value)) {
|
|
9409
|
-
visit(value);
|
|
9410
|
-
return;
|
|
9411
|
-
}
|
|
9412
|
-
if (Array.isArray(value)) {
|
|
9413
|
-
for (const item of value) walkRefs(item, visit);
|
|
9414
|
-
return;
|
|
9415
|
-
}
|
|
9416
|
-
if (typeof value === "object" && value !== null) {
|
|
9417
|
-
for (const v of Object.values(value)) walkRefs(v, visit);
|
|
9418
|
-
}
|
|
9419
|
-
}
|
|
9420
|
-
function parseOutputRef(ref) {
|
|
9421
|
-
const outputIndex = ref.indexOf(".output.");
|
|
9422
|
-
if (outputIndex === -1) return null;
|
|
9423
|
-
return {
|
|
9424
|
-
nodeId: ref.slice(0, outputIndex),
|
|
9425
|
-
fieldPath: ref.slice(outputIndex + ".output.".length)
|
|
9426
|
-
};
|
|
9427
|
-
}
|
|
9428
|
-
function getNestedValue3(obj, path) {
|
|
9429
|
-
const parts = path.split(".");
|
|
9430
|
-
let current = obj;
|
|
9431
|
-
for (const part of parts) {
|
|
9432
|
-
if (current == null || typeof current !== "object") return void 0;
|
|
9433
|
-
current = current[part];
|
|
9434
|
-
}
|
|
9435
|
-
return current;
|
|
9436
9789
|
}
|
|
9437
|
-
function
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9790
|
+
function parseTrigger(block) {
|
|
9791
|
+
const raw = block?.props?.trigger;
|
|
9792
|
+
if (!raw || typeof raw !== "string") return null;
|
|
9793
|
+
try {
|
|
9794
|
+
const parsed = JSON.parse(raw);
|
|
9795
|
+
if (parsed && typeof parsed === "object" && typeof parsed.type === "string") {
|
|
9796
|
+
return parsed;
|
|
9797
|
+
}
|
|
9798
|
+
} catch {
|
|
9799
|
+
warnOnce(`trigger-parse:${block?.id}`, `[flow-config] block ${block?.id}: props.trigger is not valid JSON; trigger ignored`);
|
|
9442
9800
|
}
|
|
9443
|
-
return
|
|
9801
|
+
return null;
|
|
9444
9802
|
}
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9803
|
+
function parseInputs(block) {
|
|
9804
|
+
const raw = block?.props?.inputs;
|
|
9805
|
+
if (!raw || typeof raw !== "string") return {};
|
|
9806
|
+
try {
|
|
9807
|
+
const parsed = JSON.parse(raw);
|
|
9808
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
9809
|
+
} catch {
|
|
9810
|
+
warnOnce(`inputs-parse:${block?.id}`, `[flow-config] block ${block?.id}: props.inputs is not valid JSON; treated as empty`);
|
|
9811
|
+
}
|
|
9812
|
+
return {};
|
|
9448
9813
|
}
|
|
9449
|
-
function
|
|
9450
|
-
const
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9814
|
+
function resolveAssignee(block) {
|
|
9815
|
+
const raw = block?.props?.assignment;
|
|
9816
|
+
if (!raw) return void 0;
|
|
9817
|
+
try {
|
|
9818
|
+
const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
9819
|
+
const did = parsed?.assignedActor?.did;
|
|
9820
|
+
return typeof did === "string" && did.length > 0 ? did : void 0;
|
|
9821
|
+
} catch {
|
|
9822
|
+
warnOnce(`assignment-parse:${block?.id}`, `[flow-config] block ${block?.id}: props.assignment is not valid JSON; assignee unresolved`);
|
|
9823
|
+
return void 0;
|
|
9455
9824
|
}
|
|
9456
|
-
return inner;
|
|
9457
9825
|
}
|
|
9458
|
-
function
|
|
9459
|
-
const
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9465
|
-
|
|
9826
|
+
function computeExpiry(block, emittedAt) {
|
|
9827
|
+
const ttlAbsolute = block?.props?.ttlAbsoluteDueDate;
|
|
9828
|
+
if (typeof ttlAbsolute === "string" && ttlAbsolute) {
|
|
9829
|
+
return ttlAbsolute;
|
|
9830
|
+
}
|
|
9831
|
+
const ttlFromEnablement = block?.props?.ttlFromEnablement;
|
|
9832
|
+
if (typeof ttlFromEnablement === "string" && ttlFromEnablement) {
|
|
9833
|
+
const ms = parseIsoDurationToMs(ttlFromEnablement);
|
|
9834
|
+
if (ms != null) {
|
|
9835
|
+
return new Date(new Date(emittedAt).getTime() + ms).toISOString();
|
|
9466
9836
|
}
|
|
9467
|
-
}
|
|
9468
|
-
|
|
9469
|
-
return items;
|
|
9837
|
+
}
|
|
9838
|
+
return new Date(new Date(emittedAt).getTime() + 7 * 24 * 60 * 60 * 1e3).toISOString();
|
|
9470
9839
|
}
|
|
9471
|
-
function
|
|
9472
|
-
|
|
9473
|
-
|
|
9474
|
-
|
|
9475
|
-
|
|
9476
|
-
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
|
|
9840
|
+
function parseIsoDurationToMs(duration) {
|
|
9841
|
+
const match = /^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/.exec(duration);
|
|
9842
|
+
if (!match) return null;
|
|
9843
|
+
const [, d, h, m, s] = match;
|
|
9844
|
+
let ms = 0;
|
|
9845
|
+
if (d) ms += parseInt(d, 10) * 24 * 60 * 60 * 1e3;
|
|
9846
|
+
if (h) ms += parseInt(h, 10) * 60 * 60 * 1e3;
|
|
9847
|
+
if (m) ms += parseInt(m, 10) * 60 * 1e3;
|
|
9848
|
+
if (s) ms += parseInt(s, 10) * 1e3;
|
|
9849
|
+
return ms;
|
|
9480
9850
|
}
|
|
9481
|
-
function
|
|
9482
|
-
const
|
|
9483
|
-
|
|
9484
|
-
|
|
9485
|
-
if (!inner.has(pendingInvocationId)) return false;
|
|
9486
|
-
inner.delete(pendingInvocationId);
|
|
9487
|
-
return true;
|
|
9851
|
+
function getActionForBlock(block) {
|
|
9852
|
+
const actionType = block?.props?.actionType;
|
|
9853
|
+
if (typeof actionType !== "string") return void 0;
|
|
9854
|
+
return getAction(actionType);
|
|
9488
9855
|
}
|
|
9489
|
-
|
|
9490
|
-
|
|
9491
|
-
|
|
9856
|
+
|
|
9857
|
+
// src/core/lib/flowEngine/emitEvents.ts
|
|
9858
|
+
function writeRunRecordAndReconcile(editor, blockId, output, events, actorDid, detailsPatch = {}) {
|
|
9859
|
+
const yDoc = editor._yDoc;
|
|
9860
|
+
if (!yDoc) return;
|
|
9861
|
+
if (events.length === 0) return;
|
|
9862
|
+
const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
9863
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9864
|
+
const details = {
|
|
9865
|
+
runId,
|
|
9866
|
+
output,
|
|
9867
|
+
events,
|
|
9868
|
+
startedAt: now,
|
|
9869
|
+
completedAt: now,
|
|
9870
|
+
actorDid,
|
|
9871
|
+
...detailsPatch
|
|
9872
|
+
};
|
|
9873
|
+
appendRunRecord(yDoc, blockId, details, actorDid);
|
|
9874
|
+
reconcilePendingInvocations(editor);
|
|
9492
9875
|
}
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
9496
|
-
|
|
9497
|
-
|
|
9498
|
-
|
|
9876
|
+
|
|
9877
|
+
// src/core/lib/flowEngine/utils.ts
|
|
9878
|
+
var buildAuthzFromProps = (props) => {
|
|
9879
|
+
const linkedClaimCollectionId = typeof props.linkedClaimCollectionId === "string" ? props.linkedClaimCollectionId.trim() : "";
|
|
9880
|
+
const authz = {};
|
|
9881
|
+
if (linkedClaimCollectionId) {
|
|
9882
|
+
authz.linkedClaim = { collectionId: linkedClaimCollectionId };
|
|
9499
9883
|
}
|
|
9500
|
-
return
|
|
9501
|
-
}
|
|
9502
|
-
|
|
9503
|
-
|
|
9504
|
-
|
|
9505
|
-
|
|
9506
|
-
|
|
9507
|
-
|
|
9508
|
-
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
}
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9884
|
+
return authz;
|
|
9885
|
+
};
|
|
9886
|
+
var buildFlowNodeFromBlock = (block) => {
|
|
9887
|
+
const base = {
|
|
9888
|
+
id: block.id,
|
|
9889
|
+
type: block.type,
|
|
9890
|
+
props: block.props || {}
|
|
9891
|
+
};
|
|
9892
|
+
const authz = buildAuthzFromProps(block.props || {});
|
|
9893
|
+
return {
|
|
9894
|
+
...base,
|
|
9895
|
+
...authz
|
|
9896
|
+
};
|
|
9897
|
+
};
|
|
9898
|
+
|
|
9899
|
+
// src/core/lib/flowEngine/runtime.ts
|
|
9900
|
+
var XERO_WORK_ITEMS_MAP_NAME = "xeroWorkItems";
|
|
9901
|
+
var XERO_CONNECTION_MAP_NAME = "xeroConnection";
|
|
9902
|
+
var ensureStateObject = (value) => {
|
|
9903
|
+
if (!value || typeof value !== "object") {
|
|
9904
|
+
return {};
|
|
9905
|
+
}
|
|
9906
|
+
return { ...value };
|
|
9907
|
+
};
|
|
9908
|
+
var createYMapManager = (map) => {
|
|
9909
|
+
return {
|
|
9910
|
+
get: (nodeId) => {
|
|
9911
|
+
const stored = map.get(nodeId);
|
|
9912
|
+
return ensureStateObject(stored);
|
|
9913
|
+
},
|
|
9914
|
+
update: (nodeId, updates) => {
|
|
9915
|
+
const current = ensureStateObject(map.get(nodeId));
|
|
9916
|
+
map.set(nodeId, { ...current, ...updates });
|
|
9522
9917
|
}
|
|
9523
|
-
}
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
|
|
9527
|
-
|
|
9528
|
-
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
}
|
|
9532
|
-
function computeBarrierInvocationId(entries, listenerBlockId) {
|
|
9533
|
-
const sorted = [...entries].sort((a, b) => a.sourceBlockId.localeCompare(b.sourceBlockId));
|
|
9534
|
-
const input = sorted.map((e) => `${e.sourceBlockId}:${e.runId}:${e.eventName}`).join("|") + `|>${listenerBlockId}`;
|
|
9535
|
-
return `bi-${fnv1a32(input)}`;
|
|
9536
|
-
}
|
|
9537
|
-
function mergeBarrierPayloads(entries) {
|
|
9538
|
-
const merged = {};
|
|
9539
|
-
for (const entry of entries) {
|
|
9540
|
-
if (entry.alias) {
|
|
9541
|
-
merged[entry.alias] = entry.payload;
|
|
9542
|
-
} else {
|
|
9543
|
-
Object.assign(merged, entry.payload);
|
|
9918
|
+
};
|
|
9919
|
+
};
|
|
9920
|
+
var createMemoryManager = () => {
|
|
9921
|
+
const memory = /* @__PURE__ */ new Map();
|
|
9922
|
+
return {
|
|
9923
|
+
get: (nodeId) => ensureStateObject(memory.get(nodeId)),
|
|
9924
|
+
update: (nodeId, updates) => {
|
|
9925
|
+
const current = ensureStateObject(memory.get(nodeId));
|
|
9926
|
+
memory.set(nodeId, { ...current, ...updates });
|
|
9544
9927
|
}
|
|
9928
|
+
};
|
|
9929
|
+
};
|
|
9930
|
+
var createRuntimeStateManager = (editor) => {
|
|
9931
|
+
if (editor?._yRuntime) {
|
|
9932
|
+
return createYMapManager(editor._yRuntime);
|
|
9545
9933
|
}
|
|
9546
|
-
return
|
|
9547
|
-
}
|
|
9548
|
-
|
|
9549
|
-
|
|
9934
|
+
return createMemoryManager();
|
|
9935
|
+
};
|
|
9936
|
+
var createYDocRuntimeManager = (yDoc) => {
|
|
9937
|
+
return createYMapManager(yDoc.getMap("runtime"));
|
|
9938
|
+
};
|
|
9939
|
+
function clearRuntimeForTemplateClone(yDoc) {
|
|
9940
|
+
const runtime = yDoc.getMap("runtime");
|
|
9941
|
+
const invocations = yDoc.getMap("invocations");
|
|
9942
|
+
const pendingInvocations = yDoc.getMap("pendingInvocations");
|
|
9943
|
+
const agentOutbox = yDoc.getMap("agentOutbox");
|
|
9944
|
+
const agentLeases = yDoc.getMap("agentLeases");
|
|
9945
|
+
const auditTrail = yDoc.getMap("auditTrail");
|
|
9946
|
+
const xeroWorkItems = yDoc.getMap(XERO_WORK_ITEMS_MAP_NAME);
|
|
9947
|
+
const xeroConnection = yDoc.getMap(XERO_CONNECTION_MAP_NAME);
|
|
9550
9948
|
yDoc.transact(() => {
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
|
|
9554
|
-
|
|
9555
|
-
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
type: RUN_RECORD_AUDIT_TYPE,
|
|
9560
|
-
details,
|
|
9561
|
-
message: void 0,
|
|
9562
|
-
meta: {
|
|
9563
|
-
timestamp: details.completedAt,
|
|
9564
|
-
userId,
|
|
9565
|
-
editable: false
|
|
9566
|
-
}
|
|
9567
|
-
};
|
|
9568
|
-
arr.push([event]);
|
|
9949
|
+
runtime.forEach((_, key) => runtime.delete(key));
|
|
9950
|
+
invocations.forEach((_, key) => invocations.delete(key));
|
|
9951
|
+
pendingInvocations.forEach((_, key) => pendingInvocations.delete(key));
|
|
9952
|
+
agentOutbox.forEach((_, key) => agentOutbox.delete(key));
|
|
9953
|
+
agentLeases.forEach((_, key) => agentLeases.delete(key));
|
|
9954
|
+
auditTrail.forEach((_, key) => auditTrail.delete(key));
|
|
9955
|
+
xeroWorkItems.forEach((_, key) => xeroWorkItems.delete(key));
|
|
9956
|
+
xeroConnection.forEach((_, key) => xeroConnection.delete(key));
|
|
9569
9957
|
});
|
|
9570
9958
|
}
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
const records = [];
|
|
9576
|
-
arr.forEach((entry) => {
|
|
9577
|
-
if (!entry || typeof entry !== "object") return;
|
|
9578
|
-
const e = entry;
|
|
9579
|
-
if (e.type === RUN_RECORD_AUDIT_TYPE && e.details) {
|
|
9580
|
-
records.push(e.details);
|
|
9581
|
-
}
|
|
9582
|
-
});
|
|
9583
|
-
return records;
|
|
9959
|
+
|
|
9960
|
+
// src/core/lib/flowCompiler/resolveRefs.ts
|
|
9961
|
+
function resolveRuntimeRefs(nb, getNodeOutput2, triggerContext) {
|
|
9962
|
+
return resolveValue(nb, getNodeOutput2, triggerContext);
|
|
9584
9963
|
}
|
|
9585
|
-
function
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
9964
|
+
function resolveValue(value, getNodeOutput2, triggerContext) {
|
|
9965
|
+
if (isRuntimeRef(value)) {
|
|
9966
|
+
return resolveRef(value.$ref, getNodeOutput2, triggerContext);
|
|
9967
|
+
}
|
|
9968
|
+
if (Array.isArray(value)) {
|
|
9969
|
+
return value.map((item) => resolveValue(item, getNodeOutput2, triggerContext));
|
|
9970
|
+
}
|
|
9971
|
+
if (typeof value === "object" && value !== null) {
|
|
9972
|
+
const result = {};
|
|
9973
|
+
for (const [key, val] of Object.entries(value)) {
|
|
9974
|
+
result[key] = resolveValue(val, getNodeOutput2, triggerContext);
|
|
9596
9975
|
}
|
|
9976
|
+
return result;
|
|
9597
9977
|
}
|
|
9598
|
-
return
|
|
9978
|
+
return value;
|
|
9599
9979
|
}
|
|
9600
|
-
function
|
|
9601
|
-
if (
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
|
|
9980
|
+
function resolveRef(ref, getNodeOutput2, triggerContext) {
|
|
9981
|
+
if (ref.startsWith("trigger.payload.")) {
|
|
9982
|
+
if (!triggerContext) {
|
|
9983
|
+
throw new Error(`Trigger ref "${ref}" used outside of a listener invocation context. trigger.payload.* refs are only valid on block.event-triggered blocks.`);
|
|
9984
|
+
}
|
|
9985
|
+
const fieldPath2 = ref.slice("trigger.payload.".length);
|
|
9986
|
+
return getNestedValue(triggerContext.payload, fieldPath2);
|
|
9987
|
+
}
|
|
9988
|
+
if (triggerContext && Object.prototype.hasOwnProperty.call(triggerContext.refSnapshots, ref)) {
|
|
9989
|
+
return triggerContext.refSnapshots[ref];
|
|
9990
|
+
}
|
|
9991
|
+
const outputIndex = ref.indexOf(".output.");
|
|
9992
|
+
if (outputIndex === -1) {
|
|
9993
|
+
throw new Error(`Invalid runtime reference "${ref}". Expected format: "nodeId.output.fieldPath" or "trigger.payload.fieldPath"`);
|
|
9994
|
+
}
|
|
9995
|
+
const nodeId = ref.slice(0, outputIndex);
|
|
9996
|
+
const fieldPath = ref.slice(outputIndex + ".output.".length);
|
|
9997
|
+
const output = getNodeOutput2(nodeId);
|
|
9998
|
+
if (!output) {
|
|
9999
|
+
return void 0;
|
|
10000
|
+
}
|
|
10001
|
+
return getNestedValue(output, fieldPath);
|
|
9617
10002
|
}
|
|
9618
10003
|
|
|
9619
|
-
// src/core/lib/flowEngine/
|
|
9620
|
-
var
|
|
9621
|
-
|
|
9622
|
-
if (
|
|
9623
|
-
|
|
9624
|
-
try {
|
|
9625
|
-
_reconcilePendingInvocationsInner(editor);
|
|
9626
|
-
} finally {
|
|
9627
|
-
_reconcileRunning = false;
|
|
10004
|
+
// src/core/lib/flowEngine/authorization.ts
|
|
10005
|
+
var isAuthorized = async (blockId, actorDid, ucanService, flowUri, schemaVersion) => {
|
|
10006
|
+
const policy = schemaVersion ? getVersionPolicy(schemaVersion) : null;
|
|
10007
|
+
if (policy && !policy.ucanRequired) {
|
|
10008
|
+
return { authorized: true };
|
|
9628
10009
|
}
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
if (!yDoc) return;
|
|
9633
|
-
const blocks = editor.document || [];
|
|
9634
|
-
if (blocks.length === 0) return;
|
|
9635
|
-
const listenersBySource = /* @__PURE__ */ new Map();
|
|
9636
|
-
const barrierListenersBySource = /* @__PURE__ */ new Map();
|
|
9637
|
-
for (const block of blocks) {
|
|
9638
|
-
const trigger = parseTrigger(block);
|
|
9639
|
-
if (!trigger) continue;
|
|
9640
|
-
if (trigger.type === "block.event") {
|
|
9641
|
-
if (!trigger.sourceBlockId || !trigger.eventName) continue;
|
|
9642
|
-
const key = `${trigger.sourceBlockId}::${trigger.eventName}`;
|
|
9643
|
-
if (!listenersBySource.has(key)) listenersBySource.set(key, []);
|
|
9644
|
-
listenersBySource.get(key).push({ block, trigger });
|
|
9645
|
-
} else if (trigger.type === "block.event.all" && trigger.sources) {
|
|
9646
|
-
for (const source of trigger.sources) {
|
|
9647
|
-
if (!source.sourceBlockId || !source.eventName) continue;
|
|
9648
|
-
const key = `${source.sourceBlockId}::${source.eventName}`;
|
|
9649
|
-
if (!barrierListenersBySource.has(key)) barrierListenersBySource.set(key, []);
|
|
9650
|
-
barrierListenersBySource.get(key).push({ block, trigger, source });
|
|
9651
|
-
}
|
|
10010
|
+
if (!ucanService) {
|
|
10011
|
+
if (!policy) {
|
|
10012
|
+
return { authorized: true };
|
|
9652
10013
|
}
|
|
10014
|
+
return {
|
|
10015
|
+
authorized: false,
|
|
10016
|
+
reason: "UCAN service is not configured. This flow version requires UCAN authorization."
|
|
10017
|
+
};
|
|
9653
10018
|
}
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
if (!runtimeMap) return void 0;
|
|
9658
|
-
const state = runtimeMap.get(nodeId);
|
|
9659
|
-
if (!state || typeof state !== "object") return void 0;
|
|
9660
|
-
const out = state.output;
|
|
9661
|
-
return out && typeof out === "object" ? out : void 0;
|
|
10019
|
+
const capability = {
|
|
10020
|
+
can: "flow/block/execute",
|
|
10021
|
+
with: `${flowUri}:${blockId}`
|
|
9662
10022
|
};
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
10023
|
+
const result = await ucanService.validateDelegationChain(actorDid, capability);
|
|
10024
|
+
if (!result.valid) {
|
|
10025
|
+
return {
|
|
10026
|
+
authorized: false,
|
|
10027
|
+
reason: result.error || "No valid capability chain found"
|
|
10028
|
+
};
|
|
10029
|
+
}
|
|
10030
|
+
const proofCids = result.proofChain?.map((d) => d.cid) || [];
|
|
10031
|
+
return {
|
|
10032
|
+
authorized: true,
|
|
10033
|
+
capabilityId: proofCids[0],
|
|
10034
|
+
proofCids
|
|
10035
|
+
};
|
|
10036
|
+
};
|
|
10037
|
+
|
|
10038
|
+
// src/core/lib/flowEngine/executor.ts
|
|
10039
|
+
var updateRuntimeAfterSuccess = (node, actorDid, runtime, actionResult, invocationCid, now) => {
|
|
10040
|
+
const updates = {
|
|
10041
|
+
submittedByDid: actionResult.submittedByDid || actorDid,
|
|
10042
|
+
evaluationStatus: actionResult.evaluationStatus || "pending",
|
|
10043
|
+
executionTimestamp: now ? now() : Date.now(),
|
|
10044
|
+
lastInvocationCid: invocationCid
|
|
10045
|
+
};
|
|
10046
|
+
if (actionResult.claimId) {
|
|
10047
|
+
updates.claimId = actionResult.claimId;
|
|
10048
|
+
}
|
|
10049
|
+
runtime.update(node.id, updates);
|
|
10050
|
+
};
|
|
10051
|
+
var executeNode = async ({ node, actorDid, actorType, entityRoomId, context, action, pin }) => {
|
|
10052
|
+
const { runtime, ucanService, invocationStore, flowUri, flowId, schemaVersion, now } = context;
|
|
10053
|
+
const auth = await isAuthorized(node.id, actorDid, ucanService, flowUri, schemaVersion);
|
|
10054
|
+
if (!auth.authorized) {
|
|
10055
|
+
return { success: false, stage: "authorization", error: auth.reason };
|
|
10056
|
+
}
|
|
10057
|
+
if (node.linkedClaim && !node.linkedClaim.collectionId) {
|
|
10058
|
+
return { success: false, stage: "claim", error: "Linked claim collection is required but missing." };
|
|
10059
|
+
}
|
|
10060
|
+
let invocationCid;
|
|
10061
|
+
let invocationData;
|
|
10062
|
+
if (ucanService && auth.proofCids && auth.proofCids.length > 0) {
|
|
10063
|
+
const capability = {
|
|
10064
|
+
can: "flow/block/execute",
|
|
10065
|
+
with: `${flowUri}:${node.id}`
|
|
10066
|
+
};
|
|
10067
|
+
try {
|
|
10068
|
+
const invocationResult = await ucanService.createAndValidateInvocation(
|
|
10069
|
+
{
|
|
10070
|
+
invokerDid: actorDid,
|
|
10071
|
+
invokerType: actorType,
|
|
10072
|
+
entityRoomId,
|
|
10073
|
+
capability,
|
|
10074
|
+
proofs: auth.proofCids,
|
|
10075
|
+
pin
|
|
10076
|
+
},
|
|
10077
|
+
flowId,
|
|
10078
|
+
node.id
|
|
10079
|
+
);
|
|
10080
|
+
if (!invocationResult.valid) {
|
|
10081
|
+
return {
|
|
10082
|
+
success: false,
|
|
10083
|
+
stage: "authorization",
|
|
10084
|
+
error: `Invocation validation failed: ${invocationResult.error}`
|
|
10085
|
+
};
|
|
9677
10086
|
}
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput2, runtimeMap);
|
|
10087
|
+
invocationCid = invocationResult.cid;
|
|
10088
|
+
invocationData = invocationResult.invocation;
|
|
10089
|
+
} catch (error) {
|
|
10090
|
+
const message = error instanceof Error ? error.message : "Failed to create invocation";
|
|
10091
|
+
return { success: false, stage: "authorization", error: message };
|
|
9684
10092
|
}
|
|
9685
10093
|
}
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
});
|
|
9704
|
-
const assigneeDid = resolveAssignee(listenerBlock) || "unassigned";
|
|
9705
|
-
const inputs = parseInputs(listenerBlock);
|
|
9706
|
-
const refSnapshots = snapshotInputRefs(inputs, getNodeOutput2);
|
|
9707
|
-
const expiresAt = computeExpiry(listenerBlock, record.completedAt);
|
|
9708
|
-
const invocation = {
|
|
9709
|
-
id,
|
|
9710
|
-
triggeringBlockId: sourceBlockId,
|
|
9711
|
-
sourceRunId: record.runId,
|
|
9712
|
-
eventName: event.name,
|
|
9713
|
-
eventIndex,
|
|
9714
|
-
payload: event.payload,
|
|
9715
|
-
refSnapshots,
|
|
9716
|
-
assigneeDid,
|
|
9717
|
-
emittedAt: record.completedAt,
|
|
9718
|
-
expiresAt
|
|
9719
|
-
};
|
|
9720
|
-
queuePendingInvocation(yDoc, listenerBlockId, invocation);
|
|
9721
|
-
if (runtimeMap) {
|
|
9722
|
-
const prev = runtimeMap.get(listenerBlockId) || {};
|
|
9723
|
-
runtimeMap.set(listenerBlockId, {
|
|
9724
|
-
...prev,
|
|
9725
|
-
pendingPayload: { ...event.payload, ...refSnapshots }
|
|
9726
|
-
});
|
|
10094
|
+
try {
|
|
10095
|
+
const result = await action();
|
|
10096
|
+
if (node.linkedClaim && !result.claimId) {
|
|
10097
|
+
if (invocationStore && invocationCid && invocationData) {
|
|
10098
|
+
const storedInvocation = {
|
|
10099
|
+
cid: invocationCid,
|
|
10100
|
+
invocation: invocationData,
|
|
10101
|
+
invokerDid: actorDid,
|
|
10102
|
+
capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
|
|
10103
|
+
executedAt: now ? now() : Date.now(),
|
|
10104
|
+
flowId,
|
|
10105
|
+
blockId: node.id,
|
|
10106
|
+
result: "failure",
|
|
10107
|
+
error: "Execution did not return a claimId for linked claim requirement.",
|
|
10108
|
+
proofCids: auth.proofCids || []
|
|
10109
|
+
};
|
|
10110
|
+
invocationStore.add(storedInvocation);
|
|
9727
10111
|
}
|
|
10112
|
+
return {
|
|
10113
|
+
success: false,
|
|
10114
|
+
stage: "claim",
|
|
10115
|
+
error: "Execution did not return a claimId for linked claim requirement.",
|
|
10116
|
+
invocationCid
|
|
10117
|
+
};
|
|
9728
10118
|
}
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
const entry = {
|
|
9742
|
-
sourceBlockId,
|
|
9743
|
-
eventName: event.name,
|
|
9744
|
-
alias: source.alias,
|
|
9745
|
-
runId: record.runId,
|
|
9746
|
-
payload: event.payload || {},
|
|
9747
|
-
emittedAt: record.completedAt
|
|
10119
|
+
if (invocationStore && invocationCid && invocationData) {
|
|
10120
|
+
const storedInvocation = {
|
|
10121
|
+
cid: invocationCid,
|
|
10122
|
+
invocation: invocationData,
|
|
10123
|
+
invokerDid: actorDid,
|
|
10124
|
+
capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
|
|
10125
|
+
executedAt: now ? now() : Date.now(),
|
|
10126
|
+
flowId,
|
|
10127
|
+
blockId: node.id,
|
|
10128
|
+
result: "success",
|
|
10129
|
+
proofCids: auth.proofCids || [],
|
|
10130
|
+
claimId: result.claimId
|
|
9748
10131
|
};
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
const
|
|
9763
|
-
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
sourceRunId: record.runId,
|
|
9774
|
-
eventName: `barrier:${allSources.map((s) => s.alias).join("+")}`,
|
|
9775
|
-
eventIndex: 0,
|
|
9776
|
-
payload: mergedPayload,
|
|
9777
|
-
refSnapshots,
|
|
9778
|
-
assigneeDid,
|
|
9779
|
-
emittedAt: record.completedAt,
|
|
9780
|
-
expiresAt
|
|
10132
|
+
invocationStore.add(storedInvocation);
|
|
10133
|
+
}
|
|
10134
|
+
updateRuntimeAfterSuccess(node, actorDid, runtime, result, invocationCid || auth.capabilityId, now);
|
|
10135
|
+
return {
|
|
10136
|
+
success: true,
|
|
10137
|
+
stage: "complete",
|
|
10138
|
+
result,
|
|
10139
|
+
capabilityId: auth.capabilityId,
|
|
10140
|
+
invocationCid
|
|
10141
|
+
};
|
|
10142
|
+
} catch (error) {
|
|
10143
|
+
const message = error instanceof Error ? error.message : "Execution failed";
|
|
10144
|
+
if (invocationStore && invocationCid && invocationData) {
|
|
10145
|
+
const storedInvocation = {
|
|
10146
|
+
cid: invocationCid,
|
|
10147
|
+
invocation: invocationData,
|
|
10148
|
+
invokerDid: actorDid,
|
|
10149
|
+
capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
|
|
10150
|
+
executedAt: now ? now() : Date.now(),
|
|
10151
|
+
flowId,
|
|
10152
|
+
blockId: node.id,
|
|
10153
|
+
result: "failure",
|
|
10154
|
+
error: message,
|
|
10155
|
+
proofCids: auth.proofCids || []
|
|
9781
10156
|
};
|
|
9782
|
-
|
|
9783
|
-
clearBarrierState(yDoc, listenerBlockId);
|
|
9784
|
-
if (runtimeMap) {
|
|
9785
|
-
const prev = runtimeMap.get(listenerBlockId) || {};
|
|
9786
|
-
runtimeMap.set(listenerBlockId, {
|
|
9787
|
-
...prev,
|
|
9788
|
-
pendingPayload: { ...mergedPayload, ...refSnapshots }
|
|
9789
|
-
});
|
|
9790
|
-
}
|
|
10157
|
+
invocationStore.add(storedInvocation);
|
|
9791
10158
|
}
|
|
10159
|
+
return { success: false, stage: "action", error: message, invocationCid };
|
|
9792
10160
|
}
|
|
9793
|
-
}
|
|
9794
|
-
|
|
9795
|
-
|
|
9796
|
-
|
|
9797
|
-
|
|
9798
|
-
|
|
9799
|
-
if (
|
|
9800
|
-
|
|
10161
|
+
};
|
|
10162
|
+
|
|
10163
|
+
// src/core/lib/flowEngine/proof.ts
|
|
10164
|
+
var PROOF_MISSING_CODE = "PROOF_MISSING";
|
|
10165
|
+
function getOutputValue(output, path) {
|
|
10166
|
+
return path.split(".").reduce((acc, key) => {
|
|
10167
|
+
if (acc && typeof acc === "object") return acc[key];
|
|
10168
|
+
return void 0;
|
|
10169
|
+
}, output);
|
|
10170
|
+
}
|
|
10171
|
+
function isProofValue(value) {
|
|
10172
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
10173
|
+
if (typeof value === "number") return !Number.isNaN(value);
|
|
10174
|
+
return !!value;
|
|
10175
|
+
}
|
|
10176
|
+
function validateActionProof(action, output) {
|
|
10177
|
+
const proof = action.proof;
|
|
10178
|
+
if (proof === "none") return { valid: true };
|
|
10179
|
+
if (proof === void 0) {
|
|
10180
|
+
if (action.sideEffect) {
|
|
10181
|
+
return {
|
|
10182
|
+
valid: false,
|
|
10183
|
+
reason: `Action '${action.type}' has a side effect but declares no proof of execution. Declare proof (or 'none') on its ActionDefinition.`
|
|
10184
|
+
};
|
|
9801
10185
|
}
|
|
9802
|
-
|
|
10186
|
+
return { valid: true };
|
|
9803
10187
|
}
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
|
|
9812
|
-
} catch {
|
|
10188
|
+
if ("validate" in proof) {
|
|
10189
|
+
let valid = false;
|
|
10190
|
+
try {
|
|
10191
|
+
valid = proof.validate(output);
|
|
10192
|
+
} catch {
|
|
10193
|
+
valid = false;
|
|
10194
|
+
}
|
|
10195
|
+
return valid ? { valid: true } : { valid: false, reason: `Action '${action.type}' returned success but its proof validator rejected the output.` };
|
|
9813
10196
|
}
|
|
9814
|
-
|
|
10197
|
+
const satisfied = proof.fields.some((field) => isProofValue(getOutputValue(output, field)));
|
|
10198
|
+
if (satisfied) return { valid: true };
|
|
10199
|
+
return {
|
|
10200
|
+
valid: false,
|
|
10201
|
+
reason: `Action '${action.type}' returned success without proof. Expected at least one of [${proof.fields.join(", ")}] in the output. Check the handler logs.`
|
|
10202
|
+
};
|
|
9815
10203
|
}
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
return typeof did === "string" && did.length > 0 ? did : void 0;
|
|
9823
|
-
} catch {
|
|
9824
|
-
return void 0;
|
|
10204
|
+
|
|
10205
|
+
// src/core/lib/flowEngine/verifiedCompletion.ts
|
|
10206
|
+
function verifyCompletion(params) {
|
|
10207
|
+
const { runtime, blockId, actionType, getInvocation, schemaVersion } = params;
|
|
10208
|
+
if (!runtime || runtime.state !== "completed") {
|
|
10209
|
+
return { status: "not_completed" };
|
|
9825
10210
|
}
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
const ttlAbsolute = block?.props?.ttlAbsoluteDueDate;
|
|
9829
|
-
if (typeof ttlAbsolute === "string" && ttlAbsolute) {
|
|
9830
|
-
return ttlAbsolute;
|
|
10211
|
+
if (runtime.manuallyVerified === true) {
|
|
10212
|
+
return { status: "verified" };
|
|
9831
10213
|
}
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
10214
|
+
if (actionType && isRepeatableAction(actionType)) {
|
|
10215
|
+
return { status: "verified" };
|
|
10216
|
+
}
|
|
10217
|
+
if (actionType) {
|
|
10218
|
+
const action = getAction(actionType);
|
|
10219
|
+
if (action) {
|
|
10220
|
+
const proofCheck = validateActionProof(action, runtime.output || {});
|
|
10221
|
+
if (!proofCheck.valid) {
|
|
10222
|
+
return { status: "unverified", reason: proofCheck.reason || "Completed without proof of execution." };
|
|
10223
|
+
}
|
|
9837
10224
|
}
|
|
9838
10225
|
}
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
if (
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
10226
|
+
const policy = schemaVersion ? getVersionPolicy(schemaVersion) : null;
|
|
10227
|
+
if (!policy?.ucanRequired) {
|
|
10228
|
+
return { status: "verified" };
|
|
10229
|
+
}
|
|
10230
|
+
const invocationCid = runtime.lastInvocationCid;
|
|
10231
|
+
if (!invocationCid) {
|
|
10232
|
+
return { status: "unverified", reason: "Completed without an invocation record on a UCAN-enforced flow." };
|
|
10233
|
+
}
|
|
10234
|
+
if (!getInvocation) {
|
|
10235
|
+
return { status: "unverified", reason: "No invocation store available to verify the completion." };
|
|
10236
|
+
}
|
|
10237
|
+
const invocation = getInvocation(invocationCid);
|
|
10238
|
+
if (!invocation) {
|
|
10239
|
+
return { status: "unverified", reason: `Invocation ${invocationCid} not found in the invocation store.` };
|
|
10240
|
+
}
|
|
10241
|
+
if (invocation.result !== "success") {
|
|
10242
|
+
return { status: "unverified", reason: "Completion references a failed invocation." };
|
|
10243
|
+
}
|
|
10244
|
+
if (invocation.blockId && invocation.blockId !== blockId) {
|
|
10245
|
+
return { status: "unverified", reason: "Completion references an invocation for a different block." };
|
|
10246
|
+
}
|
|
10247
|
+
return { status: "verified" };
|
|
9851
10248
|
}
|
|
9852
|
-
function
|
|
9853
|
-
|
|
9854
|
-
if (typeof actionType !== "string") return void 0;
|
|
9855
|
-
return getAction(actionType);
|
|
10249
|
+
function isVerifiedCompletion(params) {
|
|
10250
|
+
return verifyCompletion(params).status === "verified";
|
|
9856
10251
|
}
|
|
9857
10252
|
|
|
9858
10253
|
// src/core/lib/ucanDelegationStore.ts
|
|
@@ -10187,26 +10582,6 @@ var createMemoryInvocationStore = () => {
|
|
|
10187
10582
|
};
|
|
10188
10583
|
};
|
|
10189
10584
|
|
|
10190
|
-
// src/core/lib/flowEngine/emitEvents.ts
|
|
10191
|
-
function writeRunRecordAndReconcile(editor, blockId, output, events, actorDid, detailsPatch = {}) {
|
|
10192
|
-
const yDoc = editor._yDoc;
|
|
10193
|
-
if (!yDoc) return;
|
|
10194
|
-
if (events.length === 0) return;
|
|
10195
|
-
const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
10196
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10197
|
-
const details = {
|
|
10198
|
-
runId,
|
|
10199
|
-
output,
|
|
10200
|
-
events,
|
|
10201
|
-
startedAt: now,
|
|
10202
|
-
completedAt: now,
|
|
10203
|
-
actorDid,
|
|
10204
|
-
...detailsPatch
|
|
10205
|
-
};
|
|
10206
|
-
appendRunRecord(yDoc, blockId, details, actorDid);
|
|
10207
|
-
reconcilePendingInvocations(editor);
|
|
10208
|
-
}
|
|
10209
|
-
|
|
10210
10585
|
// src/core/lib/flowEngine/actionExecutor.ts
|
|
10211
10586
|
import * as Y3 from "yjs";
|
|
10212
10587
|
|
|
@@ -10406,6 +10781,46 @@ async function reconcileActionReadBack(params) {
|
|
|
10406
10781
|
readBack: failedReadBack
|
|
10407
10782
|
};
|
|
10408
10783
|
}
|
|
10784
|
+
const readBackActionType = readBack.actionType || readBack.kind;
|
|
10785
|
+
const actionDef = readBackActionType ? getAction(readBackActionType) : void 0;
|
|
10786
|
+
if (actionDef) {
|
|
10787
|
+
const proofCheck = validateActionProof(actionDef, finalOutput || {});
|
|
10788
|
+
if (!proofCheck.valid) {
|
|
10789
|
+
const message = proofCheck.reason || "Read-back resolved completed without proof of execution.";
|
|
10790
|
+
const proofFailedReadBack = {
|
|
10791
|
+
...nextReadBack,
|
|
10792
|
+
terminalAt: checkedIso
|
|
10793
|
+
};
|
|
10794
|
+
runtime.update(params.blockId, {
|
|
10795
|
+
state: actionDef.sideEffect ? "needs_verification" : "failed",
|
|
10796
|
+
output: finalOutput,
|
|
10797
|
+
error: { message, code: PROOF_MISSING_CODE, at: checkedAt },
|
|
10798
|
+
readBack: proofFailedReadBack
|
|
10799
|
+
});
|
|
10800
|
+
const failedRunId = writeReconciliationRunRecord({
|
|
10801
|
+
yDoc,
|
|
10802
|
+
editor: getReconcileEditor(params, yDoc, editor),
|
|
10803
|
+
blockId: params.blockId,
|
|
10804
|
+
actorDid,
|
|
10805
|
+
output: finalOutput,
|
|
10806
|
+
events,
|
|
10807
|
+
readBack: proofFailedReadBack,
|
|
10808
|
+
error: { message, code: PROOF_MISSING_CODE },
|
|
10809
|
+
now
|
|
10810
|
+
});
|
|
10811
|
+
return {
|
|
10812
|
+
success: false,
|
|
10813
|
+
blockId: params.blockId,
|
|
10814
|
+
state: "failed",
|
|
10815
|
+
output: finalOutput,
|
|
10816
|
+
events,
|
|
10817
|
+
error: message,
|
|
10818
|
+
runId: failedRunId,
|
|
10819
|
+
pendingInvocationRemoved: false,
|
|
10820
|
+
readBack: proofFailedReadBack
|
|
10821
|
+
};
|
|
10822
|
+
}
|
|
10823
|
+
}
|
|
10409
10824
|
const completedReadBack = {
|
|
10410
10825
|
...nextReadBack,
|
|
10411
10826
|
terminalAt: checkedIso
|
|
@@ -10457,6 +10872,7 @@ function parseInputs2(value) {
|
|
|
10457
10872
|
const parsed = JSON.parse(value);
|
|
10458
10873
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
10459
10874
|
} catch {
|
|
10875
|
+
warnOnce(`executor-inputs-parse:${value}`, `[flow-config] saved block inputs are not valid JSON; treated as empty`);
|
|
10460
10876
|
return {};
|
|
10461
10877
|
}
|
|
10462
10878
|
}
|
|
@@ -10638,6 +11054,8 @@ async function executeActionBlock(params) {
|
|
|
10638
11054
|
let readBack;
|
|
10639
11055
|
let rawReadBack;
|
|
10640
11056
|
let requestedAwaitingReadBack = false;
|
|
11057
|
+
let proofFailureReason = null;
|
|
11058
|
+
let proofFailureOutput;
|
|
10641
11059
|
const startedAt = now();
|
|
10642
11060
|
runtime.update(blockId, {
|
|
10643
11061
|
state: "running",
|
|
@@ -10677,6 +11095,13 @@ async function executeActionBlock(params) {
|
|
|
10677
11095
|
if (result.completion?.state === "awaiting_readback") {
|
|
10678
11096
|
requestedAwaitingReadBack = true;
|
|
10679
11097
|
rawReadBack = result.completion.readBack;
|
|
11098
|
+
} else {
|
|
11099
|
+
const proofCheck = validateActionProof(action, result.output || {});
|
|
11100
|
+
if (!proofCheck.valid) {
|
|
11101
|
+
proofFailureReason = proofCheck.reason || "Action returned success without proof of execution.";
|
|
11102
|
+
proofFailureOutput = result.output || {};
|
|
11103
|
+
throw new Error(proofFailureReason);
|
|
11104
|
+
}
|
|
10680
11105
|
}
|
|
10681
11106
|
return {
|
|
10682
11107
|
payload: result.output,
|
|
@@ -10687,6 +11112,27 @@ async function executeActionBlock(params) {
|
|
|
10687
11112
|
});
|
|
10688
11113
|
if (!outcome.success) {
|
|
10689
11114
|
const message = outcome.error || "Action execution failed";
|
|
11115
|
+
if (proofFailureReason) {
|
|
11116
|
+
const proofFailureState = action.sideEffect ? "needs_verification" : "failed";
|
|
11117
|
+
runtime.update(blockId, {
|
|
11118
|
+
state: proofFailureState,
|
|
11119
|
+
output: proofFailureOutput,
|
|
11120
|
+
error: { message, code: PROOF_MISSING_CODE, at: now() }
|
|
11121
|
+
});
|
|
11122
|
+
return {
|
|
11123
|
+
...buildFailureResult({
|
|
11124
|
+
blockId,
|
|
11125
|
+
actionType,
|
|
11126
|
+
stage: outcome.stage,
|
|
11127
|
+
error: message,
|
|
11128
|
+
pendingInvocation: inputBuild.pendingInvocation
|
|
11129
|
+
}),
|
|
11130
|
+
completionState: proofFailureState === "needs_verification" ? "needs_verification" : "failed",
|
|
11131
|
+
output: proofFailureOutput,
|
|
11132
|
+
invocationCid: outcome.invocationCid,
|
|
11133
|
+
capabilityId: outcome.capabilityId
|
|
11134
|
+
};
|
|
11135
|
+
}
|
|
10690
11136
|
updateRuntimeFailure(runtime, blockId, message, now);
|
|
10691
11137
|
return {
|
|
10692
11138
|
...buildFailureResult({
|
|
@@ -10754,6 +11200,29 @@ async function executeActionBlock(params) {
|
|
|
10754
11200
|
};
|
|
10755
11201
|
}
|
|
10756
11202
|
|
|
11203
|
+
// src/core/lib/flowEngine/validateBlockConfig.ts
|
|
11204
|
+
function validateBlockConfig(block) {
|
|
11205
|
+
const issues = [];
|
|
11206
|
+
if (!block?.props) return issues;
|
|
11207
|
+
const checkJson = (propName, value, expectObject = true) => {
|
|
11208
|
+
if (value == null || value === "") return;
|
|
11209
|
+
if (typeof value !== "string") return;
|
|
11210
|
+
try {
|
|
11211
|
+
const parsed = JSON.parse(value);
|
|
11212
|
+
if (expectObject && (parsed == null || typeof parsed !== "object")) {
|
|
11213
|
+
issues.push(`${propName} is valid JSON but not an object`);
|
|
11214
|
+
}
|
|
11215
|
+
} catch {
|
|
11216
|
+
issues.push(`${propName} is not valid JSON`);
|
|
11217
|
+
}
|
|
11218
|
+
};
|
|
11219
|
+
checkJson("inputs", block.props.inputs);
|
|
11220
|
+
checkJson("trigger", block.props.trigger);
|
|
11221
|
+
checkJson("assignment", block.props.assignment);
|
|
11222
|
+
checkJson("conditions", block.props.conditions);
|
|
11223
|
+
return issues;
|
|
11224
|
+
}
|
|
11225
|
+
|
|
10757
11226
|
// src/core/lib/flowEngine/migration.ts
|
|
10758
11227
|
var MIGRATION_REGISTRY = {};
|
|
10759
11228
|
function registerMigration(definition) {
|
|
@@ -12330,20 +12799,30 @@ function classifyBlockerCause(block, runtime) {
|
|
|
12330
12799
|
}
|
|
12331
12800
|
return void 0;
|
|
12332
12801
|
}
|
|
12333
|
-
function classifyNodeState({
|
|
12802
|
+
function classifyNodeState({
|
|
12803
|
+
block,
|
|
12804
|
+
runtime,
|
|
12805
|
+
now,
|
|
12806
|
+
pendingInvocationCount = 0,
|
|
12807
|
+
completionVerification,
|
|
12808
|
+
isRepeatable = false
|
|
12809
|
+
}) {
|
|
12810
|
+
if (isRepeatable && runtime.state === "completed") return "Active";
|
|
12811
|
+
if (runtime.state === "completed" && completionVerification?.status === "unverified") return "Blocked";
|
|
12334
12812
|
if (runtime.state === "completed" || runtime.state === "cancelled") return "Done";
|
|
12813
|
+
if (runtime.state === "needs_verification") return "Blocked";
|
|
12335
12814
|
if (runtime.state === "failed" || runtime.error) return "Blocked";
|
|
12336
12815
|
const dueAt = getDueAt(block);
|
|
12337
12816
|
if (dueAt != null && dueAt <= now) return "Overdue";
|
|
12338
12817
|
if (pendingInvocationCount > 0) return "Pending";
|
|
12339
12818
|
return "Pending";
|
|
12340
12819
|
}
|
|
12341
|
-
function snapshotNode(block, runtime, now, pendingInvocationCount = 0) {
|
|
12820
|
+
function snapshotNode(block, runtime, now, pendingInvocationCount = 0, completionVerification, isRepeatable = false) {
|
|
12342
12821
|
const nodeId = getBlockId(block);
|
|
12343
12822
|
if (!nodeId) {
|
|
12344
12823
|
throw new Error("Cannot snapshot a block without an id");
|
|
12345
12824
|
}
|
|
12346
|
-
const publicState = classifyNodeState({ block, runtime, now, pendingInvocationCount });
|
|
12825
|
+
const publicState = classifyNodeState({ block, runtime, now, pendingInvocationCount, completionVerification, isRepeatable });
|
|
12347
12826
|
const dueAt = getDueAt(block);
|
|
12348
12827
|
const assigneeDid = getAssigneeDid(block);
|
|
12349
12828
|
const snapshot = {
|
|
@@ -12358,7 +12837,13 @@ function snapshotNode(block, runtime, now, pendingInvocationCount = 0) {
|
|
|
12358
12837
|
const title = getBlockTitle(block);
|
|
12359
12838
|
if (title) snapshot.title = title;
|
|
12360
12839
|
if (publicState === "Blocked") {
|
|
12361
|
-
|
|
12840
|
+
if (runtime.state === "needs_verification") {
|
|
12841
|
+
snapshot.blockerCause = "awaiting_verification";
|
|
12842
|
+
} else if (runtime.state === "completed" && completionVerification?.status === "unverified") {
|
|
12843
|
+
snapshot.blockerCause = "unverified_completion";
|
|
12844
|
+
} else {
|
|
12845
|
+
snapshot.blockerCause = classifyBlockerCause(block, runtime) || "unknown";
|
|
12846
|
+
}
|
|
12362
12847
|
}
|
|
12363
12848
|
if (assigneeDid) snapshot.assigneeDid = assigneeDid;
|
|
12364
12849
|
if (dueAt != null) snapshot.dueAt = dueAt;
|
|
@@ -12549,7 +13034,7 @@ function statusForResult(result) {
|
|
|
12549
13034
|
function planForSnapshot(context, options, block, snapshot) {
|
|
12550
13035
|
const queued = [];
|
|
12551
13036
|
const actionType = getBlockActionType(block);
|
|
12552
|
-
|
|
13037
|
+
const queueClaimUdidWatch = () => {
|
|
12553
13038
|
const runtime = snapshot.runtime;
|
|
12554
13039
|
if (actionType === "qi/claim.submit" && runtime.output?.claimId && !runtime.output?.udid) {
|
|
12555
13040
|
const command = queueIfAuthorized(context, options, "watch_udid", snapshot.nodeId, "Claim submitted but UDID is not yet observed", {
|
|
@@ -12558,6 +13043,13 @@ function planForSnapshot(context, options, block, snapshot) {
|
|
|
12558
13043
|
});
|
|
12559
13044
|
if (command) queued.push(command);
|
|
12560
13045
|
}
|
|
13046
|
+
};
|
|
13047
|
+
if (snapshot.publicState === "Active") {
|
|
13048
|
+
queueClaimUdidWatch();
|
|
13049
|
+
return queued;
|
|
13050
|
+
}
|
|
13051
|
+
if (snapshot.publicState === "Done") {
|
|
13052
|
+
queueClaimUdidWatch();
|
|
12561
13053
|
return queued;
|
|
12562
13054
|
}
|
|
12563
13055
|
if (snapshot.publicState === "Overdue") {
|
|
@@ -12641,11 +13133,24 @@ function planRalphLoopCommands(context, options = {}) {
|
|
|
12641
13133
|
const blocks = getBlocks(context);
|
|
12642
13134
|
const snapshots = [];
|
|
12643
13135
|
const queuedCommands = [];
|
|
13136
|
+
const invocationsMap = context.yDoc.getMap("invocations");
|
|
13137
|
+
const schemaVersionRaw = context.yDoc.getMap("root").get("schema_version");
|
|
13138
|
+
const schemaVersion = typeof schemaVersionRaw === "string" ? schemaVersionRaw : void 0;
|
|
13139
|
+
const getInvocation = (cid) => invocationsMap.get(cid);
|
|
12644
13140
|
for (const block of blocks) {
|
|
12645
13141
|
const nodeId = getBlockId(block);
|
|
12646
13142
|
if (!nodeId) continue;
|
|
12647
13143
|
const pendingInvocationCount = readPendingInvocations(context.yDoc, nodeId).length;
|
|
12648
|
-
const
|
|
13144
|
+
const runtime = getRuntime3(context.yDoc, nodeId);
|
|
13145
|
+
const actionType = getBlockActionType(block);
|
|
13146
|
+
const completionVerification = verifyCompletion({
|
|
13147
|
+
runtime,
|
|
13148
|
+
blockId: nodeId,
|
|
13149
|
+
actionType,
|
|
13150
|
+
getInvocation,
|
|
13151
|
+
schemaVersion
|
|
13152
|
+
});
|
|
13153
|
+
const snapshot = snapshotNode(block, runtime, now, pendingInvocationCount, completionVerification, isRepeatableAction(actionType));
|
|
12649
13154
|
snapshots.push(snapshot);
|
|
12650
13155
|
queuedCommands.push(...planForSnapshot(context, options, block, snapshot));
|
|
12651
13156
|
}
|
|
@@ -13225,14 +13730,20 @@ var FlowAgentService = class {
|
|
|
13225
13730
|
};
|
|
13226
13731
|
|
|
13227
13732
|
export {
|
|
13733
|
+
warnOnce,
|
|
13734
|
+
STEP_COMPLETED_EVENT_NAME,
|
|
13735
|
+
STEP_COMPLETED_EVENT,
|
|
13228
13736
|
resolveActionType,
|
|
13229
13737
|
registerAction,
|
|
13230
13738
|
getAction,
|
|
13231
13739
|
getAllActions,
|
|
13740
|
+
isRepeatableAction,
|
|
13741
|
+
getAliasEntries,
|
|
13232
13742
|
hasAction,
|
|
13233
13743
|
getActionByCan,
|
|
13234
13744
|
getEventsForBlock,
|
|
13235
13745
|
getOutputSchemaForBlock,
|
|
13746
|
+
generateActionManifest,
|
|
13236
13747
|
canToType,
|
|
13237
13748
|
typeToCan,
|
|
13238
13749
|
getAllCanMappings,
|
|
@@ -13256,6 +13767,7 @@ export {
|
|
|
13256
13767
|
serializeXeroPaymentCreateInputs,
|
|
13257
13768
|
parseReferences,
|
|
13258
13769
|
resolveSingleReference,
|
|
13770
|
+
resolveReferencesDetailed,
|
|
13259
13771
|
resolveReferences,
|
|
13260
13772
|
hasReferences,
|
|
13261
13773
|
createReference,
|
|
@@ -13292,15 +13804,7 @@ export {
|
|
|
13292
13804
|
formatCoin2 as formatCoin,
|
|
13293
13805
|
formatCoinAmount,
|
|
13294
13806
|
createUcanService,
|
|
13295
|
-
buildAuthzFromProps,
|
|
13296
|
-
buildFlowNodeFromBlock,
|
|
13297
|
-
createRuntimeStateManager,
|
|
13298
|
-
clearRuntimeForTemplateClone,
|
|
13299
13807
|
isRuntimeRef,
|
|
13300
|
-
resolveRuntimeRefs,
|
|
13301
|
-
LATEST_VERSION,
|
|
13302
|
-
isAuthorized,
|
|
13303
|
-
executeNode,
|
|
13304
13808
|
RUN_RECORD_AUDIT_TYPE,
|
|
13305
13809
|
computePendingInvocationId,
|
|
13306
13810
|
snapshotInputRefs,
|
|
@@ -13313,16 +13817,32 @@ export {
|
|
|
13313
13817
|
readRunRecords,
|
|
13314
13818
|
findFailedListenersForSourceRun,
|
|
13315
13819
|
replayFailedListenerRun,
|
|
13820
|
+
VERSION_MANIFEST,
|
|
13821
|
+
LATEST_VERSION,
|
|
13822
|
+
getVersionPolicy,
|
|
13823
|
+
isVersionAtLeast,
|
|
13316
13824
|
reconcilePendingInvocations,
|
|
13317
13825
|
getActionForBlock,
|
|
13826
|
+
writeRunRecordAndReconcile,
|
|
13827
|
+
buildAuthzFromProps,
|
|
13828
|
+
buildFlowNodeFromBlock,
|
|
13829
|
+
createRuntimeStateManager,
|
|
13830
|
+
clearRuntimeForTemplateClone,
|
|
13831
|
+
resolveRuntimeRefs,
|
|
13832
|
+
isAuthorized,
|
|
13833
|
+
executeNode,
|
|
13834
|
+
PROOF_MISSING_CODE,
|
|
13835
|
+
validateActionProof,
|
|
13318
13836
|
reconcileActionReadBack,
|
|
13319
13837
|
buildActionRunInputs,
|
|
13320
13838
|
executeActionBlock,
|
|
13839
|
+
verifyCompletion,
|
|
13840
|
+
isVerifiedCompletion,
|
|
13841
|
+
validateBlockConfig,
|
|
13321
13842
|
createUcanDelegationStore,
|
|
13322
13843
|
createMemoryUcanDelegationStore,
|
|
13323
13844
|
createInvocationStore,
|
|
13324
13845
|
createMemoryInvocationStore,
|
|
13325
|
-
writeRunRecordAndReconcile,
|
|
13326
13846
|
compileBlockProps,
|
|
13327
13847
|
COMPILED_BLOCK_TYPE,
|
|
13328
13848
|
toEvaluatorOperator,
|
|
@@ -13374,4 +13894,4 @@ export {
|
|
|
13374
13894
|
executeQueuedFlowAgentCoreCommands,
|
|
13375
13895
|
FlowAgentService
|
|
13376
13896
|
};
|
|
13377
|
-
//# sourceMappingURL=chunk-
|
|
13897
|
+
//# sourceMappingURL=chunk-5TJK6JKV.js.map
|