@ixo/editor 6.6.0 → 6.8.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.
@@ -54,10 +54,6 @@ function getAction(type) {
54
54
  function getAllActions() {
55
55
  return Array.from(actions.values());
56
56
  }
57
- function isRepeatableAction(type) {
58
- if (!type) return false;
59
- return getAction(type)?.cardinality === "many";
60
- }
61
57
  function getAliasEntries() {
62
58
  return Array.from(aliases.entries());
63
59
  }
@@ -238,6 +234,33 @@ function getAllCanMappings() {
238
234
  return Object.entries(CAN_TO_TYPE).map(([can, type]) => ({ can, type }));
239
235
  }
240
236
 
237
+ // src/core/lib/actionRegistry/serviceCan.ts
238
+ var SERVICE_VERBS = ["report", "format", "advise"];
239
+ function isActionCanShape(can) {
240
+ if (typeof can !== "string" || can.length === 0) return false;
241
+ const segments = can.split("/");
242
+ return segments.length >= 2 && segments.every((segment) => segment.length > 0) && !SERVICE_VERBS.includes(segments[segments.length - 1]);
243
+ }
244
+ function serviceCan(can, verb) {
245
+ if (!SERVICE_VERBS.includes(verb)) {
246
+ throw new Error(`serviceCan: unknown service verb '${String(verb)}' \u2014 expected one of: ${SERVICE_VERBS.join(", ")}`);
247
+ }
248
+ if (!isActionCanShape(can)) {
249
+ throw new Error(`serviceCan: invalid action can '${can}' \u2014 expected a non-empty '<namespace>/<verb>' shaped string that is not itself a service can, e.g. 'claim/evaluate'`);
250
+ }
251
+ return `${can}/${verb}`;
252
+ }
253
+ function parseServiceCan(value) {
254
+ if (typeof value !== "string" || value.length === 0) return null;
255
+ const lastSlash = value.lastIndexOf("/");
256
+ if (lastSlash <= 0) return null;
257
+ const verb = value.slice(lastSlash + 1);
258
+ if (!SERVICE_VERBS.includes(verb)) return null;
259
+ const actionCan = value.slice(0, lastSlash);
260
+ if (!isActionCanShape(actionCan)) return null;
261
+ return { actionCan, verb };
262
+ }
263
+
241
264
  // src/core/lib/actionRegistry/adapters.ts
242
265
  var SERVICE_GROUP_REQUIRED_HANDLERS = {
243
266
  bid: ["submitBid", "approveBid", "rejectBid", "approveServiceAgentApplication", "approveEvaluatorApplication"],
@@ -416,6 +439,127 @@ function buildServicesFromHandlers(handlers) {
416
439
  };
417
440
  }
418
441
 
442
+ // src/core/lib/xeroWorkItems.ts
443
+ var XERO_WORK_ITEMS_MAP_NAME = "xeroWorkItems";
444
+ function getXeroWorkItemsMap(doc) {
445
+ return doc.getMap(XERO_WORK_ITEMS_MAP_NAME);
446
+ }
447
+ function getEditorXeroWorkItemsMap(editor) {
448
+ return editor?._yXeroWorkItems || null;
449
+ }
450
+ function readMapItems(map) {
451
+ if (!map) return [];
452
+ const items = [];
453
+ map.forEach((value) => {
454
+ if (value && typeof value === "object" && "id" in value) {
455
+ items.push({ ...value });
456
+ }
457
+ });
458
+ return items;
459
+ }
460
+ function mergeItem(existing, next) {
461
+ if (existing.status === "completed") {
462
+ return existing;
463
+ }
464
+ return {
465
+ ...existing,
466
+ ...next,
467
+ id: existing.id,
468
+ attempts: existing.attempts ?? [],
469
+ originalPayload: existing.originalPayload,
470
+ submittedPayload: existing.submittedPayload,
471
+ result: existing.result,
472
+ completedAt: existing.completedAt,
473
+ completedByDid: existing.completedByDid
474
+ };
475
+ }
476
+ function buildXeroInvoiceWorkKey(params) {
477
+ return ["xero", "invoice.create", params.flowId, params.evaluationBlockId, params.claimId].join(":");
478
+ }
479
+ function buildXeroPaymentWorkKey(params) {
480
+ return ["xero", "payment.create", params.flowId, params.evaluationBlockId, params.claimId, params.invoiceWorkItemId].join(":");
481
+ }
482
+ function findXeroWorkItems(map, filter = {}) {
483
+ return readMapItems(map).filter((item) => {
484
+ if (filter.kind && item.kind !== filter.kind) return false;
485
+ if (filter.assignedBlockId && item.assignedBlockId !== filter.assignedBlockId) return false;
486
+ if (filter.statuses && !filter.statuses.includes(item.status)) return false;
487
+ return true;
488
+ });
489
+ }
490
+ function upsertXeroWorkItem(map, next) {
491
+ if (!map) {
492
+ return next;
493
+ }
494
+ let existing;
495
+ map.forEach((item) => {
496
+ if (item?.idempotencyKey === next.idempotencyKey) existing = item;
497
+ });
498
+ const stored = existing ? mergeItem(existing, next) : next;
499
+ map.set(stored.id, stored);
500
+ return stored;
501
+ }
502
+ function updateXeroWorkReviewPayload(map, itemId, reviewPayload) {
503
+ if (!map) return null;
504
+ const item = map.get(itemId);
505
+ if (!item) return null;
506
+ if (item.status === "completed") return item;
507
+ const updatedItem = { ...item, reviewPayload };
508
+ map.set(itemId, updatedItem);
509
+ return updatedItem;
510
+ }
511
+ function markXeroWorkFailed(map, itemId, params) {
512
+ if (!map) return null;
513
+ const item = map.get(itemId);
514
+ if (!item) return null;
515
+ if (item.status === "completed") return item;
516
+ const at = params.at ?? Date.now();
517
+ const updatedItem = {
518
+ ...item,
519
+ status: "failed",
520
+ reviewPayload: params.payload,
521
+ error: { message: params.error, at },
522
+ attempts: [...item.attempts ?? [], { at, byDid: params.byDid, payload: params.payload, error: params.error }]
523
+ };
524
+ map.set(itemId, updatedItem);
525
+ return updatedItem;
526
+ }
527
+ function markXeroWorkCompleted(map, itemId, params) {
528
+ if (!map) return null;
529
+ const item = map.get(itemId);
530
+ if (!item) return null;
531
+ if (item.status === "completed") return item;
532
+ const at = params.at ?? Date.now();
533
+ const updatedItem = {
534
+ ...item,
535
+ status: "completed",
536
+ reviewPayload: params.payload,
537
+ submittedPayload: params.payload,
538
+ result: params.result,
539
+ error: void 0,
540
+ completedAt: at,
541
+ completedByDid: params.byDid,
542
+ attempts: [...item.attempts ?? [], { at, byDid: params.byDid, payload: params.payload, result: params.result }]
543
+ };
544
+ map.set(itemId, updatedItem);
545
+ return updatedItem;
546
+ }
547
+ function findXeroWorkItemsForEditor(editor, filter) {
548
+ return findXeroWorkItems(getEditorXeroWorkItemsMap(editor), filter);
549
+ }
550
+ function upsertXeroWorkItemForEditor(editor, next) {
551
+ return upsertXeroWorkItem(getEditorXeroWorkItemsMap(editor), next);
552
+ }
553
+ function updateXeroWorkReviewPayloadForEditor(editor, itemId, reviewPayload) {
554
+ return updateXeroWorkReviewPayload(getEditorXeroWorkItemsMap(editor), itemId, reviewPayload);
555
+ }
556
+ function markXeroWorkFailedForEditor(editor, itemId, params) {
557
+ return markXeroWorkFailed(getEditorXeroWorkItemsMap(editor), itemId, params);
558
+ }
559
+ function markXeroWorkCompletedForEditor(editor, itemId, params) {
560
+ return markXeroWorkCompleted(getEditorXeroWorkItemsMap(editor), itemId, params);
561
+ }
562
+
419
563
  // src/core/lib/matrixDm.ts
420
564
  function getHomeserver(matrixClient) {
421
565
  const userId = matrixClient.getUserId();
@@ -2916,606 +3060,89 @@ registerAction({
2916
3060
  throw new Error("surveyAnswers must be valid JSON");
2917
3061
  }
2918
3062
  }
2919
- if (!surveyAnswers || typeof surveyAnswers !== "object" || Array.isArray(surveyAnswers)) {
2920
- throw new Error("surveyAnswers must be an object");
2921
- }
2922
- let pin = String(inputs.pin || "").trim();
2923
- if (!pin) {
2924
- pin = await service.requestPin({
2925
- title: "Verify Identity",
2926
- description: "Enter your PIN to submit the claim",
2927
- submitText: "Verify"
2928
- });
2929
- }
2930
- if (!pin) {
2931
- throw new Error("PIN is required to submit claim");
2932
- }
2933
- if (isGroupExecution(inputs)) {
2934
- const { coreAddress, title, description } = requireGroupExecutionParams(inputs);
2935
- const handlers = ctx.handlers;
2936
- if (typeof handlers?.prepareGroupClaimSubmission !== "function") {
2937
- throw new Error("Acting as a POD is not available: the host does not implement prepareGroupClaimSubmission");
2938
- }
2939
- const prepared = await handlers.prepareGroupClaimSubmission({
2940
- surveyData: surveyAnswers,
2941
- deedDid,
2942
- entityDid: deedDid,
2943
- collectionId,
2944
- adminAddress,
2945
- pin,
2946
- groupAddress: coreAddress
2947
- });
2948
- const claimId2 = String(prepared?.claimId || "").trim();
2949
- if (!claimId2) throw new Error("prepareGroupClaimSubmission returned no claim identifier");
2950
- return proposeGroupExecution(ctx, {
2951
- coreAddress,
2952
- title,
2953
- description,
2954
- msgs: prepared.msgs || [],
2955
- expectedOutput: {
2956
- claimId: claimId2,
2957
- transactionHash: "",
2958
- collectionId,
2959
- deedDid,
2960
- submittedByDid: coreAddress,
2961
- submittedAt: (/* @__PURE__ */ new Date()).toISOString(),
2962
- surveyAnswers
2963
- },
2964
- completionEvent: "submitted"
2965
- });
2966
- }
2967
- const result = await service.submitClaim({
2968
- surveyData: surveyAnswers,
2969
- deedDid,
2970
- entityDid: deedDid,
2971
- collectionId,
2972
- adminAddress,
2973
- pin
2974
- });
2975
- const claimId = String(result?.claimId || result?.id || "");
2976
- if (!claimId) {
2977
- throw new Error("submitClaim returned no claim identifier");
2978
- }
2979
- const transactionHash = String(result?.transactionHash || "");
2980
- const submittedByDid = ctx.actorDid || "";
2981
- const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
2982
- const output = {
2983
- claimId,
2984
- transactionHash,
2985
- collectionId,
2986
- deedDid,
2987
- submittedByDid,
2988
- submittedAt,
2989
- surveyAnswers
2990
- };
2991
- return {
2992
- output,
2993
- events: [
2994
- {
2995
- name: "submitted",
2996
- payload: output
2997
- }
2998
- ]
2999
- };
3000
- }
3001
- });
3002
-
3003
- // src/core/lib/xeroWorkItems.ts
3004
- function getXeroWorkItemsMap(editor) {
3005
- return editor?._yXeroWorkItems || null;
3006
- }
3007
- function readMapItems(map) {
3008
- if (!map) return [];
3009
- const items = [];
3010
- map.forEach((value) => {
3011
- if (value && typeof value === "object" && "id" in value) {
3012
- items.push({ ...value });
3013
- }
3014
- });
3015
- return items;
3016
- }
3017
- function mergeItem(existing, next) {
3018
- if (existing.status === "completed") {
3019
- return existing;
3020
- }
3021
- return {
3022
- ...existing,
3023
- ...next,
3024
- id: existing.id,
3025
- attempts: existing.attempts ?? [],
3026
- originalPayload: existing.originalPayload,
3027
- submittedPayload: existing.submittedPayload,
3028
- result: existing.result,
3029
- completedAt: existing.completedAt,
3030
- completedByDid: existing.completedByDid
3031
- };
3032
- }
3033
- function buildXeroInvoiceWorkKey(params) {
3034
- return ["xero", "invoice.create", params.flowId, params.evaluationBlockId, params.claimId, params.invoiceBlockId].join(":");
3035
- }
3036
- function buildXeroPaymentWorkKey(params) {
3037
- return ["xero", "payment.create", params.flowId, params.evaluationBlockId, params.claimId, params.paymentBlockId, params.invoiceWorkItemId].join(":");
3038
- }
3039
- function findXeroWorkItemsForEditor(editor, filter) {
3040
- return readMapItems(getXeroWorkItemsMap(editor)).filter((item) => {
3041
- if (filter.kind && item.kind !== filter.kind) return false;
3042
- if (filter.assignedBlockId && item.assignedBlockId !== filter.assignedBlockId) return false;
3043
- if (filter.statuses && !filter.statuses.includes(item.status)) return false;
3044
- return true;
3045
- });
3046
- }
3047
- function upsertXeroWorkItemForEditor(editor, next) {
3048
- const map = getXeroWorkItemsMap(editor);
3049
- if (!map) {
3050
- return next;
3051
- }
3052
- let existing;
3053
- map.forEach((item) => {
3054
- if (item?.idempotencyKey === next.idempotencyKey) existing = item;
3055
- });
3056
- const stored = existing ? mergeItem(existing, next) : next;
3057
- map.set(stored.id, stored);
3058
- return stored;
3059
- }
3060
- function updateXeroWorkReviewPayloadForEditor(editor, itemId, reviewPayload) {
3061
- const map = getXeroWorkItemsMap(editor);
3062
- if (!map) return null;
3063
- const item = map.get(itemId);
3064
- if (!item) return null;
3065
- const updatedItem = { ...item, reviewPayload };
3066
- map.set(itemId, updatedItem);
3067
- return updatedItem;
3068
- }
3069
- function markXeroWorkFailedForEditor(editor, itemId, params) {
3070
- const map = getXeroWorkItemsMap(editor);
3071
- if (!map) return null;
3072
- const item = map.get(itemId);
3073
- if (!item) return null;
3074
- const at = params.at ?? Date.now();
3075
- const updatedItem = {
3076
- ...item,
3077
- status: "failed",
3078
- reviewPayload: params.payload,
3079
- error: { message: params.error, at },
3080
- attempts: [...item.attempts ?? [], { at, byDid: params.byDid, payload: params.payload, error: params.error }]
3081
- };
3082
- map.set(itemId, updatedItem);
3083
- return updatedItem;
3084
- }
3085
- function markXeroWorkCompletedForEditor(editor, itemId, params) {
3086
- const map = getXeroWorkItemsMap(editor);
3087
- if (!map) return null;
3088
- const item = map.get(itemId);
3089
- if (!item) return null;
3090
- const at = params.at ?? Date.now();
3091
- const updatedItem = {
3092
- ...item,
3093
- status: "completed",
3094
- reviewPayload: params.payload,
3095
- submittedPayload: params.payload,
3096
- result: params.result,
3097
- error: void 0,
3098
- completedAt: at,
3099
- completedByDid: params.byDid,
3100
- attempts: [...item.attempts ?? [], { at, byDid: params.byDid, payload: params.payload, result: params.result }]
3101
- };
3102
- map.set(itemId, updatedItem);
3103
- return updatedItem;
3104
- }
3105
-
3106
- // src/core/lib/actionRegistry/actions/xero/invoiceCreate.types.ts
3107
- var LINE_ITEM_FIELD_KEYS = ["Description", "Quantity", "UnitAmount", "AccountCode", "TaxType", "ItemCode", "DiscountRate"];
3108
- function emptyLineItemCells() {
3109
- return {
3110
- Description: "",
3111
- Quantity: "1",
3112
- UnitAmount: "",
3113
- AccountCode: "",
3114
- TaxType: "",
3115
- ItemCode: "",
3116
- DiscountRate: ""
3117
- };
3118
- }
3119
- function emptyLineItems() {
3120
- return { mode: "manual", rows: [emptyLineItemCells()] };
3121
- }
3122
- function coerceCells(raw) {
3123
- const base = emptyLineItemCells();
3124
- if (!raw || typeof raw !== "object") return base;
3125
- for (const key of LINE_ITEM_FIELD_KEYS) {
3126
- const v = raw[key];
3127
- if (typeof v === "string") base[key] = v;
3128
- else if (typeof v === "number") base[key] = String(v);
3129
- else if (typeof v === "boolean") base[key] = v ? "true" : "false";
3130
- }
3131
- return base;
3132
- }
3133
- function normaliseLineItems(raw) {
3134
- if (raw && typeof raw === "object") {
3135
- const candidate = raw;
3136
- if (candidate.mode === "iterative") {
3137
- return {
3138
- mode: "iterative",
3139
- source: typeof candidate.source === "string" ? candidate.source : "",
3140
- map: coerceCells(candidate.map)
3141
- };
3142
- }
3143
- if (candidate.mode === "manual") {
3144
- const rows = Array.isArray(candidate.rows) ? candidate.rows.map(coerceCells) : [];
3145
- return { mode: "manual", rows: rows.length > 0 ? rows : [emptyLineItemCells()] };
3146
- }
3147
- }
3148
- if (typeof raw === "string") {
3149
- const trimmed = raw.trim();
3150
- if (!trimmed) return emptyLineItems();
3151
- try {
3152
- const parsed = JSON.parse(trimmed);
3153
- if (Array.isArray(parsed) && parsed.length > 0) {
3154
- return { mode: "manual", rows: parsed.map(coerceCells) };
3155
- }
3156
- } catch {
3157
- }
3158
- }
3159
- return emptyLineItems();
3160
- }
3161
- function parseXeroInvoiceCreateInputs(raw) {
3162
- try {
3163
- const parsed = typeof raw === "string" ? JSON.parse(raw || "{}") : raw || {};
3164
- const conn = parsed.connection;
3165
- const connection = conn && typeof conn === "object" && typeof conn.connectedAccountId === "string" && typeof conn.entityDid === "string" ? { connectedAccountId: conn.connectedAccountId, entityDid: conn.entityDid } : null;
3166
- return {
3167
- connection,
3168
- tenant_id: typeof parsed.tenant_id === "string" ? parsed.tenant_id : "",
3169
- Type: typeof parsed.Type === "string" && parsed.Type ? parsed.Type : "ACCREC",
3170
- Status: typeof parsed.Status === "string" ? parsed.Status : "DRAFT",
3171
- Date: typeof parsed.Date === "string" ? parsed.Date : "",
3172
- DueDate: typeof parsed.DueDate === "string" ? parsed.DueDate : "",
3173
- ContactID: typeof parsed.ContactID === "string" ? parsed.ContactID : "",
3174
- ContactName: typeof parsed.ContactName === "string" ? parsed.ContactName : "",
3175
- Reference: typeof parsed.Reference === "string" ? parsed.Reference : "",
3176
- InvoiceNumber: typeof parsed.InvoiceNumber === "string" ? parsed.InvoiceNumber : "",
3177
- CurrencyCode: typeof parsed.CurrencyCode === "string" ? parsed.CurrencyCode : "",
3178
- LineItems: normaliseLineItems(parsed.LineItems)
3179
- };
3180
- } catch {
3181
- return {
3182
- connection: null,
3183
- tenant_id: "",
3184
- Type: "ACCREC",
3185
- Status: "DRAFT",
3186
- Date: "",
3187
- DueDate: "",
3188
- ContactID: "",
3189
- ContactName: "",
3190
- Reference: "",
3191
- InvoiceNumber: "",
3192
- CurrencyCode: "",
3193
- LineItems: emptyLineItems()
3194
- };
3195
- }
3196
- }
3197
- function serializeXeroInvoiceCreateInputs(inputs) {
3198
- return JSON.stringify(inputs);
3199
- }
3200
- var NUMERIC_LINE_ITEM_FIELDS = ["Quantity", "UnitAmount", "DiscountRate"];
3201
- function finaliseLineItem(cells) {
3202
- const out = {};
3203
- for (const key of LINE_ITEM_FIELD_KEYS) {
3204
- const raw = (cells[key] ?? "").trim();
3205
- if (!raw) continue;
3206
- if (NUMERIC_LINE_ITEM_FIELDS.includes(key)) {
3207
- const n = Number(raw);
3208
- if (!Number.isFinite(n)) {
3209
- throw new Error(`Line item field ${key} must be a number; got "${raw}"`);
3210
- }
3211
- out[key] = n;
3212
- } else {
3213
- out[key] = raw;
3214
- }
3215
- }
3216
- if (!out.Description) {
3217
- out.Description = "payment";
3218
- }
3219
- return out;
3220
- }
3221
-
3222
- // src/core/lib/actionRegistry/actions/xero/paymentCreate.types.ts
3223
- function parseXeroPaymentCreateInputs(raw) {
3224
- try {
3225
- const parsed = typeof raw === "string" ? JSON.parse(raw || "{}") : raw || {};
3226
- const conn = parsed.connection;
3227
- const connection = conn && typeof conn === "object" && typeof conn.connectedAccountId === "string" && typeof conn.entityDid === "string" ? { connectedAccountId: conn.connectedAccountId, entityDid: conn.entityDid } : null;
3228
- return {
3229
- connection,
3230
- tenant_id: typeof parsed.tenant_id === "string" ? parsed.tenant_id : "",
3231
- InvoiceID: typeof parsed.InvoiceID === "string" ? parsed.InvoiceID : "",
3232
- AccountID: typeof parsed.AccountID === "string" ? parsed.AccountID : "",
3233
- Date: typeof parsed.Date === "string" ? parsed.Date : "",
3234
- Amount: typeof parsed.Amount === "string" ? parsed.Amount : typeof parsed.Amount === "number" ? String(parsed.Amount) : "",
3235
- Reference: typeof parsed.Reference === "string" ? parsed.Reference : "",
3236
- CurrencyRate: typeof parsed.CurrencyRate === "string" ? parsed.CurrencyRate : ""
3237
- };
3238
- } catch {
3239
- return {
3240
- connection: null,
3241
- tenant_id: "",
3242
- InvoiceID: "",
3243
- AccountID: "",
3244
- Date: "",
3245
- Amount: "",
3246
- Reference: "",
3247
- CurrencyRate: ""
3248
- };
3249
- }
3250
- }
3251
- function serializeXeroPaymentCreateInputs(inputs) {
3252
- return JSON.stringify(inputs);
3253
- }
3254
-
3255
- // src/core/lib/flowEngine/referenceResolver.ts
3256
- var REFERENCE_REGEX = /\{\{([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_.:]+)\}\}/g;
3257
- function parseReferences(input) {
3258
- const references = [];
3259
- const regex = new RegExp(REFERENCE_REGEX);
3260
- let match;
3261
- while ((match = regex.exec(input)) !== null) {
3262
- references.push({
3263
- fullMatch: match[0],
3264
- blockId: match[1],
3265
- propPath: match[2],
3266
- startIndex: match.index,
3267
- endIndex: match.index + match[0].length
3268
- });
3269
- }
3270
- return references;
3271
- }
3272
- function getNestedValue(obj, path) {
3273
- return path.split(".").reduce((current, key) => {
3274
- return current?.[key];
3275
- }, obj);
3276
- }
3277
- function resolveSingleReferenceDetailed(blockId, propPath, editorDocument, yRuntime, scope) {
3278
- if (scope && Object.prototype.hasOwnProperty.call(scope, blockId)) {
3279
- const root = scope[blockId];
3280
- if (root == null) return { value: void 0, reason: "missing-value" };
3281
- const value2 = getNestedValue(root, propPath);
3282
- return { value: value2, reason: value2 === void 0 ? "missing-value" : void 0 };
3283
- }
3284
- if (!editorDocument || !Array.isArray(editorDocument)) {
3285
- return { value: void 0, reason: "unknown-block" };
3286
- }
3287
- const block = editorDocument.find((b) => b.id === blockId);
3288
- if (!block) {
3289
- return { value: void 0, reason: "unknown-block" };
3290
- }
3291
- if (propPath.startsWith("output.")) {
3292
- if (!yRuntime) return { value: void 0, reason: "missing-value" };
3293
- const runtimeState = yRuntime.get(blockId);
3294
- if (!runtimeState?.output) return { value: void 0, reason: "missing-value" };
3295
- const innerPath = propPath.substring("output.".length);
3296
- const direct = getNestedValue(runtimeState.output, innerPath);
3297
- if (direct !== void 0) return { value: direct };
3298
- if (runtimeState.output.data !== void 0) {
3299
- const value2 = getNestedValue(runtimeState.output.data, innerPath);
3300
- return { value: value2, reason: value2 === void 0 ? "missing-value" : void 0 };
3301
- }
3302
- if (runtimeState.output.http?.data !== void 0) {
3303
- const value2 = getNestedValue(runtimeState.output.http.data, innerPath);
3304
- return { value: value2, reason: value2 === void 0 ? "missing-value" : void 0 };
3305
- }
3306
- return { value: void 0, reason: "missing-value" };
3307
- }
3308
- if (propPath.startsWith("response.")) {
3309
- const responseData = block.props.response;
3310
- if (!responseData) {
3311
- return { value: void 0, reason: "missing-value" };
3312
- }
3313
- try {
3314
- const parsedResponse = typeof responseData === "string" ? JSON.parse(responseData) : responseData;
3315
- const innerPath = propPath.substring("response.".length);
3316
- const value2 = getNestedValue(parsedResponse, innerPath);
3317
- return { value: value2, reason: value2 === void 0 ? "missing-value" : void 0 };
3318
- } catch {
3319
- warnOnce(`ref-response-parse:${blockId}`, `[flow-config] block ${blockId}: props.response is not valid JSON; {{${blockId}.${propPath}}} cannot resolve`);
3320
- return { value: void 0, reason: "response-parse-error" };
3321
- }
3322
- }
3323
- const value = getNestedValue(block.props, propPath);
3324
- return { value, reason: value === void 0 ? "missing-value" : void 0 };
3325
- }
3326
- function resolveSingleReference(blockId, propPath, editorDocument, yRuntime, scope) {
3327
- return resolveSingleReferenceDetailed(blockId, propPath, editorDocument, yRuntime, scope).value;
3328
- }
3329
- function resolveReferencesDetailed(input, editorDocument, options = {}) {
3330
- const { fallback = "", stringifyObjects = true, yRuntime, scope, warnContext } = options;
3331
- const unresolved = [];
3332
- if (input == null) {
3333
- return { value: "", unresolved };
3334
- }
3335
- const inputStr = String(input);
3336
- const references = parseReferences(inputStr);
3337
- if (references.length === 0) {
3338
- return { value: inputStr, unresolved };
3339
- }
3340
- let result = inputStr;
3341
- for (let i = references.length - 1; i >= 0; i--) {
3342
- const ref = references[i];
3343
- const resolution = resolveSingleReferenceDetailed(ref.blockId, ref.propPath, editorDocument, yRuntime, scope);
3344
- const resolvedValue = resolution.value;
3345
- let replacementStr;
3346
- if (resolvedValue === void 0 || resolvedValue === null) {
3347
- replacementStr = fallback;
3348
- unresolved.push({ ref: ref.fullMatch, blockId: ref.blockId, propPath: ref.propPath, reason: resolution.reason || "missing-value" });
3349
- } else if (typeof resolvedValue === "object") {
3350
- replacementStr = stringifyObjects ? JSON.stringify(resolvedValue) : fallback;
3351
- } else {
3352
- replacementStr = String(resolvedValue);
3353
- }
3354
- result = result.substring(0, ref.startIndex) + replacementStr + result.substring(ref.endIndex);
3355
- }
3356
- if (warnContext && unresolved.length > 0) {
3357
- for (const entry of unresolved) {
3358
- warnOnce(`ref-unresolved:${warnContext}:${entry.ref}`, `[flow-config] ${warnContext}: reference ${entry.ref} did not resolve (${entry.reason}); using fallback '${fallback}'`);
3359
- }
3360
- }
3361
- return { value: result, unresolved };
3362
- }
3363
- function resolveReferences(input, editorDocument, options = {}) {
3364
- return resolveReferencesDetailed(input, editorDocument, options).value;
3365
- }
3366
- function hasReferences(input) {
3367
- if (input == null) return false;
3368
- return parseReferences(String(input)).length > 0;
3369
- }
3370
- function createReference(blockId, propPath) {
3371
- return `{{${blockId}.${propPath}}}`;
3372
- }
3373
-
3374
- // src/core/lib/xeroWorkItemPayloads.ts
3375
- function resolveXeroBindings(editorDocument, invoiceBlockId, paymentBlockId) {
3376
- if (!Array.isArray(editorDocument)) return { invoiceCreateBlock: null, paymentCreateBlock: null, evaluateBlockId: null };
3377
- const findById = (id, expectedActionType) => {
3378
- const trimmed = String(id || "").trim();
3379
- if (!trimmed) return null;
3380
- const block = editorDocument.find((b) => b?.id === trimmed);
3381
- if (!block || block.type !== "action" || block.props?.actionType !== expectedActionType) return null;
3382
- return block;
3383
- };
3384
- return {
3385
- invoiceCreateBlock: findById(invoiceBlockId, "qi/xero.invoice.create"),
3386
- paymentCreateBlock: findById(paymentBlockId, "qi/xero.payment.create"),
3387
- evaluateBlockId: null
3388
- };
3389
- }
3390
- function buildClaimScope(claim, claimData, evaluateBlockId, evaluateContext) {
3391
- const surveyAnswers = claimData && typeof claimData === "object" ? claimData.credentialSubject ?? claimData.surveyAnswers ?? claimData : {};
3392
- const evaluationOutput = evaluateContext?.evaluationOutput ?? {};
3393
- const scope = {
3394
- claim: {
3395
- claimId: claim?.claimId || evaluationOutput.claimId || "",
3396
- agentDid: claim?.agentDid || "",
3397
- agentAddress: claim?.agentAddress || "",
3398
- submissionDate: claim?.submissionDate || "",
3399
- surveyAnswers,
3400
- ...surveyAnswers
3401
- },
3402
- evaluation: {
3403
- transactionHash: evaluationOutput.transactionHash || "",
3404
- amount: evaluationOutput.amount || "",
3405
- date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
3406
- }
3407
- };
3408
- const trimmedId = String(evaluateBlockId || "").trim();
3409
- if (trimmedId) {
3410
- scope[trimmedId] = {
3411
- output: {
3412
- claimId: claim?.claimId || evaluationOutput.claimId || "",
3413
- collectionId: evaluateContext?.collectionId || "",
3414
- deedDid: evaluateContext?.deedDid || "",
3415
- surveyAnswers,
3416
- ...evaluationOutput
3417
- }
3418
- };
3419
- }
3420
- return scope;
3421
- }
3422
- function resolveIterativeSource(source, editorDocument, opts) {
3423
- const refs = parseReferences(source || "");
3424
- if (refs.length === 0) return [];
3425
- const ref = refs[0];
3426
- const value = resolveSingleReference(ref.blockId, ref.propPath, editorDocument, opts.yRuntime, opts.scope);
3427
- return Array.isArray(value) ? value : [];
3428
- }
3429
- function resolveCells(cells, editorDocument, opts) {
3430
- const out = {};
3431
- for (const key of LINE_ITEM_FIELD_KEYS) {
3432
- out[key] = resolveReferences(cells[key] || "", editorDocument, opts);
3433
- }
3434
- return out;
3435
- }
3436
- function buildLineItemsArray(config, editorDocument, opts) {
3437
- if (config.mode === "manual") {
3438
- if (config.rows.length === 0) throw new Error("At least one line item is required.");
3439
- return config.rows.map((row) => finaliseLineItem(resolveCells(row, editorDocument, opts)));
3440
- }
3441
- const elements = resolveIterativeSource(config.source, editorDocument, opts);
3442
- if (elements.length === 0) throw new Error("The bound source list is empty or could not be resolved.");
3443
- return elements.map((element) => {
3444
- const perItemOpts = { ...opts, scope: { ...opts.scope || {}, item: element } };
3445
- return finaliseLineItem(resolveCells(config.map, editorDocument, perItemOpts));
3446
- });
3447
- }
3448
- async function resolveInvoiceWorkPayload(block, editorDocument, scope) {
3449
- const parsed = parseXeroInvoiceCreateInputs(block?.props?.inputs);
3450
- const opts = { scope };
3451
- const resolveStr = (v) => resolveReferences(v || "", editorDocument, opts);
3452
- const payload = {
3453
- connection: parsed.connection,
3454
- tenant_id: resolveStr(parsed.tenant_id),
3455
- Type: resolveStr(parsed.Type) || "ACCREC",
3456
- Status: resolveStr(parsed.Status) || "DRAFT",
3457
- Date: resolveStr(parsed.Date),
3458
- DueDate: resolveStr(parsed.DueDate),
3459
- ContactID: resolveStr(parsed.ContactID),
3460
- ContactName: resolveStr(parsed.ContactName),
3461
- Reference: resolveStr(parsed.Reference),
3462
- InvoiceNumber: resolveStr(parsed.InvoiceNumber),
3463
- CurrencyCode: resolveStr(parsed.CurrencyCode),
3464
- LineItems: buildLineItemsArray(parsed.LineItems, editorDocument, opts)
3465
- };
3466
- let preview = [];
3467
- const resolver = getDiffResolver("qi/xero.invoice.create");
3468
- if (resolver) {
3469
- try {
3470
- preview = await resolver.resolver({ ...payload, LineItems: parsed.LineItems }, {});
3471
- } catch {
3472
- preview = [];
3063
+ if (!surveyAnswers || typeof surveyAnswers !== "object" || Array.isArray(surveyAnswers)) {
3064
+ throw new Error("surveyAnswers must be an object");
3473
3065
  }
3474
- }
3475
- return { payload, preview };
3476
- }
3477
- async function resolvePaymentWorkPayload(block, editorDocument, baseScope, settlement) {
3478
- const parsed = parseXeroPaymentCreateInputs(block?.props?.inputs);
3479
- const scope = {
3480
- ...baseScope,
3481
- invoice: { invoiceId: settlement.invoiceId, InvoiceID: settlement.invoiceId },
3482
- evaluation: {
3483
- transactionHash: settlement.transactionHash,
3484
- amount: settlement.amount,
3485
- date: settlement.date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
3066
+ let pin = String(inputs.pin || "").trim();
3067
+ if (!pin) {
3068
+ pin = await service.requestPin({
3069
+ title: "Verify Identity",
3070
+ description: "Enter your PIN to submit the claim",
3071
+ submitText: "Verify"
3072
+ });
3486
3073
  }
3487
- };
3488
- const opts = { scope };
3489
- const resolveStr = (v) => resolveReferences(v || "", editorDocument, opts);
3490
- const currencyRateRaw = resolveStr(parsed.CurrencyRate);
3491
- const currencyRate = currencyRateRaw ? Number(currencyRateRaw) : void 0;
3492
- const amountRaw = resolveStr(parsed.Amount);
3493
- const amount = Number.isFinite(Number(amountRaw)) && Number(amountRaw) > 0 ? Number(amountRaw) : settlement.amount;
3494
- const payload = {
3495
- connection: parsed.connection,
3496
- tenant_id: resolveStr(parsed.tenant_id),
3497
- InvoiceID: resolveStr(parsed.InvoiceID) || settlement.invoiceId,
3498
- AccountID: resolveStr(parsed.AccountID),
3499
- Date: resolveStr(parsed.Date) || settlement.date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
3500
- Amount: amount,
3501
- Reference: resolveStr(parsed.Reference) || settlement.transactionHash,
3502
- CurrencyRate: typeof currencyRate === "number" && Number.isFinite(currencyRate) ? currencyRate : void 0
3503
- };
3504
- let preview = [];
3505
- const resolver = getDiffResolver("qi/xero.payment.create");
3506
- if (resolver) {
3507
- try {
3508
- preview = await resolver.resolver({ ...payload, Amount: String(payload.Amount) }, {});
3509
- } catch {
3510
- preview = [];
3074
+ if (!pin) {
3075
+ throw new Error("PIN is required to submit claim");
3076
+ }
3077
+ if (isGroupExecution(inputs)) {
3078
+ const { coreAddress, title, description } = requireGroupExecutionParams(inputs);
3079
+ const handlers = ctx.handlers;
3080
+ if (typeof handlers?.prepareGroupClaimSubmission !== "function") {
3081
+ throw new Error("Acting as a POD is not available: the host does not implement prepareGroupClaimSubmission");
3082
+ }
3083
+ const prepared = await handlers.prepareGroupClaimSubmission({
3084
+ surveyData: surveyAnswers,
3085
+ deedDid,
3086
+ entityDid: deedDid,
3087
+ collectionId,
3088
+ adminAddress,
3089
+ pin,
3090
+ groupAddress: coreAddress
3091
+ });
3092
+ const claimId2 = String(prepared?.claimId || "").trim();
3093
+ if (!claimId2) throw new Error("prepareGroupClaimSubmission returned no claim identifier");
3094
+ return proposeGroupExecution(ctx, {
3095
+ coreAddress,
3096
+ title,
3097
+ description,
3098
+ msgs: prepared.msgs || [],
3099
+ expectedOutput: {
3100
+ claimId: claimId2,
3101
+ transactionHash: "",
3102
+ collectionId,
3103
+ deedDid,
3104
+ submittedByDid: coreAddress,
3105
+ submittedAt: (/* @__PURE__ */ new Date()).toISOString(),
3106
+ surveyAnswers
3107
+ },
3108
+ completionEvent: "submitted"
3109
+ });
3110
+ }
3111
+ const result = await service.submitClaim({
3112
+ surveyData: surveyAnswers,
3113
+ deedDid,
3114
+ entityDid: deedDid,
3115
+ collectionId,
3116
+ adminAddress,
3117
+ pin
3118
+ });
3119
+ const claimId = String(result?.claimId || result?.id || "");
3120
+ if (!claimId) {
3121
+ throw new Error("submitClaim returned no claim identifier");
3511
3122
  }
3123
+ const transactionHash = String(result?.transactionHash || "");
3124
+ const submittedByDid = ctx.actorDid || "";
3125
+ const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
3126
+ const output = {
3127
+ claimId,
3128
+ transactionHash,
3129
+ collectionId,
3130
+ deedDid,
3131
+ submittedByDid,
3132
+ submittedAt,
3133
+ surveyAnswers
3134
+ };
3135
+ return {
3136
+ output,
3137
+ events: [
3138
+ {
3139
+ name: "submitted",
3140
+ payload: output
3141
+ }
3142
+ ]
3143
+ };
3512
3144
  }
3513
- return { payload, preview };
3514
- }
3515
- function getInvoiceTotal(output) {
3516
- const total = typeof output.total === "number" ? output.total : Number(output.total);
3517
- return Number.isFinite(total) && total > 0 ? total : 0;
3518
- }
3145
+ });
3519
3146
 
3520
3147
  // src/core/lib/actionRegistry/actions/evaluateClaim/evaluateClaim.ts
3521
3148
  var BASELINE_OUTPUT2 = [
@@ -3560,6 +3187,27 @@ function isEvaluatorRole(role) {
3560
3187
  const normalized = String(role || "").trim().toLowerCase();
3561
3188
  return normalized === "ea" || normalized === "evaluation_agent";
3562
3189
  }
3190
+ function isTruthyFlag(value) {
3191
+ if (value === true) return true;
3192
+ if (typeof value === "number") return value === 1;
3193
+ if (typeof value === "string") {
3194
+ const normalized = value.trim().toLowerCase();
3195
+ return normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "on";
3196
+ }
3197
+ return false;
3198
+ }
3199
+ var XERO_INVOICE_DEFAULT_KEYS = ["Type", "Status", "CurrencyCode", "tenant_id", "ContactID", "ContactName"];
3200
+ function buildXeroInvoiceDefaults(raw) {
3201
+ const source = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
3202
+ const defaults = {};
3203
+ for (const key of XERO_INVOICE_DEFAULT_KEYS) {
3204
+ const value = String(source[key] ?? "").trim();
3205
+ if (value) defaults[key] = value;
3206
+ }
3207
+ if (!defaults.Type) defaults.Type = "ACCREC";
3208
+ if (!defaults.Status) defaults.Status = "DRAFT";
3209
+ return defaults;
3210
+ }
3563
3211
  registerAction({
3564
3212
  type: "qi/claim.evaluate",
3565
3213
  can: "claim/evaluate",
@@ -3648,9 +3296,12 @@ registerAction({
3648
3296
  traceCid: { type: "string", description: "Trace CID passed through to the UDID." },
3649
3297
  items: { type: "array", description: "Evaluation items passed through to the UDID." },
3650
3298
  patch: { type: "object", description: "Patch object passed through to the UDID." },
3651
- xeroInvoiceBlockId: { type: "string", description: "Bound Xero invoice block id to queue invoice work on approval." },
3652
- xeroPaymentBlockId: { type: "string", description: "Bound Xero payment block id used when resolving Xero bindings." },
3653
- claimSnapshot: { type: "object", description: "Claim snapshot used to build the Xero invoice scope." }
3299
+ xeroOracleInvoiceOnApprove: { type: "boolean", description: "Create a Xero draft invoice via the contracted xero-oracle when a claim is approved." },
3300
+ xeroInvoiceDefaults: {
3301
+ type: "object",
3302
+ description: "Optional template-level Xero invoice defaults (Type, Status, CurrencyCode, tenant_id, ContactID, ContactName) pinned into the invoice work payload."
3303
+ },
3304
+ claimSnapshot: { type: "object", description: "Claim snapshot used to build the Xero invoice work payload." }
3654
3305
  }
3655
3306
  },
3656
3307
  run: async (inputs, ctx) => {
@@ -3832,36 +3483,30 @@ registerAction({
3832
3483
  evaluatedAt,
3833
3484
  surveyAnswers
3834
3485
  };
3835
- const xeroInvoiceBlockId = String(inputs.xeroInvoiceBlockId || "").trim();
3836
- if (decision === "approve" && xeroInvoiceBlockId) {
3837
- const editorDoc = ctx.editor?.document || [];
3838
- const runtime = ctx.runtime;
3839
- if (!runtime) {
3840
- throw new Error("Cannot queue Xero invoice work: runtime state manager is unavailable");
3841
- }
3842
- const bindings = resolveXeroBindings(editorDoc, xeroInvoiceBlockId, String(inputs.xeroPaymentBlockId || ""));
3843
- const invoiceBlock = bindings.invoiceCreateBlock;
3844
- if (!invoiceBlock) {
3845
- throw new Error("Cannot queue Xero invoice work: bound invoice block was not found");
3846
- }
3486
+ if (decision === "approve" && isTruthyFlag(inputs.xeroOracleInvoiceOnApprove)) {
3847
3487
  const flowId = String(ctx.flowId || ctx.flowUri || "flow");
3848
- const scope = buildClaimScope(inputs.claimSnapshot || { claimId }, claimData || surveyAnswers, ctx.nodeId, {
3849
- deedDid,
3850
- collectionId,
3851
- evaluationOutput: output
3852
- });
3853
- const { payload } = await resolveInvoiceWorkPayload(invoiceBlock, editorDoc, scope);
3854
- const idempotencyKey = buildXeroInvoiceWorkKey({
3855
- flowId,
3856
- evaluationBlockId: ctx.nodeId,
3857
- claimId,
3858
- invoiceBlockId: invoiceBlock.id
3859
- });
3488
+ const claimSnapshot = inputs.claimSnapshot && typeof inputs.claimSnapshot === "object" && !Array.isArray(inputs.claimSnapshot) ? inputs.claimSnapshot : void 0;
3489
+ const surveyQuestions = Array.isArray(claimSnapshot?.surveyQuestions) ? claimSnapshot.surveyQuestions : Array.isArray(inputs?.surveyAnswersSchema) ? inputs.surveyAnswersSchema : [];
3490
+ const idempotencyKey = buildXeroInvoiceWorkKey({ flowId, evaluationBlockId: ctx.nodeId, claimId });
3491
+ const originalPayload = {
3492
+ claim: { claimId, collectionId, deedDid },
3493
+ surveyQuestions,
3494
+ surveyAnswers,
3495
+ evaluation: {
3496
+ decision,
3497
+ amount: normalizedCoin,
3498
+ evaluatedByDid,
3499
+ evaluatedAt,
3500
+ verificationProof,
3501
+ transactionHash
3502
+ },
3503
+ invoiceDefaults: buildXeroInvoiceDefaults(inputs.xeroInvoiceDefaults)
3504
+ };
3860
3505
  upsertXeroWorkItemForEditor(ctx.editor, {
3861
3506
  id: idempotencyKey,
3862
3507
  kind: "invoice.create",
3863
3508
  status: "pending",
3864
- assignedBlockId: invoiceBlock.id,
3509
+ assignedBlockId: ctx.nodeId,
3865
3510
  idempotencyKey,
3866
3511
  source: {
3867
3512
  claimId,
@@ -3869,15 +3514,14 @@ registerAction({
3869
3514
  deedDid,
3870
3515
  collectionId
3871
3516
  },
3872
- originalPayload: payload,
3873
- reviewPayload: payload,
3517
+ originalPayload,
3518
+ reviewPayload: originalPayload,
3874
3519
  provenance: {
3875
3520
  createdAt: Date.now(),
3876
3521
  createdByDid: ctx.actorDid,
3877
- templateBlockId: invoiceBlock.id,
3878
- templateInputsSnapshot: invoiceBlock.props?.inputs,
3522
+ templateBlockId: ctx.nodeId,
3879
3523
  evaluationOutput: output,
3880
- claimSnapshot: inputs.claimSnapshot
3524
+ claimSnapshot
3881
3525
  },
3882
3526
  attempts: []
3883
3527
  });
@@ -10591,6 +10235,127 @@ function isRuntimeRef(value) {
10591
10235
 
10592
10236
  // src/core/lib/flowEngine/triggers.ts
10593
10237
  import * as Y from "yjs";
10238
+
10239
+ // src/core/lib/flowEngine/referenceResolver.ts
10240
+ var REFERENCE_REGEX = /\{\{([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_.:]+)\}\}/g;
10241
+ function parseReferences(input) {
10242
+ const references = [];
10243
+ const regex = new RegExp(REFERENCE_REGEX);
10244
+ let match;
10245
+ while ((match = regex.exec(input)) !== null) {
10246
+ references.push({
10247
+ fullMatch: match[0],
10248
+ blockId: match[1],
10249
+ propPath: match[2],
10250
+ startIndex: match.index,
10251
+ endIndex: match.index + match[0].length
10252
+ });
10253
+ }
10254
+ return references;
10255
+ }
10256
+ function getNestedValue(obj, path) {
10257
+ return path.split(".").reduce((current, key) => {
10258
+ return current?.[key];
10259
+ }, obj);
10260
+ }
10261
+ function resolveSingleReferenceDetailed(blockId, propPath, editorDocument, yRuntime, scope) {
10262
+ if (scope && Object.prototype.hasOwnProperty.call(scope, blockId)) {
10263
+ const root = scope[blockId];
10264
+ if (root == null) return { value: void 0, reason: "missing-value" };
10265
+ const value2 = getNestedValue(root, propPath);
10266
+ return { value: value2, reason: value2 === void 0 ? "missing-value" : void 0 };
10267
+ }
10268
+ if (!editorDocument || !Array.isArray(editorDocument)) {
10269
+ return { value: void 0, reason: "unknown-block" };
10270
+ }
10271
+ const block = editorDocument.find((b) => b.id === blockId);
10272
+ if (!block) {
10273
+ return { value: void 0, reason: "unknown-block" };
10274
+ }
10275
+ if (propPath.startsWith("output.")) {
10276
+ if (!yRuntime) return { value: void 0, reason: "missing-value" };
10277
+ const runtimeState = yRuntime.get(blockId);
10278
+ if (!runtimeState?.output) return { value: void 0, reason: "missing-value" };
10279
+ const innerPath = propPath.substring("output.".length);
10280
+ const direct = getNestedValue(runtimeState.output, innerPath);
10281
+ if (direct !== void 0) return { value: direct };
10282
+ if (runtimeState.output.data !== void 0) {
10283
+ const value2 = getNestedValue(runtimeState.output.data, innerPath);
10284
+ return { value: value2, reason: value2 === void 0 ? "missing-value" : void 0 };
10285
+ }
10286
+ if (runtimeState.output.http?.data !== void 0) {
10287
+ const value2 = getNestedValue(runtimeState.output.http.data, innerPath);
10288
+ return { value: value2, reason: value2 === void 0 ? "missing-value" : void 0 };
10289
+ }
10290
+ return { value: void 0, reason: "missing-value" };
10291
+ }
10292
+ if (propPath.startsWith("response.")) {
10293
+ const responseData = block.props.response;
10294
+ if (!responseData) {
10295
+ return { value: void 0, reason: "missing-value" };
10296
+ }
10297
+ try {
10298
+ const parsedResponse = typeof responseData === "string" ? JSON.parse(responseData) : responseData;
10299
+ const innerPath = propPath.substring("response.".length);
10300
+ const value2 = getNestedValue(parsedResponse, innerPath);
10301
+ return { value: value2, reason: value2 === void 0 ? "missing-value" : void 0 };
10302
+ } catch {
10303
+ warnOnce(`ref-response-parse:${blockId}`, `[flow-config] block ${blockId}: props.response is not valid JSON; {{${blockId}.${propPath}}} cannot resolve`);
10304
+ return { value: void 0, reason: "response-parse-error" };
10305
+ }
10306
+ }
10307
+ const value = getNestedValue(block.props, propPath);
10308
+ return { value, reason: value === void 0 ? "missing-value" : void 0 };
10309
+ }
10310
+ function resolveSingleReference(blockId, propPath, editorDocument, yRuntime, scope) {
10311
+ return resolveSingleReferenceDetailed(blockId, propPath, editorDocument, yRuntime, scope).value;
10312
+ }
10313
+ function resolveReferencesDetailed(input, editorDocument, options = {}) {
10314
+ const { fallback = "", stringifyObjects = true, yRuntime, scope, warnContext } = options;
10315
+ const unresolved = [];
10316
+ if (input == null) {
10317
+ return { value: "", unresolved };
10318
+ }
10319
+ const inputStr = String(input);
10320
+ const references = parseReferences(inputStr);
10321
+ if (references.length === 0) {
10322
+ return { value: inputStr, unresolved };
10323
+ }
10324
+ let result = inputStr;
10325
+ for (let i = references.length - 1; i >= 0; i--) {
10326
+ const ref = references[i];
10327
+ const resolution = resolveSingleReferenceDetailed(ref.blockId, ref.propPath, editorDocument, yRuntime, scope);
10328
+ const resolvedValue = resolution.value;
10329
+ let replacementStr;
10330
+ if (resolvedValue === void 0 || resolvedValue === null) {
10331
+ replacementStr = fallback;
10332
+ unresolved.push({ ref: ref.fullMatch, blockId: ref.blockId, propPath: ref.propPath, reason: resolution.reason || "missing-value" });
10333
+ } else if (typeof resolvedValue === "object") {
10334
+ replacementStr = stringifyObjects ? JSON.stringify(resolvedValue) : fallback;
10335
+ } else {
10336
+ replacementStr = String(resolvedValue);
10337
+ }
10338
+ result = result.substring(0, ref.startIndex) + replacementStr + result.substring(ref.endIndex);
10339
+ }
10340
+ if (warnContext && unresolved.length > 0) {
10341
+ for (const entry of unresolved) {
10342
+ warnOnce(`ref-unresolved:${warnContext}:${entry.ref}`, `[flow-config] ${warnContext}: reference ${entry.ref} did not resolve (${entry.reason}); using fallback '${fallback}'`);
10343
+ }
10344
+ }
10345
+ return { value: result, unresolved };
10346
+ }
10347
+ function resolveReferences(input, editorDocument, options = {}) {
10348
+ return resolveReferencesDetailed(input, editorDocument, options).value;
10349
+ }
10350
+ function hasReferences(input) {
10351
+ if (input == null) return false;
10352
+ return parseReferences(String(input)).length > 0;
10353
+ }
10354
+ function createReference(blockId, propPath) {
10355
+ return `{{${blockId}.${propPath}}}`;
10356
+ }
10357
+
10358
+ // src/core/lib/flowEngine/triggers.ts
10594
10359
  var RUN_RECORD_AUDIT_TYPE = "block.run";
10595
10360
  function computePendingInvocationId(args) {
10596
10361
  const { sourceBlockId, sourceRunId, listenerBlockId, eventName, eventIndex } = args;
@@ -11216,7 +10981,7 @@ var buildFlowNodeFromBlock = (block) => {
11216
10981
  };
11217
10982
 
11218
10983
  // src/core/lib/flowEngine/runtime.ts
11219
- var XERO_WORK_ITEMS_MAP_NAME = "xeroWorkItems";
10984
+ var XERO_WORK_ITEMS_MAP_NAME2 = "xeroWorkItems";
11220
10985
  var XERO_CONNECTION_MAP_NAME = "xeroConnection";
11221
10986
  var ensureStateObject = (value) => {
11222
10987
  if (!value || typeof value !== "object") {
@@ -11262,7 +11027,7 @@ function clearRuntimeForTemplateClone(yDoc) {
11262
11027
  const agentOutbox = yDoc.getMap("agentOutbox");
11263
11028
  const agentLeases = yDoc.getMap("agentLeases");
11264
11029
  const auditTrail = yDoc.getMap("auditTrail");
11265
- const xeroWorkItems = yDoc.getMap(XERO_WORK_ITEMS_MAP_NAME);
11030
+ const xeroWorkItems = yDoc.getMap(XERO_WORK_ITEMS_MAP_NAME2);
11266
11031
  const xeroConnection = yDoc.getMap(XERO_CONNECTION_MAP_NAME);
11267
11032
  yDoc.transact(() => {
11268
11033
  runtime.forEach((_, key) => runtime.delete(key));
@@ -11530,9 +11295,6 @@ function verifyCompletion(params) {
11530
11295
  if (runtime.manuallyVerified === true) {
11531
11296
  return { status: "verified" };
11532
11297
  }
11533
- if (actionType && isRepeatableAction(actionType)) {
11534
- return { status: "verified" };
11535
- }
11536
11298
  if (actionType) {
11537
11299
  const action = getAction(actionType);
11538
11300
  if (action) {
@@ -12689,6 +12451,17 @@ var migration_0_3_to_1_0_0 = {
12689
12451
  };
12690
12452
  registerMigration(migration_0_3_to_1_0_0);
12691
12453
 
12454
+ // src/core/types/nodeContext.ts
12455
+ var NODE_CONTEXT_MAP_NAME = "nodeContext";
12456
+ var NODE_CONTEXT_OVERRIDES_MAP_NAME = "nodeContextOverrides";
12457
+ function isNodeContextRecord(value) {
12458
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
12459
+ return false;
12460
+ }
12461
+ const record = value;
12462
+ return typeof record.kind === "string" && typeof record.shapeVersion === "number" && typeof record.sourceDigest === "string" && Boolean(record.values) && typeof record.values === "object" && !Array.isArray(record.values) && Boolean(record.fieldProvenance) && typeof record.fieldProvenance === "object" && !Array.isArray(record.fieldProvenance) && typeof record.inferredAt === "string";
12463
+ }
12464
+
12692
12465
  // src/core/lib/flowCompiler/conditions.ts
12693
12466
  var OPERATOR_MAP = {
12694
12467
  // BaseUcan ConditionRef vocabulary
@@ -13802,7 +13575,10 @@ function queueAgentCommand(yDoc, command) {
13802
13575
  const { outbox } = getFlowAgentMaps(yDoc);
13803
13576
  const existing = outbox.get(command.id);
13804
13577
  if (existing) {
13805
- if (existing.type === "assign_actor" && existing.status === "failed") {
13578
+ const retryFailedAssignment = existing.type === "assign_actor" && existing.status === "failed";
13579
+ const retryFailedDiagnosis = existing.type === "diagnose_blocker" && existing.status === "failed";
13580
+ const retryUnprovenDiagnosis = existing.type === "diagnose_blocker" && existing.status === "confirmed" && typeof existing.payload.delegatedInvocationId !== "string";
13581
+ if (retryFailedAssignment || retryFailedDiagnosis || retryUnprovenDiagnosis) {
13806
13582
  const retried = {
13807
13583
  ...existing,
13808
13584
  status: "queued",
@@ -13828,7 +13604,8 @@ function queueAgentCommand(yDoc, command) {
13828
13604
  status: retried.status,
13829
13605
  idempotencyKey: retried.idempotencyKey,
13830
13606
  reason: retried.reason,
13831
- retry: true
13607
+ retry: true,
13608
+ retryCause: retryFailedDiagnosis ? "diagnosis_failed" : retryUnprovenDiagnosis ? "missing_delegated_invocation" : "assignment_failed"
13832
13609
  }
13833
13610
  });
13834
13611
  } catch (error) {
@@ -13938,8 +13715,6 @@ var COMMAND_CAPABILITIES = {
13938
13715
  notify_actor: "flow/notify",
13939
13716
  execute_action: "flow/node/execute",
13940
13717
  validate_external_state: "flow/mutation/execute",
13941
- submit_oracle_claim: "flow/oracle-claim/submit",
13942
- watch_oracle_udid: "flow/observe",
13943
13718
  archive_flow: "flow/archive",
13944
13719
  propose_config_change: "flow/config/propose"
13945
13720
  };
@@ -14117,15 +13892,7 @@ function classifyBlockerCause(block, runtime) {
14117
13892
  }
14118
13893
  return void 0;
14119
13894
  }
14120
- function classifyNodeState({
14121
- block,
14122
- runtime,
14123
- now,
14124
- pendingInvocationCount = 0,
14125
- completionVerification,
14126
- isRepeatable = false
14127
- }) {
14128
- if (isRepeatable && runtime.state === "completed") return "Active";
13895
+ function classifyNodeState({ block, runtime, now, pendingInvocationCount = 0, completionVerification }) {
14129
13896
  if (runtime.state === "completed" && completionVerification?.status === "unverified") return "Blocked";
14130
13897
  if (runtime.state === "completed" || runtime.state === "cancelled") return "Done";
14131
13898
  if (runtime.state === "needs_verification") return "Blocked";
@@ -14135,12 +13902,12 @@ function classifyNodeState({
14135
13902
  if (pendingInvocationCount > 0) return "Pending";
14136
13903
  return "Pending";
14137
13904
  }
14138
- function snapshotNode(block, runtime, now, pendingInvocationCount = 0, completionVerification, isRepeatable = false) {
13905
+ function snapshotNode(block, runtime, now, pendingInvocationCount = 0, completionVerification) {
14139
13906
  const nodeId = getBlockId(block);
14140
13907
  if (!nodeId) {
14141
13908
  throw new Error("Cannot snapshot a block without an id");
14142
13909
  }
14143
- const publicState = classifyNodeState({ block, runtime, now, pendingInvocationCount, completionVerification, isRepeatable });
13910
+ const publicState = classifyNodeState({ block, runtime, now, pendingInvocationCount, completionVerification });
14144
13911
  const dueAt = getDueAt(block);
14145
13912
  const assigneeDid = getAssigneeDid(block);
14146
13913
  const snapshot = {
@@ -14352,22 +14119,7 @@ function statusForResult(result) {
14352
14119
  function planForSnapshot(context, options, block, snapshot) {
14353
14120
  const queued = [];
14354
14121
  const actionType = getBlockActionType(block);
14355
- const queueClaimUdidWatch = () => {
14356
- const runtime = snapshot.runtime;
14357
- if (actionType === "qi/claim.submit" && runtime.output?.claimId && !runtime.output?.udid) {
14358
- const command = queueIfAuthorized(context, options, "watch_oracle_udid", snapshot.nodeId, "Oracle claim submitted but UDID is not yet observed", {
14359
- claimId: runtime.output.claimId,
14360
- timeoutMs: 864e5
14361
- });
14362
- if (command) queued.push(command);
14363
- }
14364
- };
14365
- if (snapshot.publicState === "Active") {
14366
- queueClaimUdidWatch();
14367
- return queued;
14368
- }
14369
14122
  if (snapshot.publicState === "Done") {
14370
- queueClaimUdidWatch();
14371
14123
  return queued;
14372
14124
  }
14373
14125
  if (snapshot.publicState === "Overdue") {
@@ -14407,13 +14159,6 @@ function planForSnapshot(context, options, block, snapshot) {
14407
14159
  if (command) queued.push(command);
14408
14160
  return queued;
14409
14161
  }
14410
- if (actionType === "qi/claim.submit") {
14411
- const command = queueIfAuthorized(context, options, "submit_oracle_claim", snapshot.nodeId, "Oracle claim action is pending and agent is authorized to submit", {
14412
- actionType
14413
- });
14414
- if (command) queued.push(command);
14415
- return queued;
14416
- }
14417
14162
  if (actionType && canQueueCommand("execute_action", snapshot.nodeId, context, options)) {
14418
14163
  const pendingInvocation = readPendingInvocations(context.yDoc, snapshot.nodeId)[0];
14419
14164
  const payload = {
@@ -14468,7 +14213,7 @@ function planRalphLoopCommands(context, options = {}) {
14468
14213
  getInvocation,
14469
14214
  schemaVersion
14470
14215
  });
14471
- const snapshot = snapshotNode(block, runtime, now, pendingInvocationCount, completionVerification, isRepeatableAction(actionType));
14216
+ const snapshot = snapshotNode(block, runtime, now, pendingInvocationCount, completionVerification);
14472
14217
  snapshots.push(snapshot);
14473
14218
  queuedCommands.push(...planForSnapshot(context, options, block, snapshot));
14474
14219
  }
@@ -14491,10 +14236,6 @@ async function callExecutor(command, context, executor) {
14491
14236
  return executor.notifyActor ? executor.notifyActor(command, context) : { commandId: command.id, success: false, error: "No notifyActor handler configured" };
14492
14237
  case "validate_external_state":
14493
14238
  return executor.validateExternalState ? executor.validateExternalState(command, context) : { commandId: command.id, success: false, error: "No validateExternalState handler configured" };
14494
- case "submit_oracle_claim":
14495
- return executor.submitOracleClaim ? executor.submitOracleClaim(command, context) : { commandId: command.id, success: false, error: "No submitOracleClaim handler configured" };
14496
- case "watch_oracle_udid":
14497
- return executor.watchOracleUdid ? executor.watchOracleUdid(command, context) : { commandId: command.id, success: false, error: "No watchOracleUdid handler configured" };
14498
14239
  case "archive_flow":
14499
14240
  return executor.archiveFlow ? executor.archiveFlow(command, context) : { commandId: command.id, success: false, error: "No archiveFlow handler configured" };
14500
14241
  case "propose_config_change":
@@ -15055,7 +14796,6 @@ export {
15055
14796
  registerAction,
15056
14797
  getAction,
15057
14798
  getAllActions,
15058
- isRepeatableAction,
15059
14799
  getAliasEntries,
15060
14800
  hasAction,
15061
14801
  getActionByCan,
@@ -15065,34 +14805,28 @@ export {
15065
14805
  canToType,
15066
14806
  typeToCan,
15067
14807
  getAllCanMappings,
14808
+ SERVICE_VERBS,
14809
+ serviceCan,
14810
+ parseServiceCan,
15068
14811
  buildServicesFromHandlers,
15069
14812
  getDiffResolver,
15070
14813
  hasDiffResolver,
15071
14814
  extractDid,
15072
14815
  extractSurveyAnswerSchema,
14816
+ XERO_WORK_ITEMS_MAP_NAME,
14817
+ getXeroWorkItemsMap,
14818
+ buildXeroInvoiceWorkKey,
15073
14819
  buildXeroPaymentWorkKey,
14820
+ findXeroWorkItems,
14821
+ upsertXeroWorkItem,
14822
+ updateXeroWorkReviewPayload,
14823
+ markXeroWorkFailed,
14824
+ markXeroWorkCompleted,
15074
14825
  findXeroWorkItemsForEditor,
15075
14826
  upsertXeroWorkItemForEditor,
15076
14827
  updateXeroWorkReviewPayloadForEditor,
15077
14828
  markXeroWorkFailedForEditor,
15078
14829
  markXeroWorkCompletedForEditor,
15079
- LINE_ITEM_FIELD_KEYS,
15080
- emptyLineItemCells,
15081
- parseXeroInvoiceCreateInputs,
15082
- serializeXeroInvoiceCreateInputs,
15083
- finaliseLineItem,
15084
- parseXeroPaymentCreateInputs,
15085
- serializeXeroPaymentCreateInputs,
15086
- parseReferences,
15087
- resolveSingleReference,
15088
- resolveReferencesDetailed,
15089
- resolveReferences,
15090
- hasReferences,
15091
- createReference,
15092
- resolveXeroBindings,
15093
- buildClaimScope,
15094
- resolvePaymentWorkPayload,
15095
- getInvoiceTotal,
15096
14830
  transformSurveyToCredentialSubject,
15097
14831
  buildVerifiableCredential,
15098
14832
  buildDomainCardLinkedResource,
@@ -15123,6 +14857,12 @@ export {
15123
14857
  formatCoinAmount,
15124
14858
  createUcanService,
15125
14859
  isRuntimeRef,
14860
+ parseReferences,
14861
+ resolveSingleReference,
14862
+ resolveReferencesDetailed,
14863
+ resolveReferences,
14864
+ hasReferences,
14865
+ createReference,
15126
14866
  RUN_RECORD_AUDIT_TYPE,
15127
14867
  computePendingInvocationId,
15128
14868
  snapshotInputRefs,
@@ -15163,6 +14903,9 @@ export {
15163
14903
  createMemoryUcanDelegationStore,
15164
14904
  createInvocationStore,
15165
14905
  createMemoryInvocationStore,
14906
+ NODE_CONTEXT_MAP_NAME,
14907
+ NODE_CONTEXT_OVERRIDES_MAP_NAME,
14908
+ isNodeContextRecord,
15166
14909
  compileBlockProps,
15167
14910
  COMPILED_BLOCK_TYPE,
15168
14911
  toEvaluatorOperator,
@@ -15214,4 +14957,4 @@ export {
15214
14957
  executeQueuedFlowAgentCoreCommands,
15215
14958
  FlowAgentService
15216
14959
  };
15217
- //# sourceMappingURL=chunk-EULL3QTC.js.map
14960
+ //# sourceMappingURL=chunk-ABM3ZZIX.js.map