@ixo/editor 5.38.0 → 5.40.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.
@@ -1,5 +1,12 @@
1
1
  // src/core/lib/actionRegistry/registry.ts
2
2
  var actions = /* @__PURE__ */ new Map();
3
+ var STEP_COMPLETED_EVENT_NAME = "step.completed";
4
+ var STEP_COMPLETED_EVENT = {
5
+ name: STEP_COMPLETED_EVENT_NAME,
6
+ displayName: "Completed",
7
+ description: "Fires when this step is completed.",
8
+ payloadSchema: []
9
+ };
3
10
  var ACTION_TYPE_ALIASES = {
4
11
  bid: "qi/bid.submit",
5
12
  claim: "qi/claim.submit",
@@ -51,15 +58,21 @@ function getActionByCan(can) {
51
58
  }
52
59
  function getEventsForBlock(action, inputs) {
53
60
  if (!action) return [];
61
+ let events;
54
62
  if (action.getDynamicEvents) {
55
63
  const parsed = normalizeInputs(inputs);
56
64
  try {
57
- return action.getDynamicEvents(parsed) || [];
65
+ events = action.getDynamicEvents(parsed) || [];
58
66
  } catch {
59
- return [];
67
+ events = [];
60
68
  }
69
+ } else {
70
+ events = action.events || [];
61
71
  }
62
- return action.events || [];
72
+ if (events.some((event) => event.name === STEP_COMPLETED_EVENT_NAME)) {
73
+ return events;
74
+ }
75
+ return [...events, STEP_COMPLETED_EVENT];
63
76
  }
64
77
  function getOutputSchemaForBlock(action, inputs) {
65
78
  if (!action) return [];
@@ -116,7 +129,12 @@ var CAN_TO_TYPE = {
116
129
  "pod/governance-config": "qi/pod.governance-config",
117
130
  "pod/member-multi-select": "qi/pod.member-multi-select",
118
131
  "pod/list-domain-flows": "qi/pod.list-domain-flows",
119
- // Calendar integration
132
+ // Delegated integrations (author connects once, runners execute on their behalf)
133
+ "gmail.email/send": "qi/gmail.email.send",
134
+ "outlook.email/send": "qi/outlook.email.send",
135
+ "slack.message/send": "qi/slack.message.send",
136
+ "googlecalendar.event/create": "qi/googlecalendar.event.create",
137
+ // Calendar integration (self-connected)
120
138
  "calendar.event/create": "qi/calendar.event.create",
121
139
  "calendar.event/update": "qi/calendar.event.update",
122
140
  "calendar.event/list": "qi/calendar.event.list",
@@ -238,7 +256,8 @@ function buildServicesFromHandlers(handlers) {
238
256
  integrations: handlers?.integrations?.executeTool ? {
239
257
  executeTool: async (args) => handlers.integrations.executeTool(args),
240
258
  fetchCurrentState: handlers.integrations.fetchCurrentState ? async (args) => handlers.integrations.fetchCurrentState(args) : void 0,
241
- getEntityDid: handlers?.getEntityDid ? () => handlers.getEntityDid() : void 0
259
+ getEntityDid: handlers?.getEntityDid ? () => handlers.getEntityDid() : void 0,
260
+ executeBinding: handlers.integrations.executeBinding ? async (args) => handlers.integrations.executeBinding(args) : void 0
242
261
  } : void 0,
243
262
  oracle: handlers?.generateWallet ? {
244
263
  generateWallet: async () => handlers.generateWallet(),
@@ -1157,6 +1176,10 @@ function registerFormSubmitAction(type, can) {
1157
1176
  sideEffect: true,
1158
1177
  defaultRequiresConfirmation: false,
1159
1178
  requiredCapability: "flow/execute",
1179
+ // Additive: makes the form an event SOURCE so downstream blocks can
1180
+ // auto-trigger on submission (e.g. form → send email). Nothing existing
1181
+ // consumes this; it only enables new event wiring.
1182
+ eligibleForEventTrigger: true,
1160
1183
  inputSchema: {
1161
1184
  type: "object",
1162
1185
  required: [],
@@ -1168,6 +1191,15 @@ function registerFormSubmitAction(type, can) {
1168
1191
  { path: "form.answers", displayName: "Form Answers JSON", type: "string", description: "JSON stringified form answers, matching form block runtime output." },
1169
1192
  { path: "answers", displayName: "Form Answers", type: "object", description: "Parsed form answers object for convenience." }
1170
1193
  ],
1194
+ events: [
1195
+ {
1196
+ name: "form.submitted",
1197
+ displayName: "Form submitted",
1198
+ description: "Fires when the human submits the form. Payload carries the parsed answers.",
1199
+ payloadSchema: [{ path: "answers", displayName: "Form Answers", type: "object" }],
1200
+ pendingDisplayFields: ["answers"]
1201
+ }
1202
+ ],
1171
1203
  run: async (inputs) => {
1172
1204
  const answers = normalizeAnswers(inputs.answers ?? inputs.form?.answers);
1173
1205
  const answersJson = JSON.stringify(answers);
@@ -1177,7 +1209,8 @@ function registerFormSubmitAction(type, can) {
1177
1209
  answers: answersJson
1178
1210
  },
1179
1211
  answers
1180
- }
1212
+ },
1213
+ events: [{ name: "form.submitted", payload: { answers } }]
1181
1214
  };
1182
1215
  }
1183
1216
  });
@@ -6292,6 +6325,379 @@ registerAction({
6292
6325
  }
6293
6326
  });
6294
6327
 
6328
+ // src/core/lib/actionRegistry/actions/_shared/delegatedTool.ts
6329
+ function parseBoundConnection(raw) {
6330
+ if (!raw || typeof raw !== "object") return null;
6331
+ const c = raw;
6332
+ if (typeof c.bindingId !== "string" || !c.bindingId) return null;
6333
+ if (typeof c.connectedAccountId !== "string") return null;
6334
+ if (typeof c.toolkit !== "string") return null;
6335
+ return {
6336
+ bindingId: c.bindingId,
6337
+ connectedAccountId: c.connectedAccountId,
6338
+ toolkit: c.toolkit,
6339
+ label: typeof c.label === "string" ? c.label : null
6340
+ };
6341
+ }
6342
+ function parseDelegatedToolInputs(raw) {
6343
+ let parsed = {};
6344
+ try {
6345
+ parsed = typeof raw === "string" ? JSON.parse(raw || "{}") : raw || {};
6346
+ } catch {
6347
+ parsed = {};
6348
+ }
6349
+ return { ...parsed, connection: parseBoundConnection(parsed.connection) };
6350
+ }
6351
+ function serializeDelegatedToolInputs(inputs) {
6352
+ return JSON.stringify(inputs);
6353
+ }
6354
+ function fieldValues(inputs) {
6355
+ const out = {};
6356
+ for (const [k, v] of Object.entries(inputs)) {
6357
+ if (k === "connection") continue;
6358
+ if (typeof v === "string") out[k] = v;
6359
+ }
6360
+ return out;
6361
+ }
6362
+ function missingRequired(schema, values) {
6363
+ return (schema.parameters.required || []).filter((name) => !(values[name] ?? "").trim());
6364
+ }
6365
+ function coerceToolArgs(schema, values) {
6366
+ const out = {};
6367
+ for (const [name, prop] of Object.entries(schema.parameters.properties || {})) {
6368
+ const raw = values[name];
6369
+ if (typeof raw !== "string" || raw.trim() === "") continue;
6370
+ const type = prop.type;
6371
+ if (type === "boolean") {
6372
+ out[name] = raw === "true";
6373
+ } else if (type === "array") {
6374
+ out[name] = raw.split(/[\n,]+/).map((s) => s.trim()).filter(Boolean);
6375
+ } else if (type === "number" || type === "integer") {
6376
+ const n = Number(raw);
6377
+ if (!Number.isNaN(n)) out[name] = n;
6378
+ } else {
6379
+ out[name] = raw;
6380
+ }
6381
+ }
6382
+ return out;
6383
+ }
6384
+ async function executeDelegatedTool(ctx, opts) {
6385
+ const svc = ctx.services.integrations;
6386
+ if (!svc?.executeBinding) {
6387
+ throw new Error(`${opts.toolkitLabel} integration is not configured.`);
6388
+ }
6389
+ const bindingId = opts.connection?.bindingId;
6390
+ if (!bindingId) {
6391
+ throw new Error(`No ${opts.toolkitLabel} account is bound. The template author must connect and bind ${opts.toolkitLabel}.`);
6392
+ }
6393
+ const missing = missingRequired(opts.schema, opts.values);
6394
+ if (missing.length > 0) {
6395
+ throw new Error(`Missing required field${missing.length === 1 ? "" : "s"}: ${missing.join(", ")}`);
6396
+ }
6397
+ const args = coerceToolArgs(opts.schema, opts.values);
6398
+ const result = await svc.executeBinding({ bindingId, toolSlug: opts.toolSlug, arguments: args });
6399
+ if (!result.successful) {
6400
+ if (result.code === "AUTH_EXPIRED") {
6401
+ throw new Error(`The template author's ${opts.toolkitLabel} access expired \u2014 they need to reconnect it in the template.`);
6402
+ }
6403
+ if (result.code === "UPSTREAM_4XX" && /not found|removed/i.test(result.error || "")) {
6404
+ throw new Error(`This ${opts.toolkitLabel} integration was removed by the template author.`);
6405
+ }
6406
+ throw new Error(result.error || `${opts.toolkitLabel} action failed.`);
6407
+ }
6408
+ return result.data ?? {};
6409
+ }
6410
+
6411
+ // src/core/lib/actionRegistry/actions/gmail/emailSend.types.ts
6412
+ var GMAIL_SEND_SLUG = "GMAIL_SEND_EMAIL";
6413
+ var GMAIL_SENT_EVENT = "email.sent";
6414
+ var GMAIL_SEND_SCHEMA = {
6415
+ slug: GMAIL_SEND_SLUG,
6416
+ name: "Send Email",
6417
+ description: "Send an email from the template author's Gmail account.",
6418
+ parameters: {
6419
+ type: "object",
6420
+ required: ["recipient_email", "body"],
6421
+ properties: {
6422
+ recipient_email: { type: "string", title: "To", description: "Recipient's email address." },
6423
+ subject: { type: "string", title: "Subject" },
6424
+ body: { type: "string", title: "Body", description: 'Email body. Plain text unless "HTML body" is set.' },
6425
+ cc: { type: "array", title: "Cc", description: "Comma-separated email addresses." },
6426
+ bcc: { type: "array", title: "Bcc", description: "Comma-separated email addresses." },
6427
+ is_html: { type: "boolean", title: "HTML body", description: "Render the body as HTML." }
6428
+ }
6429
+ }
6430
+ };
6431
+ var GMAIL_SEND_OUTPUT_SCHEMA = [
6432
+ { path: "messageId", displayName: "Message ID", type: "string", description: "Gmail id of the sent message" },
6433
+ { path: "threadId", displayName: "Thread ID", type: "string" },
6434
+ { path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" }
6435
+ ];
6436
+
6437
+ // src/core/lib/actionRegistry/actions/gmail/emailSend.ts
6438
+ registerAction({
6439
+ type: "qi/gmail.email.send",
6440
+ can: "gmail.email/send",
6441
+ sideEffect: true,
6442
+ defaultRequiresConfirmation: true,
6443
+ requiredCapability: "flow/block/execute",
6444
+ // Can be wired to another block's event (e.g. form submitted → send email).
6445
+ eligibleForEventTrigger: true,
6446
+ outputSchema: GMAIL_SEND_OUTPUT_SCHEMA,
6447
+ events: [
6448
+ {
6449
+ name: GMAIL_SENT_EVENT,
6450
+ displayName: "Email sent",
6451
+ description: "Fired after the email is sent from the author\u2019s Gmail.",
6452
+ payloadSchema: [
6453
+ { path: "messageId", displayName: "Message ID", type: "string" },
6454
+ { path: "recipient_email", displayName: "To", type: "string" }
6455
+ ],
6456
+ pendingDisplayFields: ["messageId"]
6457
+ }
6458
+ ],
6459
+ run: async (inputs, ctx) => {
6460
+ const parsed = parseDelegatedToolInputs(inputs);
6461
+ const values = fieldValues(parsed);
6462
+ const data = await executeDelegatedTool(ctx, {
6463
+ connection: parsed.connection,
6464
+ schema: GMAIL_SEND_SCHEMA,
6465
+ toolSlug: GMAIL_SEND_SLUG,
6466
+ values,
6467
+ toolkitLabel: "Gmail"
6468
+ });
6469
+ const envelope = data.response_data ?? data;
6470
+ const messageId = String(envelope.id ?? envelope.messageId ?? "");
6471
+ const threadId = String(envelope.threadId ?? "");
6472
+ return {
6473
+ output: {
6474
+ messageId,
6475
+ threadId,
6476
+ sentAt: (/* @__PURE__ */ new Date()).toISOString()
6477
+ },
6478
+ events: messageId ? [{ name: GMAIL_SENT_EVENT, payload: { messageId, recipient_email: values.recipient_email ?? "" } }] : void 0
6479
+ };
6480
+ }
6481
+ });
6482
+
6483
+ // src/core/lib/actionRegistry/actions/outlook/emailSend.types.ts
6484
+ var OUTLOOK_SEND_SLUG = "OUTLOOK_OUTLOOK_SEND_EMAIL";
6485
+ var OUTLOOK_SENT_EVENT = "email.sent";
6486
+ var OUTLOOK_SEND_SCHEMA = {
6487
+ slug: OUTLOOK_SEND_SLUG,
6488
+ name: "Send Email",
6489
+ description: "Send an email from the template author's Outlook account.",
6490
+ parameters: {
6491
+ type: "object",
6492
+ required: ["subject", "body", "to_email"],
6493
+ properties: {
6494
+ to_email: { type: "string", title: "To", description: "Recipient's email address." },
6495
+ to_name: { type: "string", title: "To name", description: "Optional display name for the recipient." },
6496
+ subject: { type: "string", title: "Subject" },
6497
+ body: { type: "string", title: "Body", description: 'Email body. Plain text unless "HTML body" is set.' },
6498
+ cc_emails: { type: "array", title: "Cc", description: "Comma-separated email addresses." },
6499
+ bcc_emails: { type: "array", title: "Bcc", description: "Comma-separated email addresses." },
6500
+ is_html: { type: "boolean", title: "HTML body", description: "Render the body as HTML." }
6501
+ }
6502
+ }
6503
+ };
6504
+ var OUTLOOK_SEND_OUTPUT_SCHEMA = [
6505
+ { path: "messageId", displayName: "Message ID", type: "string" },
6506
+ { path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" }
6507
+ ];
6508
+
6509
+ // src/core/lib/actionRegistry/actions/outlook/emailSend.ts
6510
+ registerAction({
6511
+ type: "qi/outlook.email.send",
6512
+ can: "outlook.email/send",
6513
+ sideEffect: true,
6514
+ defaultRequiresConfirmation: true,
6515
+ requiredCapability: "flow/block/execute",
6516
+ // Can be wired to another block's event (e.g. form submitted → send email).
6517
+ eligibleForEventTrigger: true,
6518
+ outputSchema: OUTLOOK_SEND_OUTPUT_SCHEMA,
6519
+ events: [
6520
+ {
6521
+ name: OUTLOOK_SENT_EVENT,
6522
+ displayName: "Email sent",
6523
+ description: "Fired after the email is sent from the author\u2019s Outlook.",
6524
+ payloadSchema: [
6525
+ { path: "messageId", displayName: "Message ID", type: "string" },
6526
+ { path: "to_email", displayName: "To", type: "string" }
6527
+ ],
6528
+ pendingDisplayFields: ["messageId"]
6529
+ }
6530
+ ],
6531
+ run: async (inputs, ctx) => {
6532
+ const parsed = parseDelegatedToolInputs(inputs);
6533
+ const values = fieldValues(parsed);
6534
+ const data = await executeDelegatedTool(ctx, {
6535
+ connection: parsed.connection,
6536
+ schema: OUTLOOK_SEND_SCHEMA,
6537
+ toolSlug: OUTLOOK_SEND_SLUG,
6538
+ values,
6539
+ toolkitLabel: "Outlook"
6540
+ });
6541
+ const envelope = data.response_data ?? data;
6542
+ const messageId = String(envelope.id ?? envelope.messageId ?? "");
6543
+ return {
6544
+ output: {
6545
+ messageId,
6546
+ sentAt: (/* @__PURE__ */ new Date()).toISOString()
6547
+ },
6548
+ // Outlook often returns no id, so emit unconditionally.
6549
+ events: [{ name: OUTLOOK_SENT_EVENT, payload: { messageId, to_email: values.to_email ?? "" } }]
6550
+ };
6551
+ }
6552
+ });
6553
+
6554
+ // src/core/lib/actionRegistry/actions/slack/messageSend.types.ts
6555
+ var SLACK_SEND_SLUG = "SLACK_CHAT_POST_MESSAGE";
6556
+ var SLACK_SENT_EVENT = "message.sent";
6557
+ var SLACK_SEND_SCHEMA = {
6558
+ slug: SLACK_SEND_SLUG,
6559
+ name: "Post Message",
6560
+ description: "Post a message to a Slack channel from the template author's Slack account.",
6561
+ parameters: {
6562
+ type: "object",
6563
+ required: ["channel"],
6564
+ properties: {
6565
+ channel: { type: "string", title: "Channel", description: "Channel ID or name, e.g. #general or C0123456." },
6566
+ markdown_text: {
6567
+ type: "string",
6568
+ title: "Message",
6569
+ description: "Message text in Slack markdown. Preferred over the deprecated plain text field."
6570
+ },
6571
+ thread_ts: { type: "string", title: "Thread", description: "Optional parent message timestamp to reply within a thread." }
6572
+ }
6573
+ }
6574
+ };
6575
+ var SLACK_SEND_OUTPUT_SCHEMA = [
6576
+ { path: "messageTs", displayName: "Message ts", type: "string" },
6577
+ { path: "channel", displayName: "Channel", type: "string" },
6578
+ { path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" }
6579
+ ];
6580
+
6581
+ // src/core/lib/actionRegistry/actions/slack/messageSend.ts
6582
+ registerAction({
6583
+ type: "qi/slack.message.send",
6584
+ can: "slack.message/send",
6585
+ sideEffect: true,
6586
+ defaultRequiresConfirmation: true,
6587
+ requiredCapability: "flow/block/execute",
6588
+ // Can be wired to another block's event (e.g. form submitted → post message).
6589
+ eligibleForEventTrigger: true,
6590
+ outputSchema: SLACK_SEND_OUTPUT_SCHEMA,
6591
+ events: [
6592
+ {
6593
+ name: SLACK_SENT_EVENT,
6594
+ displayName: "Message posted",
6595
+ description: "Fired after the message is posted to Slack.",
6596
+ payloadSchema: [
6597
+ { path: "messageTs", displayName: "Message ts", type: "string" },
6598
+ { path: "channel", displayName: "Channel", type: "string" }
6599
+ ],
6600
+ pendingDisplayFields: ["messageTs"]
6601
+ }
6602
+ ],
6603
+ run: async (inputs, ctx) => {
6604
+ const parsed = parseDelegatedToolInputs(inputs);
6605
+ const values = fieldValues(parsed);
6606
+ const data = await executeDelegatedTool(ctx, {
6607
+ connection: parsed.connection,
6608
+ schema: SLACK_SEND_SCHEMA,
6609
+ toolSlug: SLACK_SEND_SLUG,
6610
+ values,
6611
+ toolkitLabel: "Slack"
6612
+ });
6613
+ const envelope = data.response_data ?? data;
6614
+ const messageTs = String(envelope.ts ?? "");
6615
+ const channel = String(envelope.channel ?? values.channel ?? "");
6616
+ return {
6617
+ output: {
6618
+ messageTs,
6619
+ channel,
6620
+ sentAt: (/* @__PURE__ */ new Date()).toISOString()
6621
+ },
6622
+ events: messageTs ? [{ name: SLACK_SENT_EVENT, payload: { messageTs, channel } }] : void 0
6623
+ };
6624
+ }
6625
+ });
6626
+
6627
+ // src/core/lib/actionRegistry/actions/googlecalendar/eventCreate.types.ts
6628
+ var GOOGLECALENDAR_CREATE_SLUG = "GOOGLECALENDAR_CREATE_EVENT";
6629
+ var GOOGLECALENDAR_CREATED_EVENT = "event.created";
6630
+ var GOOGLECALENDAR_CREATE_SCHEMA = {
6631
+ slug: GOOGLECALENDAR_CREATE_SLUG,
6632
+ name: "Create Event",
6633
+ description: "Create an event on the template author's Google Calendar.",
6634
+ parameters: {
6635
+ type: "object",
6636
+ required: ["start_datetime"],
6637
+ properties: {
6638
+ start_datetime: { type: "string", title: "Start time", description: "ISO 8601, e.g. 2026-05-12T09:00:00." },
6639
+ summary: { type: "string", title: "Title" },
6640
+ description: { type: "string", title: "Description" },
6641
+ location: { type: "string", title: "Location" },
6642
+ timezone: { type: "string", title: "Timezone", description: "IANA name, e.g. Europe/London." },
6643
+ attendees: { type: "array", title: "Attendees", description: "Comma-separated email addresses." },
6644
+ calendar_id: { type: "string", title: "Calendar", description: "Use 'primary' for the author's main calendar." },
6645
+ event_duration_minutes: { type: "number", title: "Duration (minutes)", description: "Defaults to the calendar default if blank." }
6646
+ }
6647
+ }
6648
+ };
6649
+ var GOOGLECALENDAR_CREATE_OUTPUT_SCHEMA = [
6650
+ { path: "eventId", displayName: "Event ID", type: "string" },
6651
+ { path: "htmlLink", displayName: "Event link", type: "string" },
6652
+ { path: "summary", displayName: "Summary", type: "string" },
6653
+ { path: "startIso", displayName: "Start", type: "string" }
6654
+ ];
6655
+
6656
+ // src/core/lib/actionRegistry/actions/googlecalendar/eventCreate.ts
6657
+ registerAction({
6658
+ type: "qi/googlecalendar.event.create",
6659
+ can: "googlecalendar.event/create",
6660
+ sideEffect: true,
6661
+ defaultRequiresConfirmation: true,
6662
+ requiredCapability: "flow/block/execute",
6663
+ eligibleForEventTrigger: true,
6664
+ outputSchema: GOOGLECALENDAR_CREATE_OUTPUT_SCHEMA,
6665
+ events: [
6666
+ {
6667
+ name: GOOGLECALENDAR_CREATED_EVENT,
6668
+ displayName: "Calendar event created",
6669
+ description: "Fired after the event is created on the author\u2019s calendar.",
6670
+ payloadSchema: [
6671
+ { path: "eventId", displayName: "Event ID", type: "string" },
6672
+ { path: "htmlLink", displayName: "Event link", type: "string" },
6673
+ { path: "summary", displayName: "Summary", type: "string" }
6674
+ ],
6675
+ pendingDisplayFields: ["summary", "eventId"]
6676
+ }
6677
+ ],
6678
+ run: async (inputs, ctx) => {
6679
+ const parsed = parseDelegatedToolInputs(inputs);
6680
+ const values = fieldValues(parsed);
6681
+ const data = await executeDelegatedTool(ctx, {
6682
+ connection: parsed.connection,
6683
+ schema: GOOGLECALENDAR_CREATE_SCHEMA,
6684
+ toolSlug: GOOGLECALENDAR_CREATE_SLUG,
6685
+ values,
6686
+ toolkitLabel: "Google Calendar"
6687
+ });
6688
+ const envelope = data.response_data ?? data;
6689
+ const eventId = String(envelope.id ?? "");
6690
+ const htmlLink = String(envelope.htmlLink ?? "");
6691
+ const summary = String(envelope.summary ?? values.summary ?? "");
6692
+ const start = envelope.start;
6693
+ const startIso = String(start?.dateTime ?? start?.date ?? values.start_datetime ?? "");
6694
+ return {
6695
+ output: { eventId, htmlLink, summary, startIso },
6696
+ events: eventId ? [{ name: GOOGLECALENDAR_CREATED_EVENT, payload: { eventId, htmlLink, summary } }] : void 0
6697
+ };
6698
+ }
6699
+ });
6700
+
6295
6701
  // src/core/lib/actionRegistry/actions/calendar/eventCreate.types.ts
6296
6702
  var EMPTY = {
6297
6703
  connection: null,
@@ -8638,829 +9044,849 @@ var createUcanService = (config) => {
8638
9044
  };
8639
9045
  };
8640
9046
 
8641
- // src/core/lib/flowEngine/utils.ts
8642
- var buildAuthzFromProps = (props) => {
8643
- const linkedClaimCollectionId = typeof props.linkedClaimCollectionId === "string" ? props.linkedClaimCollectionId.trim() : "";
8644
- const authz = {};
8645
- if (linkedClaimCollectionId) {
8646
- authz.linkedClaim = { collectionId: linkedClaimCollectionId };
8647
- }
8648
- return authz;
8649
- };
8650
- var buildFlowNodeFromBlock = (block) => {
8651
- const base = {
8652
- id: block.id,
8653
- type: block.type,
8654
- props: block.props || {}
8655
- };
8656
- const authz = buildAuthzFromProps(block.props || {});
8657
- return {
8658
- ...base,
8659
- ...authz
8660
- };
8661
- };
8662
-
8663
- // src/core/lib/flowEngine/runtime.ts
8664
- var XERO_WORK_ITEMS_MAP_NAME = "xeroWorkItems";
8665
- var XERO_CONNECTION_MAP_NAME = "xeroConnection";
8666
- var ensureStateObject = (value) => {
8667
- if (!value || typeof value !== "object") {
8668
- return {};
8669
- }
8670
- return { ...value };
8671
- };
8672
- var createYMapManager = (map) => {
8673
- return {
8674
- get: (nodeId) => {
8675
- const stored = map.get(nodeId);
8676
- return ensureStateObject(stored);
8677
- },
8678
- update: (nodeId, updates) => {
8679
- const current = ensureStateObject(map.get(nodeId));
8680
- map.set(nodeId, { ...current, ...updates });
8681
- }
8682
- };
8683
- };
8684
- var createMemoryManager = () => {
8685
- const memory = /* @__PURE__ */ new Map();
8686
- return {
8687
- get: (nodeId) => ensureStateObject(memory.get(nodeId)),
8688
- update: (nodeId, updates) => {
8689
- const current = ensureStateObject(memory.get(nodeId));
8690
- memory.set(nodeId, { ...current, ...updates });
8691
- }
8692
- };
8693
- };
8694
- var createRuntimeStateManager = (editor) => {
8695
- if (editor?._yRuntime) {
8696
- return createYMapManager(editor._yRuntime);
8697
- }
8698
- return createMemoryManager();
8699
- };
8700
- var createYDocRuntimeManager = (yDoc) => {
8701
- return createYMapManager(yDoc.getMap("runtime"));
8702
- };
8703
- function clearRuntimeForTemplateClone(yDoc) {
8704
- const runtime = yDoc.getMap("runtime");
8705
- const invocations = yDoc.getMap("invocations");
8706
- const pendingInvocations = yDoc.getMap("pendingInvocations");
8707
- const agentOutbox = yDoc.getMap("agentOutbox");
8708
- const agentLeases = yDoc.getMap("agentLeases");
8709
- const auditTrail = yDoc.getMap("auditTrail");
8710
- const xeroWorkItems = yDoc.getMap(XERO_WORK_ITEMS_MAP_NAME);
8711
- const xeroConnection = yDoc.getMap(XERO_CONNECTION_MAP_NAME);
8712
- yDoc.transact(() => {
8713
- runtime.forEach((_, key) => runtime.delete(key));
8714
- invocations.forEach((_, key) => invocations.delete(key));
8715
- pendingInvocations.forEach((_, key) => pendingInvocations.delete(key));
8716
- agentOutbox.forEach((_, key) => agentOutbox.delete(key));
8717
- agentLeases.forEach((_, key) => agentLeases.delete(key));
8718
- auditTrail.forEach((_, key) => auditTrail.delete(key));
8719
- xeroWorkItems.forEach((_, key) => xeroWorkItems.delete(key));
8720
- xeroConnection.forEach((_, key) => xeroConnection.delete(key));
8721
- });
8722
- }
8723
-
8724
9047
  // src/core/types/baseUcan.ts
8725
9048
  function isRuntimeRef(value) {
8726
9049
  return typeof value === "object" && value !== null && "$ref" in value && typeof value.$ref === "string";
8727
9050
  }
8728
9051
 
8729
- // src/core/lib/flowCompiler/resolveRefs.ts
8730
- function resolveRuntimeRefs(nb, getNodeOutput2, triggerContext) {
8731
- return resolveValue(nb, getNodeOutput2, triggerContext);
9052
+ // src/core/lib/flowEngine/triggers.ts
9053
+ import * as Y from "yjs";
9054
+ var RUN_RECORD_AUDIT_TYPE = "block.run";
9055
+ function computePendingInvocationId(args) {
9056
+ const { sourceBlockId, sourceRunId, listenerBlockId, eventName, eventIndex } = args;
9057
+ const input = `${sourceBlockId}:${sourceRunId}:${listenerBlockId}:${eventName}:${eventIndex}`;
9058
+ return `pi-${fnv1a32(input)}`;
8732
9059
  }
8733
- function resolveValue(value, getNodeOutput2, triggerContext) {
8734
- if (isRuntimeRef(value)) {
8735
- return resolveRef(value.$ref, getNodeOutput2, triggerContext);
8736
- }
8737
- if (Array.isArray(value)) {
8738
- return value.map((item) => resolveValue(item, getNodeOutput2, triggerContext));
8739
- }
8740
- if (typeof value === "object" && value !== null) {
8741
- const result = {};
8742
- for (const [key, val] of Object.entries(value)) {
8743
- result[key] = resolveValue(val, getNodeOutput2, triggerContext);
8744
- }
8745
- return result;
8746
- }
8747
- return value;
9060
+ function snapshotInputRefs(inputs, getNodeOutput2) {
9061
+ const snapshots = {};
9062
+ walkRefs(inputs, (ref) => {
9063
+ if (ref.$ref.startsWith("trigger.")) return;
9064
+ const parsed = parseOutputRef(ref.$ref);
9065
+ if (!parsed) return;
9066
+ const output = getNodeOutput2(parsed.nodeId);
9067
+ if (!output) return;
9068
+ snapshots[ref.$ref] = getNestedValue2(output, parsed.fieldPath);
9069
+ });
9070
+ return snapshots;
8748
9071
  }
8749
- function resolveRef(ref, getNodeOutput2, triggerContext) {
8750
- if (ref.startsWith("trigger.payload.")) {
8751
- if (!triggerContext) {
8752
- throw new Error(`Trigger ref "${ref}" used outside of a listener invocation context. trigger.payload.* refs are only valid on block.event-triggered blocks.`);
8753
- }
8754
- const fieldPath2 = ref.slice("trigger.payload.".length);
8755
- return getNestedValue2(triggerContext.payload, fieldPath2);
8756
- }
8757
- if (triggerContext && Object.prototype.hasOwnProperty.call(triggerContext.refSnapshots, ref)) {
8758
- return triggerContext.refSnapshots[ref];
9072
+ function walkRefs(value, visit) {
9073
+ if (isRuntimeRef(value)) {
9074
+ visit(value);
9075
+ return;
8759
9076
  }
8760
- const outputIndex = ref.indexOf(".output.");
8761
- if (outputIndex === -1) {
8762
- throw new Error(`Invalid runtime reference "${ref}". Expected format: "nodeId.output.fieldPath" or "trigger.payload.fieldPath"`);
9077
+ if (Array.isArray(value)) {
9078
+ for (const item of value) walkRefs(item, visit);
9079
+ return;
8763
9080
  }
8764
- const nodeId = ref.slice(0, outputIndex);
8765
- const fieldPath = ref.slice(outputIndex + ".output.".length);
8766
- const output = getNodeOutput2(nodeId);
8767
- if (!output) {
8768
- return void 0;
9081
+ if (typeof value === "object" && value !== null) {
9082
+ for (const v of Object.values(value)) walkRefs(v, visit);
8769
9083
  }
8770
- return getNestedValue2(output, fieldPath);
9084
+ }
9085
+ function parseOutputRef(ref) {
9086
+ const outputIndex = ref.indexOf(".output.");
9087
+ if (outputIndex === -1) return null;
9088
+ return {
9089
+ nodeId: ref.slice(0, outputIndex),
9090
+ fieldPath: ref.slice(outputIndex + ".output.".length)
9091
+ };
8771
9092
  }
8772
9093
  function getNestedValue2(obj, path) {
8773
9094
  const parts = path.split(".");
8774
9095
  let current = obj;
8775
9096
  for (const part of parts) {
8776
- if (current == null || typeof current !== "object") {
8777
- return void 0;
8778
- }
9097
+ if (current == null || typeof current !== "object") return void 0;
8779
9098
  current = current[part];
8780
9099
  }
8781
9100
  return current;
8782
9101
  }
8783
-
8784
- // src/core/lib/flowEngine/versionManifest.ts
8785
- var VERSION_MANIFEST = {
8786
- "0.3": {
8787
- version: "0.3",
8788
- label: "Legacy",
8789
- ucanRequired: false,
8790
- delegationRootRequired: false,
8791
- whitelistOnlyAllowed: true,
8792
- unrestrictedAllowed: true,
8793
- executionPath: "legacy",
8794
- authorizationFn: "v1",
8795
- allowedAuthModes: ["anyone", "actors", "capability"],
8796
- ui: {
8797
- showDelegationPanel: false,
8798
- showWhitelistConfig: true,
8799
- showAnyoneConfig: true,
8800
- showMigrationBanner: true,
8801
- requirePinForExecution: false
8802
- },
8803
- description: "Legacy version. UCAN optional, whitelist-only authorization accepted."
8804
- },
8805
- "1.0.0": {
8806
- version: "1.0.0",
8807
- label: "UCAN Required",
8808
- /** TEMP Disablement - needs to be true TODO */
8809
- ucanRequired: false,
8810
- delegationRootRequired: true,
8811
- whitelistOnlyAllowed: false,
8812
- unrestrictedAllowed: false,
8813
- executionPath: "invocation",
8814
- authorizationFn: "v2",
8815
- allowedAuthModes: ["capability"],
8816
- ui: {
8817
- showDelegationPanel: true,
8818
- showWhitelistConfig: false,
8819
- showAnyoneConfig: false,
8820
- showMigrationBanner: false,
8821
- requirePinForExecution: true
8822
- },
8823
- description: "UCAN-enforced. Every block execution requires a valid delegation chain."
9102
+ function fnv1a32(input) {
9103
+ let hash = 2166136261;
9104
+ for (let i = 0; i < input.length; i++) {
9105
+ hash ^= input.charCodeAt(i);
9106
+ hash = Math.imul(hash, 16777619);
8824
9107
  }
8825
- };
8826
- var LATEST_VERSION = "1.0.0";
8827
- function getVersionPolicy(version) {
8828
- const policy = VERSION_MANIFEST[version];
8829
- if (!policy) {
8830
- return VERSION_MANIFEST[LATEST_VERSION];
9108
+ return (hash >>> 0).toString(16).padStart(8, "0");
9109
+ }
9110
+ var PENDING_INVOCATIONS_MAP_KEY = "pendingInvocations";
9111
+ function getPendingInvocationsMap(yDoc) {
9112
+ return yDoc.getMap(PENDING_INVOCATIONS_MAP_KEY);
9113
+ }
9114
+ function getOrCreateBlockPendingMap(yDoc, blockId) {
9115
+ const outer = getPendingInvocationsMap(yDoc);
9116
+ let inner = outer.get(blockId);
9117
+ if (!inner) {
9118
+ inner = new Y.Map();
9119
+ outer.set(blockId, inner);
8831
9120
  }
8832
- return policy;
9121
+ return inner;
8833
9122
  }
8834
-
8835
- // src/core/lib/flowEngine/authorization.ts
8836
- var isAuthorized = async (blockId, actorDid, ucanService, flowUri, schemaVersion) => {
8837
- const policy = schemaVersion ? getVersionPolicy(schemaVersion) : null;
8838
- if (policy && !policy.ucanRequired) {
8839
- return { authorized: true };
9123
+ function readPendingInvocations(yDoc, blockId) {
9124
+ const outer = getPendingInvocationsMap(yDoc);
9125
+ const inner = outer.get(blockId);
9126
+ if (!inner) return [];
9127
+ const items = [];
9128
+ inner.forEach((value) => {
9129
+ if (value && typeof value === "object") {
9130
+ items.push(value);
9131
+ }
9132
+ });
9133
+ items.sort((a, b) => a.emittedAt.localeCompare(b.emittedAt));
9134
+ return items;
9135
+ }
9136
+ function queuePendingInvocation(yDoc, listenerBlockId, invocation) {
9137
+ let created = false;
9138
+ yDoc.transact(() => {
9139
+ const inner = getOrCreateBlockPendingMap(yDoc, listenerBlockId);
9140
+ if (inner.has(invocation.id)) return;
9141
+ inner.set(invocation.id, invocation);
9142
+ created = true;
9143
+ });
9144
+ return created;
9145
+ }
9146
+ function removePendingInvocation(yDoc, listenerBlockId, pendingInvocationId) {
9147
+ const outer = getPendingInvocationsMap(yDoc);
9148
+ const inner = outer.get(listenerBlockId);
9149
+ if (!inner) return false;
9150
+ if (!inner.has(pendingInvocationId)) return false;
9151
+ inner.delete(pendingInvocationId);
9152
+ return true;
9153
+ }
9154
+ var BARRIER_STATE_MAP_KEY = "barrierState";
9155
+ function getBarrierStateMap(yDoc) {
9156
+ return yDoc.getMap(BARRIER_STATE_MAP_KEY);
9157
+ }
9158
+ function getOrCreateListenerBarrierMap(yDoc, listenerBlockId) {
9159
+ const outer = getBarrierStateMap(yDoc);
9160
+ let inner = outer.get(listenerBlockId);
9161
+ if (!inner) {
9162
+ inner = new Y.Map();
9163
+ outer.set(listenerBlockId, inner);
8840
9164
  }
8841
- if (!ucanService) {
8842
- if (!policy) {
8843
- return { authorized: true };
9165
+ return inner;
9166
+ }
9167
+ function recordBarrierEvent(yDoc, listenerBlockId, entry) {
9168
+ let written = false;
9169
+ yDoc.transact(() => {
9170
+ const inner = getOrCreateListenerBarrierMap(yDoc, listenerBlockId);
9171
+ const key = `${entry.sourceBlockId}::${entry.eventName}`;
9172
+ const existing = inner.get(key);
9173
+ if (existing && typeof existing === "object" && existing.runId === entry.runId) return;
9174
+ inner.set(key, entry);
9175
+ written = true;
9176
+ });
9177
+ return written;
9178
+ }
9179
+ function readBarrierState(yDoc, listenerBlockId) {
9180
+ const outer = getBarrierStateMap(yDoc);
9181
+ const inner = outer.get(listenerBlockId);
9182
+ if (!inner) return [];
9183
+ const entries = [];
9184
+ inner.forEach((value) => {
9185
+ if (value && typeof value === "object") {
9186
+ entries.push(value);
9187
+ }
9188
+ });
9189
+ return entries;
9190
+ }
9191
+ function clearBarrierState(yDoc, listenerBlockId) {
9192
+ const outer = getBarrierStateMap(yDoc);
9193
+ yDoc.transact(() => {
9194
+ outer.delete(listenerBlockId);
9195
+ });
9196
+ }
9197
+ function computeBarrierInvocationId(entries, listenerBlockId) {
9198
+ const sorted = [...entries].sort((a, b) => a.sourceBlockId.localeCompare(b.sourceBlockId));
9199
+ const input = sorted.map((e) => `${e.sourceBlockId}:${e.runId}:${e.eventName}`).join("|") + `|>${listenerBlockId}`;
9200
+ return `bi-${fnv1a32(input)}`;
9201
+ }
9202
+ function mergeBarrierPayloads(entries) {
9203
+ const merged = {};
9204
+ for (const entry of entries) {
9205
+ if (entry.alias) {
9206
+ merged[entry.alias] = entry.payload;
9207
+ } else {
9208
+ Object.assign(merged, entry.payload);
8844
9209
  }
8845
- return {
8846
- authorized: false,
8847
- reason: "UCAN service is not configured. This flow version requires UCAN authorization."
8848
- };
8849
9210
  }
8850
- const capability = {
8851
- can: "flow/block/execute",
8852
- with: `${flowUri}:${blockId}`
8853
- };
8854
- const result = await ucanService.validateDelegationChain(actorDid, capability);
8855
- if (!result.valid) {
8856
- return {
8857
- authorized: false,
8858
- reason: result.error || "No valid capability chain found"
9211
+ return merged;
9212
+ }
9213
+ function appendRunRecord(yDoc, blockId, details, userId) {
9214
+ const auditMap = yDoc.getMap("auditTrail");
9215
+ yDoc.transact(() => {
9216
+ let arr = auditMap.get(blockId);
9217
+ if (!arr) {
9218
+ arr = new Y.Array();
9219
+ auditMap.set(blockId, arr);
9220
+ }
9221
+ const event = {
9222
+ id: `${details.runId}`,
9223
+ blockId,
9224
+ type: RUN_RECORD_AUDIT_TYPE,
9225
+ details,
9226
+ message: void 0,
9227
+ meta: {
9228
+ timestamp: details.completedAt,
9229
+ userId,
9230
+ editable: false
9231
+ }
8859
9232
  };
9233
+ arr.push([event]);
9234
+ });
9235
+ }
9236
+ function readRunRecords(yDoc, blockId) {
9237
+ const auditMap = yDoc.getMap("auditTrail");
9238
+ const arr = auditMap.get(blockId);
9239
+ if (!arr) return [];
9240
+ const records = [];
9241
+ arr.forEach((entry) => {
9242
+ if (!entry || typeof entry !== "object") return;
9243
+ const e = entry;
9244
+ if (e.type === RUN_RECORD_AUDIT_TYPE && e.details) {
9245
+ records.push(e.details);
9246
+ }
9247
+ });
9248
+ return records;
9249
+ }
9250
+ function findFailedListenersForSourceRun(yDoc, sourceBlockId, sourceRunId, listenerBlockIds) {
9251
+ const failures = [];
9252
+ for (const listenerBlockId of listenerBlockIds) {
9253
+ const records = readRunRecords(yDoc, listenerBlockId);
9254
+ for (const record of records) {
9255
+ if (!record.error) continue;
9256
+ if (record.triggeredBy?.sourceBlockId !== sourceBlockId) continue;
9257
+ if (record.fromPendingInvocationId == null) continue;
9258
+ const sourceRunIdField = record.sourceRunId;
9259
+ if (sourceRunIdField && sourceRunIdField !== sourceRunId) continue;
9260
+ failures.push({ listenerBlockId, record });
9261
+ }
8860
9262
  }
8861
- const proofCids = result.proofChain?.map((d) => d.cid) || [];
8862
- return {
8863
- authorized: true,
8864
- capabilityId: proofCids[0],
8865
- proofCids
8866
- };
8867
- };
8868
-
8869
- // src/core/lib/flowEngine/executor.ts
8870
- var updateRuntimeAfterSuccess = (node, actorDid, runtime, actionResult, invocationCid, now) => {
8871
- const updates = {
8872
- submittedByDid: actionResult.submittedByDid || actorDid,
8873
- evaluationStatus: actionResult.evaluationStatus || "pending",
8874
- executionTimestamp: now ? now() : Date.now(),
8875
- lastInvocationCid: invocationCid
8876
- };
8877
- if (actionResult.claimId) {
8878
- updates.claimId = actionResult.claimId;
8879
- }
8880
- runtime.update(node.id, updates);
8881
- };
8882
- var executeNode = async ({ node, actorDid, actorType, entityRoomId, context, action, pin }) => {
8883
- const { runtime, ucanService, invocationStore, flowUri, flowId, schemaVersion, now } = context;
8884
- const auth = await isAuthorized(node.id, actorDid, ucanService, flowUri, schemaVersion);
8885
- if (!auth.authorized) {
8886
- return { success: false, stage: "authorization", error: auth.reason };
8887
- }
8888
- if (node.linkedClaim && !node.linkedClaim.collectionId) {
8889
- return { success: false, stage: "claim", error: "Linked claim collection is required but missing." };
9263
+ return failures;
9264
+ }
9265
+ function replayFailedListenerRun(yDoc, failedRecord, listenerBlockId, originalPayload, originalRefSnapshots, assigneeDid) {
9266
+ if (!failedRecord.triggeredBy) return false;
9267
+ const replayId = `${failedRecord.runId}:replay-${Date.now().toString(36)}`;
9268
+ const now = (/* @__PURE__ */ new Date()).toISOString();
9269
+ const replay = {
9270
+ id: replayId,
9271
+ triggeringBlockId: failedRecord.triggeredBy.sourceBlockId,
9272
+ sourceRunId: failedRecord.sourceRunId || failedRecord.runId,
9273
+ eventName: failedRecord.triggeredBy.eventName,
9274
+ eventIndex: 0,
9275
+ payload: originalPayload,
9276
+ refSnapshots: originalRefSnapshots,
9277
+ assigneeDid,
9278
+ emittedAt: now,
9279
+ expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString()
9280
+ };
9281
+ return queuePendingInvocation(yDoc, listenerBlockId, replay);
9282
+ }
9283
+
9284
+ // src/core/lib/flowEngine/reconcile.ts
9285
+ var _reconcileRunning = false;
9286
+ function reconcilePendingInvocations(editor) {
9287
+ if (_reconcileRunning) return;
9288
+ _reconcileRunning = true;
9289
+ try {
9290
+ _reconcilePendingInvocationsInner(editor);
9291
+ } finally {
9292
+ _reconcileRunning = false;
8890
9293
  }
8891
- let invocationCid;
8892
- let invocationData;
8893
- if (ucanService && auth.proofCids && auth.proofCids.length > 0) {
8894
- const capability = {
8895
- can: "flow/block/execute",
8896
- with: `${flowUri}:${node.id}`
8897
- };
8898
- try {
8899
- const invocationResult = await ucanService.createAndValidateInvocation(
8900
- {
8901
- invokerDid: actorDid,
8902
- invokerType: actorType,
8903
- entityRoomId,
8904
- capability,
8905
- proofs: auth.proofCids,
8906
- pin
8907
- },
8908
- flowId,
8909
- node.id
8910
- );
8911
- if (!invocationResult.valid) {
8912
- return {
8913
- success: false,
8914
- stage: "authorization",
8915
- error: `Invocation validation failed: ${invocationResult.error}`
8916
- };
9294
+ }
9295
+ function _reconcilePendingInvocationsInner(editor) {
9296
+ const yDoc = editor._yDoc;
9297
+ if (!yDoc) return;
9298
+ const blocks = editor.document || [];
9299
+ if (blocks.length === 0) return;
9300
+ const listenersBySource = /* @__PURE__ */ new Map();
9301
+ const barrierListenersBySource = /* @__PURE__ */ new Map();
9302
+ for (const block of blocks) {
9303
+ const trigger = parseTrigger(block);
9304
+ if (!trigger) continue;
9305
+ if (trigger.type === "block.event") {
9306
+ if (!trigger.sourceBlockId || !trigger.eventName) continue;
9307
+ const key = `${trigger.sourceBlockId}::${trigger.eventName}`;
9308
+ if (!listenersBySource.has(key)) listenersBySource.set(key, []);
9309
+ listenersBySource.get(key).push({ block, trigger });
9310
+ } else if (trigger.type === "block.event.all" && trigger.sources) {
9311
+ for (const source of trigger.sources) {
9312
+ if (!source.sourceBlockId || !source.eventName) continue;
9313
+ const key = `${source.sourceBlockId}::${source.eventName}`;
9314
+ if (!barrierListenersBySource.has(key)) barrierListenersBySource.set(key, []);
9315
+ barrierListenersBySource.get(key).push({ block, trigger, source });
8917
9316
  }
8918
- invocationCid = invocationResult.cid;
8919
- invocationData = invocationResult.invocation;
8920
- } catch (error) {
8921
- const message = error instanceof Error ? error.message : "Failed to create invocation";
8922
- return { success: false, stage: "authorization", error: message };
8923
9317
  }
8924
9318
  }
8925
- try {
8926
- const result = await action();
8927
- if (node.linkedClaim && !result.claimId) {
8928
- if (invocationStore && invocationCid && invocationData) {
8929
- const storedInvocation = {
8930
- cid: invocationCid,
8931
- invocation: invocationData,
8932
- invokerDid: actorDid,
8933
- capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
8934
- executedAt: now ? now() : Date.now(),
8935
- flowId,
8936
- blockId: node.id,
8937
- result: "failure",
8938
- error: "Execution did not return a claimId for linked claim requirement.",
8939
- proofCids: auth.proofCids || []
8940
- };
8941
- invocationStore.add(storedInvocation);
9319
+ if (listenersBySource.size === 0 && barrierListenersBySource.size === 0) return;
9320
+ const runtimeMap = editor._yRuntime;
9321
+ const getNodeOutput2 = (nodeId) => {
9322
+ if (!runtimeMap) return void 0;
9323
+ const state = runtimeMap.get(nodeId);
9324
+ if (!state || typeof state !== "object") return void 0;
9325
+ const out = state.output;
9326
+ return out && typeof out === "object" ? out : void 0;
9327
+ };
9328
+ for (const block of blocks) {
9329
+ const sourceBlockId = block?.id;
9330
+ if (!sourceBlockId) continue;
9331
+ let hasAnyListener = false;
9332
+ for (const key of listenersBySource.keys()) {
9333
+ if (key.startsWith(`${sourceBlockId}::`)) {
9334
+ hasAnyListener = true;
9335
+ break;
8942
9336
  }
8943
- return {
8944
- success: false,
8945
- stage: "claim",
8946
- error: "Execution did not return a claimId for linked claim requirement.",
8947
- invocationCid
8948
- };
8949
9337
  }
8950
- if (invocationStore && invocationCid && invocationData) {
8951
- const storedInvocation = {
8952
- cid: invocationCid,
8953
- invocation: invocationData,
8954
- invokerDid: actorDid,
8955
- capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
8956
- executedAt: now ? now() : Date.now(),
8957
- flowId,
8958
- blockId: node.id,
8959
- result: "success",
8960
- proofCids: auth.proofCids || [],
8961
- claimId: result.claimId
8962
- };
8963
- invocationStore.add(storedInvocation);
9338
+ for (const key of barrierListenersBySource.keys()) {
9339
+ if (key.startsWith(`${sourceBlockId}::`)) {
9340
+ hasAnyListener = true;
9341
+ break;
9342
+ }
8964
9343
  }
8965
- updateRuntimeAfterSuccess(node, actorDid, runtime, result, invocationCid || auth.capabilityId, now);
8966
- return {
8967
- success: true,
8968
- stage: "complete",
8969
- result,
8970
- capabilityId: auth.capabilityId,
8971
- invocationCid
8972
- };
8973
- } catch (error) {
8974
- const message = error instanceof Error ? error.message : "Execution failed";
8975
- if (invocationStore && invocationCid && invocationData) {
8976
- const storedInvocation = {
8977
- cid: invocationCid,
8978
- invocation: invocationData,
8979
- invokerDid: actorDid,
8980
- capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
8981
- executedAt: now ? now() : Date.now(),
8982
- flowId,
8983
- blockId: node.id,
8984
- result: "failure",
8985
- error: message,
8986
- proofCids: auth.proofCids || []
8987
- };
8988
- invocationStore.add(storedInvocation);
9344
+ if (!hasAnyListener) continue;
9345
+ const records = readRunRecords(yDoc, sourceBlockId);
9346
+ for (const record of records) {
9347
+ processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput2, runtimeMap);
9348
+ processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput2, runtimeMap);
8989
9349
  }
8990
- return { success: false, stage: "action", error: message, invocationCid };
8991
- }
8992
- };
8993
-
8994
- // src/core/lib/flowEngine/triggers.ts
8995
- import * as Y from "yjs";
8996
- var RUN_RECORD_AUDIT_TYPE = "block.run";
8997
- function computePendingInvocationId(args) {
8998
- const { sourceBlockId, sourceRunId, listenerBlockId, eventName, eventIndex } = args;
8999
- const input = `${sourceBlockId}:${sourceRunId}:${listenerBlockId}:${eventName}:${eventIndex}`;
9000
- return `pi-${fnv1a32(input)}`;
9001
- }
9002
- function snapshotInputRefs(inputs, getNodeOutput2) {
9003
- const snapshots = {};
9004
- walkRefs(inputs, (ref) => {
9005
- if (ref.$ref.startsWith("trigger.")) return;
9006
- const parsed = parseOutputRef(ref.$ref);
9007
- if (!parsed) return;
9008
- const output = getNodeOutput2(parsed.nodeId);
9009
- if (!output) return;
9010
- snapshots[ref.$ref] = getNestedValue3(output, parsed.fieldPath);
9011
- });
9012
- return snapshots;
9013
- }
9014
- function walkRefs(value, visit) {
9015
- if (isRuntimeRef(value)) {
9016
- visit(value);
9017
- return;
9018
- }
9019
- if (Array.isArray(value)) {
9020
- for (const item of value) walkRefs(item, visit);
9021
- return;
9022
- }
9023
- if (typeof value === "object" && value !== null) {
9024
- for (const v of Object.values(value)) walkRefs(v, visit);
9025
9350
  }
9026
9351
  }
9027
- function parseOutputRef(ref) {
9028
- const outputIndex = ref.indexOf(".output.");
9029
- if (outputIndex === -1) return null;
9030
- return {
9031
- nodeId: ref.slice(0, outputIndex),
9032
- fieldPath: ref.slice(outputIndex + ".output.".length)
9033
- };
9352
+ function processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput2, runtimeMap) {
9353
+ if (!Array.isArray(record.events)) return;
9354
+ record.events.forEach((event, eventIndex) => {
9355
+ if (!event?.name) return;
9356
+ const key = `${sourceBlockId}::${event.name}`;
9357
+ const listeners = listenersBySource.get(key);
9358
+ if (!listeners || listeners.length === 0) return;
9359
+ for (const { block: listenerBlock } of listeners) {
9360
+ const listenerBlockId = listenerBlock.id;
9361
+ if (!listenerBlockId) continue;
9362
+ const id = computePendingInvocationId({
9363
+ sourceBlockId,
9364
+ sourceRunId: record.runId,
9365
+ listenerBlockId,
9366
+ eventName: event.name,
9367
+ eventIndex
9368
+ });
9369
+ const assigneeDid = resolveAssignee(listenerBlock) || "unassigned";
9370
+ const inputs = parseInputs(listenerBlock);
9371
+ const refSnapshots = snapshotInputRefs(inputs, getNodeOutput2);
9372
+ const expiresAt = computeExpiry(listenerBlock, record.completedAt);
9373
+ const invocation = {
9374
+ id,
9375
+ triggeringBlockId: sourceBlockId,
9376
+ sourceRunId: record.runId,
9377
+ eventName: event.name,
9378
+ eventIndex,
9379
+ payload: event.payload,
9380
+ refSnapshots,
9381
+ assigneeDid,
9382
+ emittedAt: record.completedAt,
9383
+ expiresAt
9384
+ };
9385
+ queuePendingInvocation(yDoc, listenerBlockId, invocation);
9386
+ if (runtimeMap) {
9387
+ const prev = runtimeMap.get(listenerBlockId) || {};
9388
+ runtimeMap.set(listenerBlockId, {
9389
+ ...prev,
9390
+ pendingPayload: { ...event.payload, ...refSnapshots }
9391
+ });
9392
+ }
9393
+ }
9394
+ });
9034
9395
  }
9035
- function getNestedValue3(obj, path) {
9036
- const parts = path.split(".");
9037
- let current = obj;
9038
- for (const part of parts) {
9039
- if (current == null || typeof current !== "object") return void 0;
9040
- current = current[part];
9396
+ function processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput2, runtimeMap) {
9397
+ if (!Array.isArray(record.events)) return;
9398
+ for (const event of record.events) {
9399
+ if (!event?.name) continue;
9400
+ const key = `${sourceBlockId}::${event.name}`;
9401
+ const listeners = barrierListenersBySource.get(key);
9402
+ if (!listeners || listeners.length === 0) continue;
9403
+ for (const { block: listenerBlock, trigger, source } of listeners) {
9404
+ const listenerBlockId = listenerBlock.id;
9405
+ if (!listenerBlockId) continue;
9406
+ const entry = {
9407
+ sourceBlockId,
9408
+ eventName: event.name,
9409
+ alias: source.alias,
9410
+ runId: record.runId,
9411
+ payload: event.payload || {},
9412
+ emittedAt: record.completedAt
9413
+ };
9414
+ recordBarrierEvent(yDoc, listenerBlockId, entry);
9415
+ if (runtimeMap) {
9416
+ const prev = runtimeMap.get(listenerBlockId) || {};
9417
+ const existing = prev.pendingPayload || {};
9418
+ runtimeMap.set(listenerBlockId, {
9419
+ ...prev,
9420
+ pendingPayload: { ...existing, ...event.payload || {} }
9421
+ });
9422
+ }
9423
+ const allSources = trigger.sources || [];
9424
+ const currentState = readBarrierState(yDoc, listenerBlockId);
9425
+ const requiredSources = allSources.filter((s) => s.optional !== true);
9426
+ const gatingSources = requiredSources.length > 0 ? requiredSources : allSources;
9427
+ const allFired = gatingSources.length > 0 && gatingSources.every((s) => currentState.some((e) => e.sourceBlockId === s.sourceBlockId && e.eventName === s.eventName));
9428
+ if (!allFired) continue;
9429
+ const assigneeDid = resolveAssignee(listenerBlock) || "unassigned";
9430
+ const id = computeBarrierInvocationId(currentState, listenerBlockId);
9431
+ const mergedPayload = mergeBarrierPayloads(currentState);
9432
+ const inputs = parseInputs(listenerBlock);
9433
+ const refSnapshots = snapshotInputRefs(inputs, getNodeOutput2);
9434
+ const expiresAt = computeExpiry(listenerBlock, record.completedAt);
9435
+ const invocation = {
9436
+ id,
9437
+ triggeringBlockId: sourceBlockId,
9438
+ sourceRunId: record.runId,
9439
+ eventName: `barrier:${allSources.map((s) => s.alias).join("+")}`,
9440
+ eventIndex: 0,
9441
+ payload: mergedPayload,
9442
+ refSnapshots,
9443
+ assigneeDid,
9444
+ emittedAt: record.completedAt,
9445
+ expiresAt
9446
+ };
9447
+ queuePendingInvocation(yDoc, listenerBlockId, invocation);
9448
+ clearBarrierState(yDoc, listenerBlockId);
9449
+ if (runtimeMap) {
9450
+ const prev = runtimeMap.get(listenerBlockId) || {};
9451
+ runtimeMap.set(listenerBlockId, {
9452
+ ...prev,
9453
+ pendingPayload: { ...mergedPayload, ...refSnapshots }
9454
+ });
9455
+ }
9456
+ }
9041
9457
  }
9042
- return current;
9043
9458
  }
9044
- function fnv1a32(input) {
9045
- let hash = 2166136261;
9046
- for (let i = 0; i < input.length; i++) {
9047
- hash ^= input.charCodeAt(i);
9048
- hash = Math.imul(hash, 16777619);
9459
+ function parseTrigger(block) {
9460
+ const raw = block?.props?.trigger;
9461
+ if (!raw || typeof raw !== "string") return null;
9462
+ try {
9463
+ const parsed = JSON.parse(raw);
9464
+ if (parsed && typeof parsed === "object" && typeof parsed.type === "string") {
9465
+ return parsed;
9466
+ }
9467
+ } catch {
9049
9468
  }
9050
- return (hash >>> 0).toString(16).padStart(8, "0");
9469
+ return null;
9051
9470
  }
9052
- var PENDING_INVOCATIONS_MAP_KEY = "pendingInvocations";
9053
- function getPendingInvocationsMap(yDoc) {
9054
- return yDoc.getMap(PENDING_INVOCATIONS_MAP_KEY);
9471
+ function parseInputs(block) {
9472
+ const raw = block?.props?.inputs;
9473
+ if (!raw || typeof raw !== "string") return {};
9474
+ try {
9475
+ const parsed = JSON.parse(raw);
9476
+ if (parsed && typeof parsed === "object") return parsed;
9477
+ } catch {
9478
+ }
9479
+ return {};
9055
9480
  }
9056
- function getOrCreateBlockPendingMap(yDoc, blockId) {
9057
- const outer = getPendingInvocationsMap(yDoc);
9058
- let inner = outer.get(blockId);
9059
- if (!inner) {
9060
- inner = new Y.Map();
9061
- outer.set(blockId, inner);
9481
+ function resolveAssignee(block) {
9482
+ const raw = block?.props?.assignment;
9483
+ if (!raw) return void 0;
9484
+ try {
9485
+ const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
9486
+ const did = parsed?.assignedActor?.did;
9487
+ return typeof did === "string" && did.length > 0 ? did : void 0;
9488
+ } catch {
9489
+ return void 0;
9062
9490
  }
9063
- return inner;
9064
9491
  }
9065
- function readPendingInvocations(yDoc, blockId) {
9066
- const outer = getPendingInvocationsMap(yDoc);
9067
- const inner = outer.get(blockId);
9068
- if (!inner) return [];
9069
- const items = [];
9070
- inner.forEach((value) => {
9071
- if (value && typeof value === "object") {
9072
- items.push(value);
9492
+ function computeExpiry(block, emittedAt) {
9493
+ const ttlAbsolute = block?.props?.ttlAbsoluteDueDate;
9494
+ if (typeof ttlAbsolute === "string" && ttlAbsolute) {
9495
+ return ttlAbsolute;
9496
+ }
9497
+ const ttlFromEnablement = block?.props?.ttlFromEnablement;
9498
+ if (typeof ttlFromEnablement === "string" && ttlFromEnablement) {
9499
+ const ms = parseIsoDurationToMs(ttlFromEnablement);
9500
+ if (ms != null) {
9501
+ return new Date(new Date(emittedAt).getTime() + ms).toISOString();
9073
9502
  }
9074
- });
9075
- items.sort((a, b) => a.emittedAt.localeCompare(b.emittedAt));
9076
- return items;
9503
+ }
9504
+ return new Date(new Date(emittedAt).getTime() + 7 * 24 * 60 * 60 * 1e3).toISOString();
9077
9505
  }
9078
- function queuePendingInvocation(yDoc, listenerBlockId, invocation) {
9079
- let created = false;
9080
- yDoc.transact(() => {
9081
- const inner = getOrCreateBlockPendingMap(yDoc, listenerBlockId);
9082
- if (inner.has(invocation.id)) return;
9083
- inner.set(invocation.id, invocation);
9084
- created = true;
9085
- });
9086
- return created;
9506
+ function parseIsoDurationToMs(duration) {
9507
+ const match = /^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/.exec(duration);
9508
+ if (!match) return null;
9509
+ const [, d, h, m, s] = match;
9510
+ let ms = 0;
9511
+ if (d) ms += parseInt(d, 10) * 24 * 60 * 60 * 1e3;
9512
+ if (h) ms += parseInt(h, 10) * 60 * 60 * 1e3;
9513
+ if (m) ms += parseInt(m, 10) * 60 * 1e3;
9514
+ if (s) ms += parseInt(s, 10) * 1e3;
9515
+ return ms;
9087
9516
  }
9088
- function removePendingInvocation(yDoc, listenerBlockId, pendingInvocationId) {
9089
- const outer = getPendingInvocationsMap(yDoc);
9090
- const inner = outer.get(listenerBlockId);
9091
- if (!inner) return false;
9092
- if (!inner.has(pendingInvocationId)) return false;
9093
- inner.delete(pendingInvocationId);
9094
- return true;
9517
+ function getActionForBlock(block) {
9518
+ const actionType = block?.props?.actionType;
9519
+ if (typeof actionType !== "string") return void 0;
9520
+ return getAction(actionType);
9095
9521
  }
9096
- var BARRIER_STATE_MAP_KEY = "barrierState";
9097
- function getBarrierStateMap(yDoc) {
9098
- return yDoc.getMap(BARRIER_STATE_MAP_KEY);
9522
+
9523
+ // src/core/lib/flowEngine/emitEvents.ts
9524
+ function writeRunRecordAndReconcile(editor, blockId, output, events, actorDid, detailsPatch = {}) {
9525
+ const yDoc = editor._yDoc;
9526
+ if (!yDoc) return;
9527
+ if (events.length === 0) return;
9528
+ const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
9529
+ const now = (/* @__PURE__ */ new Date()).toISOString();
9530
+ const details = {
9531
+ runId,
9532
+ output,
9533
+ events,
9534
+ startedAt: now,
9535
+ completedAt: now,
9536
+ actorDid,
9537
+ ...detailsPatch
9538
+ };
9539
+ appendRunRecord(yDoc, blockId, details, actorDid);
9540
+ reconcilePendingInvocations(editor);
9099
9541
  }
9100
- function getOrCreateListenerBarrierMap(yDoc, listenerBlockId) {
9101
- const outer = getBarrierStateMap(yDoc);
9102
- let inner = outer.get(listenerBlockId);
9103
- if (!inner) {
9104
- inner = new Y.Map();
9105
- outer.set(listenerBlockId, inner);
9542
+
9543
+ // src/core/lib/flowEngine/utils.ts
9544
+ var buildAuthzFromProps = (props) => {
9545
+ const linkedClaimCollectionId = typeof props.linkedClaimCollectionId === "string" ? props.linkedClaimCollectionId.trim() : "";
9546
+ const authz = {};
9547
+ if (linkedClaimCollectionId) {
9548
+ authz.linkedClaim = { collectionId: linkedClaimCollectionId };
9106
9549
  }
9107
- return inner;
9108
- }
9109
- function recordBarrierEvent(yDoc, listenerBlockId, entry) {
9110
- let written = false;
9111
- yDoc.transact(() => {
9112
- const inner = getOrCreateListenerBarrierMap(yDoc, listenerBlockId);
9113
- const key = `${entry.sourceBlockId}::${entry.eventName}`;
9114
- const existing = inner.get(key);
9115
- if (existing && typeof existing === "object" && existing.runId === entry.runId) return;
9116
- inner.set(key, entry);
9117
- written = true;
9118
- });
9119
- return written;
9120
- }
9121
- function readBarrierState(yDoc, listenerBlockId) {
9122
- const outer = getBarrierStateMap(yDoc);
9123
- const inner = outer.get(listenerBlockId);
9124
- if (!inner) return [];
9125
- const entries = [];
9126
- inner.forEach((value) => {
9127
- if (value && typeof value === "object") {
9128
- entries.push(value);
9550
+ return authz;
9551
+ };
9552
+ var buildFlowNodeFromBlock = (block) => {
9553
+ const base = {
9554
+ id: block.id,
9555
+ type: block.type,
9556
+ props: block.props || {}
9557
+ };
9558
+ const authz = buildAuthzFromProps(block.props || {});
9559
+ return {
9560
+ ...base,
9561
+ ...authz
9562
+ };
9563
+ };
9564
+
9565
+ // src/core/lib/flowEngine/runtime.ts
9566
+ var XERO_WORK_ITEMS_MAP_NAME = "xeroWorkItems";
9567
+ var XERO_CONNECTION_MAP_NAME = "xeroConnection";
9568
+ var ensureStateObject = (value) => {
9569
+ if (!value || typeof value !== "object") {
9570
+ return {};
9571
+ }
9572
+ return { ...value };
9573
+ };
9574
+ var createYMapManager = (map) => {
9575
+ return {
9576
+ get: (nodeId) => {
9577
+ const stored = map.get(nodeId);
9578
+ return ensureStateObject(stored);
9579
+ },
9580
+ update: (nodeId, updates) => {
9581
+ const current = ensureStateObject(map.get(nodeId));
9582
+ map.set(nodeId, { ...current, ...updates });
9129
9583
  }
9130
- });
9131
- return entries;
9132
- }
9133
- function clearBarrierState(yDoc, listenerBlockId) {
9134
- const outer = getBarrierStateMap(yDoc);
9584
+ };
9585
+ };
9586
+ var createMemoryManager = () => {
9587
+ const memory = /* @__PURE__ */ new Map();
9588
+ return {
9589
+ get: (nodeId) => ensureStateObject(memory.get(nodeId)),
9590
+ update: (nodeId, updates) => {
9591
+ const current = ensureStateObject(memory.get(nodeId));
9592
+ memory.set(nodeId, { ...current, ...updates });
9593
+ }
9594
+ };
9595
+ };
9596
+ var createRuntimeStateManager = (editor) => {
9597
+ if (editor?._yRuntime) {
9598
+ return createYMapManager(editor._yRuntime);
9599
+ }
9600
+ return createMemoryManager();
9601
+ };
9602
+ var createYDocRuntimeManager = (yDoc) => {
9603
+ return createYMapManager(yDoc.getMap("runtime"));
9604
+ };
9605
+ function clearRuntimeForTemplateClone(yDoc) {
9606
+ const runtime = yDoc.getMap("runtime");
9607
+ const invocations = yDoc.getMap("invocations");
9608
+ const pendingInvocations = yDoc.getMap("pendingInvocations");
9609
+ const agentOutbox = yDoc.getMap("agentOutbox");
9610
+ const agentLeases = yDoc.getMap("agentLeases");
9611
+ const auditTrail = yDoc.getMap("auditTrail");
9612
+ const xeroWorkItems = yDoc.getMap(XERO_WORK_ITEMS_MAP_NAME);
9613
+ const xeroConnection = yDoc.getMap(XERO_CONNECTION_MAP_NAME);
9135
9614
  yDoc.transact(() => {
9136
- outer.delete(listenerBlockId);
9615
+ runtime.forEach((_, key) => runtime.delete(key));
9616
+ invocations.forEach((_, key) => invocations.delete(key));
9617
+ pendingInvocations.forEach((_, key) => pendingInvocations.delete(key));
9618
+ agentOutbox.forEach((_, key) => agentOutbox.delete(key));
9619
+ agentLeases.forEach((_, key) => agentLeases.delete(key));
9620
+ auditTrail.forEach((_, key) => auditTrail.delete(key));
9621
+ xeroWorkItems.forEach((_, key) => xeroWorkItems.delete(key));
9622
+ xeroConnection.forEach((_, key) => xeroConnection.delete(key));
9137
9623
  });
9138
9624
  }
9139
- function computeBarrierInvocationId(entries, listenerBlockId) {
9140
- const sorted = [...entries].sort((a, b) => a.sourceBlockId.localeCompare(b.sourceBlockId));
9141
- const input = sorted.map((e) => `${e.sourceBlockId}:${e.runId}:${e.eventName}`).join("|") + `|>${listenerBlockId}`;
9142
- return `bi-${fnv1a32(input)}`;
9625
+
9626
+ // src/core/lib/flowCompiler/resolveRefs.ts
9627
+ function resolveRuntimeRefs(nb, getNodeOutput2, triggerContext) {
9628
+ return resolveValue(nb, getNodeOutput2, triggerContext);
9143
9629
  }
9144
- function mergeBarrierPayloads(entries) {
9145
- const merged = {};
9146
- for (const entry of entries) {
9147
- if (entry.alias) {
9148
- merged[entry.alias] = entry.payload;
9149
- } else {
9150
- Object.assign(merged, entry.payload);
9151
- }
9630
+ function resolveValue(value, getNodeOutput2, triggerContext) {
9631
+ if (isRuntimeRef(value)) {
9632
+ return resolveRef(value.$ref, getNodeOutput2, triggerContext);
9152
9633
  }
9153
- return merged;
9154
- }
9155
- function appendRunRecord(yDoc, blockId, details, userId) {
9156
- const auditMap = yDoc.getMap("auditTrail");
9157
- yDoc.transact(() => {
9158
- let arr = auditMap.get(blockId);
9159
- if (!arr) {
9160
- arr = new Y.Array();
9161
- auditMap.set(blockId, arr);
9634
+ if (Array.isArray(value)) {
9635
+ return value.map((item) => resolveValue(item, getNodeOutput2, triggerContext));
9636
+ }
9637
+ if (typeof value === "object" && value !== null) {
9638
+ const result = {};
9639
+ for (const [key, val] of Object.entries(value)) {
9640
+ result[key] = resolveValue(val, getNodeOutput2, triggerContext);
9162
9641
  }
9163
- const event = {
9164
- id: `${details.runId}`,
9165
- blockId,
9166
- type: RUN_RECORD_AUDIT_TYPE,
9167
- details,
9168
- message: void 0,
9169
- meta: {
9170
- timestamp: details.completedAt,
9171
- userId,
9172
- editable: false
9173
- }
9174
- };
9175
- arr.push([event]);
9176
- });
9642
+ return result;
9643
+ }
9644
+ return value;
9177
9645
  }
9178
- function readRunRecords(yDoc, blockId) {
9179
- const auditMap = yDoc.getMap("auditTrail");
9180
- const arr = auditMap.get(blockId);
9181
- if (!arr) return [];
9182
- const records = [];
9183
- arr.forEach((entry) => {
9184
- if (!entry || typeof entry !== "object") return;
9185
- const e = entry;
9186
- if (e.type === RUN_RECORD_AUDIT_TYPE && e.details) {
9187
- records.push(e.details);
9646
+ function resolveRef(ref, getNodeOutput2, triggerContext) {
9647
+ if (ref.startsWith("trigger.payload.")) {
9648
+ if (!triggerContext) {
9649
+ throw new Error(`Trigger ref "${ref}" used outside of a listener invocation context. trigger.payload.* refs are only valid on block.event-triggered blocks.`);
9188
9650
  }
9189
- });
9190
- return records;
9651
+ const fieldPath2 = ref.slice("trigger.payload.".length);
9652
+ return getNestedValue3(triggerContext.payload, fieldPath2);
9653
+ }
9654
+ if (triggerContext && Object.prototype.hasOwnProperty.call(triggerContext.refSnapshots, ref)) {
9655
+ return triggerContext.refSnapshots[ref];
9656
+ }
9657
+ const outputIndex = ref.indexOf(".output.");
9658
+ if (outputIndex === -1) {
9659
+ throw new Error(`Invalid runtime reference "${ref}". Expected format: "nodeId.output.fieldPath" or "trigger.payload.fieldPath"`);
9660
+ }
9661
+ const nodeId = ref.slice(0, outputIndex);
9662
+ const fieldPath = ref.slice(outputIndex + ".output.".length);
9663
+ const output = getNodeOutput2(nodeId);
9664
+ if (!output) {
9665
+ return void 0;
9666
+ }
9667
+ return getNestedValue3(output, fieldPath);
9191
9668
  }
9192
- function findFailedListenersForSourceRun(yDoc, sourceBlockId, sourceRunId, listenerBlockIds) {
9193
- const failures = [];
9194
- for (const listenerBlockId of listenerBlockIds) {
9195
- const records = readRunRecords(yDoc, listenerBlockId);
9196
- for (const record of records) {
9197
- if (!record.error) continue;
9198
- if (record.triggeredBy?.sourceBlockId !== sourceBlockId) continue;
9199
- if (record.fromPendingInvocationId == null) continue;
9200
- const sourceRunIdField = record.sourceRunId;
9201
- if (sourceRunIdField && sourceRunIdField !== sourceRunId) continue;
9202
- failures.push({ listenerBlockId, record });
9669
+ function getNestedValue3(obj, path) {
9670
+ const parts = path.split(".");
9671
+ let current = obj;
9672
+ for (const part of parts) {
9673
+ if (current == null || typeof current !== "object") {
9674
+ return void 0;
9203
9675
  }
9676
+ current = current[part];
9204
9677
  }
9205
- return failures;
9206
- }
9207
- function replayFailedListenerRun(yDoc, failedRecord, listenerBlockId, originalPayload, originalRefSnapshots, assigneeDid) {
9208
- if (!failedRecord.triggeredBy) return false;
9209
- const replayId = `${failedRecord.runId}:replay-${Date.now().toString(36)}`;
9210
- const now = (/* @__PURE__ */ new Date()).toISOString();
9211
- const replay = {
9212
- id: replayId,
9213
- triggeringBlockId: failedRecord.triggeredBy.sourceBlockId,
9214
- sourceRunId: failedRecord.sourceRunId || failedRecord.runId,
9215
- eventName: failedRecord.triggeredBy.eventName,
9216
- eventIndex: 0,
9217
- payload: originalPayload,
9218
- refSnapshots: originalRefSnapshots,
9219
- assigneeDid,
9220
- emittedAt: now,
9221
- expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString()
9222
- };
9223
- return queuePendingInvocation(yDoc, listenerBlockId, replay);
9678
+ return current;
9224
9679
  }
9225
9680
 
9226
- // src/core/lib/flowEngine/reconcile.ts
9227
- var _reconcileRunning = false;
9228
- function reconcilePendingInvocations(editor) {
9229
- if (_reconcileRunning) return;
9230
- _reconcileRunning = true;
9231
- try {
9232
- _reconcilePendingInvocationsInner(editor);
9233
- } finally {
9234
- _reconcileRunning = false;
9235
- }
9236
- }
9237
- function _reconcilePendingInvocationsInner(editor) {
9238
- const yDoc = editor._yDoc;
9239
- if (!yDoc) return;
9240
- const blocks = editor.document || [];
9241
- if (blocks.length === 0) return;
9242
- const listenersBySource = /* @__PURE__ */ new Map();
9243
- const barrierListenersBySource = /* @__PURE__ */ new Map();
9244
- for (const block of blocks) {
9245
- const trigger = parseTrigger(block);
9246
- if (!trigger) continue;
9247
- if (trigger.type === "block.event") {
9248
- if (!trigger.sourceBlockId || !trigger.eventName) continue;
9249
- const key = `${trigger.sourceBlockId}::${trigger.eventName}`;
9250
- if (!listenersBySource.has(key)) listenersBySource.set(key, []);
9251
- listenersBySource.get(key).push({ block, trigger });
9252
- } else if (trigger.type === "block.event.all" && trigger.sources) {
9253
- for (const source of trigger.sources) {
9254
- if (!source.sourceBlockId || !source.eventName) continue;
9255
- const key = `${source.sourceBlockId}::${source.eventName}`;
9256
- if (!barrierListenersBySource.has(key)) barrierListenersBySource.set(key, []);
9257
- barrierListenersBySource.get(key).push({ block, trigger, source });
9258
- }
9259
- }
9681
+ // src/core/lib/flowEngine/versionManifest.ts
9682
+ var VERSION_MANIFEST = {
9683
+ "0.3": {
9684
+ version: "0.3",
9685
+ label: "Legacy",
9686
+ ucanRequired: false,
9687
+ delegationRootRequired: false,
9688
+ whitelistOnlyAllowed: true,
9689
+ unrestrictedAllowed: true,
9690
+ executionPath: "legacy",
9691
+ authorizationFn: "v1",
9692
+ allowedAuthModes: ["anyone", "actors", "capability"],
9693
+ ui: {
9694
+ showDelegationPanel: false,
9695
+ showWhitelistConfig: true,
9696
+ showAnyoneConfig: true,
9697
+ showMigrationBanner: true,
9698
+ requirePinForExecution: false
9699
+ },
9700
+ description: "Legacy version. UCAN optional, whitelist-only authorization accepted."
9701
+ },
9702
+ "1.0.0": {
9703
+ version: "1.0.0",
9704
+ label: "UCAN Required",
9705
+ /** TEMP Disablement - needs to be true TODO */
9706
+ ucanRequired: false,
9707
+ delegationRootRequired: true,
9708
+ whitelistOnlyAllowed: false,
9709
+ unrestrictedAllowed: false,
9710
+ executionPath: "invocation",
9711
+ authorizationFn: "v2",
9712
+ allowedAuthModes: ["capability"],
9713
+ ui: {
9714
+ showDelegationPanel: true,
9715
+ showWhitelistConfig: false,
9716
+ showAnyoneConfig: false,
9717
+ showMigrationBanner: false,
9718
+ requirePinForExecution: true
9719
+ },
9720
+ description: "UCAN-enforced. Every block execution requires a valid delegation chain."
9260
9721
  }
9261
- if (listenersBySource.size === 0 && barrierListenersBySource.size === 0) return;
9262
- const runtimeMap = editor._yRuntime;
9263
- const getNodeOutput2 = (nodeId) => {
9264
- if (!runtimeMap) return void 0;
9265
- const state = runtimeMap.get(nodeId);
9266
- if (!state || typeof state !== "object") return void 0;
9267
- const out = state.output;
9268
- return out && typeof out === "object" ? out : void 0;
9269
- };
9270
- for (const block of blocks) {
9271
- const sourceBlockId = block?.id;
9272
- if (!sourceBlockId) continue;
9273
- let hasAnyListener = false;
9274
- for (const key of listenersBySource.keys()) {
9275
- if (key.startsWith(`${sourceBlockId}::`)) {
9276
- hasAnyListener = true;
9277
- break;
9278
- }
9279
- }
9280
- for (const key of barrierListenersBySource.keys()) {
9281
- if (key.startsWith(`${sourceBlockId}::`)) {
9282
- hasAnyListener = true;
9283
- break;
9284
- }
9285
- }
9286
- if (!hasAnyListener) continue;
9287
- const records = readRunRecords(yDoc, sourceBlockId);
9288
- for (const record of records) {
9289
- processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput2, runtimeMap);
9290
- processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput2, runtimeMap);
9291
- }
9722
+ };
9723
+ var LATEST_VERSION = "1.0.0";
9724
+ function getVersionPolicy(version) {
9725
+ const policy = VERSION_MANIFEST[version];
9726
+ if (!policy) {
9727
+ return VERSION_MANIFEST[LATEST_VERSION];
9292
9728
  }
9729
+ return policy;
9293
9730
  }
9294
- function processRunRecord(yDoc, sourceBlockId, record, listenersBySource, getNodeOutput2, runtimeMap) {
9295
- if (!Array.isArray(record.events)) return;
9296
- record.events.forEach((event, eventIndex) => {
9297
- if (!event?.name) return;
9298
- const key = `${sourceBlockId}::${event.name}`;
9299
- const listeners = listenersBySource.get(key);
9300
- if (!listeners || listeners.length === 0) return;
9301
- for (const { block: listenerBlock } of listeners) {
9302
- const listenerBlockId = listenerBlock.id;
9303
- if (!listenerBlockId) continue;
9304
- const id = computePendingInvocationId({
9305
- sourceBlockId,
9306
- sourceRunId: record.runId,
9307
- listenerBlockId,
9308
- eventName: event.name,
9309
- eventIndex
9310
- });
9311
- const assigneeDid = resolveAssignee(listenerBlock) || "unassigned";
9312
- const inputs = parseInputs(listenerBlock);
9313
- const refSnapshots = snapshotInputRefs(inputs, getNodeOutput2);
9314
- const expiresAt = computeExpiry(listenerBlock, record.completedAt);
9315
- const invocation = {
9316
- id,
9317
- triggeringBlockId: sourceBlockId,
9318
- sourceRunId: record.runId,
9319
- eventName: event.name,
9320
- eventIndex,
9321
- payload: event.payload,
9322
- refSnapshots,
9323
- assigneeDid,
9324
- emittedAt: record.completedAt,
9325
- expiresAt
9326
- };
9327
- queuePendingInvocation(yDoc, listenerBlockId, invocation);
9328
- if (runtimeMap) {
9329
- const prev = runtimeMap.get(listenerBlockId) || {};
9330
- runtimeMap.set(listenerBlockId, {
9331
- ...prev,
9332
- pendingPayload: { ...event.payload, ...refSnapshots }
9333
- });
9334
- }
9335
- }
9336
- });
9337
- }
9338
- function processBarrierRunRecord(yDoc, sourceBlockId, record, barrierListenersBySource, getNodeOutput2, runtimeMap) {
9339
- if (!Array.isArray(record.events)) return;
9340
- for (const event of record.events) {
9341
- if (!event?.name) continue;
9342
- const key = `${sourceBlockId}::${event.name}`;
9343
- const listeners = barrierListenersBySource.get(key);
9344
- if (!listeners || listeners.length === 0) continue;
9345
- for (const { block: listenerBlock, trigger, source } of listeners) {
9346
- const listenerBlockId = listenerBlock.id;
9347
- if (!listenerBlockId) continue;
9348
- const entry = {
9349
- sourceBlockId,
9350
- eventName: event.name,
9351
- alias: source.alias,
9352
- runId: record.runId,
9353
- payload: event.payload || {},
9354
- emittedAt: record.completedAt
9355
- };
9356
- recordBarrierEvent(yDoc, listenerBlockId, entry);
9357
- if (runtimeMap) {
9358
- const prev = runtimeMap.get(listenerBlockId) || {};
9359
- const existing = prev.pendingPayload || {};
9360
- runtimeMap.set(listenerBlockId, {
9361
- ...prev,
9362
- pendingPayload: { ...existing, ...event.payload || {} }
9363
- });
9364
- }
9365
- const allSources = trigger.sources || [];
9366
- const currentState = readBarrierState(yDoc, listenerBlockId);
9367
- const requiredSources = allSources.filter((s) => s.optional !== true);
9368
- const gatingSources = requiredSources.length > 0 ? requiredSources : allSources;
9369
- const allFired = gatingSources.length > 0 && gatingSources.every((s) => currentState.some((e) => e.sourceBlockId === s.sourceBlockId && e.eventName === s.eventName));
9370
- if (!allFired) continue;
9371
- const assigneeDid = resolveAssignee(listenerBlock) || "unassigned";
9372
- const id = computeBarrierInvocationId(currentState, listenerBlockId);
9373
- const mergedPayload = mergeBarrierPayloads(currentState);
9374
- const inputs = parseInputs(listenerBlock);
9375
- const refSnapshots = snapshotInputRefs(inputs, getNodeOutput2);
9376
- const expiresAt = computeExpiry(listenerBlock, record.completedAt);
9377
- const invocation = {
9378
- id,
9379
- triggeringBlockId: sourceBlockId,
9380
- sourceRunId: record.runId,
9381
- eventName: `barrier:${allSources.map((s) => s.alias).join("+")}`,
9382
- eventIndex: 0,
9383
- payload: mergedPayload,
9384
- refSnapshots,
9385
- assigneeDid,
9386
- emittedAt: record.completedAt,
9387
- expiresAt
9388
- };
9389
- queuePendingInvocation(yDoc, listenerBlockId, invocation);
9390
- clearBarrierState(yDoc, listenerBlockId);
9391
- if (runtimeMap) {
9392
- const prev = runtimeMap.get(listenerBlockId) || {};
9393
- runtimeMap.set(listenerBlockId, {
9394
- ...prev,
9395
- pendingPayload: { ...mergedPayload, ...refSnapshots }
9396
- });
9397
- }
9398
- }
9731
+
9732
+ // src/core/lib/flowEngine/authorization.ts
9733
+ var isAuthorized = async (blockId, actorDid, ucanService, flowUri, schemaVersion) => {
9734
+ const policy = schemaVersion ? getVersionPolicy(schemaVersion) : null;
9735
+ if (policy && !policy.ucanRequired) {
9736
+ return { authorized: true };
9399
9737
  }
9400
- }
9401
- function parseTrigger(block) {
9402
- const raw = block?.props?.trigger;
9403
- if (!raw || typeof raw !== "string") return null;
9404
- try {
9405
- const parsed = JSON.parse(raw);
9406
- if (parsed && typeof parsed === "object" && typeof parsed.type === "string") {
9407
- return parsed;
9738
+ if (!ucanService) {
9739
+ if (!policy) {
9740
+ return { authorized: true };
9408
9741
  }
9409
- } catch {
9742
+ return {
9743
+ authorized: false,
9744
+ reason: "UCAN service is not configured. This flow version requires UCAN authorization."
9745
+ };
9410
9746
  }
9411
- return null;
9412
- }
9413
- function parseInputs(block) {
9414
- const raw = block?.props?.inputs;
9415
- if (!raw || typeof raw !== "string") return {};
9416
- try {
9417
- const parsed = JSON.parse(raw);
9418
- if (parsed && typeof parsed === "object") return parsed;
9419
- } catch {
9747
+ const capability = {
9748
+ can: "flow/block/execute",
9749
+ with: `${flowUri}:${blockId}`
9750
+ };
9751
+ const result = await ucanService.validateDelegationChain(actorDid, capability);
9752
+ if (!result.valid) {
9753
+ return {
9754
+ authorized: false,
9755
+ reason: result.error || "No valid capability chain found"
9756
+ };
9420
9757
  }
9421
- return {};
9422
- }
9423
- function resolveAssignee(block) {
9424
- const raw = block?.props?.assignment;
9425
- if (!raw) return void 0;
9426
- try {
9427
- const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
9428
- const did = parsed?.assignedActor?.did;
9429
- return typeof did === "string" && did.length > 0 ? did : void 0;
9430
- } catch {
9431
- return void 0;
9758
+ const proofCids = result.proofChain?.map((d) => d.cid) || [];
9759
+ return {
9760
+ authorized: true,
9761
+ capabilityId: proofCids[0],
9762
+ proofCids
9763
+ };
9764
+ };
9765
+
9766
+ // src/core/lib/flowEngine/executor.ts
9767
+ var updateRuntimeAfterSuccess = (node, actorDid, runtime, actionResult, invocationCid, now) => {
9768
+ const updates = {
9769
+ submittedByDid: actionResult.submittedByDid || actorDid,
9770
+ evaluationStatus: actionResult.evaluationStatus || "pending",
9771
+ executionTimestamp: now ? now() : Date.now(),
9772
+ lastInvocationCid: invocationCid
9773
+ };
9774
+ if (actionResult.claimId) {
9775
+ updates.claimId = actionResult.claimId;
9432
9776
  }
9433
- }
9434
- function computeExpiry(block, emittedAt) {
9435
- const ttlAbsolute = block?.props?.ttlAbsoluteDueDate;
9436
- if (typeof ttlAbsolute === "string" && ttlAbsolute) {
9437
- return ttlAbsolute;
9777
+ runtime.update(node.id, updates);
9778
+ };
9779
+ var executeNode = async ({ node, actorDid, actorType, entityRoomId, context, action, pin }) => {
9780
+ const { runtime, ucanService, invocationStore, flowUri, flowId, schemaVersion, now } = context;
9781
+ const auth = await isAuthorized(node.id, actorDid, ucanService, flowUri, schemaVersion);
9782
+ if (!auth.authorized) {
9783
+ return { success: false, stage: "authorization", error: auth.reason };
9438
9784
  }
9439
- const ttlFromEnablement = block?.props?.ttlFromEnablement;
9440
- if (typeof ttlFromEnablement === "string" && ttlFromEnablement) {
9441
- const ms = parseIsoDurationToMs(ttlFromEnablement);
9442
- if (ms != null) {
9443
- return new Date(new Date(emittedAt).getTime() + ms).toISOString();
9785
+ if (node.linkedClaim && !node.linkedClaim.collectionId) {
9786
+ return { success: false, stage: "claim", error: "Linked claim collection is required but missing." };
9787
+ }
9788
+ let invocationCid;
9789
+ let invocationData;
9790
+ if (ucanService && auth.proofCids && auth.proofCids.length > 0) {
9791
+ const capability = {
9792
+ can: "flow/block/execute",
9793
+ with: `${flowUri}:${node.id}`
9794
+ };
9795
+ try {
9796
+ const invocationResult = await ucanService.createAndValidateInvocation(
9797
+ {
9798
+ invokerDid: actorDid,
9799
+ invokerType: actorType,
9800
+ entityRoomId,
9801
+ capability,
9802
+ proofs: auth.proofCids,
9803
+ pin
9804
+ },
9805
+ flowId,
9806
+ node.id
9807
+ );
9808
+ if (!invocationResult.valid) {
9809
+ return {
9810
+ success: false,
9811
+ stage: "authorization",
9812
+ error: `Invocation validation failed: ${invocationResult.error}`
9813
+ };
9814
+ }
9815
+ invocationCid = invocationResult.cid;
9816
+ invocationData = invocationResult.invocation;
9817
+ } catch (error) {
9818
+ const message = error instanceof Error ? error.message : "Failed to create invocation";
9819
+ return { success: false, stage: "authorization", error: message };
9444
9820
  }
9445
9821
  }
9446
- return new Date(new Date(emittedAt).getTime() + 7 * 24 * 60 * 60 * 1e3).toISOString();
9447
- }
9448
- function parseIsoDurationToMs(duration) {
9449
- const match = /^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/.exec(duration);
9450
- if (!match) return null;
9451
- const [, d, h, m, s] = match;
9452
- let ms = 0;
9453
- if (d) ms += parseInt(d, 10) * 24 * 60 * 60 * 1e3;
9454
- if (h) ms += parseInt(h, 10) * 60 * 60 * 1e3;
9455
- if (m) ms += parseInt(m, 10) * 60 * 1e3;
9456
- if (s) ms += parseInt(s, 10) * 1e3;
9457
- return ms;
9458
- }
9459
- function getActionForBlock(block) {
9460
- const actionType = block?.props?.actionType;
9461
- if (typeof actionType !== "string") return void 0;
9462
- return getAction(actionType);
9463
- }
9822
+ try {
9823
+ const result = await action();
9824
+ if (node.linkedClaim && !result.claimId) {
9825
+ if (invocationStore && invocationCid && invocationData) {
9826
+ const storedInvocation = {
9827
+ cid: invocationCid,
9828
+ invocation: invocationData,
9829
+ invokerDid: actorDid,
9830
+ capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
9831
+ executedAt: now ? now() : Date.now(),
9832
+ flowId,
9833
+ blockId: node.id,
9834
+ result: "failure",
9835
+ error: "Execution did not return a claimId for linked claim requirement.",
9836
+ proofCids: auth.proofCids || []
9837
+ };
9838
+ invocationStore.add(storedInvocation);
9839
+ }
9840
+ return {
9841
+ success: false,
9842
+ stage: "claim",
9843
+ error: "Execution did not return a claimId for linked claim requirement.",
9844
+ invocationCid
9845
+ };
9846
+ }
9847
+ if (invocationStore && invocationCid && invocationData) {
9848
+ const storedInvocation = {
9849
+ cid: invocationCid,
9850
+ invocation: invocationData,
9851
+ invokerDid: actorDid,
9852
+ capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
9853
+ executedAt: now ? now() : Date.now(),
9854
+ flowId,
9855
+ blockId: node.id,
9856
+ result: "success",
9857
+ proofCids: auth.proofCids || [],
9858
+ claimId: result.claimId
9859
+ };
9860
+ invocationStore.add(storedInvocation);
9861
+ }
9862
+ updateRuntimeAfterSuccess(node, actorDid, runtime, result, invocationCid || auth.capabilityId, now);
9863
+ return {
9864
+ success: true,
9865
+ stage: "complete",
9866
+ result,
9867
+ capabilityId: auth.capabilityId,
9868
+ invocationCid
9869
+ };
9870
+ } catch (error) {
9871
+ const message = error instanceof Error ? error.message : "Execution failed";
9872
+ if (invocationStore && invocationCid && invocationData) {
9873
+ const storedInvocation = {
9874
+ cid: invocationCid,
9875
+ invocation: invocationData,
9876
+ invokerDid: actorDid,
9877
+ capability: { can: "flow/block/execute", with: `${flowUri}:${node.id}` },
9878
+ executedAt: now ? now() : Date.now(),
9879
+ flowId,
9880
+ blockId: node.id,
9881
+ result: "failure",
9882
+ error: message,
9883
+ proofCids: auth.proofCids || []
9884
+ };
9885
+ invocationStore.add(storedInvocation);
9886
+ }
9887
+ return { success: false, stage: "action", error: message, invocationCid };
9888
+ }
9889
+ };
9464
9890
 
9465
9891
  // src/core/lib/ucanDelegationStore.ts
9466
9892
  var ROOT_DELEGATION_KEY = "__root__";
@@ -9794,26 +10220,6 @@ var createMemoryInvocationStore = () => {
9794
10220
  };
9795
10221
  };
9796
10222
 
9797
- // src/core/lib/flowEngine/emitEvents.ts
9798
- function writeRunRecordAndReconcile(editor, blockId, output, events, actorDid, detailsPatch = {}) {
9799
- const yDoc = editor._yDoc;
9800
- if (!yDoc) return;
9801
- if (events.length === 0) return;
9802
- const runId = `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
9803
- const now = (/* @__PURE__ */ new Date()).toISOString();
9804
- const details = {
9805
- runId,
9806
- output,
9807
- events,
9808
- startedAt: now,
9809
- completedAt: now,
9810
- actorDid,
9811
- ...detailsPatch
9812
- };
9813
- appendRunRecord(yDoc, blockId, details, actorDid);
9814
- reconcilePendingInvocations(editor);
9815
- }
9816
-
9817
10223
  // src/core/lib/flowEngine/actionExecutor.ts
9818
10224
  import * as Y3 from "yjs";
9819
10225
 
@@ -10725,6 +11131,9 @@ function compileBaseUcanFlow(plan, registry) {
10725
11131
  if (cap.condition) {
10726
11132
  props.conditions = compileCondition(cap.condition, blockIndex);
10727
11133
  }
11134
+ if (cap.trigger) {
11135
+ props.trigger = compileTrigger(cap.trigger, blockIndex);
11136
+ }
10728
11137
  props.triggerMode = cap.trigger?.type || "manual";
10729
11138
  blocks.push({
10730
11139
  id: blockId,
@@ -10795,6 +11204,18 @@ function compileCondition(condition, blockIndex) {
10795
11204
  };
10796
11205
  return JSON.stringify(conditionConfig);
10797
11206
  }
11207
+ function compileTrigger(trigger, blockIndex) {
11208
+ if (trigger.type === "block.event" && trigger.sourceBlockId) {
11209
+ return JSON.stringify({ ...trigger, sourceBlockId: blockIndex[trigger.sourceBlockId.trim()] });
11210
+ }
11211
+ if (trigger.type === "block.event.all" && trigger.sources) {
11212
+ return JSON.stringify({
11213
+ ...trigger,
11214
+ sources: trigger.sources.map((src) => ({ ...src, sourceBlockId: blockIndex[src.sourceBlockId.trim()] }))
11215
+ });
11216
+ }
11217
+ return JSON.stringify(trigger);
11218
+ }
10798
11219
  function collectOutputRefs(value) {
10799
11220
  const refs = [];
10800
11221
  walk(value);
@@ -11150,9 +11571,13 @@ function mergeCompiledFlows(existing, incoming, strategy) {
11150
11571
 
11151
11572
  // src/core/lib/flowCompiler/decompile.ts
11152
11573
  function decompileToBaseUcanFlow(compiled) {
11574
+ const nodeIdByBlockId = {};
11575
+ for (const [nodeId, blockId] of Object.entries(compiled.blockIndex)) {
11576
+ nodeIdByBlockId[blockId] = nodeId;
11577
+ }
11153
11578
  const capabilities = compiled.order.map((nodeId) => {
11154
11579
  const node = compiled.nodes[nodeId];
11155
- return nodeToCapability(node);
11580
+ return nodeToCapability(node, nodeIdByBlockId);
11156
11581
  });
11157
11582
  return {
11158
11583
  kind: "qi.flow.base-ucan",
@@ -11167,7 +11592,7 @@ function decompileToBaseUcanFlow(compiled) {
11167
11592
  capabilities
11168
11593
  };
11169
11594
  }
11170
- function nodeToCapability(node) {
11595
+ function nodeToCapability(node, nodeIdByBlockId) {
11171
11596
  const cap = {
11172
11597
  id: node.id,
11173
11598
  can: node.can,
@@ -11202,7 +11627,7 @@ function nodeToCapability(node) {
11202
11627
  try {
11203
11628
  const trigger = JSON.parse(node.props.trigger);
11204
11629
  if (trigger && typeof trigger === "object" && typeof trigger.type === "string") {
11205
- cap.trigger = trigger;
11630
+ cap.trigger = triggerToNodeIds(trigger, nodeIdByBlockId);
11206
11631
  }
11207
11632
  } catch {
11208
11633
  }
@@ -11218,6 +11643,18 @@ function nodeToCapability(node) {
11218
11643
  }
11219
11644
  return cap;
11220
11645
  }
11646
+ function triggerToNodeIds(trigger, nodeIdByBlockId) {
11647
+ if (trigger.type === "block.event" && trigger.sourceBlockId) {
11648
+ return { ...trigger, sourceBlockId: nodeIdByBlockId[trigger.sourceBlockId] ?? trigger.sourceBlockId };
11649
+ }
11650
+ if (trigger.type === "block.event.all" && trigger.sources) {
11651
+ return {
11652
+ ...trigger,
11653
+ sources: trigger.sources.map((src) => ({ ...src, sourceBlockId: nodeIdByBlockId[src.sourceBlockId] ?? src.sourceBlockId }))
11654
+ };
11655
+ }
11656
+ return trigger;
11657
+ }
11221
11658
 
11222
11659
  // src/core/lib/flowCompiler/setup.ts
11223
11660
  import * as Y7 from "yjs";
@@ -12801,6 +13238,8 @@ var FlowAgentService = class {
12801
13238
  };
12802
13239
 
12803
13240
  export {
13241
+ STEP_COMPLETED_EVENT_NAME,
13242
+ STEP_COMPLETED_EVENT,
12804
13243
  resolveActionType,
12805
13244
  registerAction,
12806
13245
  getAction,
@@ -12851,6 +13290,14 @@ export {
12851
13290
  didToMatrixUserId,
12852
13291
  findOrCreateDMRoom,
12853
13292
  sendDirectMessage,
13293
+ parseDelegatedToolInputs,
13294
+ serializeDelegatedToolInputs,
13295
+ fieldValues,
13296
+ missingRequired,
13297
+ GMAIL_SEND_SCHEMA,
13298
+ OUTLOOK_SEND_SCHEMA,
13299
+ SLACK_SEND_SCHEMA,
13300
+ GOOGLECALENDAR_CREATE_SCHEMA,
12854
13301
  parseCalendarEventCreateInputs,
12855
13302
  serializeCalendarEventCreateInputs,
12856
13303
  parseAttendeesField,
@@ -12860,15 +13307,7 @@ export {
12860
13307
  formatCoin2 as formatCoin,
12861
13308
  formatCoinAmount,
12862
13309
  createUcanService,
12863
- buildAuthzFromProps,
12864
- buildFlowNodeFromBlock,
12865
- createRuntimeStateManager,
12866
- clearRuntimeForTemplateClone,
12867
13310
  isRuntimeRef,
12868
- resolveRuntimeRefs,
12869
- LATEST_VERSION,
12870
- isAuthorized,
12871
- executeNode,
12872
13311
  RUN_RECORD_AUDIT_TYPE,
12873
13312
  computePendingInvocationId,
12874
13313
  snapshotInputRefs,
@@ -12883,6 +13322,15 @@ export {
12883
13322
  replayFailedListenerRun,
12884
13323
  reconcilePendingInvocations,
12885
13324
  getActionForBlock,
13325
+ writeRunRecordAndReconcile,
13326
+ buildAuthzFromProps,
13327
+ buildFlowNodeFromBlock,
13328
+ createRuntimeStateManager,
13329
+ clearRuntimeForTemplateClone,
13330
+ resolveRuntimeRefs,
13331
+ LATEST_VERSION,
13332
+ isAuthorized,
13333
+ executeNode,
12886
13334
  reconcileActionReadBack,
12887
13335
  buildActionRunInputs,
12888
13336
  executeActionBlock,
@@ -12890,7 +13338,6 @@ export {
12890
13338
  createMemoryUcanDelegationStore,
12891
13339
  createInvocationStore,
12892
13340
  createMemoryInvocationStore,
12893
- writeRunRecordAndReconcile,
12894
13341
  compileBlockProps,
12895
13342
  COMPILED_BLOCK_TYPE,
12896
13343
  toEvaluatorOperator,
@@ -12942,4 +13389,4 @@ export {
12942
13389
  executeQueuedFlowAgentCoreCommands,
12943
13390
  FlowAgentService
12944
13391
  };
12945
- //# sourceMappingURL=chunk-7MGFSE63.js.map
13392
+ //# sourceMappingURL=chunk-KNMPGX5G.js.map