@ixo/editor 6.32.0 → 6.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action-manifest-v2.schema.json +214 -0
- package/dist/action-manifest.json +16657 -3125
- package/dist/{chunk-S7CAGFE3.js → chunk-A4CWC4AG.js} +15 -14
- package/dist/chunk-A4CWC4AG.js.map +1 -0
- package/dist/{chunk-ZXNBOVAA.js → chunk-NEOPTPDF.js} +3024 -478
- package/dist/chunk-NEOPTPDF.js.map +1 -0
- package/dist/{chunk-37OKRZEK.js → chunk-VRCRDKN5.js} +1508 -743
- package/dist/chunk-VRCRDKN5.js.map +1 -0
- package/dist/{store-CqvAkvIZ.d.ts → connections-1NtNQrsY.d.ts} +340 -323
- package/dist/core/index.cjs +23307 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.ts +608 -251
- package/dist/core/index.js +467 -2
- package/dist/core/index.js.map +1 -1
- package/dist/{graphql-client-ZY4Iw0jB.d.ts → graphql-client-Bn0tN049.d.ts} +6 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/mantine/index.d.ts +5 -5
- package/dist/mantine/index.js +2 -2
- package/dist/mantine/index.js.map +1 -1
- package/dist/{nodeContext-C-BsYuYi.d.ts → nodeContext-CV9D4s3X.d.ts} +1 -1
- package/dist/{index-Up8gA_E9.d.ts → runtime-BFwvCQDF.d.ts} +9393 -8728
- package/package.json +9 -5
- package/style.css.d.ts +5 -5
- package/dist/chunk-37OKRZEK.js.map +0 -1
- package/dist/chunk-S7CAGFE3.js.map +0 -1
- package/dist/chunk-ZXNBOVAA.js.map +0 -1
|
@@ -499,6 +499,398 @@ async function sendDirectMessage(matrixClient, targetDid, message) {
|
|
|
499
499
|
return { roomId };
|
|
500
500
|
}
|
|
501
501
|
|
|
502
|
+
// src/core/lib/actionRegistry/digest.ts
|
|
503
|
+
function compareCodeUnits(left, right) {
|
|
504
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
505
|
+
}
|
|
506
|
+
function canonicalActionJson(value) {
|
|
507
|
+
if (value === null) return "null";
|
|
508
|
+
if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
|
|
509
|
+
if (typeof value === "number") {
|
|
510
|
+
if (!Number.isFinite(value)) throw new Error("Action manifest cannot contain non-finite numbers");
|
|
511
|
+
return JSON.stringify(value);
|
|
512
|
+
}
|
|
513
|
+
if (Array.isArray(value)) return `[${value.map((item) => item === void 0 ? "null" : canonicalActionJson(item)).join(",")}]`;
|
|
514
|
+
if (typeof value === "object") {
|
|
515
|
+
const fields = Object.entries(value).filter(([, item]) => item !== void 0).sort(([a], [b]) => compareCodeUnits(a, b));
|
|
516
|
+
return `{${fields.map(([key, item]) => `${JSON.stringify(key)}:${canonicalActionJson(item)}`).join(",")}}`;
|
|
517
|
+
}
|
|
518
|
+
throw new Error(`Action manifest cannot contain ${typeof value}`);
|
|
519
|
+
}
|
|
520
|
+
var SHA256_K = new Uint32Array([
|
|
521
|
+
1116352408,
|
|
522
|
+
1899447441,
|
|
523
|
+
3049323471,
|
|
524
|
+
3921009573,
|
|
525
|
+
961987163,
|
|
526
|
+
1508970993,
|
|
527
|
+
2453635748,
|
|
528
|
+
2870763221,
|
|
529
|
+
3624381080,
|
|
530
|
+
310598401,
|
|
531
|
+
607225278,
|
|
532
|
+
1426881987,
|
|
533
|
+
1925078388,
|
|
534
|
+
2162078206,
|
|
535
|
+
2614888103,
|
|
536
|
+
3248222580,
|
|
537
|
+
3835390401,
|
|
538
|
+
4022224774,
|
|
539
|
+
264347078,
|
|
540
|
+
604807628,
|
|
541
|
+
770255983,
|
|
542
|
+
1249150122,
|
|
543
|
+
1555081692,
|
|
544
|
+
1996064986,
|
|
545
|
+
2554220882,
|
|
546
|
+
2821834349,
|
|
547
|
+
2952996808,
|
|
548
|
+
3210313671,
|
|
549
|
+
3336571891,
|
|
550
|
+
3584528711,
|
|
551
|
+
113926993,
|
|
552
|
+
338241895,
|
|
553
|
+
666307205,
|
|
554
|
+
773529912,
|
|
555
|
+
1294757372,
|
|
556
|
+
1396182291,
|
|
557
|
+
1695183700,
|
|
558
|
+
1986661051,
|
|
559
|
+
2177026350,
|
|
560
|
+
2456956037,
|
|
561
|
+
2730485921,
|
|
562
|
+
2820302411,
|
|
563
|
+
3259730800,
|
|
564
|
+
3345764771,
|
|
565
|
+
3516065817,
|
|
566
|
+
3600352804,
|
|
567
|
+
4094571909,
|
|
568
|
+
275423344,
|
|
569
|
+
430227734,
|
|
570
|
+
506948616,
|
|
571
|
+
659060556,
|
|
572
|
+
883997877,
|
|
573
|
+
958139571,
|
|
574
|
+
1322822218,
|
|
575
|
+
1537002063,
|
|
576
|
+
1747873779,
|
|
577
|
+
1955562222,
|
|
578
|
+
2024104815,
|
|
579
|
+
2227730452,
|
|
580
|
+
2361852424,
|
|
581
|
+
2428436474,
|
|
582
|
+
2756734187,
|
|
583
|
+
3204031479,
|
|
584
|
+
3329325298
|
|
585
|
+
]);
|
|
586
|
+
function rotateRight(value, bits) {
|
|
587
|
+
return value >>> bits | value << 32 - bits;
|
|
588
|
+
}
|
|
589
|
+
function sha256Hex(text) {
|
|
590
|
+
const input = new TextEncoder().encode(text);
|
|
591
|
+
const bitLength = input.length * 8;
|
|
592
|
+
const paddedLength = Math.ceil((input.length + 9) / 64) * 64;
|
|
593
|
+
const bytes = new Uint8Array(paddedLength);
|
|
594
|
+
bytes.set(input);
|
|
595
|
+
bytes[input.length] = 128;
|
|
596
|
+
const view = new DataView(bytes.buffer);
|
|
597
|
+
view.setUint32(paddedLength - 8, Math.floor(bitLength / 4294967296), false);
|
|
598
|
+
view.setUint32(paddedLength - 4, bitLength >>> 0, false);
|
|
599
|
+
const state = new Uint32Array([1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]);
|
|
600
|
+
const words = new Uint32Array(64);
|
|
601
|
+
for (let offset = 0; offset < bytes.length; offset += 64) {
|
|
602
|
+
for (let index = 0; index < 16; index += 1) words[index] = view.getUint32(offset + index * 4, false);
|
|
603
|
+
for (let index = 16; index < 64; index += 1) {
|
|
604
|
+
const s0 = rotateRight(words[index - 15], 7) ^ rotateRight(words[index - 15], 18) ^ words[index - 15] >>> 3;
|
|
605
|
+
const s1 = rotateRight(words[index - 2], 17) ^ rotateRight(words[index - 2], 19) ^ words[index - 2] >>> 10;
|
|
606
|
+
words[index] = words[index - 16] + s0 + words[index - 7] + s1 >>> 0;
|
|
607
|
+
}
|
|
608
|
+
let a = state[0];
|
|
609
|
+
let b = state[1];
|
|
610
|
+
let c = state[2];
|
|
611
|
+
let d = state[3];
|
|
612
|
+
let e = state[4];
|
|
613
|
+
let f = state[5];
|
|
614
|
+
let g = state[6];
|
|
615
|
+
let h = state[7];
|
|
616
|
+
for (let index = 0; index < 64; index += 1) {
|
|
617
|
+
const sum1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
|
|
618
|
+
const choice = e & f ^ ~e & g;
|
|
619
|
+
const temp1 = h + sum1 + choice + SHA256_K[index] + words[index] >>> 0;
|
|
620
|
+
const sum0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
|
|
621
|
+
const majority = a & b ^ a & c ^ b & c;
|
|
622
|
+
const temp2 = sum0 + majority >>> 0;
|
|
623
|
+
h = g;
|
|
624
|
+
g = f;
|
|
625
|
+
f = e;
|
|
626
|
+
e = d + temp1 >>> 0;
|
|
627
|
+
d = c;
|
|
628
|
+
c = b;
|
|
629
|
+
b = a;
|
|
630
|
+
a = temp1 + temp2 >>> 0;
|
|
631
|
+
}
|
|
632
|
+
state[0] = state[0] + a >>> 0;
|
|
633
|
+
state[1] = state[1] + b >>> 0;
|
|
634
|
+
state[2] = state[2] + c >>> 0;
|
|
635
|
+
state[3] = state[3] + d >>> 0;
|
|
636
|
+
state[4] = state[4] + e >>> 0;
|
|
637
|
+
state[5] = state[5] + f >>> 0;
|
|
638
|
+
state[6] = state[6] + g >>> 0;
|
|
639
|
+
state[7] = state[7] + h >>> 0;
|
|
640
|
+
}
|
|
641
|
+
return Array.from(state, (word) => word.toString(16).padStart(8, "0")).join("");
|
|
642
|
+
}
|
|
643
|
+
function sha256Digest(value) {
|
|
644
|
+
return `sha256:${sha256Hex(canonicalActionJson(value))}`;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// src/core/lib/actionRegistry/topicSemanticRecords.ts
|
|
648
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
649
|
+
var stringArray = { type: "array", items: { type: "string" } };
|
|
650
|
+
var digest = { type: "string", pattern: "^sha256:[a-f0-9]{64}$" };
|
|
651
|
+
var dateTime = {
|
|
652
|
+
type: "string",
|
|
653
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$"
|
|
654
|
+
};
|
|
655
|
+
var MAX_TOPIC_SEMANTIC_RECORDS_PER_RECEIPT = 25;
|
|
656
|
+
var MAX_TOPIC_SEMANTIC_RECORD_BATCH_BYTES = 32 * 1024;
|
|
657
|
+
function closed(required, properties) {
|
|
658
|
+
return { type: "object", required, additionalProperties: false, properties };
|
|
659
|
+
}
|
|
660
|
+
var TOPIC_SEMANTIC_RECORD_DEFINITIONS = [
|
|
661
|
+
{
|
|
662
|
+
type: "org.ixo.topic.claim-submission",
|
|
663
|
+
version: 1,
|
|
664
|
+
displayName: "Claim submitted",
|
|
665
|
+
description: "Records that a claim was submitted to a collection.",
|
|
666
|
+
valueSchema: closed(["claimId", "collectionId", "deedDid", "submittedByDid", "submittedAt", "transactionHash", "submissionDigest"], {
|
|
667
|
+
claimId: { type: "string" },
|
|
668
|
+
collectionId: { type: "string" },
|
|
669
|
+
deedDid: { type: "string" },
|
|
670
|
+
submittedByDid: { type: "string" },
|
|
671
|
+
submittedAt: dateTime,
|
|
672
|
+
transactionHash: { type: "string" },
|
|
673
|
+
submissionDigest: digest
|
|
674
|
+
})
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
type: "org.ixo.topic.claim-evaluation",
|
|
678
|
+
version: 1,
|
|
679
|
+
displayName: "Claim evaluated",
|
|
680
|
+
description: "Records the governed evaluation outcome for a claim.",
|
|
681
|
+
valueSchema: closed(["claimId", "collectionId", "deedDid", "decision", "evaluatedByDid", "evaluatedAt", "transactionHash", "evidenceDigest"], {
|
|
682
|
+
claimId: { type: "string" },
|
|
683
|
+
collectionId: { type: "string" },
|
|
684
|
+
deedDid: { type: "string" },
|
|
685
|
+
decision: { type: "string" },
|
|
686
|
+
evaluatedByDid: { type: "string" },
|
|
687
|
+
evaluatedAt: dateTime,
|
|
688
|
+
verificationProof: { type: "string" },
|
|
689
|
+
transactionHash: { type: "string" },
|
|
690
|
+
evidenceDigest: digest
|
|
691
|
+
})
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
type: "org.ixo.topic.proposal-receipt",
|
|
695
|
+
version: 1,
|
|
696
|
+
displayName: "Governance proposal update",
|
|
697
|
+
description: "Records the creation of, or a vote on, a governance proposal.",
|
|
698
|
+
valueSchema: closed(["event", "proposalId", "proposalContractAddress"], {
|
|
699
|
+
event: { type: "string", enum: ["created", "vote-cast"] },
|
|
700
|
+
actionType: { type: "string" },
|
|
701
|
+
proposalId: { type: "string" },
|
|
702
|
+
proposalContractAddress: { type: "string" },
|
|
703
|
+
coreAddress: { type: "string" },
|
|
704
|
+
title: { type: "string" },
|
|
705
|
+
proposalTitle: { type: "string" },
|
|
706
|
+
descriptionDigest: digest,
|
|
707
|
+
proposalDescriptionDigest: digest,
|
|
708
|
+
status: { type: "string" },
|
|
709
|
+
vote: { type: "string" },
|
|
710
|
+
rationaleDigest: digest,
|
|
711
|
+
actorDid: { type: "string" },
|
|
712
|
+
votedAt: dateTime,
|
|
713
|
+
createdAt: dateTime
|
|
714
|
+
})
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
type: "org.ixo.topic.work-event",
|
|
718
|
+
version: 1,
|
|
719
|
+
displayName: "Work update",
|
|
720
|
+
description: "Records an assignment, dispatch, checkpoint, submission, or acceptance of work.",
|
|
721
|
+
valueSchema: closed(["workId", "resourceType", "phase", "assigneeDid", "note", "artifactReferences", "evidenceReferences", "actorDid", "occurredAt"], {
|
|
722
|
+
workId: { type: "string" },
|
|
723
|
+
resourceType: { type: "string" },
|
|
724
|
+
phase: { type: "string", enum: ["requested", "dispatched", "in_progress", "ready_for_review", "completed"] },
|
|
725
|
+
assigneeDid: { type: "string" },
|
|
726
|
+
note: { type: "string" },
|
|
727
|
+
artifactReferences: stringArray,
|
|
728
|
+
evidenceReferences: stringArray,
|
|
729
|
+
actorDid: { type: "string" },
|
|
730
|
+
occurredAt: dateTime
|
|
731
|
+
})
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
type: "org.ixo.topic.agent-result",
|
|
735
|
+
version: 1,
|
|
736
|
+
displayName: "Agent result",
|
|
737
|
+
description: "Records a delegated agent result and its evidence references without exposing invocation inputs.",
|
|
738
|
+
valueSchema: closed(["sessionId", "result", "resultDigest", "evidenceReferences", "evidenceDigest", "providerReceiptReference"], {
|
|
739
|
+
sessionId: { type: "string" },
|
|
740
|
+
result: { description: "Provider-owned result extension point." },
|
|
741
|
+
resultDigest: digest,
|
|
742
|
+
evidenceReferences: stringArray,
|
|
743
|
+
evidenceDigest: digest,
|
|
744
|
+
providerReceiptReference: { type: "string" }
|
|
745
|
+
})
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
type: "org.ixo.topic.agent-cancellation",
|
|
749
|
+
version: 1,
|
|
750
|
+
displayName: "Agent cancelled",
|
|
751
|
+
description: "Records cancellation of a delegated agent session.",
|
|
752
|
+
valueSchema: closed(["sessionId", "status", "providerReceiptReference"], {
|
|
753
|
+
sessionId: { type: "string" },
|
|
754
|
+
status: { const: "cancelled" },
|
|
755
|
+
providerReceiptReference: { type: "string" }
|
|
756
|
+
})
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
type: "org.ixo.topic.evidence",
|
|
760
|
+
version: 1,
|
|
761
|
+
displayName: "Evidence collected",
|
|
762
|
+
description: "Records collected evidence, provenance, and stable evidence references.",
|
|
763
|
+
valueSchema: closed(["question", "evidence", "provenance", "evidenceReferences", "evidenceDigest"], {
|
|
764
|
+
question: { type: "string" },
|
|
765
|
+
evidence: { description: "Provider-owned evidence extension point." },
|
|
766
|
+
provenance: { description: "Provider-owned provenance extension point." },
|
|
767
|
+
evidenceReferences: stringArray,
|
|
768
|
+
evidenceDigest: digest
|
|
769
|
+
})
|
|
770
|
+
},
|
|
771
|
+
...["proposed", "accepted"].map(
|
|
772
|
+
(status) => ({
|
|
773
|
+
type: `org.ixo.topic.${status}-answer`,
|
|
774
|
+
version: 1,
|
|
775
|
+
displayName: status === "accepted" ? "Answer accepted" : "Answer proposed",
|
|
776
|
+
description: status === "accepted" ? "Records an answer accepted by the stated authority." : "Records an answer proposed for review.",
|
|
777
|
+
valueSchema: closed(["answer", "status", "proposedAnswerRecordId", "authorityDid", "evidenceReferences", "limitations", "occurredAt"], {
|
|
778
|
+
answer: { type: "string" },
|
|
779
|
+
status: { const: status },
|
|
780
|
+
proposedAnswerRecordId: { type: "string" },
|
|
781
|
+
authorityDid: { type: "string" },
|
|
782
|
+
evidenceReferences: stringArray,
|
|
783
|
+
limitations: { type: "string" },
|
|
784
|
+
occurredAt: dateTime
|
|
785
|
+
})
|
|
786
|
+
})
|
|
787
|
+
),
|
|
788
|
+
...["assertion", "review"].map(
|
|
789
|
+
(kind) => ({
|
|
790
|
+
type: `org.ixo.topic.evaluation-${kind}`,
|
|
791
|
+
version: 1,
|
|
792
|
+
displayName: kind === "review" ? "Evaluation reviewed" : "Evaluation assertion",
|
|
793
|
+
description: kind === "review" ? "Records a signed human review of an evaluation assertion." : "Records a signed evaluation assertion.",
|
|
794
|
+
valueSchema: closed(
|
|
795
|
+
[kind === "review" ? "reviewId" : "assertionId", "providerResult", "methodologyRevision", "rubricRevision", "evaluatorDid", "evidenceReferences", "signature"],
|
|
796
|
+
{
|
|
797
|
+
assertionId: { type: "string" },
|
|
798
|
+
reviewId: { type: "string" },
|
|
799
|
+
providerResult: { type: "object", description: "Provider-owned evaluation result extension point.", additionalProperties: true },
|
|
800
|
+
methodologyRevision: { type: "string" },
|
|
801
|
+
rubricRevision: { type: "string" },
|
|
802
|
+
evaluatorDid: { type: "string" },
|
|
803
|
+
evidenceReferences: stringArray,
|
|
804
|
+
signature: { type: "string" }
|
|
805
|
+
}
|
|
806
|
+
)
|
|
807
|
+
})
|
|
808
|
+
),
|
|
809
|
+
{
|
|
810
|
+
type: "org.ixo.topic.settlement-record",
|
|
811
|
+
version: 1,
|
|
812
|
+
displayName: "Settlement update",
|
|
813
|
+
description: "Records the provider reference and terminal status of an approved settlement execution.",
|
|
814
|
+
valueSchema: closed(["settlementId", "transactionReference", "providerReceiptReference", "status"], {
|
|
815
|
+
settlementId: { type: "string" },
|
|
816
|
+
transactionReference: { type: "string" },
|
|
817
|
+
providerReceiptReference: { type: "string" },
|
|
818
|
+
status: { type: "string", enum: ["submitted", "confirmed", "needs_verification"] }
|
|
819
|
+
})
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
type: "org.ixo.topic.incident-escalation",
|
|
823
|
+
version: 1,
|
|
824
|
+
displayName: "Incident escalated",
|
|
825
|
+
description: "Records that an incident was escalated to the stated recipients.",
|
|
826
|
+
valueSchema: closed(["severity", "affectedResources", "recipients", "evidenceReferences", "summary", "escalationId", "notifiedAt", "providerReceiptReferences"], {
|
|
827
|
+
severity: { type: "string", enum: ["low", "medium", "high", "critical"] },
|
|
828
|
+
affectedResources: stringArray,
|
|
829
|
+
recipients: stringArray,
|
|
830
|
+
evidenceReferences: stringArray,
|
|
831
|
+
summary: { type: "string" },
|
|
832
|
+
escalationId: { type: "string" },
|
|
833
|
+
notifiedAt: dateTime,
|
|
834
|
+
providerReceiptReferences: stringArray
|
|
835
|
+
})
|
|
836
|
+
},
|
|
837
|
+
{
|
|
838
|
+
type: "org.ixo.topic.incident-mitigation",
|
|
839
|
+
version: 1,
|
|
840
|
+
displayName: "Incident mitigation recorded",
|
|
841
|
+
description: "Records mitigation work for an incident without changing the incident lifecycle.",
|
|
842
|
+
valueSchema: closed(["mitigation", "affectedResources", "evidenceReferences", "recordedBy", "occurredAt"], {
|
|
843
|
+
mitigation: { type: "string" },
|
|
844
|
+
affectedResources: stringArray,
|
|
845
|
+
evidenceReferences: stringArray,
|
|
846
|
+
recordedBy: { type: "string" },
|
|
847
|
+
occurredAt: dateTime
|
|
848
|
+
})
|
|
849
|
+
}
|
|
850
|
+
];
|
|
851
|
+
var definitionsByType = new Map(TOPIC_SEMANTIC_RECORD_DEFINITIONS.map((definition) => [definition.type, definition]));
|
|
852
|
+
var ajv = new Ajv2020({ allErrors: true, strict: false });
|
|
853
|
+
var validators = /* @__PURE__ */ new Map();
|
|
854
|
+
function getTopicSemanticRecordDefinitions(types) {
|
|
855
|
+
return types.map((type) => definitionsByType.get(type)).filter((definition) => !!definition);
|
|
856
|
+
}
|
|
857
|
+
function validateTopicSemanticRecord(record, topic) {
|
|
858
|
+
if (!record || typeof record !== "object" || Array.isArray(record)) return { valid: false, code: "INVALID_RECORD_ENVELOPE" };
|
|
859
|
+
const candidate = record;
|
|
860
|
+
const envelopeKeys = /* @__PURE__ */ new Set(["type", "id", "version", "value", "evidenceReferences"]);
|
|
861
|
+
if (Object.keys(candidate).some((key) => !envelopeKeys.has(key)) || typeof candidate.type !== "string" || typeof candidate.id !== "string" || !candidate.id || !Number.isInteger(candidate.version) || Number(candidate.version) < 1 || !candidate.value || typeof candidate.value !== "object" || Array.isArray(candidate.value)) {
|
|
862
|
+
return { valid: false, code: "INVALID_RECORD_ENVELOPE" };
|
|
863
|
+
}
|
|
864
|
+
if (candidate.evidenceReferences !== void 0 && (!Array.isArray(candidate.evidenceReferences) || candidate.evidenceReferences.some((item) => typeof item !== "string"))) {
|
|
865
|
+
return { valid: false, code: "INVALID_RECORD_ENVELOPE" };
|
|
866
|
+
}
|
|
867
|
+
const definition = topic.semanticRecordTypes.find((item) => item.type === candidate.type);
|
|
868
|
+
if (!definition) return { valid: false, code: "UNKNOWN_RECORD_TYPE" };
|
|
869
|
+
if (candidate.version !== definition.version) return { valid: false, code: "UNSUPPORTED_RECORD_VERSION" };
|
|
870
|
+
const validatorKey = `${definition.type}@${definition.version}:${sha256Digest(definition.valueSchema)}`;
|
|
871
|
+
let validate = validators.get(validatorKey);
|
|
872
|
+
if (!validate) {
|
|
873
|
+
validate = ajv.compile(definition.valueSchema);
|
|
874
|
+
validators.set(validatorKey, validate);
|
|
875
|
+
}
|
|
876
|
+
if (!validate(candidate.value)) return { valid: false, code: "INVALID_RECORD_VALUE", errors: validate.errors || void 0 };
|
|
877
|
+
return { valid: true, definition };
|
|
878
|
+
}
|
|
879
|
+
function validateTopicSemanticRecordBatch(records, topic) {
|
|
880
|
+
if (records.length > MAX_TOPIC_SEMANTIC_RECORDS_PER_RECEIPT) return { valid: false, code: "TOO_MANY_RECORDS" };
|
|
881
|
+
for (const record of records) {
|
|
882
|
+
const validation = validateTopicSemanticRecord(record, topic);
|
|
883
|
+
if (!validation.valid) return validation;
|
|
884
|
+
}
|
|
885
|
+
try {
|
|
886
|
+
const byteLength = new TextEncoder().encode(canonicalActionJson(records)).byteLength;
|
|
887
|
+
if (byteLength > MAX_TOPIC_SEMANTIC_RECORD_BATCH_BYTES) return { valid: false, code: "RECORD_BATCH_TOO_LARGE" };
|
|
888
|
+
} catch {
|
|
889
|
+
return { valid: false, code: "INVALID_RECORD_BATCH" };
|
|
890
|
+
}
|
|
891
|
+
return { valid: true };
|
|
892
|
+
}
|
|
893
|
+
|
|
502
894
|
// src/core/lib/actionRegistry/canMapping.ts
|
|
503
895
|
var CAN_TO_TYPE = {
|
|
504
896
|
"flow/run.start": "qi/flow.run.start",
|
|
@@ -548,10 +940,11 @@ var CAN_TO_TYPE = {
|
|
|
548
940
|
"outlook.email/send": "qi/outlook.email.send",
|
|
549
941
|
"slack.message/send": "qi/slack.message.send",
|
|
550
942
|
"googlecalendar.event/create": "qi/googlecalendar.event.create",
|
|
551
|
-
// Calendar
|
|
552
|
-
|
|
553
|
-
"calendar.event/
|
|
554
|
-
"calendar.event/
|
|
943
|
+
// Google Calendar, self-connected (renamed from qi/calendar.* — IXO-4420 §5).
|
|
944
|
+
// The cans keep their historical values so existing UCAN grants still match.
|
|
945
|
+
"calendar.event/create": "qi/googlecalendar.event.create-self",
|
|
946
|
+
"calendar.event/update": "qi/googlecalendar.event.update-self",
|
|
947
|
+
"calendar.event/list": "qi/googlecalendar.event.list-self",
|
|
555
948
|
// Xero integration
|
|
556
949
|
"xero.contact/create": "qi/xero.contact.create",
|
|
557
950
|
"xero.invoice/create": "qi/xero.invoice.create",
|
|
@@ -580,6 +973,159 @@ function getAllCanMappings() {
|
|
|
580
973
|
return Object.entries(CAN_TO_TYPE).map(([can, type]) => ({ can, type }));
|
|
581
974
|
}
|
|
582
975
|
|
|
976
|
+
// src/core/lib/actionRegistry/presentation.ts
|
|
977
|
+
var PRESENTATION = {
|
|
978
|
+
oracle: { displayName: "Ask your Agent", description: "Send a prompt to the Personal Agent" },
|
|
979
|
+
"oracle.prompt": { displayName: "Oracle Prompt", description: "Send a prompt to the Personal Agent" },
|
|
980
|
+
"qi/bid.evaluate": { displayName: "Evaluate Bid", description: "Approve or reject a bid" },
|
|
981
|
+
"qi/bid.submit": { displayName: "Bid", description: "Submit a bid application" },
|
|
982
|
+
"qi/agent.cancel": { displayName: "Cancel Agent", description: "Cancel a delegated agent session" },
|
|
983
|
+
"qi/agent.invoke": { displayName: "Invoke Agent", description: "Delegate a task to an authorised agent runtime" },
|
|
984
|
+
"qi/answer.accept": { displayName: "Accept Answer", description: "Accept a proposed answer using the stated authority" },
|
|
985
|
+
"qi/answer.propose": { displayName: "Propose Answer", description: "Propose an answer for review in the Topic" },
|
|
986
|
+
"qi/blueprint.artifact-preview": { displayName: "Preview Blueprint Artifact", description: "Preview an authored Blueprint artifact before saving it" },
|
|
987
|
+
"qi/blueprint.artifact-save": { displayName: "Save Blueprint Artifact", description: "Save an authored artifact to the Blueprint workspace" },
|
|
988
|
+
"qi/blueprint.checklist-update": { displayName: "Update Blueprint Checklist", description: "Record progress against the Blueprint authoring checklist" },
|
|
989
|
+
"qi/blueprint.guided-authoring": { displayName: "Guide Blueprint Authoring", description: "Generate guided authoring input for the current Blueprint phase" },
|
|
990
|
+
"qi/blueprint.journey-resume": { displayName: "Resume Blueprint Journey", description: "Resume an existing Blueprint authoring journey" },
|
|
991
|
+
"qi/blueprint.phase-confirm": { displayName: "Confirm Blueprint Phase", description: "Confirm completion of the current Blueprint authoring phase" },
|
|
992
|
+
"qi/blueprint.publish": { displayName: "Publish Blueprint", description: "Publish an approved Blueprint release" },
|
|
993
|
+
"qi/blueprint.release-compile": { displayName: "Compile Blueprint Release", description: "Compile Blueprint artifacts into a release candidate" },
|
|
994
|
+
"qi/blueprint.review-respond": { displayName: "Respond to Blueprint Review", description: "Respond to review feedback on a Blueprint release" },
|
|
995
|
+
"qi/blueprint.review-submit": { displayName: "Submit Blueprint Review", description: "Submit a Blueprint release for review" },
|
|
996
|
+
"qi/blueprint.reviewer-assign": { displayName: "Assign Blueprint Reviewer", description: "Assign a reviewer to a Blueprint release" },
|
|
997
|
+
"qi/blueprint.workspace-start": { displayName: "Start Blueprint Workspace", description: "Create a workspace for a new Blueprint authoring journey" },
|
|
998
|
+
"qi/calendar.event.create": { displayName: "Create Calendar event", description: "Create an event on a connected Calendar" },
|
|
999
|
+
"qi/calendar.event.list": { displayName: "List Calendar events", description: "Fetch events from a connected Calendar" },
|
|
1000
|
+
"qi/calendar.event.update": { displayName: "Update Calendar event", description: "Replace an existing Calendar event" },
|
|
1001
|
+
"qi/carbon.harvest": { displayName: "Harvest Carbon", description: "Claim carbon credits into your wallet" },
|
|
1002
|
+
"qi/carbon.loadBatches": { displayName: "Load Carbon Batches", description: "Reconcile harvestable and retireable CARBON credits" },
|
|
1003
|
+
"qi/carbon.retire": { displayName: "Retire Carbon", description: "Permanently retire carbon credits to offset impact" },
|
|
1004
|
+
"qi/claim.evaluate": { displayName: "Evaluate Claim", description: "Approve or reject a claim" },
|
|
1005
|
+
"qi/claim.submit": { displayName: "Claim", description: "Submit a claim" },
|
|
1006
|
+
"qi/collection.lifecycle": { displayName: "Claim Collection", description: "Create and manage a claim collection lifecycle" },
|
|
1007
|
+
"qi/collection.create": { displayName: "Create Claim Collection", description: "Pin a protocol release and create its exact on-chain collection record" },
|
|
1008
|
+
"qi/collection.users": { displayName: "Collection Users", description: "Add, list and revoke claim collection contributors & evaluators" },
|
|
1009
|
+
"qi/credential.store": { displayName: "Store Credential", description: "Store a verifiable credential in Matrix room state" },
|
|
1010
|
+
"qi/domain.card-preview": { displayName: "Preview Domain Card", description: "Review an oracle-enriched domain card and approve it before signing" },
|
|
1011
|
+
"qi/domain.sign": { displayName: "Sign Domain", description: "Sign the domain card credential and create the entity on-chain" },
|
|
1012
|
+
"qi/email.send": { displayName: "Email", description: "Send an email to a user" },
|
|
1013
|
+
"qi/eval.connect": { displayName: "Connect Evaluation Service", description: "Authorize one evaluation service for one existing claim collection" },
|
|
1014
|
+
"qi/eval.engine": { displayName: "Evaluation Engine", description: "Enroll a collection and publish its claim-approval rules in one step" },
|
|
1015
|
+
"qi/evaluation.review": { displayName: "Review Evaluation", description: "Review and sign an evaluation assertion" },
|
|
1016
|
+
"qi/evaluation.run": { displayName: "Run Evaluation", description: "Run an evaluation against the selected methodology and rubric" },
|
|
1017
|
+
"qi/evidence.collect": { displayName: "Collect Evidence", description: "Collect evidence and provenance from approved sources" },
|
|
1018
|
+
"qi/flow.run.close": { displayName: "Close Flow Run", description: "Close the current Flow run with a terminal outcome" },
|
|
1019
|
+
"qi/flow.run.start": { displayName: "Start Flow Run", description: "Start a new governed Flow run" },
|
|
1020
|
+
"qi/entity.createOracle": { displayName: "Create Oracle Entity", description: "Create the oracle entity on-chain" },
|
|
1021
|
+
"qi/entity.transfer": { displayName: "Transfer Entity", description: "Transfer ownership of an entity to a new owner" },
|
|
1022
|
+
"qi/form.submit": { displayName: "Form Submit", description: "Submit a form response" },
|
|
1023
|
+
"qi/gmail.email.send": { displayName: "Send Gmail email", description: "Send an email from the template author's Gmail account" },
|
|
1024
|
+
"qi/googlecalendar.event.create": { displayName: "Create Calendar event (delegated)", description: "Create an event on the template author's Google Calendar" },
|
|
1025
|
+
"qi/googlecalendar.event.create-self": { displayName: "Create Calendar event", description: "Create an event on a connected Calendar" },
|
|
1026
|
+
"qi/googlecalendar.event.list-self": { displayName: "List Calendar events", description: "Fetch events from a connected Calendar" },
|
|
1027
|
+
"qi/googlecalendar.event.update-self": { displayName: "Update Calendar event", description: "Replace an existing Calendar event" },
|
|
1028
|
+
"qi/governance.authz.exec": { displayName: "Execute Authorized Action", description: "Propose executing a message the POD was authorized to run" },
|
|
1029
|
+
"qi/governance.authz.grant": { displayName: "Grant Authorization", description: "Propose granting an address authorization to act for the POD" },
|
|
1030
|
+
"qi/governance.authz.revoke": { displayName: "Revoke Authorization", description: "Propose revoking a previously granted authorization" },
|
|
1031
|
+
"qi/governance.chain-governance-vote": { displayName: "Chain Governance Vote", description: "Propose casting the POD's vote on a chain governance proposal" },
|
|
1032
|
+
"qi/governance.contract.execute": { displayName: "Execute Contract", description: "Propose executing a message on a smart contract as the DAO" },
|
|
1033
|
+
"qi/governance.contract.instantiate": { displayName: "Instantiate Contract", description: "Propose instantiating a new smart contract from a code id" },
|
|
1034
|
+
"qi/governance.contract.manage-cw20": { displayName: "Manage Token List", description: "Propose tracking or untracking a cw20 token in the DAO treasury" },
|
|
1035
|
+
"qi/governance.contract.migrate": { displayName: "Migrate Contract", description: "Propose migrating a smart contract the DAO administers to a new code id" },
|
|
1036
|
+
"qi/governance.contract.update-admin": { displayName: "Update Contract Admin", description: "Propose transferring admin rights over a smart contract to a new address" },
|
|
1037
|
+
"qi/governance.custom-message": { displayName: "Custom Message", description: "Propose executing a raw JSON cosmos message (advanced)" },
|
|
1038
|
+
"qi/governance.dao.accept-to-marketplace": { displayName: "Accept to Marketplace", description: "Propose marking an entity as verified on the marketplace" },
|
|
1039
|
+
"qi/governance.dao.admin-exec": { displayName: "DAO Admin Execute", description: "Propose executing admin messages on a SubDAO this POD administers" },
|
|
1040
|
+
"qi/governance.dao.create-entity": { displayName: "Create Entity", description: "Propose broadcasting a raw entity-creation message" },
|
|
1041
|
+
"qi/governance.dao.join": { displayName: "Join Entity", description: "Propose linking this POD as a member of another entity" },
|
|
1042
|
+
"qi/governance.dao.manage-storage": { displayName: "Manage Storage Items", description: "Propose setting or removing an item in the DAO's on-chain storage" },
|
|
1043
|
+
"qi/governance.dao.manage-subdaos": { displayName: "Manage SubDAOs", description: "Propose recognising or removing SubDAOs of this DAO" },
|
|
1044
|
+
"qi/governance.dao.update-info": { displayName: "Update DAO Info", description: "Propose replacing the DAO's name, description and image" },
|
|
1045
|
+
"qi/governance.member-proposal": { displayName: "Membership Proposal", description: "Propose adding/removing members or changing voting power" },
|
|
1046
|
+
"qi/governance.nft.burn": { displayName: "Burn NFT", description: "Propose permanently burning an NFT held by the treasury" },
|
|
1047
|
+
"qi/governance.nft.manage-collections": { displayName: "Manage NFT Collections", description: "Propose tracking or untracking an NFT collection in the treasury" },
|
|
1048
|
+
"qi/governance.nft.transfer": { displayName: "Transfer NFT", description: "Propose transferring an NFT from the treasury" },
|
|
1049
|
+
"qi/governance.settings-proposal": { displayName: "Governance Settings Proposal", description: "Propose new voting rules (period, quorum, thresholds)" },
|
|
1050
|
+
"qi/governance.staking.stake": { displayName: "Stake Treasury Tokens", description: "Propose staking treasury IXO with a validator \u2014 stake, unstake, restake or claim rewards" },
|
|
1051
|
+
"qi/governance.staking.stake-to-group": { displayName: "Stake to POD", description: "Propose staking treasury cw20 tokens into a POD's staking contract" },
|
|
1052
|
+
"qi/governance.submission-config-proposal": { displayName: "Proposal Submission Rules", description: "Propose changing who may submit proposals and the required deposit" },
|
|
1053
|
+
"qi/governance.transaction.mint": { displayName: "Mint Governance Tokens", description: "Propose minting new governance tokens to an address" },
|
|
1054
|
+
"qi/governance.transaction.perform-token-swap": { displayName: "Fund Token Swap", description: "Propose funding the POD\u2019s side of a token swap contract" },
|
|
1055
|
+
"qi/governance.transaction.send-funds": { displayName: "Send Funds", description: "Propose sending funds from the POD treasury" },
|
|
1056
|
+
"qi/governance.transaction.send-group-token": { displayName: "Send POD Tokens", description: "Propose transferring cw20 POD tokens from the treasury" },
|
|
1057
|
+
"qi/governance.transaction.withdraw-token-swap": { displayName: "Withdraw Token Swap", description: "Propose withdrawing the POD\u2019s funds from a token swap contract" },
|
|
1058
|
+
"qi/governance.validator.actions": { displayName: "Validator Actions", description: "Propose a validator operation with the POD's validator account" },
|
|
1059
|
+
"qi/http.request": { displayName: "HTTP Request", description: "Make an HTTP API request" },
|
|
1060
|
+
"qi/http.fetch": { displayName: "Fetch HTTP Resource", description: "Read data from an HTTP endpoint" },
|
|
1061
|
+
"qi/incident.escalate": { displayName: "Escalate Incident", description: "Notify the stated recipients about an incident escalation" },
|
|
1062
|
+
"qi/incident.mitigation.record": { displayName: "Record Incident Mitigation", description: "Record mitigation work and evidence for an incident" },
|
|
1063
|
+
"qi/human.checkbox.set": { displayName: "Checkbox Set", description: "Record a checkbox response" },
|
|
1064
|
+
"qi/human.form.submit": { displayName: "Human Form Submit", description: "Submit a human-completed form" },
|
|
1065
|
+
"qi/identity.create": { displayName: "Create Identity", description: "Create an IID document and Matrix account for a user" },
|
|
1066
|
+
"qi/iid.create": { displayName: "Create IID", description: "Create an IID document on-chain" },
|
|
1067
|
+
"qi/kyc.verify": { displayName: "Identity Verification (KYC)", description: "Verify your identity and save the issued credential to your Vault" },
|
|
1068
|
+
"qi/matrix.dm": { displayName: "Matrix DM", description: "Send a direct message via Matrix" },
|
|
1069
|
+
"qi/matrix.register": { displayName: "Register Matrix Account", description: "Create a Matrix account and access token" },
|
|
1070
|
+
"qi/notification.push": { displayName: "Push Notification", description: "Send a push notification" },
|
|
1071
|
+
"qi/oracle.configureOracle": { displayName: "Configure Oracle", description: "Store oracle secrets and configuration in one step" },
|
|
1072
|
+
"qi/oracle.contract": { displayName: "Create Oracle Contract", description: "Establish the user-oracle Matrix DM room" },
|
|
1073
|
+
"qi/oracle.deploy": { displayName: "Deploy Oracle", description: "Build, deploy, and start the oracle" },
|
|
1074
|
+
"qi/oracle.deploySetup": { displayName: "Set Up Oracle Deployment", description: "Build and prepare the oracle for deployment" },
|
|
1075
|
+
"qi/oracle.deployStart": { displayName: "Start Oracle Deployment", description: "Start the oracle deployment process" },
|
|
1076
|
+
"qi/oracle.invoke": { displayName: "Ask your Agent", description: "Send a prompt to the Personal Agent" },
|
|
1077
|
+
"qi/oracle.storeConfig": { displayName: "Store Oracle Config", description: "Store the oracle configuration in Matrix room state" },
|
|
1078
|
+
"qi/oracle.storeSecrets": { displayName: "Store Oracle Secrets", description: "Store oracle secrets in Matrix room state" },
|
|
1079
|
+
"qi/oracle.storeSecretsAndConfig": { displayName: "Store Oracle Secrets & Config", description: "Store oracle secrets and configuration in Matrix room state" },
|
|
1080
|
+
"qi/outlook.email.send": { displayName: "Send Outlook email", description: "Send an email from the template author's Outlook account" },
|
|
1081
|
+
"qi/pod.domain-indexer-lookup": { displayName: "Define Purpose", description: "Capture intent and find matching Blueprint candidates" },
|
|
1082
|
+
"qi/pod.domain-single-selection": { displayName: "Select Blueprint", description: "Choose a Blueprint protocol for the POD" },
|
|
1083
|
+
"qi/pod.entity-single-selection": { displayName: "Select Parent Organisation", description: "Pick a parent entity where you hold a role" },
|
|
1084
|
+
"qi/pod.governance-config": { displayName: "Configure Governance", description: "Set the governance group type and decision policy" },
|
|
1085
|
+
"qi/pod.list-domain-flows": { displayName: "Select Flow Templates", description: "Choose protocol flow templates to import" },
|
|
1086
|
+
"qi/pod.member-multi-select": { displayName: "Configure Membership", description: "Define POD members, roles, and voting power" },
|
|
1087
|
+
"qi/proposal.create": { displayName: "Create Proposal", description: "Create an on-chain governance proposal" },
|
|
1088
|
+
"qi/proposal.vote": { displayName: "Vote on Proposal", description: "Cast a vote on a governance proposal" },
|
|
1089
|
+
"qi/protocol.select": { displayName: "Select Protocol", description: "Select a protocol from a configured list" },
|
|
1090
|
+
"qi/sandbox.provision": { displayName: "Provision Sandbox", description: "Provision a sandbox environment for the oracle" },
|
|
1091
|
+
"qi/settlement.execute": { displayName: "Execute Settlement", description: "Execute an approved settlement using the configured provider" },
|
|
1092
|
+
"qi/slack.message.send": { displayName: "Post Slack message", description: "Post a message to a channel from the template author's Slack account" },
|
|
1093
|
+
"qi/topic.action.cancel": { displayName: "Cancel Topic Action", description: "Cancel an outstanding governed Action request" },
|
|
1094
|
+
"qi/topic.action.receipt.record": { displayName: "Record Action Receipt", description: "Record a signed Action receipt against its Topic request" },
|
|
1095
|
+
"qi/topic.action.request": { displayName: "Request Topic Action", description: "Request an Action against the current Topic revision" },
|
|
1096
|
+
"qi/topic.context.link": { displayName: "Link Topic Context", description: "Link a referenced resource or service to the Topic" },
|
|
1097
|
+
"qi/topic.contract.confirm-setup": { displayName: "Confirm Topic Setup", description: "Confirm the exact proposed Topic setup revision" },
|
|
1098
|
+
"qi/topic.decision.record": { displayName: "Record Topic Decision", description: "Record an authorised decision and its rationale" },
|
|
1099
|
+
"qi/topic.file.attach-reference": { displayName: "Attach File Reference", description: "Attach a pinned VFS file reference to the Topic" },
|
|
1100
|
+
"qi/topic.flow.bind": { displayName: "Bind Flow to Topic", description: "Bind a governed Flow revision to the Topic" },
|
|
1101
|
+
"qi/topic.flow.unbind": { displayName: "Unbind Flow from Topic", description: "Remove an existing Flow binding from the Topic" },
|
|
1102
|
+
"qi/topic.outcome.propose": { displayName: "Propose Topic Outcome", description: "Propose an evidence-backed outcome for review" },
|
|
1103
|
+
"qi/topic.status.transition": { displayName: "Change Topic Status", description: "Move the Topic between permitted lifecycle statuses" },
|
|
1104
|
+
"qi/wallet.fund": { displayName: "Fund Wallet", description: "Fund a wallet with an on-chain transfer" },
|
|
1105
|
+
"qi/wallet.generate": { displayName: "Generate Wallet", description: "Generate an IXO wallet and DID" },
|
|
1106
|
+
"qi/wallet.generateAndFund": { displayName: "Generate & Fund Wallet", description: "Generate an IXO wallet and fund it on-chain" },
|
|
1107
|
+
"qi/work.accept": { displayName: "Accept Work", description: "Accept submitted work using the stated authority" },
|
|
1108
|
+
"qi/work.assign": { displayName: "Assign Work", description: "Assign a referenced unit of work" },
|
|
1109
|
+
"qi/work.checkpoint": { displayName: "Record Work Checkpoint", description: "Record progress and evidence for work in progress" },
|
|
1110
|
+
"qi/work.dispatch": { displayName: "Dispatch Work", description: "Dispatch assigned work to its assignee" },
|
|
1111
|
+
"qi/work.submit": { displayName: "Submit Work", description: "Submit completed work for review" },
|
|
1112
|
+
"qi/xero.contact.create": { displayName: "Create Xero contact", description: "Add a customer or supplier in Xero" },
|
|
1113
|
+
"qi/xero.invoice.create": { displayName: "Create Xero invoice", description: "Draft a new Xero invoice" },
|
|
1114
|
+
"qi/xero.invoice.list": { displayName: "List Xero invoices", description: "Fetch invoices from Xero" },
|
|
1115
|
+
"qi/xero.payment.create": { displayName: "Record Xero payment", description: "Settle a Xero bill \u2014 typically wired to capture an on-chain tx hash as the reference" }
|
|
1116
|
+
};
|
|
1117
|
+
function humanizeActionType(type) {
|
|
1118
|
+
const withoutNamespace = type.replace(/^qi\//, "").replace(/^oracle\.?/, "oracle ");
|
|
1119
|
+
const words = withoutNamespace.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[./_-]+/).filter(Boolean);
|
|
1120
|
+
return words.map((word) => /^(iid|kyc|dao|nft|http|dm|pod|cw20)$/i.test(word) ? word.toUpperCase() : word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
1121
|
+
}
|
|
1122
|
+
function getActionPresentation(type) {
|
|
1123
|
+
const registered = PRESENTATION[type];
|
|
1124
|
+
if (registered) return registered;
|
|
1125
|
+
const displayName = humanizeActionType(type) || "Action";
|
|
1126
|
+
return { displayName, description: `Perform ${displayName.toLowerCase()} as part of this flow.` };
|
|
1127
|
+
}
|
|
1128
|
+
|
|
583
1129
|
// src/core/lib/warnOnce.ts
|
|
584
1130
|
var seen = /* @__PURE__ */ new Set();
|
|
585
1131
|
function warnOnce(key, message) {
|
|
@@ -588,8 +1134,160 @@ function warnOnce(key, message) {
|
|
|
588
1134
|
console.warn(message);
|
|
589
1135
|
}
|
|
590
1136
|
|
|
1137
|
+
// src/core/lib/actionRegistry/types.ts
|
|
1138
|
+
var TOPIC_ACTION_BASE_KINDS = ["task", "agent_task", "proposal", "evaluation", "claims", "question", "discussion", "incident"];
|
|
1139
|
+
var CollectionStateEnum = /* @__PURE__ */ ((CollectionStateEnum2) => {
|
|
1140
|
+
CollectionStateEnum2[CollectionStateEnum2["OPEN"] = 0] = "OPEN";
|
|
1141
|
+
CollectionStateEnum2[CollectionStateEnum2["PAUSED"] = 1] = "PAUSED";
|
|
1142
|
+
CollectionStateEnum2[CollectionStateEnum2["CLOSED"] = 2] = "CLOSED";
|
|
1143
|
+
return CollectionStateEnum2;
|
|
1144
|
+
})(CollectionStateEnum || {});
|
|
1145
|
+
|
|
1146
|
+
// src/core/lib/actionRegistry/topicPolicy.ts
|
|
1147
|
+
var ALL_KINDS = [...TOPIC_ACTION_BASE_KINDS];
|
|
1148
|
+
var HUMAN_OWNED = [
|
|
1149
|
+
/^qi\/human\./,
|
|
1150
|
+
/^qi\/governance\./,
|
|
1151
|
+
/^qi\/proposal\./,
|
|
1152
|
+
/^qi\/(claim|bid)\.(submit|evaluate)$/,
|
|
1153
|
+
/^qi\/(domain\.sign|entity\.|iid\.|identity\.|kyc\.)/,
|
|
1154
|
+
/^qi\/(carbon\.(harvest|retire)|wallet\.fund)/,
|
|
1155
|
+
/^qi\/xero\.(invoice|payment)\.create$/,
|
|
1156
|
+
/^qi\/settlement\./,
|
|
1157
|
+
/^qi\/topic\.(flow\.(bind|unbind)|status\.transition|contract\.accept|outcome\.confirm)$/,
|
|
1158
|
+
/^qi\/work\.accept$/,
|
|
1159
|
+
/^qi\/answer\.accept$/
|
|
1160
|
+
];
|
|
1161
|
+
var RESTRICTED = [/^qi\/oracle\.(configure|deploy|store)/i, /^qi\/(matrix\.register|sandbox\.provision|entity\.createOracle|wallet\.generate)/];
|
|
1162
|
+
function any(patterns, value) {
|
|
1163
|
+
return patterns.some((pattern) => pattern.test(value));
|
|
1164
|
+
}
|
|
1165
|
+
function requiredServices(type) {
|
|
1166
|
+
if (type.startsWith("qi/http.")) return ["http"];
|
|
1167
|
+
if (type === "qi/email.send") return ["email"];
|
|
1168
|
+
if (type === "qi/notification.push") return ["notifications"];
|
|
1169
|
+
if (/^qi\/(gmail|outlook|slack|googlecalendar|calendar|xero)\./.test(type)) return ["integrations"];
|
|
1170
|
+
if (/^qi\/flow\.run\./.test(type)) return ["flowRuns"];
|
|
1171
|
+
if (/^qi\/blueprint\./.test(type)) return ["blueprint"];
|
|
1172
|
+
if (/^qi\/bid\./.test(type)) return ["bid"];
|
|
1173
|
+
if (/^qi\/claim\./.test(type)) return ["claim"];
|
|
1174
|
+
if (type === "qi/collection.lifecycle") return ["collection"];
|
|
1175
|
+
if (type === "qi/collection.users") return ["collectionUsers"];
|
|
1176
|
+
if (/^qi\/(matrix\.dm|credential\.store)/.test(type)) return ["matrix"];
|
|
1177
|
+
if (/^qi\/(oracle\.|wallet\.|iid\.|matrix\.register|identity\.|entity\.createOracle|sandbox\.)/.test(type) || type === "qi/oracle.invoke") return ["oracle"];
|
|
1178
|
+
if (/^qi\/carbon\./.test(type)) return ["carbon"];
|
|
1179
|
+
if (/^qi\/entity\.transfer/.test(type)) return ["entity"];
|
|
1180
|
+
if (/^qi\/kyc\./.test(type)) return ["kyc"];
|
|
1181
|
+
if (/^qi\/eval\./.test(type)) return ["evalRegister", "rubric"];
|
|
1182
|
+
if (/^qi\/topic\./.test(type)) return ["topic"];
|
|
1183
|
+
if (/^qi\/agent\./.test(type)) return ["agents"];
|
|
1184
|
+
if (/^qi\/evidence\./.test(type)) return ["evidence"];
|
|
1185
|
+
if (/^qi\/evaluation\./.test(type)) return ["evaluations"];
|
|
1186
|
+
if (/^qi\/settlement\./.test(type)) return ["settlement"];
|
|
1187
|
+
if (/^qi\/incident\.escalate/.test(type)) return ["incidents"];
|
|
1188
|
+
if (/^qi\/(work|answer|incident\.mitigation)\./.test(type)) return ["topic"];
|
|
1189
|
+
if (/^qi\/(governance|proposal|domain\.)\./.test(type)) return ["portalHandlers"];
|
|
1190
|
+
return [];
|
|
1191
|
+
}
|
|
1192
|
+
function riskTier(action) {
|
|
1193
|
+
const type = action.type;
|
|
1194
|
+
if (/^qi\/(settlement\.execute|carbon\.retire)$/.test(type)) return "critical";
|
|
1195
|
+
if (/^qi\/(governance\.|claim\.evaluate|entity\.transfer|domain\.sign|xero\.payment\.create|wallet\.fund)/.test(type)) return "high";
|
|
1196
|
+
if (!action.sideEffect || /\.(list|loadBatches|card-preview|fetch)$/.test(type)) return "low";
|
|
1197
|
+
return "medium";
|
|
1198
|
+
}
|
|
1199
|
+
function sensitivePaths(type) {
|
|
1200
|
+
const input = [];
|
|
1201
|
+
const output = [];
|
|
1202
|
+
if (type.startsWith("qi/http.")) {
|
|
1203
|
+
input.push("headers.authorization", "headers.cookie", "headers.x-api-key", "body");
|
|
1204
|
+
output.push("data", "response");
|
|
1205
|
+
}
|
|
1206
|
+
if (/^qi\/(email|gmail|outlook|slack|notification|matrix\.dm)/.test(type)) {
|
|
1207
|
+
input.push("to", "cc", "bcc", "body", "template", "variables");
|
|
1208
|
+
output.push("providerResponse");
|
|
1209
|
+
}
|
|
1210
|
+
if (/^qi\/(gmail|outlook|slack|googlecalendar|calendar|xero)\./.test(type)) input.push("connection", "bindingId");
|
|
1211
|
+
if (/^qi\/(kyc|credential)\./.test(type)) {
|
|
1212
|
+
input.push("data", "credential");
|
|
1213
|
+
output.push("credential", "surveyAnswers");
|
|
1214
|
+
}
|
|
1215
|
+
if (/^qi\/(oracle\.|wallet\.|iid\.|matrix\.register|identity\.|entity\.createOracle|sandbox\.)/.test(type)) {
|
|
1216
|
+
input.push("mnemonic", "pin", "secrets", "config");
|
|
1217
|
+
output.push("mnemonic", "privateKey", "matrixAccessToken", "matrixPassword", "matrixRecoveryPhrase", "secrets");
|
|
1218
|
+
}
|
|
1219
|
+
if (type === "qi/oracle.invoke") {
|
|
1220
|
+
input.push("prompt");
|
|
1221
|
+
output.push("result");
|
|
1222
|
+
}
|
|
1223
|
+
if (/^qi\/(form|human\.form|claim|bid)\./.test(type)) {
|
|
1224
|
+
input.push("answers", "surveyAnswers", "surveyData");
|
|
1225
|
+
output.push("answers", "surveyAnswers", "surveyData");
|
|
1226
|
+
}
|
|
1227
|
+
return { sensitiveInputPaths: [...new Set(input)].sort(), sensitiveOutputPaths: [...new Set(output)].sort() };
|
|
1228
|
+
}
|
|
1229
|
+
function topicKindsAndRelevance(type) {
|
|
1230
|
+
if (any(RESTRICTED, type)) return { supportedBaseKinds: ["agent_task"], relevance: "restricted" };
|
|
1231
|
+
if (/^qi\/flow\.run\./.test(type)) return { supportedBaseKinds: ALL_KINDS, relevance: "recommended" };
|
|
1232
|
+
if (/^qi\/(oracle\.invoke|agent\.)/.test(type)) return { supportedBaseKinds: ["agent_task", "question", "task"], relevance: "recommended" };
|
|
1233
|
+
if (/^qi\/(governance\.|proposal\.|topic\.decision)/.test(type)) return { supportedBaseKinds: ["proposal", "discussion", "evaluation"], relevance: "recommended" };
|
|
1234
|
+
if (/^qi\/(eval\.|evaluation\.)/.test(type)) return { supportedBaseKinds: ["evaluation", "claims"], relevance: "recommended" };
|
|
1235
|
+
if (/^qi\/(claim\.|collection\.|settlement\.)/.test(type)) return { supportedBaseKinds: ["claims", "evaluation"], relevance: "recommended" };
|
|
1236
|
+
if (/^qi\/(work\.)/.test(type)) return { supportedBaseKinds: ["task", "discussion", "incident"], relevance: "recommended" };
|
|
1237
|
+
if (/^qi\/(evidence\.|answer\.)/.test(type)) return { supportedBaseKinds: ["question", "evaluation"], relevance: "recommended" };
|
|
1238
|
+
if (/^qi\/incident\./.test(type)) return { supportedBaseKinds: ["incident"], relevance: "recommended" };
|
|
1239
|
+
if (/^qi\/topic\./.test(type)) return { supportedBaseKinds: ALL_KINDS, relevance: "recommended" };
|
|
1240
|
+
if (/^qi\/(http\.|domain\.card-preview|calendar\.|googlecalendar\.)/.test(type))
|
|
1241
|
+
return { supportedBaseKinds: ["question", "evaluation", "agent_task", "task"], relevance: "contextual" };
|
|
1242
|
+
if (/^qi\/(email\.|gmail\.|outlook\.|slack\.|matrix\.dm|notification\.)/.test(type)) {
|
|
1243
|
+
return { supportedBaseKinds: ["task", "question", "discussion", "incident"], relevance: "contextual" };
|
|
1244
|
+
}
|
|
1245
|
+
return { supportedBaseKinds: ALL_KINDS, relevance: "contextual" };
|
|
1246
|
+
}
|
|
1247
|
+
function topicSemanticRecords(type) {
|
|
1248
|
+
if (/^qi\/(governance\.|proposal\.)/.test(type)) return ["org.ixo.topic.proposal-receipt"];
|
|
1249
|
+
if (/^qi\/claim\.submit/.test(type)) return ["org.ixo.topic.claim-submission"];
|
|
1250
|
+
if (/^qi\/claim\.evaluate/.test(type)) return ["org.ixo.topic.claim-evaluation"];
|
|
1251
|
+
if (/^qi\/evaluation\./.test(type)) return ["org.ixo.topic.evaluation-assertion"];
|
|
1252
|
+
return [];
|
|
1253
|
+
}
|
|
1254
|
+
function normalizeActionPolicy(action) {
|
|
1255
|
+
const sensitive = sensitivePaths(action.type);
|
|
1256
|
+
const topicSelection = topicKindsAndRelevance(action.type);
|
|
1257
|
+
const semanticRecords = topicSemanticRecords(action.type);
|
|
1258
|
+
const declaredTopic = action.topic;
|
|
1259
|
+
const sensitiveInputPaths = [.../* @__PURE__ */ new Set([...action.sensitiveInputPaths || [], ...sensitive.sensitiveInputPaths])].sort();
|
|
1260
|
+
const sensitiveOutputPaths = [.../* @__PURE__ */ new Set([...action.sensitiveOutputPaths || [], ...sensitive.sensitiveOutputPaths])].sort();
|
|
1261
|
+
return {
|
|
1262
|
+
executionOwner: action.executionOwner || (any(HUMAN_OWNED, action.type) ? "human" : "agent"),
|
|
1263
|
+
riskTier: action.riskTier || riskTier(action),
|
|
1264
|
+
requiredServices: [...new Set(action.requiredServices || requiredServices(action.type))].sort(),
|
|
1265
|
+
sensitiveInputPaths,
|
|
1266
|
+
sensitiveOutputPaths,
|
|
1267
|
+
topic: declaredTopic ? {
|
|
1268
|
+
...declaredTopic,
|
|
1269
|
+
semanticRecordTypes: declaredTopic.semanticRecordTypes || getTopicSemanticRecordDefinitions(declaredTopic.permittedTopicRecordTypes),
|
|
1270
|
+
permittedTopicRecordTypes: (declaredTopic.semanticRecordTypes || getTopicSemanticRecordDefinitions(declaredTopic.permittedTopicRecordTypes)).map(
|
|
1271
|
+
(definition) => definition.type
|
|
1272
|
+
)
|
|
1273
|
+
} : {
|
|
1274
|
+
...topicSelection,
|
|
1275
|
+
writeBackMode: semanticRecords.length > 0 ? "semantic-record" : "receipt-only",
|
|
1276
|
+
semanticRecordTypes: getTopicSemanticRecordDefinitions(semanticRecords),
|
|
1277
|
+
permittedTopicRecordTypes: semanticRecords,
|
|
1278
|
+
lifecycleEffect: "none",
|
|
1279
|
+
requiredTopicAbilities: ["topic/request-action", "topic/record-action"],
|
|
1280
|
+
redactionPolicy: { mode: "paths", sensitiveInputPaths, sensitiveOutputPaths }
|
|
1281
|
+
}
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
|
|
591
1285
|
// src/core/lib/actionRegistry/registry.ts
|
|
592
1286
|
var actions = /* @__PURE__ */ new Map();
|
|
1287
|
+
var registryRevision = 0;
|
|
1288
|
+
function getRegistryRevision() {
|
|
1289
|
+
return registryRevision;
|
|
1290
|
+
}
|
|
593
1291
|
var STEP_COMPLETED_EVENT_NAME = "step.completed";
|
|
594
1292
|
var STEP_COMPLETED_EVENT = {
|
|
595
1293
|
name: STEP_COMPLETED_EVENT_NAME,
|
|
@@ -604,6 +1302,7 @@ var neverDone = {
|
|
|
604
1302
|
isDone: () => false
|
|
605
1303
|
};
|
|
606
1304
|
var ACTION_TYPE_ALIASES = {
|
|
1305
|
+
oracle: "qi/oracle.invoke",
|
|
607
1306
|
bid: "qi/bid.submit",
|
|
608
1307
|
claim: "qi/claim.submit",
|
|
609
1308
|
evaluateBid: "qi/bid.evaluate",
|
|
@@ -627,7 +1326,20 @@ var ACTION_TYPE_ALIASES = {
|
|
|
627
1326
|
MemberMultiSelect: "qi/pod.member-multi-select",
|
|
628
1327
|
governanceConfig: "qi/pod.governance-config",
|
|
629
1328
|
listDomainFlows: "qi/pod.list-domain-flows",
|
|
630
|
-
"matrix.dm": "qi/matrix.dm"
|
|
1329
|
+
"matrix.dm": "qi/matrix.dm",
|
|
1330
|
+
// The qi/calendar.* namespace is retired permanently (IXO-4420 §5): these
|
|
1331
|
+
// blocks were always Google Calendar via Composio, misnamed as neutral.
|
|
1332
|
+
// Aliases keep every existing document loading; because resolveActionType
|
|
1333
|
+
// checks aliases before registered types, no new canonical action can ever
|
|
1334
|
+
// be registered under these names — the IXO-native calendar (M4) takes
|
|
1335
|
+
// qi/ixo.calendar.event.* instead.
|
|
1336
|
+
"qi/calendar.event.create": "qi/googlecalendar.event.create-self",
|
|
1337
|
+
"qi/calendar.event.update": "qi/googlecalendar.event.update-self",
|
|
1338
|
+
"qi/calendar.event.list": "qi/googlecalendar.event.list-self",
|
|
1339
|
+
// Topic v3 called setup confirmation "accept contract". Persisted Flow
|
|
1340
|
+
// documents keep that action key, so resolve it to the v4 confirmation
|
|
1341
|
+
// contract while new documents use the canonical name.
|
|
1342
|
+
"qi/topic.contract.accept": "qi/topic.contract.confirm-setup"
|
|
631
1343
|
};
|
|
632
1344
|
var aliases = new Map(Object.entries(ACTION_TYPE_ALIASES));
|
|
633
1345
|
function resolveActionType(type) {
|
|
@@ -678,13 +1390,17 @@ function capabilityPatternCoversCan(pattern, can, options = {}) {
|
|
|
678
1390
|
return false;
|
|
679
1391
|
}
|
|
680
1392
|
function registerAction(definition) {
|
|
681
|
-
const
|
|
682
|
-
|
|
1393
|
+
const presentation = getActionPresentation(definition.type);
|
|
1394
|
+
definition.displayName = definition.displayName?.trim() || presentation.displayName;
|
|
1395
|
+
definition.description = definition.description?.trim() || presentation.description;
|
|
1396
|
+
if (definition.can) definition.can = normalizeCan(definition.can);
|
|
1397
|
+
Object.assign(definition, normalizeActionPolicy(definition));
|
|
1398
|
+
if (!definition.done) {
|
|
683
1399
|
warnOnce(`missing-done-contract:${definition.type}`, `[flow-config] action ${definition.type}: no done contract declared; defaulting to state === 'completed'`);
|
|
684
|
-
|
|
685
|
-
return;
|
|
1400
|
+
definition.done = doneWhenCompleted;
|
|
686
1401
|
}
|
|
687
|
-
actions.set(definition.type,
|
|
1402
|
+
actions.set(definition.type, definition);
|
|
1403
|
+
registryRevision += 1;
|
|
688
1404
|
}
|
|
689
1405
|
function getAction(type) {
|
|
690
1406
|
return actions.get(resolveActionType(type));
|
|
@@ -770,6 +1486,8 @@ function normalizeInputs(inputs) {
|
|
|
770
1486
|
}
|
|
771
1487
|
|
|
772
1488
|
// src/core/lib/actionRegistry/manifest.ts
|
|
1489
|
+
var ACTION_MANIFEST_VERSION = "4";
|
|
1490
|
+
var ACTION_REGISTRY_VERSION = "6.32.0-topic-actions.3";
|
|
773
1491
|
function serializeProof(action) {
|
|
774
1492
|
const proof = action.proof;
|
|
775
1493
|
if (proof === "none" || proof === void 0) return { kind: "none" };
|
|
@@ -784,42 +1502,445 @@ function serializeDone(action) {
|
|
|
784
1502
|
cardinality: action.cardinality || "once"
|
|
785
1503
|
};
|
|
786
1504
|
}
|
|
787
|
-
function
|
|
788
|
-
const
|
|
1505
|
+
function serializeAction(action, aliases2) {
|
|
1506
|
+
const can = action.can || actionTypeToCan(action.type) || "";
|
|
1507
|
+
return {
|
|
1508
|
+
type: action.type,
|
|
1509
|
+
displayName: action.displayName,
|
|
1510
|
+
description: action.description,
|
|
1511
|
+
aliases: aliases2,
|
|
1512
|
+
can,
|
|
1513
|
+
effectiveCapability: {
|
|
1514
|
+
action: can,
|
|
1515
|
+
...action.requiredCapability ? { flowExecution: action.requiredCapability } : {},
|
|
1516
|
+
topicWriteBack: [...action.topic?.requiredTopicAbilities || []].sort()
|
|
1517
|
+
},
|
|
1518
|
+
sideEffect: action.sideEffect,
|
|
1519
|
+
defaultRequiresConfirmation: action.defaultRequiresConfirmation,
|
|
1520
|
+
executionOwner: action.executionOwner || "agent",
|
|
1521
|
+
riskTier: action.riskTier || "medium",
|
|
1522
|
+
requiredServices: [...action.requiredServices || []].sort(),
|
|
1523
|
+
sensitiveInputPaths: [...action.sensitiveInputPaths || []].sort(),
|
|
1524
|
+
sensitiveOutputPaths: [...action.sensitiveOutputPaths || []].sort(),
|
|
1525
|
+
hidden: action.hiddenFromAuthoring === true,
|
|
1526
|
+
deprecated: action.deprecated === true,
|
|
1527
|
+
...action.supersededBy ? { supersededBy: action.supersededBy } : {},
|
|
1528
|
+
proof: serializeProof(action),
|
|
1529
|
+
done: serializeDone(action),
|
|
1530
|
+
inputSchema: action.inputSchema || {},
|
|
1531
|
+
outputSchema: action.outputSchema || [],
|
|
1532
|
+
events: (action.events || []).map((event) => ({
|
|
1533
|
+
name: event.name,
|
|
1534
|
+
displayName: event.displayName,
|
|
1535
|
+
description: event.description,
|
|
1536
|
+
payloadSchema: event.payloadSchema
|
|
1537
|
+
})),
|
|
1538
|
+
hasDynamicEvents: !!action.getDynamicEvents,
|
|
1539
|
+
hasDynamicOutputSchema: !!action.getDynamicOutputSchema,
|
|
1540
|
+
eligibleForEventTrigger: !!action.eligibleForEventTrigger,
|
|
1541
|
+
eligibleForTimeTrigger: !!action.eligibleForTimeTrigger,
|
|
1542
|
+
...action.scheduling ? { scheduling: action.scheduling } : {},
|
|
1543
|
+
hasCustomInputValidation: !!action.getMissingInputs,
|
|
1544
|
+
topic: action.topic
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
function contractDigestPayload(entry) {
|
|
1548
|
+
const { displayName: _displayName, description: _description, topic, ...contract } = entry;
|
|
1549
|
+
const { semanticRecordTypes, ...legacyTopic } = topic;
|
|
1550
|
+
const semanticContracts = semanticRecordTypes.map(({ displayName: _recordDisplayName, description: _recordDescription, ...definition }) => definition);
|
|
1551
|
+
return {
|
|
1552
|
+
...contract,
|
|
1553
|
+
topic: semanticContracts.length > 0 ? { ...legacyTopic, semanticRecordTypes: semanticContracts } : legacyTopic
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
function buildActionManifest() {
|
|
789
1557
|
const aliasesByType = /* @__PURE__ */ new Map();
|
|
790
|
-
for (const [alias, canonical] of
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
if (action.
|
|
810
|
-
if (action.
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
1558
|
+
for (const [alias, canonical] of getAliasEntries()) {
|
|
1559
|
+
aliasesByType.set(canonical, [...aliasesByType.get(canonical) || [], alias]);
|
|
1560
|
+
}
|
|
1561
|
+
const actions2 = getAllActions().map((action) => serializeAction(action, [...aliasesByType.get(action.type) || []].sort(compareCodeUnits))).sort((a, b) => compareCodeUnits(a.type, b.type)).map((entry) => ({ ...entry, contractDigest: sha256Digest(contractDigestPayload(entry)) }));
|
|
1562
|
+
const payload = { manifestVersion: ACTION_MANIFEST_VERSION, registryVersion: ACTION_REGISTRY_VERSION, actions: actions2 };
|
|
1563
|
+
return { ...payload, manifestDigest: sha256Digest(payload) };
|
|
1564
|
+
}
|
|
1565
|
+
var cached;
|
|
1566
|
+
function generateActionManifest() {
|
|
1567
|
+
const revision = getRegistryRevision();
|
|
1568
|
+
if (cached && cached.revision === revision) return cached.manifest;
|
|
1569
|
+
const manifest = buildActionManifest();
|
|
1570
|
+
cached = { revision, manifest };
|
|
1571
|
+
return manifest;
|
|
1572
|
+
}
|
|
1573
|
+
function actionManifestIssues(manifest = generateActionManifest()) {
|
|
1574
|
+
const issues = [];
|
|
1575
|
+
for (const action of manifest.actions) {
|
|
1576
|
+
const publicAction = !action.hidden;
|
|
1577
|
+
if (!action.can) issues.push({ actionType: action.type, code: "MISSING_CAN", message: "Action has no canonical can ability." });
|
|
1578
|
+
if (!action.displayName.trim() || !action.description.trim() || action.displayName === action.type || action.description.includes(action.type) || /^Perform .* as part of this flow\.$/.test(action.description)) {
|
|
1579
|
+
issues.push({ actionType: action.type, code: "MISSING_PRESENTATION", message: "Action needs a non-technical display name and description." });
|
|
1580
|
+
}
|
|
1581
|
+
if (publicAction && Object.keys(action.inputSchema).length === 0) {
|
|
1582
|
+
issues.push({ actionType: action.type, code: "MISSING_INPUT_SCHEMA", message: "Public Action has no input schema." });
|
|
1583
|
+
}
|
|
1584
|
+
if (publicAction && action.outputSchema.length === 0 && !action.hasDynamicOutputSchema) {
|
|
1585
|
+
issues.push({ actionType: action.type, code: "MISSING_OUTPUT_SCHEMA", message: "Public Action has no output schema." });
|
|
1586
|
+
}
|
|
1587
|
+
if (action.sideEffect && action.proof.kind === "none") {
|
|
1588
|
+
issues.push({ actionType: action.type, code: "SIDE_EFFECT_WITHOUT_PROOF", message: "Side-effecting Action declares no proof." });
|
|
1589
|
+
}
|
|
1590
|
+
if (!action.topic) issues.push({ actionType: action.type, code: "MISSING_TOPIC_POLICY", message: "Action has no Topic compatibility policy." });
|
|
1591
|
+
else if (action.topic.lifecycleEffect !== "none" || action.topic.supportedBaseKinds.length === 0) {
|
|
1592
|
+
issues.push({ actionType: action.type, code: "INVALID_TOPIC_POLICY", message: "Topic policy must support a base Kind and cannot imply lifecycle effects." });
|
|
1593
|
+
}
|
|
1594
|
+
if (action.topic.semanticRecordTypes.some(
|
|
1595
|
+
(definition) => !definition.type || !definition.version || !definition.displayName.trim() || !definition.description.trim() || Object.keys(definition.valueSchema).length === 0
|
|
1596
|
+
) || action.topic.permittedTopicRecordTypes.join("|") !== action.topic.semanticRecordTypes.map((definition) => definition.type).join("|")) {
|
|
1597
|
+
issues.push({
|
|
1598
|
+
actionType: action.type,
|
|
1599
|
+
code: "INVALID_SEMANTIC_RECORD_DEFINITION",
|
|
1600
|
+
message: "Topic semantic record definitions must be complete and determine the compatibility type list."
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
return issues;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
// src/core/lib/actionRegistry/manifestV2.schema.ts
|
|
1608
|
+
var ACTION_MANIFEST_V2_SCHEMA = {
|
|
1609
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1610
|
+
$id: "https://ixo.world/schemas/qi/action-manifest-v2.schema.json",
|
|
1611
|
+
type: "object",
|
|
1612
|
+
additionalProperties: false,
|
|
1613
|
+
required: ["manifestVersion", "manifestId", "generatedAt", "package", "issuer", "compatibility", "actions", "primitives", "integrity"],
|
|
1614
|
+
properties: {
|
|
1615
|
+
manifestVersion: { const: "2.0" },
|
|
1616
|
+
manifestId: { type: "string", minLength: 1 },
|
|
1617
|
+
generatedAt: {
|
|
1618
|
+
type: "string",
|
|
1619
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$"
|
|
1620
|
+
},
|
|
1621
|
+
package: {
|
|
1622
|
+
type: "object",
|
|
1623
|
+
additionalProperties: false,
|
|
1624
|
+
required: ["name", "version", "sourceCommit"],
|
|
1625
|
+
properties: {
|
|
1626
|
+
name: { type: "string", minLength: 1 },
|
|
1627
|
+
version: { type: "string", minLength: 1 },
|
|
1628
|
+
sourceCommit: { type: "string", minLength: 1 },
|
|
1629
|
+
buildId: { type: "string", minLength: 1 },
|
|
1630
|
+
distributionUrl: { type: "string", pattern: "^[a-zA-Z][a-zA-Z0-9+.-]*:" }
|
|
1631
|
+
}
|
|
1632
|
+
},
|
|
1633
|
+
issuer: {
|
|
1634
|
+
type: "object",
|
|
1635
|
+
additionalProperties: false,
|
|
1636
|
+
required: ["did", "verificationMethod"],
|
|
1637
|
+
properties: {
|
|
1638
|
+
did: { type: "string", pattern: "^did:[a-z0-9]+:.+" },
|
|
1639
|
+
verificationMethod: { type: "string", pattern: "^did:[a-z0-9]+:.+#.+" }
|
|
1640
|
+
}
|
|
1641
|
+
},
|
|
1642
|
+
compatibility: {
|
|
1643
|
+
type: "object",
|
|
1644
|
+
additionalProperties: false,
|
|
1645
|
+
required: ["engine", "flowSchemaVersions", "features"],
|
|
1646
|
+
properties: {
|
|
1647
|
+
engine: {
|
|
1648
|
+
type: "object",
|
|
1649
|
+
additionalProperties: false,
|
|
1650
|
+
required: ["versionRange"],
|
|
1651
|
+
properties: {
|
|
1652
|
+
versionRange: { type: "string", minLength: 1 }
|
|
1653
|
+
}
|
|
1654
|
+
},
|
|
1655
|
+
flowSchemaVersions: {
|
|
1656
|
+
type: "array",
|
|
1657
|
+
minItems: 1,
|
|
1658
|
+
uniqueItems: true,
|
|
1659
|
+
items: { type: "string", minLength: 1 }
|
|
1660
|
+
},
|
|
1661
|
+
features: {
|
|
1662
|
+
type: "array",
|
|
1663
|
+
minItems: 1,
|
|
1664
|
+
uniqueItems: true,
|
|
1665
|
+
items: { type: "string", minLength: 1 }
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
},
|
|
1669
|
+
actions: {
|
|
1670
|
+
type: "array",
|
|
1671
|
+
items: { $ref: "#/$defs/contract" }
|
|
1672
|
+
},
|
|
1673
|
+
primitives: {
|
|
1674
|
+
type: "array",
|
|
1675
|
+
items: { $ref: "#/$defs/contract" }
|
|
1676
|
+
},
|
|
1677
|
+
integrity: {
|
|
1678
|
+
type: "object",
|
|
1679
|
+
additionalProperties: false,
|
|
1680
|
+
required: ["canonicalization", "digest", "signature"],
|
|
1681
|
+
properties: {
|
|
1682
|
+
canonicalization: { const: "RFC8785" },
|
|
1683
|
+
digest: {
|
|
1684
|
+
type: "object",
|
|
1685
|
+
additionalProperties: false,
|
|
1686
|
+
required: ["algorithm", "value"],
|
|
1687
|
+
properties: {
|
|
1688
|
+
algorithm: { const: "SHA-256" },
|
|
1689
|
+
value: { type: "string", pattern: "^sha256:[0-9a-f]{64}$" }
|
|
1690
|
+
}
|
|
1691
|
+
},
|
|
1692
|
+
signature: {
|
|
1693
|
+
type: "object",
|
|
1694
|
+
additionalProperties: false,
|
|
1695
|
+
required: ["algorithm", "verificationMethod", "value"],
|
|
1696
|
+
properties: {
|
|
1697
|
+
algorithm: { type: "string", minLength: 1 },
|
|
1698
|
+
verificationMethod: { type: "string", pattern: "^did:[a-z0-9]+:.+#.+" },
|
|
1699
|
+
value: { type: "string", minLength: 1 }
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
},
|
|
1705
|
+
$defs: {
|
|
1706
|
+
contract: {
|
|
1707
|
+
type: "object",
|
|
1708
|
+
required: ["type", "contractVersion"],
|
|
1709
|
+
properties: {
|
|
1710
|
+
type: { type: "string", minLength: 1 },
|
|
1711
|
+
contractVersion: { type: "string", minLength: 1 },
|
|
1712
|
+
aliases: {
|
|
1713
|
+
type: "array",
|
|
1714
|
+
uniqueItems: true,
|
|
1715
|
+
items: { type: "string", minLength: 1 }
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
};
|
|
1721
|
+
|
|
1722
|
+
// src/core/lib/actionRegistry/manifestV2.ts
|
|
1723
|
+
import Ajv20202 from "ajv/dist/2020.js";
|
|
1724
|
+
var ActionManifestV2VerificationError = class extends Error {
|
|
1725
|
+
constructor(code, message) {
|
|
1726
|
+
super(message);
|
|
1727
|
+
this.name = "ActionManifestV2VerificationError";
|
|
1728
|
+
this.code = code;
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
var validateSignedManifest = new Ajv20202({ allErrors: true, strict: false }).compile(ACTION_MANIFEST_V2_SCHEMA);
|
|
1732
|
+
var SIGNATURE_DOMAIN = "qi.action-manifest.v2\0";
|
|
1733
|
+
function isPlainObject(value) {
|
|
1734
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1735
|
+
return prototype === Object.prototype || prototype === null;
|
|
1736
|
+
}
|
|
1737
|
+
function assertValidUnicode(value) {
|
|
1738
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1739
|
+
const codeUnit = value.charCodeAt(index);
|
|
1740
|
+
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
1741
|
+
const next = value.charCodeAt(index + 1);
|
|
1742
|
+
if (!(next >= 56320 && next <= 57343)) {
|
|
1743
|
+
throw new TypeError("Manifest v2 contains an unpaired Unicode surrogate");
|
|
1744
|
+
}
|
|
1745
|
+
index += 1;
|
|
1746
|
+
} else if (codeUnit >= 56320 && codeUnit <= 57343) {
|
|
1747
|
+
throw new TypeError("Manifest v2 contains an unpaired Unicode surrogate");
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
function canonicalizeManifestV2Payload(payload) {
|
|
1752
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
1753
|
+
const serialize = (value) => {
|
|
1754
|
+
if (value === null || typeof value === "boolean") {
|
|
1755
|
+
return JSON.stringify(value);
|
|
1756
|
+
}
|
|
1757
|
+
if (typeof value === "string") {
|
|
1758
|
+
assertValidUnicode(value);
|
|
1759
|
+
return JSON.stringify(value);
|
|
1760
|
+
}
|
|
1761
|
+
if (typeof value === "number") {
|
|
1762
|
+
if (!Number.isFinite(value)) {
|
|
1763
|
+
throw new TypeError("Manifest v2 contains a non-finite number");
|
|
1764
|
+
}
|
|
1765
|
+
return JSON.stringify(value);
|
|
1766
|
+
}
|
|
1767
|
+
if (typeof value !== "object") {
|
|
1768
|
+
throw new TypeError(`Manifest v2 contains a non-JSON value: ${typeof value}`);
|
|
1769
|
+
}
|
|
1770
|
+
if (ancestors.has(value)) {
|
|
1771
|
+
throw new TypeError("Manifest v2 contains a circular reference");
|
|
1772
|
+
}
|
|
1773
|
+
ancestors.add(value);
|
|
1774
|
+
try {
|
|
1775
|
+
if (Array.isArray(value)) {
|
|
1776
|
+
return `[${value.map((entry) => serialize(entry)).join(",")}]`;
|
|
1777
|
+
}
|
|
1778
|
+
if (!isPlainObject(value)) {
|
|
1779
|
+
throw new TypeError("Manifest v2 contains a non-plain object");
|
|
1780
|
+
}
|
|
1781
|
+
return `{${Object.keys(value).sort().map((key) => {
|
|
1782
|
+
assertValidUnicode(key);
|
|
1783
|
+
return `${JSON.stringify(key)}:${serialize(value[key])}`;
|
|
1784
|
+
}).join(",")}}`;
|
|
1785
|
+
} finally {
|
|
1786
|
+
ancestors.delete(value);
|
|
1787
|
+
}
|
|
1788
|
+
};
|
|
1789
|
+
return serialize(payload);
|
|
1790
|
+
}
|
|
1791
|
+
async function computeActionManifestV2Digest(payload) {
|
|
1792
|
+
if (!globalThis.crypto?.subtle) {
|
|
1793
|
+
throw new Error("Web Crypto is required to digest an Action Manifest v2 contract");
|
|
1794
|
+
}
|
|
1795
|
+
const canonicalPayload = canonicalizeManifestV2Payload(payload);
|
|
1796
|
+
const digest2 = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonicalPayload));
|
|
1797
|
+
const hex = Array.from(new Uint8Array(digest2), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1798
|
+
return `sha256:${hex}`;
|
|
1799
|
+
}
|
|
1800
|
+
function signatureInput(digest2) {
|
|
1801
|
+
return new TextEncoder().encode(`${SIGNATURE_DOMAIN}${digest2}`);
|
|
1802
|
+
}
|
|
1803
|
+
function schemaFailure() {
|
|
1804
|
+
const details = (validateSignedManifest.errors || []).map((error) => `${error.instancePath || "/"} ${error.message || "is invalid"}`).join("; ");
|
|
1805
|
+
return new ActionManifestV2VerificationError("INVALID_MANIFEST", details ? `Action Manifest v2 is invalid: ${details}` : "Action Manifest v2 is invalid");
|
|
1806
|
+
}
|
|
1807
|
+
function payloadFromSignedManifest(manifest) {
|
|
1808
|
+
const { integrity: _integrity, ...payload } = manifest;
|
|
1809
|
+
return payload;
|
|
1810
|
+
}
|
|
1811
|
+
function assertContractIdentities(payload) {
|
|
1812
|
+
const contracts = [...payload.actions, ...payload.primitives];
|
|
1813
|
+
const canonicalTypes = /* @__PURE__ */ new Set();
|
|
1814
|
+
for (const contract of contracts) {
|
|
1815
|
+
if (canonicalTypes.has(contract.type)) {
|
|
1816
|
+
throw new ActionManifestV2VerificationError("DUPLICATE_CONTRACT_TYPE", `Manifest contains duplicate canonical contract type: ${contract.type}`);
|
|
1817
|
+
}
|
|
1818
|
+
canonicalTypes.add(contract.type);
|
|
1819
|
+
}
|
|
1820
|
+
for (const [groupName, group] of [
|
|
1821
|
+
["actions", payload.actions],
|
|
1822
|
+
["primitives", payload.primitives]
|
|
1823
|
+
]) {
|
|
1824
|
+
for (let index = 1; index < group.length; index += 1) {
|
|
1825
|
+
if (group[index - 1].type > group[index].type) {
|
|
1826
|
+
throw new ActionManifestV2VerificationError("UNSORTED_CONTRACTS", `Manifest ${groupName} must be sorted by canonical type`);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
const claimedNames = new Map(Array.from(canonicalTypes, (type) => [type, type]));
|
|
1831
|
+
for (const contract of contracts) {
|
|
1832
|
+
const aliases2 = contract.aliases;
|
|
1833
|
+
if (!Array.isArray(aliases2)) continue;
|
|
1834
|
+
for (const alias of aliases2) {
|
|
1835
|
+
const existingOwner = claimedNames.get(alias);
|
|
1836
|
+
if (existingOwner) {
|
|
1837
|
+
throw new ActionManifestV2VerificationError("AMBIGUOUS_ALIAS", `Manifest alias ${alias} conflicts with contract ${existingOwner}`);
|
|
1838
|
+
}
|
|
1839
|
+
claimedNames.set(alias, contract.type);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
function deepFreeze(value) {
|
|
1844
|
+
for (const child of Object.values(value)) {
|
|
1845
|
+
if (typeof child === "object" && child !== null && !Object.isFrozen(child)) {
|
|
1846
|
+
deepFreeze(child);
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
return Object.freeze(value);
|
|
1850
|
+
}
|
|
1851
|
+
async function signActionManifestV2(payload, signer) {
|
|
1852
|
+
if (signer.issuerDid !== payload.issuer.did || signer.verificationMethod !== payload.issuer.verificationMethod) {
|
|
1853
|
+
throw new ActionManifestV2VerificationError("SIGNER_MISMATCH", "The Manifest v2 signer does not match the declared issuer verification method");
|
|
1854
|
+
}
|
|
1855
|
+
assertContractIdentities(payload);
|
|
1856
|
+
let digest2;
|
|
1857
|
+
try {
|
|
1858
|
+
digest2 = await computeActionManifestV2Digest(payload);
|
|
1859
|
+
} catch (error) {
|
|
1860
|
+
throw new ActionManifestV2VerificationError(
|
|
1861
|
+
"INVALID_MANIFEST",
|
|
1862
|
+
error instanceof Error ? `Action Manifest v2 cannot be canonicalized: ${error.message}` : "Action Manifest v2 cannot be canonicalized"
|
|
1863
|
+
);
|
|
1864
|
+
}
|
|
1865
|
+
const signature = await signer.sign(signatureInput(digest2));
|
|
1866
|
+
if (!signature) {
|
|
1867
|
+
throw new ActionManifestV2VerificationError("INVALID_SIGNATURE", "The Manifest v2 signer returned an empty signature");
|
|
1868
|
+
}
|
|
1869
|
+
const signed = {
|
|
1870
|
+
...payload,
|
|
1871
|
+
integrity: {
|
|
1872
|
+
canonicalization: "RFC8785",
|
|
1873
|
+
digest: {
|
|
1874
|
+
algorithm: "SHA-256",
|
|
1875
|
+
value: digest2
|
|
1876
|
+
},
|
|
1877
|
+
signature: {
|
|
1878
|
+
algorithm: signer.algorithm,
|
|
1879
|
+
verificationMethod: signer.verificationMethod,
|
|
1880
|
+
value: signature
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
};
|
|
1884
|
+
if (!validateSignedManifest(signed)) {
|
|
1885
|
+
throw schemaFailure();
|
|
1886
|
+
}
|
|
1887
|
+
assertContractIdentities(payload);
|
|
1888
|
+
return signed;
|
|
1889
|
+
}
|
|
1890
|
+
async function verifyActionManifestV2(candidate, trustPolicy) {
|
|
1891
|
+
if (typeof candidate !== "object" || candidate === null || !("manifestVersion" in candidate) || candidate.manifestVersion !== "2.0") {
|
|
1892
|
+
throw new ActionManifestV2VerificationError("UNSUPPORTED_MANIFEST_VERSION", "Only Action Manifest version 2.0 can cross this execution boundary");
|
|
1893
|
+
}
|
|
1894
|
+
if (!("integrity" in candidate) || typeof candidate.integrity !== "object") {
|
|
1895
|
+
throw new ActionManifestV2VerificationError("UNSIGNED_MANIFEST", "A signature and digest are required to load Action Manifest v2 for execution");
|
|
1896
|
+
}
|
|
1897
|
+
if (!validateSignedManifest(candidate)) {
|
|
1898
|
+
throw schemaFailure();
|
|
1899
|
+
}
|
|
1900
|
+
const manifest = candidate;
|
|
1901
|
+
const { issuer, integrity } = manifest;
|
|
1902
|
+
assertContractIdentities(manifest);
|
|
1903
|
+
if (!trustPolicy.trustedIssuerDids.includes(issuer.did)) {
|
|
1904
|
+
throw new ActionManifestV2VerificationError("UNTRUSTED_ISSUER", `Manifest issuer is not trusted: ${issuer.did}`);
|
|
1905
|
+
}
|
|
1906
|
+
if (trustPolicy.revokedIssuerDids?.includes(issuer.did)) {
|
|
1907
|
+
throw new ActionManifestV2VerificationError("REVOKED_ISSUER", `Manifest issuer is revoked: ${issuer.did}`);
|
|
1908
|
+
}
|
|
1909
|
+
if (integrity.signature.verificationMethod !== issuer.verificationMethod || !integrity.signature.verificationMethod.startsWith(`${issuer.did}#`)) {
|
|
1910
|
+
throw new ActionManifestV2VerificationError("VERIFICATION_METHOD_MISMATCH", "The signature verification method does not belong to the declared manifest issuer");
|
|
1911
|
+
}
|
|
1912
|
+
if (!trustPolicy.allowedSignatureAlgorithms.includes(integrity.signature.algorithm)) {
|
|
1913
|
+
throw new ActionManifestV2VerificationError("DISALLOWED_SIGNATURE_ALGORITHM", `Manifest signature algorithm is not allowed: ${integrity.signature.algorithm}`);
|
|
1914
|
+
}
|
|
1915
|
+
let digest2;
|
|
1916
|
+
try {
|
|
1917
|
+
digest2 = await computeActionManifestV2Digest(payloadFromSignedManifest(manifest));
|
|
1918
|
+
} catch (error) {
|
|
1919
|
+
throw new ActionManifestV2VerificationError(
|
|
1920
|
+
"INVALID_MANIFEST",
|
|
1921
|
+
error instanceof Error ? `Action Manifest v2 cannot be canonicalized: ${error.message}` : "Action Manifest v2 cannot be canonicalized"
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1924
|
+
if (digest2 !== integrity.digest.value) {
|
|
1925
|
+
throw new ActionManifestV2VerificationError("DIGEST_MISMATCH", "Manifest payload does not match its signed digest");
|
|
1926
|
+
}
|
|
1927
|
+
let validSignature = false;
|
|
1928
|
+
try {
|
|
1929
|
+
validSignature = await trustPolicy.verifySignature({
|
|
1930
|
+
issuerDid: issuer.did,
|
|
1931
|
+
verificationMethod: integrity.signature.verificationMethod,
|
|
1932
|
+
algorithm: integrity.signature.algorithm,
|
|
1933
|
+
data: signatureInput(digest2),
|
|
1934
|
+
signature: integrity.signature.value
|
|
1935
|
+
});
|
|
1936
|
+
} catch {
|
|
1937
|
+
validSignature = false;
|
|
1938
|
+
}
|
|
1939
|
+
if (!validSignature) {
|
|
1940
|
+
throw new ActionManifestV2VerificationError("INVALID_SIGNATURE", "Manifest signature verification failed");
|
|
1941
|
+
}
|
|
1942
|
+
const trustedSnapshot = JSON.parse(JSON.stringify(manifest));
|
|
1943
|
+
return deepFreeze(trustedSnapshot);
|
|
823
1944
|
}
|
|
824
1945
|
|
|
825
1946
|
// src/core/lib/actionRegistry/inputRequirements.ts
|
|
@@ -938,7 +2059,8 @@ function buildServicesFromHandlers(handlers) {
|
|
|
938
2059
|
request: async (params) => {
|
|
939
2060
|
const fetchOptions = {
|
|
940
2061
|
method: params.method,
|
|
941
|
-
headers: { "Content-Type": "application/json", ...params.headers }
|
|
2062
|
+
headers: { "Content-Type": "application/json", ...params.headers },
|
|
2063
|
+
redirect: "error"
|
|
942
2064
|
};
|
|
943
2065
|
if (params.method !== "GET" && params.body) {
|
|
944
2066
|
fetchOptions.body = typeof params.body === "string" ? params.body : JSON.stringify(params.body);
|
|
@@ -952,7 +2074,9 @@ function buildServicesFromHandlers(handlers) {
|
|
|
952
2074
|
return {
|
|
953
2075
|
status: res.status,
|
|
954
2076
|
headers: responseHeaders,
|
|
955
|
-
data
|
|
2077
|
+
data,
|
|
2078
|
+
responseDigest: sha256Digest(data),
|
|
2079
|
+
requestId: sha256Digest({ url: params.url, method: params.method, status: res.status, data })
|
|
956
2080
|
};
|
|
957
2081
|
}
|
|
958
2082
|
},
|
|
@@ -1323,6 +2447,29 @@ function markXeroWorkCompletedForEditor(editor, itemId, params) {
|
|
|
1323
2447
|
|
|
1324
2448
|
// src/core/lib/flowCompiler/connections.ts
|
|
1325
2449
|
import * as Y from "yjs";
|
|
2450
|
+
|
|
2451
|
+
// src/core/lib/yjsTypes.ts
|
|
2452
|
+
function getExistingYMap(yDoc, key) {
|
|
2453
|
+
if (!yDoc.share.has(key)) return void 0;
|
|
2454
|
+
return yDoc.getMap(key);
|
|
2455
|
+
}
|
|
2456
|
+
function isYMapLike(value) {
|
|
2457
|
+
if (!value || typeof value !== "object") return false;
|
|
2458
|
+
const candidate = value;
|
|
2459
|
+
return typeof candidate.get === "function" && typeof candidate.set === "function" && typeof candidate.has === "function" && typeof candidate.forEach === "function";
|
|
2460
|
+
}
|
|
2461
|
+
function isYXmlElementLike(value) {
|
|
2462
|
+
if (!value || typeof value !== "object") return false;
|
|
2463
|
+
const candidate = value;
|
|
2464
|
+
return typeof candidate.nodeName === "string" && typeof candidate.getAttribute === "function" && typeof candidate.getAttributes === "function";
|
|
2465
|
+
}
|
|
2466
|
+
function isYDocLike(value) {
|
|
2467
|
+
if (!value || typeof value !== "object") return false;
|
|
2468
|
+
const candidate = value;
|
|
2469
|
+
return typeof candidate.getMap === "function" && typeof candidate.getXmlFragment === "function" && typeof candidate.transact === "function";
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
// src/core/lib/flowCompiler/connections.ts
|
|
1326
2473
|
var FLOW_CONNECTIONS_MAP_KEY = "qi.flow.connections";
|
|
1327
2474
|
var FLOW_CONNECTION_BINDINGS_MAP_KEY = "qi.flow.connectionBindings";
|
|
1328
2475
|
var REQUIREMENT_KEYS = ["org", "bankAccount"];
|
|
@@ -1361,13 +2508,13 @@ function removeFlowConnection(yDoc, toolkit) {
|
|
|
1361
2508
|
function setFlowConnectionOptional(yDoc, toolkit, optional) {
|
|
1362
2509
|
yDoc.transact(() => {
|
|
1363
2510
|
const entry = getConnectionsMap(yDoc).get(toolkit);
|
|
1364
|
-
if (entry
|
|
2511
|
+
if (isYMapLike(entry)) mergeOptionalFlag(entry, "optional", optional);
|
|
1365
2512
|
});
|
|
1366
2513
|
}
|
|
1367
2514
|
function setFlowConnectionRequires(yDoc, toolkit, requires) {
|
|
1368
2515
|
yDoc.transact(() => {
|
|
1369
2516
|
const entry = getConnectionsMap(yDoc).get(toolkit);
|
|
1370
|
-
if (entry
|
|
2517
|
+
if (isYMapLike(entry)) entry.set("requires", toRequirementKeys(requires));
|
|
1371
2518
|
});
|
|
1372
2519
|
}
|
|
1373
2520
|
function readFlowConnectionBindings(yDoc) {
|
|
@@ -1414,13 +2561,13 @@ function getBindingsMap(yDoc) {
|
|
|
1414
2561
|
}
|
|
1415
2562
|
function ensureEntry(map, toolkit) {
|
|
1416
2563
|
const existing = map.get(toolkit);
|
|
1417
|
-
if (existing
|
|
2564
|
+
if (isYMapLike(existing)) return existing;
|
|
1418
2565
|
const created = new Y.Map();
|
|
1419
2566
|
map.set(toolkit, created);
|
|
1420
2567
|
return created;
|
|
1421
2568
|
}
|
|
1422
2569
|
function toConnection(value) {
|
|
1423
|
-
if (!(value
|
|
2570
|
+
if (!isYMapLike(value)) return void 0;
|
|
1424
2571
|
const toolkit = value.get("toolkit");
|
|
1425
2572
|
if (typeof toolkit !== "string" || toolkit.length === 0) return void 0;
|
|
1426
2573
|
const connection = { toolkit, requires: toRequirementKeys(value.get("requires")) };
|
|
@@ -1432,7 +2579,7 @@ function toConnection(value) {
|
|
|
1432
2579
|
return connection;
|
|
1433
2580
|
}
|
|
1434
2581
|
function toBinding(value) {
|
|
1435
|
-
if (!(value
|
|
2582
|
+
if (!isYMapLike(value)) return void 0;
|
|
1436
2583
|
const toolkit = toNonEmptyString(value.get("toolkit"));
|
|
1437
2584
|
const connectedAccountId = toNonEmptyString(value.get("connectedAccountId"));
|
|
1438
2585
|
const entityDid = toNonEmptyString(value.get("entityDid"));
|
|
@@ -2212,38 +3359,215 @@ for (const spec of ACTIONS) {
|
|
|
2212
3359
|
});
|
|
2213
3360
|
}
|
|
2214
3361
|
|
|
2215
|
-
// src/core/lib/actionRegistry/actions/governance/
|
|
2216
|
-
var
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
3362
|
+
// src/core/lib/actionRegistry/actions/governance/_shared.ts
|
|
3363
|
+
var GOVERNANCE_REQUIRED_FIELDS = {
|
|
3364
|
+
"qi/governance.authz.exec": ["authzExecActionType"],
|
|
3365
|
+
"qi/governance.authz.grant": ["grantee", "msgTypeUrl"],
|
|
3366
|
+
"qi/governance.authz.revoke": ["grantee", "msgTypeUrl"],
|
|
3367
|
+
"qi/governance.chain-governance-vote": ["proposalId", "vote"],
|
|
3368
|
+
"qi/governance.contract.execute": ["address", "message"],
|
|
3369
|
+
"qi/governance.contract.instantiate": ["codeId", "label", "message"],
|
|
3370
|
+
"qi/governance.contract.manage-cw20": ["adding", "address"],
|
|
3371
|
+
"qi/governance.contract.migrate": ["contract", "codeId", "msg"],
|
|
3372
|
+
"qi/governance.contract.update-admin": ["contract", "newAdmin"],
|
|
3373
|
+
"qi/governance.custom-message": ["message"],
|
|
3374
|
+
"qi/governance.dao.accept-to-marketplace": ["did", "relayerNodeAddress", "relayerNodeDid"],
|
|
3375
|
+
"qi/governance.dao.admin-exec": ["targetCoreAddress", "msgs"],
|
|
3376
|
+
"qi/governance.dao.create-entity": ["typeUrl", "value"],
|
|
3377
|
+
"qi/governance.dao.join": ["entityDid", "memberId"],
|
|
3378
|
+
"qi/governance.dao.manage-storage": ["setting", "key", "value"],
|
|
3379
|
+
"qi/governance.dao.manage-subdaos": [],
|
|
3380
|
+
"qi/governance.dao.update-info": ["name"],
|
|
3381
|
+
"qi/governance.member-proposal": ["operation", "members"],
|
|
3382
|
+
"qi/governance.nft.burn": ["collection", "tokenId"],
|
|
3383
|
+
"qi/governance.nft.manage-collections": ["adding", "address"],
|
|
3384
|
+
"qi/governance.nft.transfer": ["collection", "tokenId", "recipient"],
|
|
3385
|
+
"qi/governance.staking.stake": ["stakeType", "amount"],
|
|
3386
|
+
"qi/governance.staking.stake-to-group": ["tokenContract", "stakingContract", "amount"],
|
|
3387
|
+
"qi/governance.settings-proposal": ["votingPeriodHours", "quorumPercent", "thresholdPercent"],
|
|
3388
|
+
"qi/governance.submission-config-proposal": ["anyoneCanPropose", "depositRequired"],
|
|
3389
|
+
"qi/governance.transaction.mint": ["recipient", "amount"],
|
|
3390
|
+
"qi/governance.transaction.send-funds": ["recipient", "denom", "amount"],
|
|
3391
|
+
"qi/governance.transaction.perform-token-swap": ["tokenSwapContractAddress", "selfPartyType", "selfPartyDenomOrAddress", "selfPartyAmount"],
|
|
3392
|
+
"qi/governance.transaction.send-group-token": ["tokenContract", "recipient", "amount"],
|
|
3393
|
+
"qi/governance.transaction.withdraw-token-swap": ["tokenSwapContractAddress"],
|
|
3394
|
+
"qi/governance.validator.actions": ["validatorActionType"]
|
|
3395
|
+
};
|
|
3396
|
+
var GOVERNANCE_FIELDS = {
|
|
3397
|
+
"qi/governance.authz.exec": ["authzExecActionType", "delegatorAddress", "validatorAddress", "validatorDstAddress", "amount", "custom"],
|
|
3398
|
+
"qi/governance.authz.grant": ["grantee", "msgTypeUrl"],
|
|
3399
|
+
"qi/governance.authz.revoke": ["grantee", "msgTypeUrl"],
|
|
3400
|
+
"qi/governance.chain-governance-vote": ["proposalId", "vote"],
|
|
3401
|
+
"qi/governance.contract.execute": ["address", "message", "funds"],
|
|
3402
|
+
"qi/governance.contract.instantiate": ["codeId", "label", "admin", "message", "funds"],
|
|
3403
|
+
"qi/governance.contract.manage-cw20": ["adding", "address"],
|
|
3404
|
+
"qi/governance.contract.migrate": ["contract", "codeId", "msg"],
|
|
3405
|
+
"qi/governance.contract.update-admin": ["contract", "newAdmin"],
|
|
3406
|
+
"qi/governance.custom-message": ["message"],
|
|
3407
|
+
"qi/governance.dao.accept-to-marketplace": ["did", "relayerNodeAddress", "relayerNodeDid"],
|
|
3408
|
+
"qi/governance.dao.admin-exec": ["targetCoreAddress", "msgs"],
|
|
3409
|
+
"qi/governance.dao.create-entity": ["typeUrl", "value"],
|
|
3410
|
+
"qi/governance.dao.join": ["entityDid", "memberId"],
|
|
3411
|
+
"qi/governance.dao.manage-storage": ["setting", "key", "value"],
|
|
3412
|
+
"qi/governance.dao.manage-subdaos": ["toAdd", "toRemove"],
|
|
3413
|
+
"qi/governance.dao.update-info": ["name", "daoDescription", "imageUrl", "automaticallyAddCw20s", "automaticallyAddCw721s"],
|
|
3414
|
+
"qi/governance.member-proposal": ["operation", "members"],
|
|
3415
|
+
"qi/governance.nft.burn": ["collection", "tokenId"],
|
|
3416
|
+
"qi/governance.nft.manage-collections": ["adding", "address"],
|
|
3417
|
+
"qi/governance.nft.transfer": ["collection", "tokenId", "recipient", "executeSmartContract", "smartContractMsg"],
|
|
3418
|
+
"qi/governance.staking.stake": ["stakeType", "validator", "toValidator", "amount"],
|
|
3419
|
+
"qi/governance.staking.stake-to-group": ["tokenContract", "stakingContract", "amount"],
|
|
3420
|
+
"qi/governance.settings-proposal": ["votingPeriodHours", "quorumPercent", "thresholdPercent", "allowRevoting"],
|
|
3421
|
+
"qi/governance.submission-config-proposal": ["anyoneCanPropose", "depositRequired", "depositAmount", "depositRefundPolicy"],
|
|
3422
|
+
"qi/governance.transaction.mint": ["recipient", "amount"],
|
|
3423
|
+
"qi/governance.transaction.send-funds": ["recipient", "denom", "amount"],
|
|
3424
|
+
"qi/governance.transaction.perform-token-swap": ["tokenSwapContractAddress", "selfPartyType", "selfPartyDenomOrAddress", "selfPartyAmount"],
|
|
3425
|
+
"qi/governance.transaction.send-group-token": ["tokenContract", "recipient", "amount"],
|
|
3426
|
+
"qi/governance.transaction.withdraw-token-swap": ["tokenSwapContractAddress"],
|
|
3427
|
+
"qi/governance.validator.actions": ["validatorActionType", "createMsg", "editMsg"]
|
|
3428
|
+
};
|
|
3429
|
+
var BOOLEAN_FIELDS = /* @__PURE__ */ new Set(["adding", "automaticallyAddCw20s", "automaticallyAddCw721s", "executeSmartContract", "anyoneCanPropose", "depositRequired", "allowRevoting"]);
|
|
3430
|
+
var NUMBER_FIELDS = /* @__PURE__ */ new Set(["codeId", "vote", "votingPeriodHours", "quorumPercent", "thresholdPercent"]);
|
|
3431
|
+
var ARRAY_FIELDS = /* @__PURE__ */ new Set(["funds", "msgs", "toAdd", "toRemove", "members"]);
|
|
3432
|
+
var OBJECT_FIELDS = /* @__PURE__ */ new Set(["value"]);
|
|
3433
|
+
function governanceProperty(name) {
|
|
3434
|
+
if (BOOLEAN_FIELDS.has(name)) return { type: "boolean" };
|
|
3435
|
+
if (NUMBER_FIELDS.has(name)) return { type: "number" };
|
|
3436
|
+
if (ARRAY_FIELDS.has(name)) return { type: "array", items: {} };
|
|
3437
|
+
if (OBJECT_FIELDS.has(name)) return { type: "object" };
|
|
3438
|
+
return { type: "string" };
|
|
3439
|
+
}
|
|
3440
|
+
function governanceInputSchema(type, extraFields = {}, requiredOverride) {
|
|
3441
|
+
const properties = {
|
|
3442
|
+
coreAddress: { type: "string", description: "DAO core contract address." },
|
|
3443
|
+
title: { type: "string", description: "Proposal title voters see." },
|
|
3444
|
+
description: { type: "string", description: "Long-form proposal description voters see." }
|
|
3445
|
+
};
|
|
3446
|
+
for (const field of GOVERNANCE_FIELDS[type] || []) properties[field] = governanceProperty(field);
|
|
3447
|
+
Object.assign(properties, extraFields);
|
|
3448
|
+
return {
|
|
3449
|
+
type: "object",
|
|
3450
|
+
required: ["coreAddress", ...requiredOverride || GOVERNANCE_REQUIRED_FIELDS[type] || []],
|
|
3451
|
+
additionalProperties: false,
|
|
3452
|
+
properties
|
|
3453
|
+
};
|
|
2227
3454
|
}
|
|
2228
|
-
|
|
2229
|
-
type: "
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
3455
|
+
var STANDARD_OUTPUT_SCHEMA = [
|
|
3456
|
+
{ path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
|
|
3457
|
+
{ path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
|
|
3458
|
+
{ path: "proposalDescription", displayName: "Proposal Description", type: "string", description: "The long-form description voters see on-chain" },
|
|
3459
|
+
{ path: "status", displayName: "Proposal Status", type: "string", description: "Current proposal status (open, passed, rejected, executed, etc.)" },
|
|
3460
|
+
{ path: "proposalContractAddress", displayName: "Proposal Contract Address", type: "string", description: "The proposal module contract address" },
|
|
3461
|
+
{ path: "coreAddress", displayName: "Core Address", type: "string", description: "The DAO core contract address" },
|
|
3462
|
+
{ path: "createdAt", displayName: "Created At", type: "string", description: "ISO timestamp of proposal creation" }
|
|
3463
|
+
];
|
|
3464
|
+
function registerGovernanceProposalAction(spec) {
|
|
3465
|
+
registerAction({
|
|
3466
|
+
type: spec.type,
|
|
3467
|
+
can: spec.can,
|
|
3468
|
+
sideEffect: true,
|
|
3469
|
+
proof: { fields: ["proposalId"] },
|
|
3470
|
+
done: doneWhenCompleted,
|
|
3471
|
+
defaultRequiresConfirmation: true,
|
|
3472
|
+
requiredCapability: "flow/block/execute",
|
|
3473
|
+
inputSchema: governanceInputSchema(spec.type),
|
|
3474
|
+
outputSchema: [...STANDARD_OUTPUT_SCHEMA, ...spec.extraOutputSchema || []],
|
|
3475
|
+
run: async (inputs, ctx) => {
|
|
3476
|
+
const handlers = ctx.handlers;
|
|
3477
|
+
if (!handlers) {
|
|
3478
|
+
throw new Error("Handlers not available");
|
|
3479
|
+
}
|
|
3480
|
+
if (!handlers.getPreProposalContractAddress || !handlers.getGroupContractAddress || !handlers.getProposalContractAddress || !handlers.createProposal) {
|
|
3481
|
+
throw new Error("Governance proposal handlers not available");
|
|
3482
|
+
}
|
|
3483
|
+
const coreAddress = String(inputs.coreAddress || "").trim();
|
|
3484
|
+
if (!coreAddress) throw new Error("coreAddress is required");
|
|
3485
|
+
const actions2 = spec.buildActions(inputs);
|
|
3486
|
+
if (!actions2.length) throw new Error("The proposal must contain at least one action");
|
|
3487
|
+
const title = String(inputs.title || "").trim() || spec.defaultTitle(inputs);
|
|
3488
|
+
const description = String(inputs.description || "").trim() || (spec.defaultDescription ? spec.defaultDescription(inputs) : title);
|
|
3489
|
+
const { preProposalContractAddress } = await handlers.getPreProposalContractAddress({ coreAddress });
|
|
3490
|
+
const { groupContractAddress } = await handlers.getGroupContractAddress({ coreAddress });
|
|
3491
|
+
const { proposalContractAddress } = await handlers.getProposalContractAddress({ coreAddress });
|
|
3492
|
+
const proposalId = await handlers.createProposal({
|
|
3493
|
+
preProposalContractAddress,
|
|
3494
|
+
title,
|
|
3495
|
+
description,
|
|
3496
|
+
actions: actions2,
|
|
3497
|
+
coreAddress,
|
|
3498
|
+
groupContractAddress
|
|
3499
|
+
});
|
|
3500
|
+
if (proposalId === void 0 || proposalId === null || String(proposalId).trim() === "") {
|
|
3501
|
+
throw new Error("Proposal creation returned no proposal id. Check the handler logs.");
|
|
3502
|
+
}
|
|
3503
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3504
|
+
const output = {
|
|
3505
|
+
proposalId: String(proposalId),
|
|
3506
|
+
proposalTitle: title,
|
|
3507
|
+
proposalDescription: description,
|
|
3508
|
+
status: "open",
|
|
3509
|
+
proposalContractAddress: proposalContractAddress || "",
|
|
3510
|
+
coreAddress,
|
|
3511
|
+
createdAt,
|
|
3512
|
+
...spec.buildExtraOutput ? spec.buildExtraOutput(inputs) : {}
|
|
3513
|
+
};
|
|
3514
|
+
return {
|
|
3515
|
+
output,
|
|
3516
|
+
topicRecords: ctx.topic ? [
|
|
3517
|
+
{
|
|
3518
|
+
type: "org.ixo.topic.proposal-receipt",
|
|
3519
|
+
id: sha256Digest({ topicId: ctx.topic.topicId, proposalContractAddress, proposalId }),
|
|
3520
|
+
version: 1,
|
|
3521
|
+
value: {
|
|
3522
|
+
event: "created",
|
|
3523
|
+
actionType: spec.type,
|
|
3524
|
+
proposalId: String(proposalId),
|
|
3525
|
+
proposalContractAddress: proposalContractAddress || "",
|
|
3526
|
+
coreAddress,
|
|
3527
|
+
proposalTitle: title,
|
|
3528
|
+
proposalDescriptionDigest: sha256Digest(description),
|
|
3529
|
+
createdAt
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
] : void 0
|
|
3533
|
+
};
|
|
3534
|
+
}
|
|
3535
|
+
});
|
|
3536
|
+
}
|
|
3537
|
+
|
|
3538
|
+
// src/core/lib/actionRegistry/actions/governance/memberProposal.ts
|
|
3539
|
+
var VALID_OPERATIONS = ["add", "remove", "update-weight"];
|
|
3540
|
+
function defaultTitle(operation, count) {
|
|
3541
|
+
const noun = count === 1 ? "member" : "members";
|
|
3542
|
+
switch (operation) {
|
|
3543
|
+
case "add":
|
|
3544
|
+
return `Add ${count} ${noun}`;
|
|
3545
|
+
case "remove":
|
|
3546
|
+
return `Remove ${count} ${noun}`;
|
|
3547
|
+
case "update-weight":
|
|
3548
|
+
return `Update voting power for ${count} ${noun}`;
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
registerAction({
|
|
3552
|
+
type: "qi/governance.member-proposal",
|
|
3553
|
+
can: "governance/member-proposal",
|
|
3554
|
+
sideEffect: true,
|
|
3555
|
+
proof: { fields: ["proposalId"] },
|
|
3556
|
+
done: doneWhenCompleted,
|
|
3557
|
+
defaultRequiresConfirmation: true,
|
|
3558
|
+
requiredCapability: "flow/block/execute",
|
|
3559
|
+
inputSchema: governanceInputSchema("qi/governance.member-proposal"),
|
|
3560
|
+
outputSchema: [
|
|
3561
|
+
{ path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
|
|
3562
|
+
{ path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
|
|
3563
|
+
{ path: "proposalDescription", displayName: "Proposal Description", type: "string", description: "The long-form description voters see on-chain" },
|
|
3564
|
+
{ path: "status", displayName: "Proposal Status", type: "string", description: "Current proposal status (open, passed, rejected, executed, etc.)" },
|
|
3565
|
+
{ path: "proposalContractAddress", displayName: "Proposal Contract Address", type: "string", description: "The proposal module contract address" },
|
|
3566
|
+
{ path: "coreAddress", displayName: "Core Address", type: "string", description: "The DAO core contract address" },
|
|
3567
|
+
{ path: "operation", displayName: "Operation", type: "string", description: "add | remove | update-weight" },
|
|
3568
|
+
{ path: "createdAt", displayName: "Created At", type: "string", description: "ISO timestamp of proposal creation" }
|
|
3569
|
+
],
|
|
3570
|
+
// Mirrors run()'s throw-preamble (there is no inputSchema to declare
|
|
2247
3571
|
// requiredness on): coreAddress, operation and a non-empty members list.
|
|
2248
3572
|
getMissingInputs: (inputs) => {
|
|
2249
3573
|
const missing = [];
|
|
@@ -2333,6 +3657,7 @@ registerAction({
|
|
|
2333
3657
|
done: doneWhenCompleted,
|
|
2334
3658
|
defaultRequiresConfirmation: true,
|
|
2335
3659
|
requiredCapability: "flow/block/execute",
|
|
3660
|
+
inputSchema: governanceInputSchema("qi/governance.settings-proposal"),
|
|
2336
3661
|
outputSchema: [
|
|
2337
3662
|
{ path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
|
|
2338
3663
|
{ path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
|
|
@@ -2414,70 +3739,6 @@ registerAction({
|
|
|
2414
3739
|
}
|
|
2415
3740
|
});
|
|
2416
3741
|
|
|
2417
|
-
// src/core/lib/actionRegistry/actions/governance/_shared.ts
|
|
2418
|
-
var STANDARD_OUTPUT_SCHEMA = [
|
|
2419
|
-
{ path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
|
|
2420
|
-
{ path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
|
|
2421
|
-
{ path: "proposalDescription", displayName: "Proposal Description", type: "string", description: "The long-form description voters see on-chain" },
|
|
2422
|
-
{ path: "status", displayName: "Proposal Status", type: "string", description: "Current proposal status (open, passed, rejected, executed, etc.)" },
|
|
2423
|
-
{ path: "proposalContractAddress", displayName: "Proposal Contract Address", type: "string", description: "The proposal module contract address" },
|
|
2424
|
-
{ path: "coreAddress", displayName: "Core Address", type: "string", description: "The DAO core contract address" },
|
|
2425
|
-
{ path: "createdAt", displayName: "Created At", type: "string", description: "ISO timestamp of proposal creation" }
|
|
2426
|
-
];
|
|
2427
|
-
function registerGovernanceProposalAction(spec) {
|
|
2428
|
-
registerAction({
|
|
2429
|
-
type: spec.type,
|
|
2430
|
-
can: spec.can,
|
|
2431
|
-
sideEffect: true,
|
|
2432
|
-
proof: { fields: ["proposalId"] },
|
|
2433
|
-
done: doneWhenCompleted,
|
|
2434
|
-
defaultRequiresConfirmation: true,
|
|
2435
|
-
requiredCapability: "flow/block/execute",
|
|
2436
|
-
outputSchema: [...STANDARD_OUTPUT_SCHEMA, ...spec.extraOutputSchema || []],
|
|
2437
|
-
run: async (inputs, ctx) => {
|
|
2438
|
-
const handlers = ctx.handlers;
|
|
2439
|
-
if (!handlers) {
|
|
2440
|
-
throw new Error("Handlers not available");
|
|
2441
|
-
}
|
|
2442
|
-
if (!handlers.getPreProposalContractAddress || !handlers.getGroupContractAddress || !handlers.getProposalContractAddress || !handlers.createProposal) {
|
|
2443
|
-
throw new Error("Governance proposal handlers not available");
|
|
2444
|
-
}
|
|
2445
|
-
const coreAddress = String(inputs.coreAddress || "").trim();
|
|
2446
|
-
if (!coreAddress) throw new Error("coreAddress is required");
|
|
2447
|
-
const actions2 = spec.buildActions(inputs);
|
|
2448
|
-
if (!actions2.length) throw new Error("The proposal must contain at least one action");
|
|
2449
|
-
const title = String(inputs.title || "").trim() || spec.defaultTitle(inputs);
|
|
2450
|
-
const description = String(inputs.description || "").trim() || (spec.defaultDescription ? spec.defaultDescription(inputs) : title);
|
|
2451
|
-
const { preProposalContractAddress } = await handlers.getPreProposalContractAddress({ coreAddress });
|
|
2452
|
-
const { groupContractAddress } = await handlers.getGroupContractAddress({ coreAddress });
|
|
2453
|
-
const { proposalContractAddress } = await handlers.getProposalContractAddress({ coreAddress });
|
|
2454
|
-
const proposalId = await handlers.createProposal({
|
|
2455
|
-
preProposalContractAddress,
|
|
2456
|
-
title,
|
|
2457
|
-
description,
|
|
2458
|
-
actions: actions2,
|
|
2459
|
-
coreAddress,
|
|
2460
|
-
groupContractAddress
|
|
2461
|
-
});
|
|
2462
|
-
if (proposalId === void 0 || proposalId === null || String(proposalId).trim() === "") {
|
|
2463
|
-
throw new Error("Proposal creation returned no proposal id. Check the handler logs.");
|
|
2464
|
-
}
|
|
2465
|
-
return {
|
|
2466
|
-
output: {
|
|
2467
|
-
proposalId: String(proposalId),
|
|
2468
|
-
proposalTitle: title,
|
|
2469
|
-
proposalDescription: description,
|
|
2470
|
-
status: "open",
|
|
2471
|
-
proposalContractAddress: proposalContractAddress || "",
|
|
2472
|
-
coreAddress,
|
|
2473
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2474
|
-
...spec.buildExtraOutput ? spec.buildExtraOutput(inputs) : {}
|
|
2475
|
-
}
|
|
2476
|
-
};
|
|
2477
|
-
}
|
|
2478
|
-
});
|
|
2479
|
-
}
|
|
2480
|
-
|
|
2481
3742
|
// src/core/lib/actionRegistry/actions/governance/submissionConfigProposal.ts
|
|
2482
3743
|
var REFUND_POLICIES = ["always", "only_passed", "never"];
|
|
2483
3744
|
registerGovernanceProposalAction({
|
|
@@ -2539,6 +3800,7 @@ registerAction({
|
|
|
2539
3800
|
done: doneWhenCompleted,
|
|
2540
3801
|
defaultRequiresConfirmation: true,
|
|
2541
3802
|
requiredCapability: "flow/block/execute",
|
|
3803
|
+
inputSchema: governanceInputSchema("qi/governance.transaction.send-funds"),
|
|
2542
3804
|
outputSchema: [
|
|
2543
3805
|
{ path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
|
|
2544
3806
|
{ path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
|
|
@@ -3458,66 +4720,157 @@ registerGovernanceProposalAction({
|
|
|
3458
4720
|
});
|
|
3459
4721
|
|
|
3460
4722
|
// src/core/lib/actionRegistry/actions/httpRequest.ts
|
|
4723
|
+
var SENSITIVE_HEADERS = /* @__PURE__ */ new Set(["authorization", "cookie", "proxy-authorization", "x-api-key"]);
|
|
4724
|
+
var MUTATING_METHODS = ["POST", "PUT", "PATCH", "DELETE"];
|
|
4725
|
+
var REQUEST_METHODS = [...MUTATING_METHODS, "GET", "HEAD"];
|
|
4726
|
+
function publicHttpUrl(raw) {
|
|
4727
|
+
const value = String(raw || "").trim();
|
|
4728
|
+
let url;
|
|
4729
|
+
try {
|
|
4730
|
+
url = new URL(value);
|
|
4731
|
+
} catch {
|
|
4732
|
+
throw new Error("HTTP endpoint must be an absolute URL");
|
|
4733
|
+
}
|
|
4734
|
+
if (url.protocol !== "https:") throw new Error("HTTP Actions require an HTTPS endpoint");
|
|
4735
|
+
if (url.username || url.password) throw new Error("Credentials must not be embedded in an HTTP endpoint");
|
|
4736
|
+
const host = url.hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
4737
|
+
if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local") || host === "::1" || host.startsWith("fc") || host.startsWith("fd") || host.startsWith("fe80:") || /^127\./.test(host) || /^10\./.test(host) || /^169\.254\./.test(host) || /^192\.168\./.test(host) || /^172\.(1[6-9]|2\d|3[01])\./.test(host) || host === "0.0.0.0") {
|
|
4738
|
+
throw new Error("HTTP endpoint must not resolve to a local, private, link-local, or metadata address");
|
|
4739
|
+
}
|
|
4740
|
+
return url.toString();
|
|
4741
|
+
}
|
|
4742
|
+
function safeHeaders(raw) {
|
|
4743
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
4744
|
+
const headers = {};
|
|
4745
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
4746
|
+
if (SENSITIVE_HEADERS.has(key.toLowerCase())) {
|
|
4747
|
+
throw new Error(`Sensitive header '${key}' must be supplied through a host-managed credential binding, not Action input`);
|
|
4748
|
+
}
|
|
4749
|
+
headers[key] = String(value);
|
|
4750
|
+
}
|
|
4751
|
+
return headers;
|
|
4752
|
+
}
|
|
4753
|
+
var INPUT_PROPERTIES = {
|
|
4754
|
+
endpoint: { type: "string", format: "uri", description: "Public HTTPS request URL. Either endpoint or url is required." },
|
|
4755
|
+
url: { type: "string", format: "uri", description: "Legacy alias for endpoint." },
|
|
4756
|
+
method: { type: "string", description: "HTTP method." },
|
|
4757
|
+
headers: { type: "object", additionalProperties: { type: "string" }, description: "Non-secret request headers. Authorization and cookies are rejected." },
|
|
4758
|
+
body: { description: "Request body for a mutating HTTP request." }
|
|
4759
|
+
};
|
|
4760
|
+
var OUTPUT_SCHEMA2 = [
|
|
4761
|
+
{ path: "requestId", displayName: "Request ID", type: "string", description: "Host invocation identifier." },
|
|
4762
|
+
{ path: "status", displayName: "HTTP Status", type: "number" },
|
|
4763
|
+
{ path: "responseDigest", displayName: "Response Digest", type: "string", description: "Digest of the response body." },
|
|
4764
|
+
{ path: "data", displayName: "Response Data", type: "object", description: "Full result retained in the Flow timeline, not copied into Topic state." },
|
|
4765
|
+
{ path: "response", displayName: "Response JSON", type: "string" },
|
|
4766
|
+
{ path: "traceReference", displayName: "Trace Reference", type: "string" }
|
|
4767
|
+
];
|
|
4768
|
+
function missingEndpoint(inputs) {
|
|
4769
|
+
return String(inputs.endpoint || inputs.url || "").trim() ? [] : ["endpoint"];
|
|
4770
|
+
}
|
|
3461
4771
|
registerAction({
|
|
3462
|
-
type: "qi/http.
|
|
3463
|
-
can: "http/
|
|
4772
|
+
type: "qi/http.fetch",
|
|
4773
|
+
can: "http/fetch",
|
|
3464
4774
|
sideEffect: false,
|
|
3465
|
-
proof: { fields: ["
|
|
4775
|
+
proof: { fields: ["responseDigest"] },
|
|
3466
4776
|
done: doneWhenCompleted,
|
|
3467
4777
|
defaultRequiresConfirmation: false,
|
|
3468
|
-
// HTTP request can be triggered as a listener — a human assignee is DM'd
|
|
3469
|
-
// to invoke it when the upstream event fires. See §3.6 of the
|
|
3470
|
-
// events-and-triggers plan.
|
|
3471
4778
|
eligibleForEventTrigger: true,
|
|
3472
4779
|
inputSchema: {
|
|
3473
4780
|
type: "object",
|
|
3474
|
-
required: [
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
url: { type: "string", description: "Alias for endpoint; the request URL. Either url or endpoint satisfies the URL requirement." },
|
|
3478
|
-
method: { type: "string", description: "HTTP method (GET, POST, etc.). Defaults to GET." },
|
|
3479
|
-
headers: { type: "object", description: "Request headers as a key/value map." },
|
|
3480
|
-
body: { type: "string", description: "Request body; sent for non-GET methods. Accepts a string or an object (serialized to JSON)." }
|
|
3481
|
-
}
|
|
4781
|
+
required: [],
|
|
4782
|
+
additionalProperties: false,
|
|
4783
|
+
properties: { ...INPUT_PROPERTIES, method: { type: "string", enum: ["GET", "HEAD"], default: "GET" } }
|
|
3482
4784
|
},
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
getMissingInputs: (inputs) => String(inputs.endpoint || inputs.url || "").trim() ? [] : ["endpoint"],
|
|
4785
|
+
getMissingInputs: missingEndpoint,
|
|
4786
|
+
outputSchema: OUTPUT_SCHEMA2,
|
|
3486
4787
|
run: async (inputs, ctx) => {
|
|
3487
|
-
const
|
|
3488
|
-
const
|
|
3489
|
-
if (
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
4788
|
+
const url = publicHttpUrl(inputs.endpoint ?? inputs.url);
|
|
4789
|
+
const method = String(inputs.method || "GET").toUpperCase();
|
|
4790
|
+
if (method !== "GET" && method !== "HEAD") throw new Error("qi/http.fetch permits GET or HEAD only");
|
|
4791
|
+
const headers = safeHeaders(inputs.headers);
|
|
4792
|
+
const service = ctx.services.http;
|
|
4793
|
+
if (service) {
|
|
4794
|
+
const result = await service.request({
|
|
4795
|
+
url,
|
|
4796
|
+
method,
|
|
4797
|
+
headers,
|
|
4798
|
+
security: { denyPrivateNetworks: true, maxRedirects: 0, stripSensitiveHeadersOnRedirect: true }
|
|
4799
|
+
});
|
|
4800
|
+
const responseDigest2 = result.responseDigest || sha256Digest(result.data);
|
|
3498
4801
|
return {
|
|
3499
4802
|
output: {
|
|
4803
|
+
requestId: result.requestId || responseDigest2,
|
|
3500
4804
|
status: result.status,
|
|
4805
|
+
responseDigest: responseDigest2,
|
|
3501
4806
|
data: result.data,
|
|
3502
|
-
response: JSON.stringify(result.data, null, 2)
|
|
4807
|
+
response: JSON.stringify(result.data, null, 2),
|
|
4808
|
+
traceReference: result.traceReference || ""
|
|
3503
4809
|
}
|
|
3504
4810
|
};
|
|
3505
4811
|
}
|
|
3506
|
-
const
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
4812
|
+
const response = await fetch(url, { method, headers, redirect: "error" });
|
|
4813
|
+
const text = method === "HEAD" ? "" : await response.text();
|
|
4814
|
+
let data = text;
|
|
4815
|
+
try {
|
|
4816
|
+
data = text ? JSON.parse(text) : {};
|
|
4817
|
+
} catch {
|
|
3512
4818
|
}
|
|
3513
|
-
const
|
|
3514
|
-
const data = await response.json().catch(() => ({}));
|
|
4819
|
+
const responseDigest = sha256Digest(data);
|
|
3515
4820
|
return {
|
|
3516
4821
|
output: {
|
|
4822
|
+
requestId: responseDigest,
|
|
3517
4823
|
status: response.status,
|
|
4824
|
+
responseDigest,
|
|
3518
4825
|
data,
|
|
3519
|
-
response: JSON.stringify(data, null, 2)
|
|
4826
|
+
response: typeof data === "string" ? data : JSON.stringify(data, null, 2),
|
|
4827
|
+
traceReference: ""
|
|
4828
|
+
}
|
|
4829
|
+
};
|
|
4830
|
+
}
|
|
4831
|
+
});
|
|
4832
|
+
registerAction({
|
|
4833
|
+
type: "qi/http.request",
|
|
4834
|
+
can: "http/request",
|
|
4835
|
+
sideEffect: true,
|
|
4836
|
+
proof: { fields: ["requestId"] },
|
|
4837
|
+
done: doneWhenCompleted,
|
|
4838
|
+
defaultRequiresConfirmation: true,
|
|
4839
|
+
requiredCapability: "flow/block/execute",
|
|
4840
|
+
eligibleForEventTrigger: true,
|
|
4841
|
+
inputSchema: {
|
|
4842
|
+
type: "object",
|
|
4843
|
+
required: [],
|
|
4844
|
+
additionalProperties: false,
|
|
4845
|
+
properties: {
|
|
4846
|
+
...INPUT_PROPERTIES,
|
|
4847
|
+
method: {
|
|
4848
|
+
type: "string",
|
|
4849
|
+
enum: REQUEST_METHODS,
|
|
4850
|
+
default: "GET",
|
|
4851
|
+
description: "POST/PUT/PATCH/DELETE for mutations. GET/HEAD remain accepted for legacy Flows but use the same confirmation and receipt policy; new read Actions should use qi/http.fetch."
|
|
3520
4852
|
}
|
|
4853
|
+
}
|
|
4854
|
+
},
|
|
4855
|
+
getMissingInputs: missingEndpoint,
|
|
4856
|
+
outputSchema: OUTPUT_SCHEMA2,
|
|
4857
|
+
run: async (inputs, ctx) => {
|
|
4858
|
+
const service = ctx.services.http;
|
|
4859
|
+
if (!service) throw new Error("Mutating HTTP requests require the host HTTP service; native fetch is not permitted");
|
|
4860
|
+
const url = publicHttpUrl(inputs.endpoint ?? inputs.url);
|
|
4861
|
+
const method = String(inputs.method || "GET").toUpperCase();
|
|
4862
|
+
if (!REQUEST_METHODS.includes(method)) throw new Error(`qi/http.request method must be one of ${REQUEST_METHODS.join(", ")}`);
|
|
4863
|
+
const result = await service.request({
|
|
4864
|
+
url,
|
|
4865
|
+
method,
|
|
4866
|
+
headers: safeHeaders(inputs.headers),
|
|
4867
|
+
body: inputs.body,
|
|
4868
|
+
security: { denyPrivateNetworks: true, maxRedirects: 0, stripSensitiveHeadersOnRedirect: true }
|
|
4869
|
+
});
|
|
4870
|
+
const responseDigest = result.responseDigest || sha256Digest(result.data);
|
|
4871
|
+
const requestId = result.requestId || sha256Digest({ url, method, status: result.status, responseDigest });
|
|
4872
|
+
return {
|
|
4873
|
+
output: { requestId, status: result.status, responseDigest, data: result.data, response: JSON.stringify(result.data, null, 2), traceReference: result.traceReference || "" }
|
|
3521
4874
|
};
|
|
3522
4875
|
}
|
|
3523
4876
|
});
|
|
@@ -3602,7 +4955,7 @@ registerAction({
|
|
|
3602
4955
|
type: "qi/human.checkbox.set",
|
|
3603
4956
|
can: "human/checkbox",
|
|
3604
4957
|
sideEffect: true,
|
|
3605
|
-
proof: "
|
|
4958
|
+
proof: { fields: ["attestationId"] },
|
|
3606
4959
|
done: doneWhenCompleted,
|
|
3607
4960
|
defaultRequiresConfirmation: false,
|
|
3608
4961
|
requiredCapability: "flow/execute",
|
|
@@ -3613,9 +4966,17 @@ registerAction({
|
|
|
3613
4966
|
checked: { type: "boolean", description: "Whether the checkbox should be checked (defaults to true)." }
|
|
3614
4967
|
}
|
|
3615
4968
|
},
|
|
3616
|
-
|
|
4969
|
+
outputSchema: [
|
|
4970
|
+
{ path: "checked", displayName: "Checked", type: "boolean" },
|
|
4971
|
+
{ path: "attestationId", displayName: "Attestation ID", type: "string", description: "Proof identifier for the human checkbox attestation." },
|
|
4972
|
+
{ path: "attestedAt", displayName: "Attested At", type: "string" },
|
|
4973
|
+
{ path: "attestedBy", displayName: "Attested By", type: "string" }
|
|
4974
|
+
],
|
|
4975
|
+
run: async (inputs, ctx) => {
|
|
3617
4976
|
const checked = inputs.checked !== void 0 ? !!inputs.checked : true;
|
|
3618
|
-
|
|
4977
|
+
const attestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4978
|
+
const attestationId = sha256Digest({ action: "qi/human.checkbox.set", checked, actorDid: ctx.actorDid, flowId: ctx.flowId, nodeId: ctx.nodeId, attestedAt });
|
|
4979
|
+
return { output: { checked, attestationId, attestedAt, attestedBy: ctx.actorDid } };
|
|
3619
4980
|
}
|
|
3620
4981
|
});
|
|
3621
4982
|
|
|
@@ -3645,7 +5006,7 @@ function registerFormSubmitAction(type, can) {
|
|
|
3645
5006
|
type,
|
|
3646
5007
|
can,
|
|
3647
5008
|
sideEffect: true,
|
|
3648
|
-
proof: "
|
|
5009
|
+
proof: { fields: ["submissionId"] },
|
|
3649
5010
|
done: doneWhenCompleted,
|
|
3650
5011
|
defaultRequiresConfirmation: false,
|
|
3651
5012
|
requiredCapability: "flow/execute",
|
|
@@ -3662,7 +5023,11 @@ function registerFormSubmitAction(type, can) {
|
|
|
3662
5023
|
},
|
|
3663
5024
|
outputSchema: [
|
|
3664
5025
|
{ path: "form.answers", displayName: "Form Answers JSON", type: "string", description: "JSON stringified form answers, matching form block runtime output." },
|
|
3665
|
-
{ path: "answers", displayName: "Form Answers", type: "object", description: "Parsed form answers object for convenience." }
|
|
5026
|
+
{ path: "answers", displayName: "Form Answers", type: "object", description: "Parsed form answers object for convenience." },
|
|
5027
|
+
{ path: "submissionId", displayName: "Submission ID", type: "string", description: "Stable proof identifier for this submission." },
|
|
5028
|
+
{ path: "answersDigest", displayName: "Answers Digest", type: "string", description: "Content digest; safe to place in a Topic receipt." },
|
|
5029
|
+
{ path: "submittedAt", displayName: "Submitted At", type: "string" },
|
|
5030
|
+
{ path: "submittedBy", displayName: "Submitted By", type: "string" }
|
|
3666
5031
|
],
|
|
3667
5032
|
events: [
|
|
3668
5033
|
{
|
|
@@ -3673,15 +5038,22 @@ function registerFormSubmitAction(type, can) {
|
|
|
3673
5038
|
pendingDisplayFields: ["answers"]
|
|
3674
5039
|
}
|
|
3675
5040
|
],
|
|
3676
|
-
run: async (inputs) => {
|
|
5041
|
+
run: async (inputs, ctx) => {
|
|
3677
5042
|
const answers = normalizeAnswers(inputs.answers ?? inputs.form?.answers);
|
|
3678
5043
|
const answersJson = JSON.stringify(answers);
|
|
5044
|
+
const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5045
|
+
const answersDigest = sha256Digest(answers);
|
|
5046
|
+
const submissionId = sha256Digest({ type, flowId: ctx.flowId, sessionRunId: ctx.sessionRunId || "", nodeId: ctx.nodeId, submittedAt, answersDigest });
|
|
3679
5047
|
return {
|
|
3680
5048
|
output: {
|
|
3681
5049
|
form: {
|
|
3682
5050
|
answers: answersJson
|
|
3683
5051
|
},
|
|
3684
|
-
answers
|
|
5052
|
+
answers,
|
|
5053
|
+
submissionId,
|
|
5054
|
+
answersDigest,
|
|
5055
|
+
submittedAt,
|
|
5056
|
+
submittedBy: ctx.actorDid
|
|
3685
5057
|
},
|
|
3686
5058
|
events: [{ name: "form.submitted", payload: { answers } }]
|
|
3687
5059
|
};
|
|
@@ -3710,20 +5082,39 @@ registerAction({
|
|
|
3710
5082
|
outputSchema: [
|
|
3711
5083
|
{ path: "runId", displayName: "Session run id", type: "string" },
|
|
3712
5084
|
{ path: "eventId", displayName: "Started event id", type: "string" },
|
|
3713
|
-
{ path: "startedAt", displayName: "Started at", type: "number" }
|
|
5085
|
+
{ path: "startedAt", displayName: "Started at", type: "number" },
|
|
5086
|
+
{ path: "sessionId", displayName: "Session ID", type: "string" },
|
|
5087
|
+
{ path: "flowRevision", displayName: "Flow revision", type: "string" },
|
|
5088
|
+
{ path: "topicBindingId", displayName: "Topic binding ID", type: "string" }
|
|
5089
|
+
],
|
|
5090
|
+
events: [
|
|
5091
|
+
{
|
|
5092
|
+
name: "flow.run.started",
|
|
5093
|
+
displayName: "Flow run started",
|
|
5094
|
+
description: "Emitted when a pinned Flow run starts; it does not change Topic status.",
|
|
5095
|
+
payloadSchema: [
|
|
5096
|
+
{ path: "runId", displayName: "Run ID", type: "string" },
|
|
5097
|
+
{ path: "topicBindingId", displayName: "Topic binding ID", type: "string" }
|
|
5098
|
+
]
|
|
5099
|
+
}
|
|
3714
5100
|
],
|
|
3715
5101
|
run: async (inputs, ctx) => {
|
|
3716
5102
|
if (!ctx.services.flowRuns?.start) {
|
|
3717
5103
|
throw new Error("flowRuns.start handler not available");
|
|
3718
5104
|
}
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
5105
|
+
const lifecycle = await ctx.services.flowRuns.start({
|
|
5106
|
+
actorDid: ctx.actorDid,
|
|
5107
|
+
flowId: ctx.flowId,
|
|
5108
|
+
flowUri: ctx.flowUri,
|
|
5109
|
+
...typeof inputs.label === "string" && inputs.label ? { label: inputs.label } : {}
|
|
5110
|
+
});
|
|
5111
|
+
const output = {
|
|
5112
|
+
...lifecycle,
|
|
5113
|
+
sessionId: lifecycle.runId,
|
|
5114
|
+
flowRevision: ctx.flowRevision || "",
|
|
5115
|
+
topicBindingId: ctx.topic?.bindingId || ""
|
|
3726
5116
|
};
|
|
5117
|
+
return { output, events: [{ name: "flow.run.started", payload: { runId: lifecycle.runId, topicBindingId: output.topicBindingId } }] };
|
|
3727
5118
|
}
|
|
3728
5119
|
});
|
|
3729
5120
|
registerAction({
|
|
@@ -3748,7 +5139,22 @@ registerAction({
|
|
|
3748
5139
|
{ path: "status", displayName: "Terminal status", type: "string" },
|
|
3749
5140
|
{ path: "eventId", displayName: "Terminal event id", type: "string" },
|
|
3750
5141
|
{ path: "closedAt", displayName: "Closed at", type: "number" },
|
|
3751
|
-
{ path: "cancelledAt", displayName: "Cancelled at", type: "number" }
|
|
5142
|
+
{ path: "cancelledAt", displayName: "Cancelled at", type: "number" },
|
|
5143
|
+
{ path: "runId", displayName: "Session run ID", type: "string" },
|
|
5144
|
+
{ path: "flowRevision", displayName: "Flow revision", type: "string" },
|
|
5145
|
+
{ path: "topicBindingId", displayName: "Topic binding ID", type: "string" }
|
|
5146
|
+
],
|
|
5147
|
+
events: [
|
|
5148
|
+
{
|
|
5149
|
+
name: "flow.run.closed",
|
|
5150
|
+
displayName: "Flow run closed",
|
|
5151
|
+
description: "Emitted when the Flow run closes; Topic resolution remains an explicit, separate Action.",
|
|
5152
|
+
payloadSchema: [
|
|
5153
|
+
{ path: "runId", displayName: "Run ID", type: "string" },
|
|
5154
|
+
{ path: "status", displayName: "Status", type: "string" },
|
|
5155
|
+
{ path: "topicBindingId", displayName: "Topic binding ID", type: "string" }
|
|
5156
|
+
]
|
|
5157
|
+
}
|
|
3752
5158
|
],
|
|
3753
5159
|
run: async (inputs, ctx) => {
|
|
3754
5160
|
if (!ctx.sessionRunId) {
|
|
@@ -3758,24 +5164,30 @@ registerAction({
|
|
|
3758
5164
|
throw new Error("flowRuns lifecycle handler not available");
|
|
3759
5165
|
}
|
|
3760
5166
|
if (inputs.cancel === true) {
|
|
3761
|
-
|
|
3762
|
-
output: await ctx.services.flowRuns.cancel({
|
|
3763
|
-
actorDid: ctx.actorDid,
|
|
3764
|
-
flowId: ctx.flowId,
|
|
3765
|
-
flowUri: ctx.flowUri,
|
|
3766
|
-
runId: ctx.sessionRunId,
|
|
3767
|
-
...typeof inputs.reason === "string" && inputs.reason ? { reason: inputs.reason } : {}
|
|
3768
|
-
})
|
|
3769
|
-
};
|
|
3770
|
-
}
|
|
3771
|
-
return {
|
|
3772
|
-
output: await ctx.services.flowRuns.close({
|
|
5167
|
+
const lifecycle2 = await ctx.services.flowRuns.cancel({
|
|
3773
5168
|
actorDid: ctx.actorDid,
|
|
3774
5169
|
flowId: ctx.flowId,
|
|
3775
5170
|
flowUri: ctx.flowUri,
|
|
3776
5171
|
runId: ctx.sessionRunId,
|
|
3777
|
-
|
|
3778
|
-
})
|
|
5172
|
+
...typeof inputs.reason === "string" && inputs.reason ? { reason: inputs.reason } : {}
|
|
5173
|
+
});
|
|
5174
|
+
const output2 = { ...lifecycle2, runId: ctx.sessionRunId, flowRevision: ctx.flowRevision || "", topicBindingId: ctx.topic?.bindingId || "" };
|
|
5175
|
+
return {
|
|
5176
|
+
output: output2,
|
|
5177
|
+
events: [{ name: "flow.run.closed", payload: { runId: ctx.sessionRunId, status: lifecycle2.status, topicBindingId: output2.topicBindingId } }]
|
|
5178
|
+
};
|
|
5179
|
+
}
|
|
5180
|
+
const lifecycle = await ctx.services.flowRuns.close({
|
|
5181
|
+
actorDid: ctx.actorDid,
|
|
5182
|
+
flowId: ctx.flowId,
|
|
5183
|
+
flowUri: ctx.flowUri,
|
|
5184
|
+
runId: ctx.sessionRunId,
|
|
5185
|
+
allowIncomplete: inputs.allowIncomplete === true
|
|
5186
|
+
});
|
|
5187
|
+
const output = { ...lifecycle, runId: ctx.sessionRunId, flowRevision: ctx.flowRevision || "", topicBindingId: ctx.topic?.bindingId || "" };
|
|
5188
|
+
return {
|
|
5189
|
+
output,
|
|
5190
|
+
events: [{ name: "flow.run.closed", payload: { runId: ctx.sessionRunId, status: lifecycle.status, topicBindingId: output.topicBindingId } }]
|
|
3779
5191
|
};
|
|
3780
5192
|
}
|
|
3781
5193
|
});
|
|
@@ -3804,6 +5216,10 @@ registerAction({
|
|
|
3804
5216
|
replyTo: { type: "string", description: "Reply-to address." }
|
|
3805
5217
|
}
|
|
3806
5218
|
},
|
|
5219
|
+
outputSchema: [
|
|
5220
|
+
{ path: "messageId", displayName: "Message ID", type: "string", description: "Provider or host notification identifier." },
|
|
5221
|
+
{ path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp supplied by the provider or host." }
|
|
5222
|
+
],
|
|
3807
5223
|
run: async (inputs, ctx) => {
|
|
3808
5224
|
if (!ctx.services.notify) {
|
|
3809
5225
|
throw new Error("Notification service not configured");
|
|
@@ -4457,6 +5873,22 @@ registerAction({
|
|
|
4457
5873
|
};
|
|
4458
5874
|
return {
|
|
4459
5875
|
output,
|
|
5876
|
+
topicRecords: ctx.topic ? [
|
|
5877
|
+
{
|
|
5878
|
+
type: "org.ixo.topic.claim-submission",
|
|
5879
|
+
id: sha256Digest({ topicId: ctx.topic.topicId, collectionId, claimId }),
|
|
5880
|
+
version: 1,
|
|
5881
|
+
value: {
|
|
5882
|
+
claimId,
|
|
5883
|
+
collectionId,
|
|
5884
|
+
deedDid,
|
|
5885
|
+
submittedByDid,
|
|
5886
|
+
submittedAt,
|
|
5887
|
+
transactionHash,
|
|
5888
|
+
submissionDigest: sha256Digest(surveyAnswers)
|
|
5889
|
+
}
|
|
5890
|
+
}
|
|
5891
|
+
] : void 0,
|
|
4460
5892
|
events: [
|
|
4461
5893
|
{
|
|
4462
5894
|
name: "submitted",
|
|
@@ -4811,7 +6243,7 @@ registerAction({
|
|
|
4811
6243
|
const flowId = String(ctx.flowId || ctx.flowUri || "flow");
|
|
4812
6244
|
const claimSnapshot = inputs.claimSnapshot && typeof inputs.claimSnapshot === "object" && !Array.isArray(inputs.claimSnapshot) ? inputs.claimSnapshot : void 0;
|
|
4813
6245
|
const surveyQuestions = Array.isArray(claimSnapshot?.surveyQuestions) ? claimSnapshot.surveyQuestions : Array.isArray(inputs?.surveyAnswersSchema) ? inputs.surveyAnswersSchema : [];
|
|
4814
|
-
const
|
|
6246
|
+
const idempotencyKey2 = buildXeroInvoiceWorkKey({ flowId, evaluationBlockId: ctx.nodeId, claimId });
|
|
4815
6247
|
const originalPayload = {
|
|
4816
6248
|
claim: { claimId, collectionId, deedDid },
|
|
4817
6249
|
surveyQuestions,
|
|
@@ -4827,11 +6259,11 @@ registerAction({
|
|
|
4827
6259
|
invoiceDefaults: buildXeroInvoiceDefaults(inputs.xeroInvoiceDefaults)
|
|
4828
6260
|
};
|
|
4829
6261
|
upsertXeroWorkItemForEditor(ctx.editor, {
|
|
4830
|
-
id:
|
|
6262
|
+
id: idempotencyKey2,
|
|
4831
6263
|
kind: "invoice.create",
|
|
4832
6264
|
status: "pending",
|
|
4833
6265
|
assignedBlockId: ctx.nodeId,
|
|
4834
|
-
idempotencyKey,
|
|
6266
|
+
idempotencyKey: idempotencyKey2,
|
|
4835
6267
|
source: {
|
|
4836
6268
|
claimId,
|
|
4837
6269
|
evaluationBlockId: ctx.nodeId,
|
|
@@ -4871,6 +6303,25 @@ registerAction({
|
|
|
4871
6303
|
};
|
|
4872
6304
|
return {
|
|
4873
6305
|
output,
|
|
6306
|
+
topicRecords: ctx.topic ? [
|
|
6307
|
+
{
|
|
6308
|
+
type: "org.ixo.topic.claim-evaluation",
|
|
6309
|
+
id: sha256Digest({ topicId: ctx.topic.topicId, collectionId, claimId, evaluatedAt, decision }),
|
|
6310
|
+
version: 1,
|
|
6311
|
+
value: {
|
|
6312
|
+
claimId,
|
|
6313
|
+
collectionId,
|
|
6314
|
+
deedDid,
|
|
6315
|
+
decision,
|
|
6316
|
+
evaluatedByDid,
|
|
6317
|
+
evaluatedAt,
|
|
6318
|
+
verificationProof,
|
|
6319
|
+
transactionHash,
|
|
6320
|
+
evidenceDigest: sha256Digest(surveyAnswers)
|
|
6321
|
+
},
|
|
6322
|
+
evidenceReferences: verificationProof ? [verificationProof] : []
|
|
6323
|
+
}
|
|
6324
|
+
] : void 0,
|
|
4874
6325
|
events: [{ name: eventName, payload: eventPayload }]
|
|
4875
6326
|
};
|
|
4876
6327
|
}
|
|
@@ -4953,7 +6404,23 @@ registerAction({
|
|
|
4953
6404
|
proposalContractAddress: proposalContractAddress || "",
|
|
4954
6405
|
coreAddress,
|
|
4955
6406
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4956
|
-
}
|
|
6407
|
+
},
|
|
6408
|
+
topicRecords: ctx.topic ? [
|
|
6409
|
+
{
|
|
6410
|
+
type: "org.ixo.topic.proposal-receipt",
|
|
6411
|
+
id: sha256Digest({ topicId: ctx.topic.topicId, proposalContractAddress, proposalId }),
|
|
6412
|
+
version: 1,
|
|
6413
|
+
value: {
|
|
6414
|
+
event: "created",
|
|
6415
|
+
proposalId: String(proposalId),
|
|
6416
|
+
proposalContractAddress: proposalContractAddress || "",
|
|
6417
|
+
coreAddress,
|
|
6418
|
+
title,
|
|
6419
|
+
descriptionDigest: sha256Digest(description),
|
|
6420
|
+
status: "open"
|
|
6421
|
+
}
|
|
6422
|
+
}
|
|
6423
|
+
] : void 0
|
|
4957
6424
|
};
|
|
4958
6425
|
}
|
|
4959
6426
|
});
|
|
@@ -5008,13 +6475,30 @@ registerAction({
|
|
|
5008
6475
|
rationale: rationale || void 0,
|
|
5009
6476
|
proposalContractAddress
|
|
5010
6477
|
});
|
|
6478
|
+
const votedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5011
6479
|
return {
|
|
5012
6480
|
output: {
|
|
5013
6481
|
vote,
|
|
5014
6482
|
rationale: rationale || "",
|
|
5015
6483
|
proposalId: String(proposalId),
|
|
5016
|
-
votedAt
|
|
5017
|
-
}
|
|
6484
|
+
votedAt
|
|
6485
|
+
},
|
|
6486
|
+
topicRecords: ctx.topic ? [
|
|
6487
|
+
{
|
|
6488
|
+
type: "org.ixo.topic.proposal-receipt",
|
|
6489
|
+
id: sha256Digest({ topicId: ctx.topic.topicId, proposalContractAddress, proposalId, actorDid: ctx.actorDid, votedAt }),
|
|
6490
|
+
version: 1,
|
|
6491
|
+
value: {
|
|
6492
|
+
event: "vote-cast",
|
|
6493
|
+
proposalId: String(proposalId),
|
|
6494
|
+
proposalContractAddress,
|
|
6495
|
+
vote,
|
|
6496
|
+
rationaleDigest: sha256Digest(rationale),
|
|
6497
|
+
actorDid: ctx.actorDid,
|
|
6498
|
+
votedAt
|
|
6499
|
+
}
|
|
6500
|
+
}
|
|
6501
|
+
] : void 0
|
|
5018
6502
|
};
|
|
5019
6503
|
}
|
|
5020
6504
|
});
|
|
@@ -6826,29 +8310,46 @@ registerAction({
|
|
|
6826
8310
|
|
|
6827
8311
|
// src/core/lib/actionRegistry/actions/oracle.ts
|
|
6828
8312
|
registerAction({
|
|
6829
|
-
type: "oracle",
|
|
6830
|
-
can: "oracle/
|
|
6831
|
-
sideEffect:
|
|
6832
|
-
proof: "
|
|
8313
|
+
type: "qi/oracle.invoke",
|
|
8314
|
+
can: "oracle/invoke",
|
|
8315
|
+
sideEffect: true,
|
|
8316
|
+
proof: { fields: ["resultDigest"] },
|
|
6833
8317
|
done: doneWhenCompleted,
|
|
6834
8318
|
defaultRequiresConfirmation: false,
|
|
6835
8319
|
inputSchema: {
|
|
6836
8320
|
type: "object",
|
|
6837
8321
|
required: ["prompt"],
|
|
6838
8322
|
properties: {
|
|
6839
|
-
prompt: { type: "string", description: "The prompt text sent to the
|
|
8323
|
+
prompt: { type: "string", description: "The prompt text sent to the Agent." }
|
|
6840
8324
|
}
|
|
6841
8325
|
},
|
|
6842
|
-
|
|
8326
|
+
sensitiveInputPaths: ["prompt"],
|
|
8327
|
+
sensitiveOutputPaths: ["result"],
|
|
8328
|
+
outputSchema: [
|
|
8329
|
+
{ path: "prompt", displayName: "Prompt", type: "string", description: "Legacy Flow-timeline echo; redacted from Topic receipts." },
|
|
8330
|
+
{ path: "sessionId", displayName: "Session ID", type: "string", description: "Private Oracle session identifier." },
|
|
8331
|
+
{ path: "result", displayName: "Result", type: "object", description: "Full result retained in the Flow timeline." },
|
|
8332
|
+
{ path: "resultDigest", displayName: "Result Digest", type: "string", description: "Content digest safe for a Topic receipt." },
|
|
8333
|
+
{ path: "evidenceDigest", displayName: "Evidence Digest", type: "string", description: "Digest of evidence references returned by the Oracle." }
|
|
8334
|
+
],
|
|
6843
8335
|
run: async (inputs, ctx) => {
|
|
6844
8336
|
const prompt = String(inputs.prompt || "").trim();
|
|
6845
8337
|
if (!prompt) throw new Error("prompt is required");
|
|
6846
8338
|
if (!ctx.handlers?.askCompanion) {
|
|
6847
8339
|
throw new Error("askCompanion handler is not available");
|
|
6848
8340
|
}
|
|
6849
|
-
await ctx.handlers.askCompanion(prompt);
|
|
8341
|
+
const raw = await ctx.handlers.askCompanion(prompt);
|
|
8342
|
+
const envelope = raw && typeof raw === "object" ? raw : { result: raw };
|
|
8343
|
+
const result = envelope.result ?? envelope.response ?? envelope.message ?? raw ?? null;
|
|
8344
|
+
const evidence = Array.isArray(envelope.evidenceReferences) ? envelope.evidenceReferences : Array.isArray(envelope.evidence) ? envelope.evidence : [];
|
|
6850
8345
|
return {
|
|
6851
|
-
output: {
|
|
8346
|
+
output: {
|
|
8347
|
+
prompt,
|
|
8348
|
+
sessionId: String(envelope.sessionId ?? envelope.runId ?? ""),
|
|
8349
|
+
result,
|
|
8350
|
+
resultDigest: sha256Digest(result),
|
|
8351
|
+
evidenceDigest: sha256Digest(evidence)
|
|
8352
|
+
}
|
|
6852
8353
|
};
|
|
6853
8354
|
}
|
|
6854
8355
|
});
|
|
@@ -6984,6 +8485,7 @@ registerAction({
|
|
|
6984
8485
|
});
|
|
6985
8486
|
|
|
6986
8487
|
// src/core/lib/actionRegistry/actions/walletFund.ts
|
|
8488
|
+
var DEFAULT_DENOM = "uixo";
|
|
6987
8489
|
registerAction({
|
|
6988
8490
|
type: "qi/wallet.fund",
|
|
6989
8491
|
can: "wallet/fund",
|
|
@@ -6996,20 +8498,37 @@ registerAction({
|
|
|
6996
8498
|
required: ["address"],
|
|
6997
8499
|
properties: {
|
|
6998
8500
|
address: { type: "string", description: "The IXO wallet address to fund." },
|
|
6999
|
-
amount: { type: "number", description: "Funding amount in base units (defaults to 250000)." }
|
|
8501
|
+
amount: { type: "number", description: "Funding amount in the denom\u2019s base units (defaults to 250000)." },
|
|
8502
|
+
denom: { type: "string", description: "Base denom to send, e.g. uixo. Defaults to uixo." },
|
|
8503
|
+
fromAddress: {
|
|
8504
|
+
type: "string",
|
|
8505
|
+
description: "Wallet the tokens leave. Defaults to the signed-in user\u2019s wallet; any other address must be one they can act on."
|
|
8506
|
+
}
|
|
7000
8507
|
}
|
|
7001
8508
|
},
|
|
7002
|
-
outputSchema: [
|
|
8509
|
+
outputSchema: [
|
|
8510
|
+
{ path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The funding transaction hash" },
|
|
8511
|
+
{ path: "denom", displayName: "Denom", type: "string", description: "The base denom that was sent" },
|
|
8512
|
+
{ path: "amount", displayName: "Amount", type: "string", description: "The amount sent, in the denom\u2019s base units" },
|
|
8513
|
+
{ path: "fromAddress", displayName: "From Address", type: "string", description: "The wallet the tokens left (blank when the signer\u2019s own wallet)" }
|
|
8514
|
+
],
|
|
7003
8515
|
run: async (inputs, ctx) => {
|
|
7004
8516
|
if (!ctx.services.oracle?.fundWallet) {
|
|
7005
8517
|
throw new Error("oracle.fundWallet handler not available");
|
|
7006
8518
|
}
|
|
7007
8519
|
if (!inputs.address) throw new Error("address is required");
|
|
8520
|
+
const denom = String(inputs.denom || "").trim() || DEFAULT_DENOM;
|
|
8521
|
+
const amount = Number(inputs.amount) || 25e4;
|
|
8522
|
+
if (!Number.isFinite(amount) || amount <= 0) throw new Error("amount must be greater than 0");
|
|
8523
|
+
if (!Number.isInteger(amount)) throw new Error(`amount must be a whole number of ${denom} base units`);
|
|
8524
|
+
const fromAddress = String(inputs.fromAddress || "").trim();
|
|
7008
8525
|
const result = await ctx.services.oracle.fundWallet({
|
|
7009
8526
|
address: inputs.address,
|
|
7010
|
-
amount
|
|
8527
|
+
amount,
|
|
8528
|
+
denom,
|
|
8529
|
+
...fromAddress ? { fromAddress } : {}
|
|
7011
8530
|
});
|
|
7012
|
-
return { output: result };
|
|
8531
|
+
return { output: { ...result, denom, amount: String(amount), fromAddress } };
|
|
7013
8532
|
}
|
|
7014
8533
|
});
|
|
7015
8534
|
|
|
@@ -7025,7 +8544,12 @@ registerAction({
|
|
|
7025
8544
|
type: "object",
|
|
7026
8545
|
required: [],
|
|
7027
8546
|
properties: {
|
|
7028
|
-
amount: { type: "number", description: "Funding amount in base units for the generated wallet (defaults to 250000)." }
|
|
8547
|
+
amount: { type: "number", description: "Funding amount in base units for the generated wallet (defaults to 250000)." },
|
|
8548
|
+
denom: { type: "string", description: "Base denom to send, e.g. uixo. Defaults to uixo." },
|
|
8549
|
+
fromAddress: {
|
|
8550
|
+
type: "string",
|
|
8551
|
+
description: "Wallet the funding leaves. Defaults to the signed-in user\u2019s wallet; any other address must be one they can act on."
|
|
8552
|
+
}
|
|
7029
8553
|
}
|
|
7030
8554
|
},
|
|
7031
8555
|
outputSchema: [
|
|
@@ -7033,7 +8557,8 @@ registerAction({
|
|
|
7033
8557
|
{ path: "did", displayName: "DID", type: "string", description: "The DID derived from the wallet address" },
|
|
7034
8558
|
{ path: "pubKey", displayName: "Public Key", type: "string", description: "The secp256k1 public key (hex)" },
|
|
7035
8559
|
{ path: "mnemonic", displayName: "Mnemonic", type: "string", description: "The BIP39 mnemonic seed phrase" },
|
|
7036
|
-
{ path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The funding transaction hash" }
|
|
8560
|
+
{ path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The funding transaction hash" },
|
|
8561
|
+
{ path: "denom", displayName: "Denom", type: "string", description: "The base denom that was sent" }
|
|
7037
8562
|
],
|
|
7038
8563
|
run: async (inputs, ctx) => {
|
|
7039
8564
|
if (!ctx.services.oracle?.generateWallet) {
|
|
@@ -7046,9 +8571,13 @@ registerAction({
|
|
|
7046
8571
|
if (!walletResult?.address) {
|
|
7047
8572
|
throw new Error("generateWallet did not return an address");
|
|
7048
8573
|
}
|
|
8574
|
+
const denom = String(inputs.denom || "").trim() || "uixo";
|
|
8575
|
+
const fromAddress = String(inputs.fromAddress || "").trim();
|
|
7049
8576
|
const fundResult = await ctx.services.oracle.fundWallet({
|
|
7050
8577
|
address: walletResult.address,
|
|
7051
|
-
amount: inputs.amount || 25e4
|
|
8578
|
+
amount: inputs.amount || 25e4,
|
|
8579
|
+
denom,
|
|
8580
|
+
...fromAddress ? { fromAddress } : {}
|
|
7052
8581
|
});
|
|
7053
8582
|
if (!fundResult?.transactionHash) {
|
|
7054
8583
|
throw new Error("fundWallet did not return a transactionHash");
|
|
@@ -7059,7 +8588,8 @@ registerAction({
|
|
|
7059
8588
|
did: walletResult.did,
|
|
7060
8589
|
pubKey: walletResult.pubKey,
|
|
7061
8590
|
mnemonic: walletResult.mnemonic,
|
|
7062
|
-
transactionHash: fundResult.transactionHash
|
|
8591
|
+
transactionHash: fundResult.transactionHash,
|
|
8592
|
+
denom
|
|
7063
8593
|
}
|
|
7064
8594
|
};
|
|
7065
8595
|
}
|
|
@@ -8105,14 +9635,6 @@ var COLLECTION_CREATED_EVENT = {
|
|
|
8105
9635
|
pendingDisplayFields: ["collectionId", "entity"]
|
|
8106
9636
|
};
|
|
8107
9637
|
|
|
8108
|
-
// src/core/lib/actionRegistry/types.ts
|
|
8109
|
-
var CollectionStateEnum = /* @__PURE__ */ ((CollectionStateEnum2) => {
|
|
8110
|
-
CollectionStateEnum2[CollectionStateEnum2["OPEN"] = 0] = "OPEN";
|
|
8111
|
-
CollectionStateEnum2[CollectionStateEnum2["PAUSED"] = 1] = "PAUSED";
|
|
8112
|
-
CollectionStateEnum2[CollectionStateEnum2["CLOSED"] = 2] = "CLOSED";
|
|
8113
|
-
return CollectionStateEnum2;
|
|
8114
|
-
})(CollectionStateEnum || {});
|
|
8115
|
-
|
|
8116
9638
|
// src/core/lib/actionRegistry/actions/collection/collection.ts
|
|
8117
9639
|
function normalizeQuota(quota) {
|
|
8118
9640
|
if (quota === void 0 || quota === null) return void 0;
|
|
@@ -9014,6 +10536,7 @@ async function runEvalRegister(inputs, ctx) {
|
|
|
9014
10536
|
const allowAiChecks = inputs.allowAiChecks !== false;
|
|
9015
10537
|
const allowImageChecks = allowAiChecks && inputs.allowImageChecks !== false;
|
|
9016
10538
|
const allowChainEvaluation = inputs.allowChainEvaluation !== false;
|
|
10539
|
+
const allowZeroPayoutApprovals = inputs.allowZeroPayoutApprovals === true;
|
|
9017
10540
|
if (!collectionId) throw new Error("collectionId is required");
|
|
9018
10541
|
if (!deedDid) throw new Error("deedDid (entity/deed DID) is required");
|
|
9019
10542
|
if (!ownerDid) throw new Error("ownerDid is required (pass it explicitly, or run as the collection owner)");
|
|
@@ -9052,7 +10575,7 @@ async function runEvalRegister(inputs, ctx) {
|
|
|
9052
10575
|
// (see above), and a host that copies `params` field-by-field would otherwise forward an
|
|
9053
10576
|
// explicit `undefined` as the erasing empty value.
|
|
9054
10577
|
...description !== void 0 ? { description } : {},
|
|
9055
|
-
settings: { allowAiChecks, allowImageChecks, allowChainEvaluation }
|
|
10578
|
+
settings: { allowAiChecks, allowImageChecks, allowChainEvaluation, allowZeroPayoutApprovals }
|
|
9056
10579
|
});
|
|
9057
10580
|
const registrationId = String(registration?.id || "").trim();
|
|
9058
10581
|
if (!registrationId) {
|
|
@@ -9125,8 +10648,12 @@ function canonicalJson(value) {
|
|
|
9125
10648
|
throw new Error(`canonicalJson: unsupported value of type ${typeof value}`);
|
|
9126
10649
|
}
|
|
9127
10650
|
|
|
10651
|
+
// src/core/lib/actionRegistry/actions/evalRubric/pathGrammar.ts
|
|
10652
|
+
var FORM_SEGMENT = "[A-Za-z0-9_-]+(?::[A-Za-z0-9_-]+)?";
|
|
10653
|
+
|
|
9128
10654
|
// src/core/lib/actionRegistry/actions/evalRubric/fieldCatalog.ts
|
|
9129
|
-
var SEGMENT =
|
|
10655
|
+
var SEGMENT = new RegExp(`^${FORM_SEGMENT}$`);
|
|
10656
|
+
var NAME_PATH = new RegExp(`^${FORM_SEGMENT}(?:\\.${FORM_SEGMENT})*$`);
|
|
9130
10657
|
function scalarKind(type, inputType) {
|
|
9131
10658
|
switch (type) {
|
|
9132
10659
|
case "text":
|
|
@@ -9209,7 +10736,7 @@ function extractRubricFieldCatalog(surveyTemplate, proof = "") {
|
|
|
9209
10736
|
}
|
|
9210
10737
|
const name = typeof el.name === "string" ? el.name.trim() : "";
|
|
9211
10738
|
const type = typeof el.type === "string" ? el.type : "";
|
|
9212
|
-
if (!name || !
|
|
10739
|
+
if (!name || !NAME_PATH.test(name) || seen2.has(name) || type === "html" || type === "expression") continue;
|
|
9213
10740
|
const field = extractQuestion(el, name, type);
|
|
9214
10741
|
if (!field) continue;
|
|
9215
10742
|
seen2.add(name);
|
|
@@ -9305,14 +10832,15 @@ function titleOf(el) {
|
|
|
9305
10832
|
return title || humanize2(typeof el.name === "string" ? el.name : "");
|
|
9306
10833
|
}
|
|
9307
10834
|
function humanize2(name) {
|
|
9308
|
-
|
|
10835
|
+
const bare = name.split(".").map((seg) => seg.includes(":") ? seg.split(":").pop() : seg).join(".");
|
|
10836
|
+
return bare.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
9309
10837
|
}
|
|
9310
10838
|
function stripHtml2(s) {
|
|
9311
10839
|
return s.replace(/<[^>]*>/g, "").replace(/\s+/g, " ").trim();
|
|
9312
10840
|
}
|
|
9313
10841
|
|
|
9314
10842
|
// src/core/lib/actionRegistry/actions/evalRubric/schemaGate.ts
|
|
9315
|
-
import
|
|
10843
|
+
import Ajv20203 from "ajv/dist/2020.js";
|
|
9316
10844
|
|
|
9317
10845
|
// src/core/lib/actionRegistry/actions/evalRubric/types.ts
|
|
9318
10846
|
var RUBRIC_CTX_TOKENS = [
|
|
@@ -9396,7 +10924,7 @@ async function getValidator(fetchSchema, evalEngineUrl) {
|
|
|
9396
10924
|
if (compiledValidator) return compiledValidator;
|
|
9397
10925
|
const schema = await fetchSchema(evalEngineUrl);
|
|
9398
10926
|
if (!schema || typeof schema !== "object") throw new Error("the rules service returned no schema");
|
|
9399
|
-
const validate = new
|
|
10927
|
+
const validate = new Ajv20203({ allErrors: true, strict: false }).compile(schema);
|
|
9400
10928
|
compiledValidator = validate;
|
|
9401
10929
|
return validate;
|
|
9402
10930
|
}
|
|
@@ -9494,7 +11022,7 @@ var ExpressionSyntaxError = class extends Error {
|
|
|
9494
11022
|
};
|
|
9495
11023
|
var IDENT = /[A-Za-z_][A-Za-z0-9_]*/y;
|
|
9496
11024
|
var NUMBER = /(?:\d+\.?\d*|\.\d+)/y;
|
|
9497
|
-
var FIELD_REF =
|
|
11025
|
+
var FIELD_REF = new RegExp(`\\$${FORM_SEGMENT}(?:\\.${FORM_SEGMENT}|\\[\\*\\])*`, "y");
|
|
9498
11026
|
var SIGIL_REF = /~[A-Za-z_][A-Za-z0-9_]*/y;
|
|
9499
11027
|
var CTX_REF = /ctx(?:\.[A-Za-z][A-Za-z0-9]*)+/y;
|
|
9500
11028
|
function parseExpression(src) {
|
|
@@ -9657,8 +11185,9 @@ var CTX_TOKEN_KIND = {
|
|
|
9657
11185
|
"ctx.submitter.priorApprovedCount": "number",
|
|
9658
11186
|
"ctx.collection.projectBoundary": "geo"
|
|
9659
11187
|
};
|
|
9660
|
-
var FIELD_REF2 =
|
|
9661
|
-
var ROW_REF =
|
|
11188
|
+
var FIELD_REF2 = new RegExp(`^\\$${FORM_SEGMENT}(?:\\.${FORM_SEGMENT}|\\[\\*\\])*$`);
|
|
11189
|
+
var ROW_REF = new RegExp(`^\\.${FORM_SEGMENT}$`);
|
|
11190
|
+
var FIELD_ROOT = new RegExp(`^\\$${FORM_SEGMENT}`);
|
|
9662
11191
|
var DERIVED_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
9663
11192
|
var EXT_REF = /^ext\.([A-Za-z0-9_-]+)\.(valid|score|reason)$/;
|
|
9664
11193
|
var AI_REF = /^ai\.([A-Za-z0-9_-]+)\.(valid|reason)$/;
|
|
@@ -9690,7 +11219,7 @@ function validateRubric(body, catalog, authoringCatalog) {
|
|
|
9690
11219
|
error("RUB_SCHEMA_DRIFT", `'${ref}' no longer exists on the claim form (deleted or renamed since the rules were authored)`, path);
|
|
9691
11220
|
return void 0;
|
|
9692
11221
|
}
|
|
9693
|
-
const rootName =
|
|
11222
|
+
const rootName = FIELD_ROOT.exec(ref)?.[0] ?? ref;
|
|
9694
11223
|
if (ref !== rootName && fieldIndex.has(rootName)) {
|
|
9695
11224
|
error("RUB_FIELD_PATH", `'${ref}' does not address a row/column of ${rootName}`, path);
|
|
9696
11225
|
} else {
|
|
@@ -10763,6 +12292,11 @@ registerAction({
|
|
|
10763
12292
|
allowAiChecks: { type: "boolean", default: true, description: "Engine setting: allow paid AI checks for this collection." },
|
|
10764
12293
|
allowImageChecks: { type: "boolean", default: true, description: "Engine setting: allow fake-photo detection (needs AI checks on)." },
|
|
10765
12294
|
allowChainEvaluation: { type: "boolean", default: true, description: "Engine setting: submit the decision on chain (releases payment); makes adminAddress required." },
|
|
12295
|
+
allowZeroPayoutApprovals: {
|
|
12296
|
+
type: "boolean",
|
|
12297
|
+
default: false,
|
|
12298
|
+
description: "Engine setting: approve on chain even when the approval pays nothing (attestation-only collections). Opt-in \u2014 otherwise such approvals wait for the owner."
|
|
12299
|
+
},
|
|
10766
12300
|
evaluateMaxAmount: { type: "array", description: "Per-claim payout cap on the evaluate grant, base-unit coins in the owner's denoms." },
|
|
10767
12301
|
// ---- rules (qi/eval.rubric) ----
|
|
10768
12302
|
rubric: {
|
|
@@ -10935,6 +12469,27 @@ registerAction({
|
|
|
10935
12469
|
});
|
|
10936
12470
|
|
|
10937
12471
|
// src/core/lib/actionRegistry/actions/_shared/delegatedTool.ts
|
|
12472
|
+
function delegatedToolInputSchema(schema) {
|
|
12473
|
+
return {
|
|
12474
|
+
type: "object",
|
|
12475
|
+
required: ["connection", ...schema.parameters.required],
|
|
12476
|
+
additionalProperties: false,
|
|
12477
|
+
properties: {
|
|
12478
|
+
connection: {
|
|
12479
|
+
type: "object",
|
|
12480
|
+
required: ["bindingId", "connectedAccountId", "toolkit"],
|
|
12481
|
+
additionalProperties: false,
|
|
12482
|
+
properties: {
|
|
12483
|
+
bindingId: { type: "string", minLength: 1, description: "Opaque, server-side delegated credential binding." },
|
|
12484
|
+
connectedAccountId: { type: "string" },
|
|
12485
|
+
toolkit: { type: "string" },
|
|
12486
|
+
label: { type: ["string", "null"] }
|
|
12487
|
+
}
|
|
12488
|
+
},
|
|
12489
|
+
...schema.parameters.properties
|
|
12490
|
+
}
|
|
12491
|
+
};
|
|
12492
|
+
}
|
|
10938
12493
|
function parseBoundConnection(raw) {
|
|
10939
12494
|
if (!raw || typeof raw !== "object") return null;
|
|
10940
12495
|
const c = raw;
|
|
@@ -11021,7 +12576,10 @@ async function executeDelegatedTool(ctx, opts) {
|
|
|
11021
12576
|
}
|
|
11022
12577
|
throw new Error(result.error || `${opts.toolkitLabel} action failed.`);
|
|
11023
12578
|
}
|
|
11024
|
-
return
|
|
12579
|
+
return {
|
|
12580
|
+
data: result.data ?? {},
|
|
12581
|
+
providerInvocationReceipt: result.providerInvocationReceipt
|
|
12582
|
+
};
|
|
11025
12583
|
}
|
|
11026
12584
|
|
|
11027
12585
|
// src/core/lib/actionRegistry/actions/gmail/emailSend.types.ts
|
|
@@ -11047,7 +12605,9 @@ var GMAIL_SEND_SCHEMA = {
|
|
|
11047
12605
|
var GMAIL_SEND_OUTPUT_SCHEMA = [
|
|
11048
12606
|
{ path: "messageId", displayName: "Message ID", type: "string", description: "Gmail id of the sent message" },
|
|
11049
12607
|
{ path: "threadId", displayName: "Thread ID", type: "string" },
|
|
11050
|
-
{ path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" }
|
|
12608
|
+
{ path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" },
|
|
12609
|
+
{ path: "providerReceiptId", displayName: "Provider Receipt ID", type: "string", description: "Signed integration-host invocation receipt identifier" },
|
|
12610
|
+
{ path: "providerInvocationReceipt", displayName: "Provider Receipt", type: "object", description: "Signed integration-host invocation receipt" }
|
|
11051
12611
|
];
|
|
11052
12612
|
|
|
11053
12613
|
// src/core/lib/actionRegistry/actions/gmail/emailSend.ts
|
|
@@ -11062,6 +12622,7 @@ registerAction({
|
|
|
11062
12622
|
requiredCapability: "flow/block/execute",
|
|
11063
12623
|
// Can be wired to another block's event (e.g. form submitted → send email).
|
|
11064
12624
|
eligibleForEventTrigger: true,
|
|
12625
|
+
inputSchema: delegatedToolInputSchema(GMAIL_SEND_SCHEMA),
|
|
11065
12626
|
// Mirrors executeDelegatedTool's gates: the bound connection plus the
|
|
11066
12627
|
// tool schema's required fields, so orchestrators ask before run() throws.
|
|
11067
12628
|
getMissingInputs: (inputs) => delegatedToolMissingInputs(GMAIL_SEND_SCHEMA, inputs),
|
|
@@ -11081,13 +12642,14 @@ registerAction({
|
|
|
11081
12642
|
run: async (inputs, ctx) => {
|
|
11082
12643
|
const parsed = parseDelegatedToolInputs(inputs);
|
|
11083
12644
|
const values = fieldValues(parsed);
|
|
11084
|
-
const
|
|
12645
|
+
const execution = await executeDelegatedTool(ctx, {
|
|
11085
12646
|
connection: parsed.connection,
|
|
11086
12647
|
schema: GMAIL_SEND_SCHEMA,
|
|
11087
12648
|
toolSlug: GMAIL_SEND_SLUG,
|
|
11088
12649
|
values,
|
|
11089
12650
|
toolkitLabel: "Gmail"
|
|
11090
12651
|
});
|
|
12652
|
+
const { data, providerInvocationReceipt } = execution;
|
|
11091
12653
|
const envelope = data.response_data ?? data;
|
|
11092
12654
|
const messageId = String(envelope.id ?? envelope.messageId ?? "");
|
|
11093
12655
|
const threadId = String(envelope.threadId ?? "");
|
|
@@ -11095,7 +12657,9 @@ registerAction({
|
|
|
11095
12657
|
output: {
|
|
11096
12658
|
messageId,
|
|
11097
12659
|
threadId,
|
|
11098
|
-
sentAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12660
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12661
|
+
providerReceiptId: providerInvocationReceipt?.id || "",
|
|
12662
|
+
providerInvocationReceipt
|
|
11099
12663
|
},
|
|
11100
12664
|
events: messageId ? [{ name: GMAIL_SENT_EVENT, payload: { messageId, recipient_email: values.recipient_email ?? "" } }] : void 0
|
|
11101
12665
|
};
|
|
@@ -11125,7 +12689,9 @@ var OUTLOOK_SEND_SCHEMA = {
|
|
|
11125
12689
|
};
|
|
11126
12690
|
var OUTLOOK_SEND_OUTPUT_SCHEMA = [
|
|
11127
12691
|
{ path: "messageId", displayName: "Message ID", type: "string" },
|
|
11128
|
-
{ path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" }
|
|
12692
|
+
{ path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" },
|
|
12693
|
+
{ path: "providerReceiptId", displayName: "Provider Receipt ID", type: "string", description: "Required signed proof when Outlook returns no message id" },
|
|
12694
|
+
{ path: "providerInvocationReceipt", displayName: "Provider Receipt", type: "object", description: "Signed integration-host invocation receipt" }
|
|
11129
12695
|
];
|
|
11130
12696
|
|
|
11131
12697
|
// src/core/lib/actionRegistry/actions/outlook/emailSend.ts
|
|
@@ -11133,16 +12699,15 @@ registerAction({
|
|
|
11133
12699
|
type: "qi/outlook.email.send",
|
|
11134
12700
|
can: "outlook.email/send",
|
|
11135
12701
|
sideEffect: true,
|
|
11136
|
-
// Outlook
|
|
11137
|
-
//
|
|
11138
|
-
|
|
11139
|
-
// requiring messageId, which would push every id-less send to needs_verification.
|
|
11140
|
-
proof: "none",
|
|
12702
|
+
// Outlook often returns no message id. The integration host must therefore
|
|
12703
|
+
// return a signed provider invocation receipt for proof of the side effect.
|
|
12704
|
+
proof: { fields: ["messageId", "providerReceiptId"] },
|
|
11141
12705
|
done: doneWhenCompleted,
|
|
11142
12706
|
defaultRequiresConfirmation: true,
|
|
11143
12707
|
requiredCapability: "flow/block/execute",
|
|
11144
12708
|
// Can be wired to another block's event (e.g. form submitted → send email).
|
|
11145
12709
|
eligibleForEventTrigger: true,
|
|
12710
|
+
inputSchema: delegatedToolInputSchema(OUTLOOK_SEND_SCHEMA),
|
|
11146
12711
|
// Mirrors executeDelegatedTool's gates: the bound connection plus the
|
|
11147
12712
|
// tool schema's required fields, so orchestrators ask before run() throws.
|
|
11148
12713
|
getMissingInputs: (inputs) => delegatedToolMissingInputs(OUTLOOK_SEND_SCHEMA, inputs),
|
|
@@ -11162,181 +12727,1003 @@ registerAction({
|
|
|
11162
12727
|
run: async (inputs, ctx) => {
|
|
11163
12728
|
const parsed = parseDelegatedToolInputs(inputs);
|
|
11164
12729
|
const values = fieldValues(parsed);
|
|
11165
|
-
const
|
|
12730
|
+
const execution = await executeDelegatedTool(ctx, {
|
|
11166
12731
|
connection: parsed.connection,
|
|
11167
12732
|
schema: OUTLOOK_SEND_SCHEMA,
|
|
11168
12733
|
toolSlug: OUTLOOK_SEND_SLUG,
|
|
11169
12734
|
values,
|
|
11170
12735
|
toolkitLabel: "Outlook"
|
|
11171
12736
|
});
|
|
12737
|
+
const { data, providerInvocationReceipt } = execution;
|
|
11172
12738
|
const envelope = data.response_data ?? data;
|
|
11173
12739
|
const messageId = String(envelope.id ?? envelope.messageId ?? "");
|
|
12740
|
+
if (!messageId && !providerInvocationReceipt?.id) {
|
|
12741
|
+
throw new Error("Outlook returned no message id and the integration host returned no signed provider invocation receipt.");
|
|
12742
|
+
}
|
|
12743
|
+
return {
|
|
12744
|
+
output: {
|
|
12745
|
+
messageId,
|
|
12746
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12747
|
+
providerReceiptId: providerInvocationReceipt?.id || "",
|
|
12748
|
+
providerInvocationReceipt
|
|
12749
|
+
},
|
|
12750
|
+
// Outlook often returns no id, so emit unconditionally.
|
|
12751
|
+
events: [{ name: OUTLOOK_SENT_EVENT, payload: { messageId, to_email: values.to_email ?? "" } }]
|
|
12752
|
+
};
|
|
12753
|
+
}
|
|
12754
|
+
});
|
|
12755
|
+
|
|
12756
|
+
// src/core/lib/actionRegistry/actions/slack/messageSend.types.ts
|
|
12757
|
+
var SLACK_SEND_SLUG = "SLACK_CHAT_POST_MESSAGE";
|
|
12758
|
+
var SLACK_SENT_EVENT = "message.sent";
|
|
12759
|
+
var SLACK_SEND_SCHEMA = {
|
|
12760
|
+
slug: SLACK_SEND_SLUG,
|
|
12761
|
+
name: "Post Message",
|
|
12762
|
+
description: "Post a message to a Slack channel from the template author's Slack account.",
|
|
12763
|
+
parameters: {
|
|
12764
|
+
type: "object",
|
|
12765
|
+
required: ["channel"],
|
|
12766
|
+
properties: {
|
|
12767
|
+
channel: { type: "string", title: "Channel", description: "Channel ID or name, e.g. #general or C0123456." },
|
|
12768
|
+
markdown_text: {
|
|
12769
|
+
type: "string",
|
|
12770
|
+
title: "Message",
|
|
12771
|
+
description: "Message text in Slack markdown. Preferred over the deprecated plain text field."
|
|
12772
|
+
},
|
|
12773
|
+
thread_ts: { type: "string", title: "Thread", description: "Optional parent message timestamp to reply within a thread." }
|
|
12774
|
+
}
|
|
12775
|
+
}
|
|
12776
|
+
};
|
|
12777
|
+
var SLACK_SEND_OUTPUT_SCHEMA = [
|
|
12778
|
+
{ path: "messageTs", displayName: "Message ts", type: "string" },
|
|
12779
|
+
{ path: "channel", displayName: "Channel", type: "string" },
|
|
12780
|
+
{ path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" },
|
|
12781
|
+
{ path: "providerReceiptId", displayName: "Provider Receipt ID", type: "string", description: "Signed integration-host invocation receipt identifier" },
|
|
12782
|
+
{ path: "providerInvocationReceipt", displayName: "Provider Receipt", type: "object", description: "Signed integration-host invocation receipt" }
|
|
12783
|
+
];
|
|
12784
|
+
|
|
12785
|
+
// src/core/lib/actionRegistry/actions/slack/messageSend.ts
|
|
12786
|
+
registerAction({
|
|
12787
|
+
type: "qi/slack.message.send",
|
|
12788
|
+
can: "slack.message/send",
|
|
12789
|
+
sideEffect: true,
|
|
12790
|
+
// Proof of execution: Slack returns the posted message timestamp (ts).
|
|
12791
|
+
proof: { fields: ["messageTs"] },
|
|
12792
|
+
done: doneWhenCompleted,
|
|
12793
|
+
defaultRequiresConfirmation: true,
|
|
12794
|
+
requiredCapability: "flow/block/execute",
|
|
12795
|
+
// Can be wired to another block's event (e.g. form submitted → post message).
|
|
12796
|
+
eligibleForEventTrigger: true,
|
|
12797
|
+
inputSchema: delegatedToolInputSchema(SLACK_SEND_SCHEMA),
|
|
12798
|
+
// Mirrors executeDelegatedTool's gates: the bound connection plus the
|
|
12799
|
+
// tool schema's required fields, so orchestrators ask before run() throws.
|
|
12800
|
+
getMissingInputs: (inputs) => delegatedToolMissingInputs(SLACK_SEND_SCHEMA, inputs),
|
|
12801
|
+
outputSchema: SLACK_SEND_OUTPUT_SCHEMA,
|
|
12802
|
+
events: [
|
|
12803
|
+
{
|
|
12804
|
+
name: SLACK_SENT_EVENT,
|
|
12805
|
+
displayName: "Message posted",
|
|
12806
|
+
description: "Fired after the message is posted to Slack.",
|
|
12807
|
+
payloadSchema: [
|
|
12808
|
+
{ path: "messageTs", displayName: "Message ts", type: "string" },
|
|
12809
|
+
{ path: "channel", displayName: "Channel", type: "string" }
|
|
12810
|
+
],
|
|
12811
|
+
pendingDisplayFields: ["messageTs"]
|
|
12812
|
+
}
|
|
12813
|
+
],
|
|
12814
|
+
run: async (inputs, ctx) => {
|
|
12815
|
+
const parsed = parseDelegatedToolInputs(inputs);
|
|
12816
|
+
const values = fieldValues(parsed);
|
|
12817
|
+
const execution = await executeDelegatedTool(ctx, {
|
|
12818
|
+
connection: parsed.connection,
|
|
12819
|
+
schema: SLACK_SEND_SCHEMA,
|
|
12820
|
+
toolSlug: SLACK_SEND_SLUG,
|
|
12821
|
+
values,
|
|
12822
|
+
toolkitLabel: "Slack"
|
|
12823
|
+
});
|
|
12824
|
+
const { data, providerInvocationReceipt } = execution;
|
|
12825
|
+
const envelope = data.response_data ?? data;
|
|
12826
|
+
const messageTs = String(envelope.ts ?? "");
|
|
12827
|
+
const channel = String(envelope.channel ?? values.channel ?? "");
|
|
12828
|
+
return {
|
|
12829
|
+
output: {
|
|
12830
|
+
messageTs,
|
|
12831
|
+
channel,
|
|
12832
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12833
|
+
providerReceiptId: providerInvocationReceipt?.id || "",
|
|
12834
|
+
providerInvocationReceipt
|
|
12835
|
+
},
|
|
12836
|
+
events: messageTs ? [{ name: SLACK_SENT_EVENT, payload: { messageTs, channel } }] : void 0
|
|
12837
|
+
};
|
|
12838
|
+
}
|
|
12839
|
+
});
|
|
12840
|
+
|
|
12841
|
+
// src/core/lib/actionRegistry/actions/googlecalendar/eventCreate.types.ts
|
|
12842
|
+
var GOOGLECALENDAR_CREATE_SLUG = "GOOGLECALENDAR_CREATE_EVENT";
|
|
12843
|
+
var GOOGLECALENDAR_CREATED_EVENT = "event.created";
|
|
12844
|
+
var GOOGLECALENDAR_CREATE_SCHEMA = {
|
|
12845
|
+
slug: GOOGLECALENDAR_CREATE_SLUG,
|
|
12846
|
+
name: "Create Event",
|
|
12847
|
+
description: "Create an event on the template author's Google Calendar.",
|
|
12848
|
+
parameters: {
|
|
12849
|
+
type: "object",
|
|
12850
|
+
required: ["start_datetime"],
|
|
12851
|
+
properties: {
|
|
12852
|
+
start_datetime: { type: "string", title: "Start time", description: "ISO 8601, e.g. 2026-05-12T09:00:00." },
|
|
12853
|
+
summary: { type: "string", title: "Title" },
|
|
12854
|
+
description: { type: "string", title: "Description" },
|
|
12855
|
+
location: { type: "string", title: "Location" },
|
|
12856
|
+
timezone: { type: "string", title: "Timezone", description: "IANA name, e.g. Europe/London." },
|
|
12857
|
+
attendees: { type: "array", title: "Attendees", description: "Comma-separated email addresses." },
|
|
12858
|
+
calendar_id: { type: "string", title: "Calendar", description: "Use 'primary' for the author's main calendar." },
|
|
12859
|
+
event_duration_minutes: { type: "number", title: "Duration (minutes)", description: "Defaults to the calendar default if blank." }
|
|
12860
|
+
}
|
|
12861
|
+
}
|
|
12862
|
+
};
|
|
12863
|
+
var GOOGLECALENDAR_CREATE_OUTPUT_SCHEMA = [
|
|
12864
|
+
{ path: "eventId", displayName: "Event ID", type: "string" },
|
|
12865
|
+
{ path: "htmlLink", displayName: "Event link", type: "string" },
|
|
12866
|
+
{ path: "summary", displayName: "Summary", type: "string" },
|
|
12867
|
+
{ path: "startIso", displayName: "Start", type: "string" },
|
|
12868
|
+
{ path: "providerReceiptId", displayName: "Provider Receipt ID", type: "string", description: "Signed integration-host invocation receipt identifier" },
|
|
12869
|
+
{ path: "providerInvocationReceipt", displayName: "Provider Receipt", type: "object", description: "Signed integration-host invocation receipt" }
|
|
12870
|
+
];
|
|
12871
|
+
|
|
12872
|
+
// src/core/lib/actionRegistry/actions/googlecalendar/eventCreate.ts
|
|
12873
|
+
registerAction({
|
|
12874
|
+
type: "qi/googlecalendar.event.create",
|
|
12875
|
+
can: "googlecalendar.event/create",
|
|
12876
|
+
sideEffect: true,
|
|
12877
|
+
// Proof of execution: the created event's id. Matches qi/calendar.event.create.
|
|
12878
|
+
proof: { fields: ["eventId"] },
|
|
12879
|
+
done: doneWhenCompleted,
|
|
12880
|
+
defaultRequiresConfirmation: true,
|
|
12881
|
+
requiredCapability: "flow/block/execute",
|
|
12882
|
+
eligibleForEventTrigger: true,
|
|
12883
|
+
inputSchema: delegatedToolInputSchema(GOOGLECALENDAR_CREATE_SCHEMA),
|
|
12884
|
+
// Mirrors executeDelegatedTool's gates: the bound connection plus the
|
|
12885
|
+
// tool schema's required fields, so orchestrators ask before run() throws.
|
|
12886
|
+
getMissingInputs: (inputs) => delegatedToolMissingInputs(GOOGLECALENDAR_CREATE_SCHEMA, inputs),
|
|
12887
|
+
outputSchema: GOOGLECALENDAR_CREATE_OUTPUT_SCHEMA,
|
|
12888
|
+
events: [
|
|
12889
|
+
{
|
|
12890
|
+
name: GOOGLECALENDAR_CREATED_EVENT,
|
|
12891
|
+
displayName: "Calendar event created",
|
|
12892
|
+
description: "Fired after the event is created on the author\u2019s calendar.",
|
|
12893
|
+
payloadSchema: [
|
|
12894
|
+
{ path: "eventId", displayName: "Event ID", type: "string" },
|
|
12895
|
+
{ path: "htmlLink", displayName: "Event link", type: "string" },
|
|
12896
|
+
{ path: "summary", displayName: "Summary", type: "string" }
|
|
12897
|
+
],
|
|
12898
|
+
pendingDisplayFields: ["summary", "eventId"]
|
|
12899
|
+
}
|
|
12900
|
+
],
|
|
12901
|
+
run: async (inputs, ctx) => {
|
|
12902
|
+
const parsed = parseDelegatedToolInputs(inputs);
|
|
12903
|
+
const values = fieldValues(parsed);
|
|
12904
|
+
const execution = await executeDelegatedTool(ctx, {
|
|
12905
|
+
connection: parsed.connection,
|
|
12906
|
+
schema: GOOGLECALENDAR_CREATE_SCHEMA,
|
|
12907
|
+
toolSlug: GOOGLECALENDAR_CREATE_SLUG,
|
|
12908
|
+
values,
|
|
12909
|
+
toolkitLabel: "Google Calendar"
|
|
12910
|
+
});
|
|
12911
|
+
const { data, providerInvocationReceipt } = execution;
|
|
12912
|
+
const envelope = data.response_data ?? data;
|
|
12913
|
+
const eventId = String(envelope.id ?? "");
|
|
12914
|
+
const htmlLink = String(envelope.htmlLink ?? "");
|
|
12915
|
+
const summary = String(envelope.summary ?? values.summary ?? "");
|
|
12916
|
+
const start = envelope.start;
|
|
12917
|
+
const startIso = String(start?.dateTime ?? start?.date ?? values.start_datetime ?? "");
|
|
12918
|
+
return {
|
|
12919
|
+
output: { eventId, htmlLink, summary, startIso, providerReceiptId: providerInvocationReceipt?.id || "", providerInvocationReceipt },
|
|
12920
|
+
events: eventId ? [{ name: GOOGLECALENDAR_CREATED_EVENT, payload: { eventId, htmlLink, summary } }] : void 0
|
|
12921
|
+
};
|
|
12922
|
+
}
|
|
12923
|
+
});
|
|
12924
|
+
|
|
12925
|
+
// src/core/lib/actionRegistry/actions/topicActions.ts
|
|
12926
|
+
var ALL_KINDS2 = ["task", "agent_task", "proposal", "evaluation", "claims", "question", "discussion", "incident"];
|
|
12927
|
+
function topicMetadata(supportedBaseKinds, permittedTopicRecordTypes, requiredTopicAbilities, relevance = "recommended", sensitiveInputPaths = [], sensitiveOutputPaths = []) {
|
|
12928
|
+
const semanticRecordTypes = getTopicSemanticRecordDefinitions(permittedTopicRecordTypes);
|
|
12929
|
+
if (semanticRecordTypes.length !== permittedTopicRecordTypes.length) {
|
|
12930
|
+
throw new Error(`Topic Action metadata names a semantic record type without a complete definition`);
|
|
12931
|
+
}
|
|
12932
|
+
return {
|
|
12933
|
+
supportedBaseKinds,
|
|
12934
|
+
relevance,
|
|
12935
|
+
writeBackMode: permittedTopicRecordTypes.length > 0 ? "semantic-record" : "receipt-only",
|
|
12936
|
+
semanticRecordTypes,
|
|
12937
|
+
permittedTopicRecordTypes: semanticRecordTypes.map((definition) => definition.type),
|
|
12938
|
+
lifecycleEffect: "none",
|
|
12939
|
+
requiredTopicAbilities,
|
|
12940
|
+
redactionPolicy: { mode: "paths", sensitiveInputPaths, sensitiveOutputPaths }
|
|
12941
|
+
};
|
|
12942
|
+
}
|
|
12943
|
+
function requiredString(value, name) {
|
|
12944
|
+
const normalized = String(value || "").trim();
|
|
12945
|
+
if (!normalized) throw new Error(`${name} is required`);
|
|
12946
|
+
return normalized;
|
|
12947
|
+
}
|
|
12948
|
+
function requiredArray(value, name) {
|
|
12949
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) throw new Error(`${name} must be an array of strings`);
|
|
12950
|
+
return value.map(String);
|
|
12951
|
+
}
|
|
12952
|
+
function topicContext(ctx) {
|
|
12953
|
+
if (!ctx.topic) throw new Error("This Action requires a revision-bound Topic execution context");
|
|
12954
|
+
return ctx.topic;
|
|
12955
|
+
}
|
|
12956
|
+
function topicService(ctx) {
|
|
12957
|
+
topicContext(ctx);
|
|
12958
|
+
if (!ctx.services.topic) throw new Error("The host did not grant the capability-checked Topic service");
|
|
12959
|
+
return ctx.services.topic;
|
|
12960
|
+
}
|
|
12961
|
+
function idempotencyKey(actionType, inputs, ctx) {
|
|
12962
|
+
const explicit = String(inputs.idempotencyKey || "").trim();
|
|
12963
|
+
if (explicit) return explicit;
|
|
12964
|
+
const topic = topicContext(ctx);
|
|
12965
|
+
return sha256Digest({ actionType, topicId: topic.topicId, topicRevision: topic.topicRevision, requestId: topic.requestId, inputs });
|
|
12966
|
+
}
|
|
12967
|
+
function semanticRecord(type, value, ctx) {
|
|
12968
|
+
const topic = topicContext(ctx);
|
|
12969
|
+
const digest2 = sha256Digest(value);
|
|
12970
|
+
return {
|
|
12971
|
+
digest: digest2,
|
|
12972
|
+
record: {
|
|
12973
|
+
type,
|
|
12974
|
+
id: sha256Digest({ type, topicId: topic.topicId, requestId: topic.requestId, digest: digest2 }),
|
|
12975
|
+
version: 1,
|
|
12976
|
+
value,
|
|
12977
|
+
evidenceReferences: Array.isArray(value.evidenceReferences) ? value.evidenceReferences : void 0
|
|
12978
|
+
}
|
|
12979
|
+
};
|
|
12980
|
+
}
|
|
12981
|
+
function registerTopicOperation(spec) {
|
|
12982
|
+
registerAction({
|
|
12983
|
+
type: spec.type,
|
|
12984
|
+
can: spec.can,
|
|
12985
|
+
sideEffect: true,
|
|
12986
|
+
proof: { fields: ["operationId"] },
|
|
12987
|
+
done: doneWhenCompleted,
|
|
12988
|
+
defaultRequiresConfirmation: spec.confirmation === true,
|
|
12989
|
+
requiredCapability: "flow/block/execute",
|
|
12990
|
+
executionOwner: spec.owner || "agent",
|
|
12991
|
+
hiddenFromAuthoring: spec.hidden,
|
|
12992
|
+
riskTier: spec.confirmation ? "high" : "medium",
|
|
12993
|
+
requiredServices: ["topic"],
|
|
12994
|
+
topic: topicMetadata(spec.kinds || ALL_KINDS2, [], [spec.ability]),
|
|
12995
|
+
inputSchema: spec.inputSchema,
|
|
12996
|
+
outputSchema: [
|
|
12997
|
+
{ path: "operationId", displayName: "Topic operation ID", type: "string" },
|
|
12998
|
+
{ path: "topicRevision", displayName: "Topic revision", type: "string" },
|
|
12999
|
+
{ path: "proofReference", displayName: "Operation proof", type: "string" }
|
|
13000
|
+
],
|
|
13001
|
+
run: async (inputs, ctx) => {
|
|
13002
|
+
const service = topicService(ctx);
|
|
13003
|
+
const payload = await spec.buildPayload(inputs, ctx);
|
|
13004
|
+
return {
|
|
13005
|
+
output: await service.appendOperation({
|
|
13006
|
+
context: topicContext(ctx),
|
|
13007
|
+
actorDid: ctx.actorDid,
|
|
13008
|
+
operationType: spec.operationType,
|
|
13009
|
+
payload,
|
|
13010
|
+
idempotencyKey: idempotencyKey(spec.type, inputs, ctx)
|
|
13011
|
+
})
|
|
13012
|
+
};
|
|
13013
|
+
}
|
|
13014
|
+
});
|
|
13015
|
+
}
|
|
13016
|
+
registerTopicOperation({
|
|
13017
|
+
type: "qi/topic.flow.bind",
|
|
13018
|
+
can: "topic/flow.bind",
|
|
13019
|
+
operationType: "bind-flow",
|
|
13020
|
+
confirmation: true,
|
|
13021
|
+
owner: "human",
|
|
13022
|
+
ability: "topic/bind-flow",
|
|
13023
|
+
inputSchema: {
|
|
13024
|
+
type: "object",
|
|
13025
|
+
required: ["flowUri", "flowRevision", "flowDigest", "actionManifestDigest", "controllerDid", "role", "startPolicy", "triggerPolicy", "receiptPolicy"],
|
|
13026
|
+
additionalProperties: false,
|
|
13027
|
+
properties: {
|
|
13028
|
+
bindingId: { type: "string" },
|
|
13029
|
+
flowUri: { type: "string" },
|
|
13030
|
+
flowRevision: { type: "string" },
|
|
13031
|
+
flowDigest: { type: "string", pattern: "^sha256:" },
|
|
13032
|
+
actionManifestDigest: { type: "string", pattern: "^sha256:" },
|
|
13033
|
+
controllerDid: { type: "string", pattern: "^did:" },
|
|
13034
|
+
role: { type: "string", enum: ["primary", "supporting"] },
|
|
13035
|
+
startPolicy: { type: "string", enum: ["manual", "on-topic-active", "scheduled", "event"] },
|
|
13036
|
+
triggerPolicy: { type: "object" },
|
|
13037
|
+
receiptPolicy: { type: "string", enum: ["all", "terminal"] },
|
|
13038
|
+
capabilityReferences: { type: "array", minItems: 2, uniqueItems: true, items: { type: "string" } },
|
|
13039
|
+
idempotencyKey: { type: "string" }
|
|
13040
|
+
}
|
|
13041
|
+
},
|
|
13042
|
+
buildPayload: (inputs, ctx) => {
|
|
13043
|
+
const topic = topicContext(ctx);
|
|
13044
|
+
return {
|
|
13045
|
+
binding: {
|
|
13046
|
+
version: 1,
|
|
13047
|
+
bindingId: String(inputs.bindingId || sha256Digest({ topicId: topic.topicId, flowUri: inputs.flowUri, flowRevision: inputs.flowRevision })),
|
|
13048
|
+
topicId: topic.topicId,
|
|
13049
|
+
flowUri: requiredString(inputs.flowUri, "flowUri"),
|
|
13050
|
+
flowRevision: requiredString(inputs.flowRevision, "flowRevision"),
|
|
13051
|
+
flowDigest: requiredString(inputs.flowDigest, "flowDigest"),
|
|
13052
|
+
actionManifestDigest: requiredString(inputs.actionManifestDigest, "actionManifestDigest"),
|
|
13053
|
+
controllerDid: requiredString(inputs.controllerDid, "controllerDid"),
|
|
13054
|
+
role: requiredString(inputs.role, "role"),
|
|
13055
|
+
startPolicy: requiredString(inputs.startPolicy, "startPolicy"),
|
|
13056
|
+
triggerPolicy: inputs.triggerPolicy || {},
|
|
13057
|
+
receiptPolicy: requiredString(inputs.receiptPolicy, "receiptPolicy"),
|
|
13058
|
+
status: "active",
|
|
13059
|
+
capability: topic.topicCapabilityReference,
|
|
13060
|
+
capabilityReferences: Array.isArray(inputs.capabilityReferences) ? inputs.capabilityReferences.map(String) : [],
|
|
13061
|
+
createdBy: ctx.actorDid,
|
|
13062
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13063
|
+
}
|
|
13064
|
+
};
|
|
13065
|
+
}
|
|
13066
|
+
});
|
|
13067
|
+
registerTopicOperation({
|
|
13068
|
+
type: "qi/topic.flow.unbind",
|
|
13069
|
+
can: "topic/flow.unbind",
|
|
13070
|
+
operationType: "unbind-flow",
|
|
13071
|
+
confirmation: true,
|
|
13072
|
+
owner: "human",
|
|
13073
|
+
ability: "topic/bind-flow",
|
|
13074
|
+
inputSchema: {
|
|
13075
|
+
type: "object",
|
|
13076
|
+
required: ["bindingId"],
|
|
13077
|
+
additionalProperties: false,
|
|
13078
|
+
properties: { bindingId: { type: "string" }, reason: { type: "string" }, idempotencyKey: { type: "string" } }
|
|
13079
|
+
},
|
|
13080
|
+
buildPayload: (inputs) => ({ bindingId: requiredString(inputs.bindingId, "bindingId"), reason: String(inputs.reason || "") })
|
|
13081
|
+
});
|
|
13082
|
+
registerTopicOperation({
|
|
13083
|
+
type: "qi/topic.action.request",
|
|
13084
|
+
can: "topic/action.request",
|
|
13085
|
+
operationType: "request-action",
|
|
13086
|
+
ability: "topic/request-action",
|
|
13087
|
+
inputSchema: {
|
|
13088
|
+
type: "object",
|
|
13089
|
+
required: ["actionType", "actionContractDigest", "inputDigest"],
|
|
13090
|
+
additionalProperties: false,
|
|
13091
|
+
properties: {
|
|
13092
|
+
actionType: { type: "string" },
|
|
13093
|
+
actionContractDigest: { type: "string", pattern: "^sha256:" },
|
|
13094
|
+
inputDigest: { type: "string", pattern: "^sha256:" },
|
|
13095
|
+
inputReference: { type: "string" },
|
|
13096
|
+
requestId: { type: "string" },
|
|
13097
|
+
safeInputSummary: { type: "object" },
|
|
13098
|
+
executorPreference: { type: "string", enum: ["qi-flow", "qiforge", "mcp"] },
|
|
13099
|
+
bindingId: { type: "string" },
|
|
13100
|
+
confirmationPolicy: { type: "string", enum: ["inherit", "required"] },
|
|
13101
|
+
transitionCode: { type: "string" },
|
|
13102
|
+
idempotencyKey: { type: "string" }
|
|
13103
|
+
}
|
|
13104
|
+
},
|
|
13105
|
+
buildPayload: (inputs, ctx) => {
|
|
13106
|
+
const actionType = requiredString(inputs.actionType, "actionType");
|
|
13107
|
+
if (actionType === "qi/topic.action.request") throw new Error("A Topic Action request cannot recursively request itself");
|
|
13108
|
+
const target = getAction(actionType);
|
|
13109
|
+
if (!target) throw new Error(`Unknown Action type '${actionType}'`);
|
|
13110
|
+
const manifestEntry = generateActionManifest().actions.find((entry) => entry.type === target.type);
|
|
13111
|
+
const suppliedDigest = requiredString(inputs.actionContractDigest, "actionContractDigest");
|
|
13112
|
+
if (!manifestEntry || manifestEntry.contractDigest !== suppliedDigest) throw new Error("Action contract digest does not match the live registry");
|
|
13113
|
+
const kind = topicContext(ctx).kind;
|
|
13114
|
+
const baseKind = kind.source === "standard" ? kind.kind : kind.baseKind;
|
|
13115
|
+
if (!target.topic?.supportedBaseKinds.includes(baseKind)) throw new Error(`Action '${actionType}' does not support Topic base Kind '${baseKind}'`);
|
|
13116
|
+
const topic = topicContext(ctx);
|
|
13117
|
+
const inputDigest = requiredString(inputs.inputDigest, "inputDigest");
|
|
13118
|
+
const derived = (purpose) => sha256Digest({ purpose, parentRequestId: topic.requestId, topicId: topic.topicId, actionType: target.type, inputDigest });
|
|
13119
|
+
return {
|
|
13120
|
+
...inputs.transitionCode ? { transitionCode: String(inputs.transitionCode) } : {},
|
|
13121
|
+
request: {
|
|
13122
|
+
version: 1,
|
|
13123
|
+
requestId: String(inputs.requestId || derived("topic-action-request")),
|
|
13124
|
+
topicId: topic.topicId,
|
|
13125
|
+
topicRevision: topic.topicRevision,
|
|
13126
|
+
actionType: target.type,
|
|
13127
|
+
actionContractDigest: suppliedDigest,
|
|
13128
|
+
executor: inputs.executorPreference || "qi-flow",
|
|
13129
|
+
inputs: { digest: inputDigest, ...inputs.inputReference ? { ref: String(inputs.inputReference) } : {} },
|
|
13130
|
+
requestedBy: ctx.actorDid,
|
|
13131
|
+
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13132
|
+
idempotencyKey: String(inputs.idempotencyKey || derived("topic-action-idempotency")),
|
|
13133
|
+
confirmation: target.defaultRequiresConfirmation || inputs.confirmationPolicy === "required" ? "required" : topic.confirmationReference ? "confirmed" : "not-required",
|
|
13134
|
+
...inputs.bindingId ? { flowBindingId: String(inputs.bindingId) } : {},
|
|
13135
|
+
capability: topic.topicCapabilityReference
|
|
13136
|
+
}
|
|
13137
|
+
};
|
|
13138
|
+
}
|
|
13139
|
+
});
|
|
13140
|
+
registerTopicOperation({
|
|
13141
|
+
type: "qi/topic.action.receipt.record",
|
|
13142
|
+
can: "topic/action.receipt.record",
|
|
13143
|
+
operationType: "record-action-receipt",
|
|
13144
|
+
ability: "topic/record-action",
|
|
13145
|
+
hidden: true,
|
|
13146
|
+
inputSchema: {
|
|
13147
|
+
type: "object",
|
|
13148
|
+
required: ["receipt"],
|
|
13149
|
+
additionalProperties: false,
|
|
13150
|
+
properties: { receipt: { type: "object" }, idempotencyKey: { type: "string" } }
|
|
13151
|
+
},
|
|
13152
|
+
buildPayload: (inputs, ctx) => {
|
|
13153
|
+
if (!inputs.receipt || typeof inputs.receipt !== "object" || Array.isArray(inputs.receipt)) throw new Error("receipt must be an ActionReceiptV2 object");
|
|
13154
|
+
const receipt = inputs.receipt;
|
|
13155
|
+
if (receipt.topicId !== topicContext(ctx).topicId) throw new Error("Receipt Topic does not match the execution context");
|
|
13156
|
+
if (receipt.version !== 2 || !receipt.signature || !receipt.issuerDid) throw new Error("Receipt requires a v2 issuer signature");
|
|
13157
|
+
const receiptAction = receipt.action;
|
|
13158
|
+
const action = getAction(String(receiptAction?.type || ""));
|
|
13159
|
+
const manifestEntry = action && generateActionManifest().actions.find((entry) => entry.type === action.type);
|
|
13160
|
+
if (!manifestEntry || manifestEntry.contractDigest !== receiptAction?.contractDigest) throw new Error("Receipt Action contract digest does not match the live registry");
|
|
13161
|
+
return { receipt };
|
|
13162
|
+
}
|
|
13163
|
+
});
|
|
13164
|
+
registerTopicOperation({
|
|
13165
|
+
type: "qi/topic.action.cancel",
|
|
13166
|
+
can: "topic/action.cancel",
|
|
13167
|
+
operationType: "cancel-action",
|
|
13168
|
+
ability: "topic/cancel-action",
|
|
13169
|
+
inputSchema: {
|
|
13170
|
+
type: "object",
|
|
13171
|
+
required: ["requestId", "reason"],
|
|
13172
|
+
additionalProperties: false,
|
|
13173
|
+
properties: { requestId: { type: "string" }, reason: { type: "string" }, idempotencyKey: { type: "string" } }
|
|
13174
|
+
},
|
|
13175
|
+
buildPayload: (inputs) => ({ requestId: requiredString(inputs.requestId, "requestId"), reason: requiredString(inputs.reason, "reason") })
|
|
13176
|
+
});
|
|
13177
|
+
registerTopicOperation({
|
|
13178
|
+
type: "qi/topic.status.transition",
|
|
13179
|
+
can: "topic/status.transition",
|
|
13180
|
+
operationType: "change-status",
|
|
13181
|
+
confirmation: true,
|
|
13182
|
+
owner: "human",
|
|
13183
|
+
ability: "topic/change-status",
|
|
13184
|
+
inputSchema: {
|
|
13185
|
+
type: "object",
|
|
13186
|
+
required: ["from", "to", "transitionCode", "reason"],
|
|
13187
|
+
additionalProperties: false,
|
|
13188
|
+
properties: {
|
|
13189
|
+
from: { type: "string" },
|
|
13190
|
+
to: { type: "string" },
|
|
13191
|
+
transitionCode: { type: "string" },
|
|
13192
|
+
reason: { type: "string" },
|
|
13193
|
+
idempotencyKey: { type: "string" }
|
|
13194
|
+
}
|
|
13195
|
+
},
|
|
13196
|
+
buildPayload: async (inputs, ctx) => {
|
|
13197
|
+
const from = requiredString(inputs.from, "from");
|
|
13198
|
+
const to = requiredString(inputs.to, "to");
|
|
13199
|
+
if (to === "resolved") {
|
|
13200
|
+
const topic = topicContext(ctx);
|
|
13201
|
+
const projection = await topicService(ctx).readProjection?.({ topicId: topic.topicId, topicRevision: topic.topicRevision, requestId: topic.requestId });
|
|
13202
|
+
if (!projection) throw new Error("Resolution requires a current Topic projection so completion policy can be verified");
|
|
13203
|
+
const completion = projection.completion;
|
|
13204
|
+
const outcome = projection.outcome;
|
|
13205
|
+
if (completion?.requiresOutcomeRecord === true && !outcome?.outcomeRecordId) throw new Error("Topic policy requires an accepted outcome record before resolution");
|
|
13206
|
+
}
|
|
13207
|
+
return {
|
|
13208
|
+
from,
|
|
13209
|
+
to,
|
|
13210
|
+
transitionCode: requiredString(inputs.transitionCode, "transitionCode"),
|
|
13211
|
+
reason: requiredString(inputs.reason, "reason")
|
|
13212
|
+
};
|
|
13213
|
+
}
|
|
13214
|
+
});
|
|
13215
|
+
registerTopicOperation({
|
|
13216
|
+
type: "qi/topic.contract.confirm-setup",
|
|
13217
|
+
can: "topic/contract.confirm-setup",
|
|
13218
|
+
operationType: "confirm-setup",
|
|
13219
|
+
confirmation: true,
|
|
13220
|
+
owner: "human",
|
|
13221
|
+
ability: "topic/confirm-setup",
|
|
13222
|
+
inputSchema: {
|
|
13223
|
+
type: "object",
|
|
13224
|
+
required: ["contractRevision", "contractDigest", "confirmationReference"],
|
|
13225
|
+
additionalProperties: false,
|
|
13226
|
+
properties: {
|
|
13227
|
+
contractRevision: { type: "string" },
|
|
13228
|
+
contractDigest: { type: "string", pattern: "^sha256:" },
|
|
13229
|
+
confirmationReference: { type: "string" },
|
|
13230
|
+
transitionCode: { type: "string" },
|
|
13231
|
+
idempotencyKey: { type: "string" }
|
|
13232
|
+
}
|
|
13233
|
+
},
|
|
13234
|
+
buildPayload: (inputs, ctx) => {
|
|
13235
|
+
const revision = requiredString(inputs.contractRevision, "contractRevision");
|
|
13236
|
+
const digest2 = requiredString(inputs.contractDigest, "contractDigest");
|
|
13237
|
+
if (revision !== topicContext(ctx).contract.revision || digest2 !== topicContext(ctx).contract.digest)
|
|
13238
|
+
throw new Error("Setup confirmation must target the exact proposed contract revision and digest");
|
|
13239
|
+
return {
|
|
13240
|
+
contractRevision: revision,
|
|
13241
|
+
contractDigest: digest2,
|
|
13242
|
+
confirmationReference: requiredString(inputs.confirmationReference, "confirmationReference"),
|
|
13243
|
+
...inputs.transitionCode ? { transitionCode: String(inputs.transitionCode) } : {}
|
|
13244
|
+
};
|
|
13245
|
+
}
|
|
13246
|
+
});
|
|
13247
|
+
registerTopicOperation({
|
|
13248
|
+
type: "qi/topic.outcome.propose",
|
|
13249
|
+
can: "topic/outcome.propose",
|
|
13250
|
+
operationType: "update-contract",
|
|
13251
|
+
ability: "topic/update-contract",
|
|
13252
|
+
inputSchema: {
|
|
13253
|
+
type: "object",
|
|
13254
|
+
required: ["statement"],
|
|
13255
|
+
additionalProperties: false,
|
|
13256
|
+
properties: { statement: { type: "string" }, evidenceReferences: { type: "array", items: { type: "string" } }, idempotencyKey: { type: "string" } }
|
|
13257
|
+
},
|
|
13258
|
+
buildPayload: (inputs) => ({
|
|
13259
|
+
patch: {
|
|
13260
|
+
outcome: {
|
|
13261
|
+
statement: requiredString(inputs.statement, "statement"),
|
|
13262
|
+
status: "proposed",
|
|
13263
|
+
evidenceReferences: requiredArray(inputs.evidenceReferences || [], "evidenceReferences")
|
|
13264
|
+
}
|
|
13265
|
+
}
|
|
13266
|
+
})
|
|
13267
|
+
});
|
|
13268
|
+
registerTopicOperation({
|
|
13269
|
+
type: "qi/topic.decision.record",
|
|
13270
|
+
can: "topic/decision.record",
|
|
13271
|
+
operationType: "record-decision",
|
|
13272
|
+
ability: "topic/record-decision",
|
|
13273
|
+
inputSchema: {
|
|
13274
|
+
type: "object",
|
|
13275
|
+
required: ["decision", "authorityDid", "rationale"],
|
|
13276
|
+
additionalProperties: false,
|
|
13277
|
+
properties: {
|
|
13278
|
+
decision: { type: "string" },
|
|
13279
|
+
authorityDid: { type: "string", pattern: "^did:" },
|
|
13280
|
+
rationale: { type: "string" },
|
|
13281
|
+
alternatives: { type: "array", items: { type: "string" } },
|
|
13282
|
+
receiptReferences: { type: "array", items: { type: "string" } },
|
|
13283
|
+
evidenceReferences: { type: "array", items: { type: "string" } },
|
|
13284
|
+
idempotencyKey: { type: "string" }
|
|
13285
|
+
}
|
|
13286
|
+
},
|
|
13287
|
+
buildPayload: (inputs) => ({
|
|
13288
|
+
decision: requiredString(inputs.decision, "decision"),
|
|
13289
|
+
authorityDid: requiredString(inputs.authorityDid, "authorityDid"),
|
|
13290
|
+
rationale: requiredString(inputs.rationale, "rationale"),
|
|
13291
|
+
alternatives: requiredArray(inputs.alternatives || [], "alternatives"),
|
|
13292
|
+
receiptReferences: requiredArray(inputs.receiptReferences || [], "receiptReferences"),
|
|
13293
|
+
evidenceReferences: requiredArray(inputs.evidenceReferences || [], "evidenceReferences")
|
|
13294
|
+
})
|
|
13295
|
+
});
|
|
13296
|
+
registerTopicOperation({
|
|
13297
|
+
type: "qi/topic.context.link",
|
|
13298
|
+
can: "topic/context.link",
|
|
13299
|
+
operationType: "link-context",
|
|
13300
|
+
ability: "topic/link-context",
|
|
13301
|
+
inputSchema: {
|
|
13302
|
+
type: "object",
|
|
13303
|
+
required: ["contextType", "id"],
|
|
13304
|
+
additionalProperties: false,
|
|
13305
|
+
properties: {
|
|
13306
|
+
contextType: { type: "string", enum: ["ixo.resource", "ixo.flow", "matrix.conversation", "ixo.entity", "ixo.service"] },
|
|
13307
|
+
id: { type: "string" },
|
|
13308
|
+
label: { type: "string" },
|
|
13309
|
+
reference: { type: "string" },
|
|
13310
|
+
idempotencyKey: { type: "string" }
|
|
13311
|
+
}
|
|
13312
|
+
},
|
|
13313
|
+
buildPayload: (inputs) => ({
|
|
13314
|
+
type: requiredString(inputs.contextType, "contextType"),
|
|
13315
|
+
id: requiredString(inputs.id, "id"),
|
|
13316
|
+
label: String(inputs.label || ""),
|
|
13317
|
+
reference: String(inputs.reference || "")
|
|
13318
|
+
})
|
|
13319
|
+
});
|
|
13320
|
+
registerTopicOperation({
|
|
13321
|
+
type: "qi/topic.file.attach-reference",
|
|
13322
|
+
can: "topic/file.attach-reference",
|
|
13323
|
+
operationType: "attach-files",
|
|
13324
|
+
ability: "topic/attach-files",
|
|
13325
|
+
inputSchema: {
|
|
13326
|
+
type: "object",
|
|
13327
|
+
required: ["resource", "fileId", "version", "cid", "contentHash", "path", "name", "mimeType", "size"],
|
|
13328
|
+
additionalProperties: false,
|
|
13329
|
+
properties: {
|
|
13330
|
+
resource: { type: "string" },
|
|
13331
|
+
fileId: { type: "string" },
|
|
13332
|
+
version: { type: "number" },
|
|
13333
|
+
cid: { type: "string" },
|
|
13334
|
+
contentHash: { type: "string" },
|
|
13335
|
+
path: { type: "string" },
|
|
13336
|
+
name: { type: "string" },
|
|
13337
|
+
mimeType: { type: "string" },
|
|
13338
|
+
size: { type: "number" },
|
|
13339
|
+
idempotencyKey: { type: "string" }
|
|
13340
|
+
}
|
|
13341
|
+
},
|
|
13342
|
+
buildPayload: (inputs) => {
|
|
13343
|
+
if ("bytes" in inputs || "content" in inputs || "capability" in inputs)
|
|
13344
|
+
throw new Error("Topic file Actions accept pinned references only; bytes and access grants are forbidden");
|
|
13345
|
+
return {
|
|
13346
|
+
attachments: [
|
|
13347
|
+
{
|
|
13348
|
+
provider: "ixo.vfs",
|
|
13349
|
+
resource: inputs.resource,
|
|
13350
|
+
fileId: inputs.fileId,
|
|
13351
|
+
version: inputs.version,
|
|
13352
|
+
cid: inputs.cid,
|
|
13353
|
+
contentHash: inputs.contentHash,
|
|
13354
|
+
path: inputs.path,
|
|
13355
|
+
name: inputs.name,
|
|
13356
|
+
mimeType: inputs.mimeType,
|
|
13357
|
+
size: inputs.size
|
|
13358
|
+
}
|
|
13359
|
+
]
|
|
13360
|
+
};
|
|
13361
|
+
}
|
|
13362
|
+
});
|
|
13363
|
+
function registerSemanticAction(spec) {
|
|
13364
|
+
registerAction({
|
|
13365
|
+
type: spec.type,
|
|
13366
|
+
can: spec.can,
|
|
13367
|
+
sideEffect: true,
|
|
13368
|
+
proof: { fields: ["recordDigest"] },
|
|
13369
|
+
done: doneWhenCompleted,
|
|
13370
|
+
defaultRequiresConfirmation: spec.confirmation === true,
|
|
13371
|
+
requiredCapability: "flow/block/execute",
|
|
13372
|
+
executionOwner: spec.owner || "agent",
|
|
13373
|
+
riskTier: spec.riskTier,
|
|
13374
|
+
requiredServices: spec.requiredServices || ["topic"],
|
|
13375
|
+
sensitiveInputPaths: spec.sensitiveInputPaths,
|
|
13376
|
+
sensitiveOutputPaths: spec.sensitiveOutputPaths,
|
|
13377
|
+
topic: topicMetadata(spec.kinds, [spec.recordType], ["topic/request-action", "topic/record-action"], "recommended", spec.sensitiveInputPaths, spec.sensitiveOutputPaths),
|
|
13378
|
+
inputSchema: spec.inputSchema,
|
|
13379
|
+
outputSchema: [
|
|
13380
|
+
{ path: "recordId", displayName: "Semantic record ID", type: "string" },
|
|
13381
|
+
{ path: "recordType", displayName: "Semantic record type", type: "string" },
|
|
13382
|
+
{ path: "recordDigest", displayName: "Semantic record digest", type: "string" },
|
|
13383
|
+
{ path: "record", displayName: "Semantic record", type: "object" }
|
|
13384
|
+
],
|
|
13385
|
+
run: async (inputs, ctx) => {
|
|
13386
|
+
topicContext(ctx);
|
|
13387
|
+
const value = await spec.execute(inputs, ctx);
|
|
13388
|
+
const { record, digest: digest2 } = semanticRecord(spec.recordType, value, ctx);
|
|
13389
|
+
return { output: { recordId: record.id, recordType: record.type, recordDigest: digest2, record: value }, topicRecords: [record] };
|
|
13390
|
+
}
|
|
13391
|
+
});
|
|
13392
|
+
}
|
|
13393
|
+
var WORK_PHASES = {
|
|
13394
|
+
"qi/work.assign": "requested",
|
|
13395
|
+
"qi/work.dispatch": "dispatched",
|
|
13396
|
+
"qi/work.checkpoint": "in_progress",
|
|
13397
|
+
"qi/work.submit": "ready_for_review",
|
|
13398
|
+
"qi/work.accept": "completed"
|
|
13399
|
+
};
|
|
13400
|
+
for (const [type, phase] of Object.entries(WORK_PHASES)) {
|
|
13401
|
+
registerSemanticAction({
|
|
13402
|
+
type,
|
|
13403
|
+
can: type.replace("qi/", "").replace(".", "/"),
|
|
13404
|
+
kinds: ["task", "discussion", "incident"],
|
|
13405
|
+
recordType: "org.ixo.topic.work-event",
|
|
13406
|
+
confirmation: type === "qi/work.accept",
|
|
13407
|
+
owner: type === "qi/work.accept" ? "human" : "agent",
|
|
13408
|
+
inputSchema: {
|
|
13409
|
+
type: "object",
|
|
13410
|
+
required: ["workId", "resourceType"],
|
|
13411
|
+
additionalProperties: false,
|
|
13412
|
+
properties: {
|
|
13413
|
+
workId: { type: "string" },
|
|
13414
|
+
resourceType: { type: "string" },
|
|
13415
|
+
assigneeDid: { type: "string" },
|
|
13416
|
+
note: { type: "string" },
|
|
13417
|
+
artifactReferences: { type: "array", items: { type: "string" } },
|
|
13418
|
+
evidenceReferences: { type: "array", items: { type: "string" } }
|
|
13419
|
+
}
|
|
13420
|
+
},
|
|
13421
|
+
execute: (inputs, ctx) => ({
|
|
13422
|
+
workId: requiredString(inputs.workId, "workId"),
|
|
13423
|
+
resourceType: requiredString(inputs.resourceType, "resourceType"),
|
|
13424
|
+
phase,
|
|
13425
|
+
assigneeDid: String(inputs.assigneeDid || ""),
|
|
13426
|
+
note: String(inputs.note || ""),
|
|
13427
|
+
artifactReferences: requiredArray(inputs.artifactReferences || [], "artifactReferences"),
|
|
13428
|
+
evidenceReferences: requiredArray(inputs.evidenceReferences || [], "evidenceReferences"),
|
|
13429
|
+
actorDid: ctx.actorDid,
|
|
13430
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13431
|
+
})
|
|
13432
|
+
});
|
|
13433
|
+
}
|
|
13434
|
+
registerSemanticAction({
|
|
13435
|
+
type: "qi/agent.invoke",
|
|
13436
|
+
can: "agent/invoke",
|
|
13437
|
+
kinds: ["agent_task", "question", "task"],
|
|
13438
|
+
recordType: "org.ixo.topic.agent-result",
|
|
13439
|
+
requiredServices: ["agents"],
|
|
13440
|
+
sensitiveInputPaths: ["prompt", "toolPolicy"],
|
|
13441
|
+
sensitiveOutputPaths: ["record.result"],
|
|
13442
|
+
inputSchema: {
|
|
13443
|
+
type: "object",
|
|
13444
|
+
required: ["agentDid", "capsuleReference", "toolPolicy"],
|
|
13445
|
+
additionalProperties: false,
|
|
13446
|
+
properties: {
|
|
13447
|
+
agentDid: { type: "string", pattern: "^did:" },
|
|
13448
|
+
capsuleReference: { type: "string" },
|
|
13449
|
+
toolPolicy: { type: "object" },
|
|
13450
|
+
budget: { type: "object" },
|
|
13451
|
+
deadline: { type: "string" },
|
|
13452
|
+
executorPreference: { type: "string", enum: ["qi-flow", "qiforge", "mcp"] },
|
|
13453
|
+
prompt: { type: "string" }
|
|
13454
|
+
}
|
|
13455
|
+
},
|
|
13456
|
+
execute: async (inputs, ctx) => {
|
|
13457
|
+
if (!ctx.services.agents) throw new Error("Agent runtime service is not configured");
|
|
13458
|
+
const result = await ctx.services.agents.invoke({
|
|
13459
|
+
agentDid: requiredString(inputs.agentDid, "agentDid"),
|
|
13460
|
+
capsuleReference: requiredString(inputs.capsuleReference, "capsuleReference"),
|
|
13461
|
+
toolPolicy: inputs.toolPolicy || {},
|
|
13462
|
+
budget: inputs.budget,
|
|
13463
|
+
deadline: inputs.deadline,
|
|
13464
|
+
executorPreference: inputs.executorPreference,
|
|
13465
|
+
prompt: inputs.prompt
|
|
13466
|
+
});
|
|
13467
|
+
return {
|
|
13468
|
+
sessionId: result.sessionId,
|
|
13469
|
+
result: result.result,
|
|
13470
|
+
resultDigest: sha256Digest(result.result),
|
|
13471
|
+
evidenceReferences: result.evidenceReferences || [],
|
|
13472
|
+
evidenceDigest: sha256Digest(result.evidenceReferences || []),
|
|
13473
|
+
providerReceiptReference: result.providerReceiptReference
|
|
13474
|
+
};
|
|
13475
|
+
}
|
|
13476
|
+
});
|
|
13477
|
+
registerSemanticAction({
|
|
13478
|
+
type: "qi/agent.cancel",
|
|
13479
|
+
can: "agent/cancel",
|
|
13480
|
+
kinds: ["agent_task", "question", "task"],
|
|
13481
|
+
recordType: "org.ixo.topic.agent-cancellation",
|
|
13482
|
+
requiredServices: ["agents"],
|
|
13483
|
+
inputSchema: { type: "object", required: ["sessionId"], additionalProperties: false, properties: { sessionId: { type: "string" }, reason: { type: "string" } } },
|
|
13484
|
+
execute: async (inputs, ctx) => {
|
|
13485
|
+
if (!ctx.services.agents) throw new Error("Agent runtime service is not configured");
|
|
13486
|
+
return ctx.services.agents.cancel({ sessionId: requiredString(inputs.sessionId, "sessionId"), reason: String(inputs.reason || "") });
|
|
13487
|
+
}
|
|
13488
|
+
});
|
|
13489
|
+
registerSemanticAction({
|
|
13490
|
+
type: "qi/evidence.collect",
|
|
13491
|
+
can: "evidence/collect",
|
|
13492
|
+
kinds: ["question", "evaluation"],
|
|
13493
|
+
recordType: "org.ixo.topic.evidence",
|
|
13494
|
+
requiredServices: ["evidence"],
|
|
13495
|
+
sensitiveOutputPaths: ["record.evidence"],
|
|
13496
|
+
inputSchema: {
|
|
13497
|
+
type: "object",
|
|
13498
|
+
required: ["question", "sourceReferences"],
|
|
13499
|
+
additionalProperties: false,
|
|
13500
|
+
properties: { question: { type: "string" }, sourceReferences: { type: "array", items: { type: "string" } }, constraints: { type: "object" } }
|
|
13501
|
+
},
|
|
13502
|
+
execute: async (inputs, ctx) => {
|
|
13503
|
+
if (!ctx.services.evidence) throw new Error("Evidence service is not configured");
|
|
13504
|
+
const result = await ctx.services.evidence.collect({
|
|
13505
|
+
question: requiredString(inputs.question, "question"),
|
|
13506
|
+
sourceReferences: requiredArray(inputs.sourceReferences, "sourceReferences"),
|
|
13507
|
+
constraints: inputs.constraints
|
|
13508
|
+
});
|
|
11174
13509
|
return {
|
|
11175
|
-
|
|
11176
|
-
|
|
11177
|
-
|
|
11178
|
-
|
|
11179
|
-
|
|
11180
|
-
events: [{ name: OUTLOOK_SENT_EVENT, payload: { messageId, to_email: values.to_email ?? "" } }]
|
|
13510
|
+
question: inputs.question,
|
|
13511
|
+
evidence: result.evidence,
|
|
13512
|
+
provenance: result.provenance,
|
|
13513
|
+
evidenceReferences: result.evidenceReferences,
|
|
13514
|
+
evidenceDigest: sha256Digest(result.evidence)
|
|
11181
13515
|
};
|
|
11182
13516
|
}
|
|
11183
13517
|
});
|
|
11184
|
-
|
|
11185
|
-
|
|
11186
|
-
|
|
11187
|
-
|
|
11188
|
-
|
|
11189
|
-
|
|
11190
|
-
|
|
11191
|
-
|
|
11192
|
-
|
|
13518
|
+
for (const accepted of [false, true]) {
|
|
13519
|
+
registerSemanticAction({
|
|
13520
|
+
type: accepted ? "qi/answer.accept" : "qi/answer.propose",
|
|
13521
|
+
can: accepted ? "answer/accept" : "answer/propose",
|
|
13522
|
+
kinds: ["question"],
|
|
13523
|
+
recordType: accepted ? "org.ixo.topic.accepted-answer" : "org.ixo.topic.proposed-answer",
|
|
13524
|
+
confirmation: accepted,
|
|
13525
|
+
owner: accepted ? "human" : "agent",
|
|
13526
|
+
inputSchema: {
|
|
13527
|
+
type: "object",
|
|
13528
|
+
required: accepted ? ["answer", "acceptanceAuthorityDid", "proposedAnswerRecordId"] : ["answer"],
|
|
13529
|
+
additionalProperties: false,
|
|
13530
|
+
properties: {
|
|
13531
|
+
answer: { type: "string" },
|
|
13532
|
+
proposedAnswerRecordId: { type: "string" },
|
|
13533
|
+
acceptanceAuthorityDid: { type: "string", pattern: "^did:" },
|
|
13534
|
+
evidenceReferences: { type: "array", items: { type: "string" } },
|
|
13535
|
+
limitations: { type: "string" }
|
|
13536
|
+
}
|
|
13537
|
+
},
|
|
13538
|
+
execute: (inputs, ctx) => ({
|
|
13539
|
+
answer: requiredString(inputs.answer, "answer"),
|
|
13540
|
+
status: accepted ? "accepted" : "proposed",
|
|
13541
|
+
proposedAnswerRecordId: String(inputs.proposedAnswerRecordId || ""),
|
|
13542
|
+
authorityDid: accepted ? requiredString(inputs.acceptanceAuthorityDid, "acceptanceAuthorityDid") : ctx.actorDid,
|
|
13543
|
+
evidenceReferences: requiredArray(inputs.evidenceReferences || [], "evidenceReferences"),
|
|
13544
|
+
limitations: String(inputs.limitations || ""),
|
|
13545
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13546
|
+
})
|
|
13547
|
+
});
|
|
13548
|
+
}
|
|
13549
|
+
for (const review of [false, true]) {
|
|
13550
|
+
registerSemanticAction({
|
|
13551
|
+
type: review ? "qi/evaluation.review" : "qi/evaluation.run",
|
|
13552
|
+
can: review ? "evaluation/review" : "evaluation/run",
|
|
13553
|
+
kinds: ["evaluation", "claims"],
|
|
13554
|
+
recordType: review ? "org.ixo.topic.evaluation-review" : "org.ixo.topic.evaluation-assertion",
|
|
13555
|
+
requiredServices: ["evaluations"],
|
|
13556
|
+
confirmation: review,
|
|
13557
|
+
owner: review ? "human" : "agent",
|
|
13558
|
+
inputSchema: {
|
|
13559
|
+
type: "object",
|
|
13560
|
+
required: review ? ["assertionReference", "methodologyRevision", "rubricRevision"] : ["subjectReference", "methodologyRevision", "rubricRevision"],
|
|
13561
|
+
additionalProperties: true,
|
|
13562
|
+
properties: {
|
|
13563
|
+
subjectReference: { type: "string" },
|
|
13564
|
+
assertionReference: { type: "string" },
|
|
13565
|
+
methodologyRevision: { type: "string" },
|
|
13566
|
+
rubricRevision: { type: "string" },
|
|
13567
|
+
evidenceReferences: { type: "array", items: { type: "string" } }
|
|
13568
|
+
}
|
|
13569
|
+
},
|
|
13570
|
+
execute: async (inputs, ctx) => {
|
|
13571
|
+
if (!ctx.services.evaluations) throw new Error("Evaluation runtime service is not configured");
|
|
13572
|
+
const result = review ? await ctx.services.evaluations.review(inputs) : await ctx.services.evaluations.run(inputs);
|
|
13573
|
+
const value = "assertion" in result ? result.assertion : result.review;
|
|
13574
|
+
return {
|
|
13575
|
+
providerResult: value,
|
|
13576
|
+
assertionId: "assertionId" in result ? result.assertionId : void 0,
|
|
13577
|
+
reviewId: "reviewId" in result ? result.reviewId : void 0,
|
|
13578
|
+
methodologyRevision: inputs.methodologyRevision,
|
|
13579
|
+
rubricRevision: inputs.rubricRevision,
|
|
13580
|
+
evaluatorDid: ctx.actorDid,
|
|
13581
|
+
evidenceReferences: result.evidenceReferences,
|
|
13582
|
+
signature: result.signature
|
|
13583
|
+
};
|
|
13584
|
+
}
|
|
13585
|
+
});
|
|
13586
|
+
}
|
|
13587
|
+
registerSemanticAction({
|
|
13588
|
+
type: "qi/settlement.execute",
|
|
13589
|
+
can: "settlement/execute",
|
|
13590
|
+
kinds: ["claims"],
|
|
13591
|
+
recordType: "org.ixo.topic.settlement-record",
|
|
13592
|
+
confirmation: true,
|
|
13593
|
+
owner: "human",
|
|
13594
|
+
riskTier: "critical",
|
|
13595
|
+
requiredServices: ["settlement"],
|
|
13596
|
+
inputSchema: {
|
|
11193
13597
|
type: "object",
|
|
11194
|
-
required: ["
|
|
13598
|
+
required: ["approvedClaimReference", "amount", "asset", "recipient", "policyReference", "confirmationReference"],
|
|
13599
|
+
additionalProperties: false,
|
|
11195
13600
|
properties: {
|
|
11196
|
-
|
|
11197
|
-
|
|
11198
|
-
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
}
|
|
11202
|
-
thread_ts: { type: "string", title: "Thread", description: "Optional parent message timestamp to reply within a thread." }
|
|
13601
|
+
approvedClaimReference: { type: "string" },
|
|
13602
|
+
amount: { type: "string" },
|
|
13603
|
+
asset: { type: "string" },
|
|
13604
|
+
recipient: { type: "string" },
|
|
13605
|
+
policyReference: { type: "string" },
|
|
13606
|
+
confirmationReference: { type: "string" }
|
|
11203
13607
|
}
|
|
13608
|
+
},
|
|
13609
|
+
execute: async (inputs, ctx) => {
|
|
13610
|
+
if (!ctx.services.settlement) throw new Error("Settlement service is not configured");
|
|
13611
|
+
return ctx.services.settlement.execute({
|
|
13612
|
+
approvedClaimReference: requiredString(inputs.approvedClaimReference, "approvedClaimReference"),
|
|
13613
|
+
amount: requiredString(inputs.amount, "amount"),
|
|
13614
|
+
asset: requiredString(inputs.asset, "asset"),
|
|
13615
|
+
recipient: requiredString(inputs.recipient, "recipient"),
|
|
13616
|
+
policyReference: requiredString(inputs.policyReference, "policyReference"),
|
|
13617
|
+
confirmationReference: requiredString(inputs.confirmationReference, "confirmationReference")
|
|
13618
|
+
});
|
|
11204
13619
|
}
|
|
11205
|
-
};
|
|
11206
|
-
|
|
11207
|
-
|
|
11208
|
-
|
|
11209
|
-
|
|
11210
|
-
|
|
11211
|
-
|
|
11212
|
-
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
|
|
11221
|
-
|
|
11222
|
-
|
|
11223
|
-
eligibleForEventTrigger: true,
|
|
11224
|
-
// Mirrors executeDelegatedTool's gates: the bound connection plus the
|
|
11225
|
-
// tool schema's required fields, so orchestrators ask before run() throws.
|
|
11226
|
-
getMissingInputs: (inputs) => delegatedToolMissingInputs(SLACK_SEND_SCHEMA, inputs),
|
|
11227
|
-
outputSchema: SLACK_SEND_OUTPUT_SCHEMA,
|
|
11228
|
-
events: [
|
|
11229
|
-
{
|
|
11230
|
-
name: SLACK_SENT_EVENT,
|
|
11231
|
-
displayName: "Message posted",
|
|
11232
|
-
description: "Fired after the message is posted to Slack.",
|
|
11233
|
-
payloadSchema: [
|
|
11234
|
-
{ path: "messageTs", displayName: "Message ts", type: "string" },
|
|
11235
|
-
{ path: "channel", displayName: "Channel", type: "string" }
|
|
11236
|
-
],
|
|
11237
|
-
pendingDisplayFields: ["messageTs"]
|
|
13620
|
+
});
|
|
13621
|
+
registerSemanticAction({
|
|
13622
|
+
type: "qi/incident.escalate",
|
|
13623
|
+
can: "incident/escalate",
|
|
13624
|
+
kinds: ["incident"],
|
|
13625
|
+
recordType: "org.ixo.topic.incident-escalation",
|
|
13626
|
+
confirmation: true,
|
|
13627
|
+
requiredServices: ["incidents"],
|
|
13628
|
+
inputSchema: {
|
|
13629
|
+
type: "object",
|
|
13630
|
+
required: ["severity", "affectedResources", "recipients", "summary"],
|
|
13631
|
+
additionalProperties: false,
|
|
13632
|
+
properties: {
|
|
13633
|
+
severity: { type: "string", enum: ["low", "medium", "high", "critical"] },
|
|
13634
|
+
affectedResources: { type: "array", items: { type: "string" } },
|
|
13635
|
+
recipients: { type: "array", items: { type: "string" } },
|
|
13636
|
+
evidenceReferences: { type: "array", items: { type: "string" } },
|
|
13637
|
+
summary: { type: "string" }
|
|
11238
13638
|
}
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
const
|
|
11243
|
-
|
|
11244
|
-
|
|
11245
|
-
|
|
11246
|
-
|
|
11247
|
-
|
|
11248
|
-
toolkitLabel: "Slack"
|
|
11249
|
-
});
|
|
11250
|
-
const envelope = data.response_data ?? data;
|
|
11251
|
-
const messageTs = String(envelope.ts ?? "");
|
|
11252
|
-
const channel = String(envelope.channel ?? values.channel ?? "");
|
|
11253
|
-
return {
|
|
11254
|
-
output: {
|
|
11255
|
-
messageTs,
|
|
11256
|
-
channel,
|
|
11257
|
-
sentAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11258
|
-
},
|
|
11259
|
-
events: messageTs ? [{ name: SLACK_SENT_EVENT, payload: { messageTs, channel } }] : void 0
|
|
13639
|
+
},
|
|
13640
|
+
execute: async (inputs, ctx) => {
|
|
13641
|
+
if (!ctx.services.incidents) throw new Error("Incident service is not configured");
|
|
13642
|
+
const payload = {
|
|
13643
|
+
severity: requiredString(inputs.severity, "severity"),
|
|
13644
|
+
affectedResources: requiredArray(inputs.affectedResources, "affectedResources"),
|
|
13645
|
+
recipients: requiredArray(inputs.recipients, "recipients"),
|
|
13646
|
+
evidenceReferences: requiredArray(inputs.evidenceReferences || [], "evidenceReferences"),
|
|
13647
|
+
summary: requiredString(inputs.summary, "summary")
|
|
11260
13648
|
};
|
|
13649
|
+
return { ...payload, ...await ctx.services.incidents.escalate(payload) };
|
|
11261
13650
|
}
|
|
11262
13651
|
});
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
|
|
11266
|
-
|
|
11267
|
-
|
|
11268
|
-
|
|
11269
|
-
name: "Create Event",
|
|
11270
|
-
description: "Create an event on the template author's Google Calendar.",
|
|
11271
|
-
parameters: {
|
|
13652
|
+
registerSemanticAction({
|
|
13653
|
+
type: "qi/incident.mitigation.record",
|
|
13654
|
+
can: "incident/mitigation.record",
|
|
13655
|
+
kinds: ["incident"],
|
|
13656
|
+
recordType: "org.ixo.topic.incident-mitigation",
|
|
13657
|
+
inputSchema: {
|
|
11272
13658
|
type: "object",
|
|
11273
|
-
required: ["
|
|
13659
|
+
required: ["mitigation", "affectedResources"],
|
|
13660
|
+
additionalProperties: false,
|
|
11274
13661
|
properties: {
|
|
11275
|
-
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11279
|
-
timezone: { type: "string", title: "Timezone", description: "IANA name, e.g. Europe/London." },
|
|
11280
|
-
attendees: { type: "array", title: "Attendees", description: "Comma-separated email addresses." },
|
|
11281
|
-
calendar_id: { type: "string", title: "Calendar", description: "Use 'primary' for the author's main calendar." },
|
|
11282
|
-
event_duration_minutes: { type: "number", title: "Duration (minutes)", description: "Defaults to the calendar default if blank." }
|
|
13662
|
+
mitigation: { type: "string" },
|
|
13663
|
+
affectedResources: { type: "array", items: { type: "string" } },
|
|
13664
|
+
evidenceReferences: { type: "array", items: { type: "string" } },
|
|
13665
|
+
occurredAt: { type: "string" }
|
|
11283
13666
|
}
|
|
11284
|
-
}
|
|
11285
|
-
|
|
11286
|
-
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
// src/core/lib/actionRegistry/actions/googlecalendar/eventCreate.ts
|
|
13667
|
+
},
|
|
13668
|
+
execute: (inputs, ctx) => ({
|
|
13669
|
+
mitigation: requiredString(inputs.mitigation, "mitigation"),
|
|
13670
|
+
affectedResources: requiredArray(inputs.affectedResources, "affectedResources"),
|
|
13671
|
+
evidenceReferences: requiredArray(inputs.evidenceReferences || [], "evidenceReferences"),
|
|
13672
|
+
recordedBy: ctx.actorDid,
|
|
13673
|
+
occurredAt: String(inputs.occurredAt || (/* @__PURE__ */ new Date()).toISOString())
|
|
13674
|
+
})
|
|
13675
|
+
});
|
|
11294
13676
|
registerAction({
|
|
11295
|
-
type: "qi/
|
|
11296
|
-
can: "
|
|
13677
|
+
type: "qi/topic.remind",
|
|
13678
|
+
can: "topic/reminder.deliver",
|
|
13679
|
+
displayName: "Send a Topic reminder",
|
|
13680
|
+
description: "Posts a reminder into the Topic thread and notifies the people responsible for it.",
|
|
11297
13681
|
sideEffect: true,
|
|
11298
|
-
|
|
11299
|
-
proof: { fields: ["eventId"] },
|
|
13682
|
+
proof: { fields: ["deliveredAt"] },
|
|
11300
13683
|
done: doneWhenCompleted,
|
|
11301
|
-
defaultRequiresConfirmation:
|
|
11302
|
-
|
|
11303
|
-
|
|
11304
|
-
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
|
|
11316
|
-
|
|
11317
|
-
|
|
11318
|
-
|
|
13684
|
+
defaultRequiresConfirmation: false,
|
|
13685
|
+
executionOwner: "agent",
|
|
13686
|
+
riskTier: "low",
|
|
13687
|
+
requiredServices: ["topic"],
|
|
13688
|
+
eligibleForTimeTrigger: true,
|
|
13689
|
+
scheduling: {
|
|
13690
|
+
riskTier: "R0",
|
|
13691
|
+
misfirePolicies: ["run_once_immediately", "skip"],
|
|
13692
|
+
// Reminders never queue: two firings of the same reminder are the same
|
|
13693
|
+
// reminder, and delivering it twice is the failure mode, not the goal.
|
|
13694
|
+
overlapPolicies: ["skip"],
|
|
13695
|
+
approvalRequirement: "none",
|
|
13696
|
+
idempotencyStrategy: "idempotency_key",
|
|
13697
|
+
inputModes: ["resolve_at_fire"],
|
|
13698
|
+
runModes: ["new_run_per_occurrence"]
|
|
13699
|
+
},
|
|
13700
|
+
topic: topicMetadata(ALL_KINDS2, [], ["topic/manage-reminder"], "contextual"),
|
|
13701
|
+
inputSchema: {
|
|
13702
|
+
type: "object",
|
|
13703
|
+
required: ["recipients"],
|
|
13704
|
+
additionalProperties: false,
|
|
13705
|
+
properties: {
|
|
13706
|
+
recipients: { type: "array", items: { type: "string" } },
|
|
13707
|
+
message: { type: "string" },
|
|
13708
|
+
idempotencyKey: { type: "string" }
|
|
11319
13709
|
}
|
|
13710
|
+
},
|
|
13711
|
+
outputSchema: [
|
|
13712
|
+
{ path: "deliveredAt", displayName: "Delivered at", type: "string" },
|
|
13713
|
+
{ path: "recipients", displayName: "Reminded", type: "string" }
|
|
11320
13714
|
],
|
|
11321
13715
|
run: async (inputs, ctx) => {
|
|
11322
|
-
const
|
|
11323
|
-
|
|
11324
|
-
const
|
|
11325
|
-
|
|
11326
|
-
schema: GOOGLECALENDAR_CREATE_SCHEMA,
|
|
11327
|
-
toolSlug: GOOGLECALENDAR_CREATE_SLUG,
|
|
11328
|
-
values,
|
|
11329
|
-
toolkitLabel: "Google Calendar"
|
|
11330
|
-
});
|
|
11331
|
-
const envelope = data.response_data ?? data;
|
|
11332
|
-
const eventId = String(envelope.id ?? "");
|
|
11333
|
-
const htmlLink = String(envelope.htmlLink ?? "");
|
|
11334
|
-
const summary = String(envelope.summary ?? values.summary ?? "");
|
|
11335
|
-
const start = envelope.start;
|
|
11336
|
-
const startIso = String(start?.dateTime ?? start?.date ?? values.start_datetime ?? "");
|
|
13716
|
+
const service = topicService(ctx);
|
|
13717
|
+
if (!service.deliverReminder) throw new Error("The host did not grant a Topic reminder delivery service");
|
|
13718
|
+
const recipients = requiredArray(inputs.recipients, "recipients");
|
|
13719
|
+
if (recipients.length === 0) throw new Error("A reminder needs at least one recipient");
|
|
11337
13720
|
return {
|
|
11338
|
-
output: {
|
|
11339
|
-
|
|
13721
|
+
output: await service.deliverReminder({
|
|
13722
|
+
context: topicContext(ctx),
|
|
13723
|
+
recipients,
|
|
13724
|
+
message: String(inputs.message || ""),
|
|
13725
|
+
idempotencyKey: idempotencyKey("qi/topic.remind", inputs, ctx)
|
|
13726
|
+
})
|
|
11340
13727
|
};
|
|
11341
13728
|
}
|
|
11342
13729
|
});
|
|
@@ -11394,7 +13781,13 @@ function parseAttendeesField(raw) {
|
|
|
11394
13781
|
// src/core/lib/actionRegistry/actions/calendar/eventCreate.ts
|
|
11395
13782
|
var CALENDAR_EVENT_CREATE_SLUG = "GOOGLECALENDAR_CREATE_EVENT";
|
|
11396
13783
|
registerAction({
|
|
11397
|
-
|
|
13784
|
+
// Provider-explicit identity (IXO-4420 §5): this block is Google Calendar
|
|
13785
|
+
// via Composio with a per-runner pinned connection — `-self` distinguishes
|
|
13786
|
+
// it from the delegated `qi/googlecalendar.event.create`. The retired
|
|
13787
|
+
// `qi/calendar.event.create` name resolves here via ACTION_TYPE_ALIASES.
|
|
13788
|
+
// The `can` deliberately keeps its historical value so existing UCAN grants
|
|
13789
|
+
// keep matching; `qi/ixo.calendar.event.*` (M4) gets its own can namespace.
|
|
13790
|
+
type: "qi/googlecalendar.event.create-self",
|
|
11398
13791
|
can: "calendar.event/create",
|
|
11399
13792
|
sideEffect: true,
|
|
11400
13793
|
proof: { fields: ["eventId"] },
|
|
@@ -11503,7 +13896,9 @@ registerAction({
|
|
|
11503
13896
|
var CALENDAR_EVENT_UPDATE_SLUG = "GOOGLECALENDAR_UPDATE_EVENT";
|
|
11504
13897
|
var CALENDAR_EVENT_GET_SLUG = "GOOGLECALENDAR_EVENTS_GET";
|
|
11505
13898
|
registerAction({
|
|
11506
|
-
|
|
13899
|
+
// Provider-explicit identity (IXO-4420 §5); `qi/calendar.event.update` is
|
|
13900
|
+
// a permanent alias. `can` keeps its historical value — see eventCreate.ts.
|
|
13901
|
+
type: "qi/googlecalendar.event.update-self",
|
|
11507
13902
|
can: "calendar.event/update",
|
|
11508
13903
|
sideEffect: true,
|
|
11509
13904
|
proof: { fields: ["eventId"] },
|
|
@@ -11614,7 +14009,9 @@ registerAction({
|
|
|
11614
14009
|
// src/core/lib/actionRegistry/actions/calendar/eventList.ts
|
|
11615
14010
|
var CALENDAR_EVENT_LIST_SLUG = "GOOGLECALENDAR_EVENTS_LIST";
|
|
11616
14011
|
registerAction({
|
|
11617
|
-
|
|
14012
|
+
// Provider-explicit identity (IXO-4420 §5); `qi/calendar.event.list` is
|
|
14013
|
+
// a permanent alias. `can` keeps its historical value — see eventCreate.ts.
|
|
14014
|
+
type: "qi/googlecalendar.event.list-self",
|
|
11618
14015
|
can: "calendar.event/list",
|
|
11619
14016
|
sideEffect: false,
|
|
11620
14017
|
proof: "none",
|
|
@@ -12424,7 +14821,7 @@ registerDiffResolver("evaluateClaim", {
|
|
|
12424
14821
|
});
|
|
12425
14822
|
|
|
12426
14823
|
// src/core/lib/actionRegistry/actions/calendar/eventCreate.diff.ts
|
|
12427
|
-
registerDiffResolver("qi/
|
|
14824
|
+
registerDiffResolver("qi/googlecalendar.event.create-self", {
|
|
12428
14825
|
resolver: async (inputs, _ctx) => {
|
|
12429
14826
|
const attendees = parseAttendeesField(String(inputs.attendees || ""));
|
|
12430
14827
|
const calendarId = String(inputs.calendar_id || "").trim() || "primary";
|
|
@@ -12505,7 +14902,7 @@ registerDiffResolver("qi/calendar.event.create", {
|
|
|
12505
14902
|
});
|
|
12506
14903
|
|
|
12507
14904
|
// src/core/lib/actionRegistry/actions/calendar/eventUpdate.diff.ts
|
|
12508
|
-
registerDiffResolver("qi/
|
|
14905
|
+
registerDiffResolver("qi/googlecalendar.event.update-self", {
|
|
12509
14906
|
resolver: async (inputs, ctx) => {
|
|
12510
14907
|
const connection = inputs.connection || {};
|
|
12511
14908
|
const connectedAccountId = connection.connectedAccountId;
|
|
@@ -12761,14 +15158,79 @@ registerDiffResolver("qi/xero.payment.create", {
|
|
|
12761
15158
|
}
|
|
12762
15159
|
});
|
|
12763
15160
|
|
|
15161
|
+
// src/core/utils/tokenAmount.ts
|
|
15162
|
+
var DECIMAL_RE = /^-?\d*(\.\d*)?$/;
|
|
15163
|
+
function toBaseUnits(displayAmount, exponent) {
|
|
15164
|
+
const raw = String(displayAmount ?? "").trim();
|
|
15165
|
+
if (!raw) return { ok: false, error: "Enter an amount" };
|
|
15166
|
+
if (!DECIMAL_RE.test(raw)) return { ok: false, error: `\u201C${raw}\u201D is not a valid amount` };
|
|
15167
|
+
if (raw.startsWith("-")) return { ok: false, error: "Amount must be greater than 0" };
|
|
15168
|
+
if (!Number.isInteger(exponent) || exponent < 0) return { ok: false, error: `Unknown decimals for this token` };
|
|
15169
|
+
const [whole = "", fraction = ""] = raw.split(".");
|
|
15170
|
+
if (fraction.length > exponent) {
|
|
15171
|
+
return {
|
|
15172
|
+
ok: false,
|
|
15173
|
+
error: exponent === 0 ? "This token has no decimal places \u2014 enter a whole number" : `This token has at most ${exponent} decimal places`
|
|
15174
|
+
};
|
|
15175
|
+
}
|
|
15176
|
+
const shifted = `${whole}${fraction.padEnd(exponent, "0")}`.replace(/^0+(?=\d)/, "");
|
|
15177
|
+
const value = shifted === "" ? "0" : shifted;
|
|
15178
|
+
if (value === "0") return { ok: false, error: "Amount must be greater than 0" };
|
|
15179
|
+
return { ok: true, value };
|
|
15180
|
+
}
|
|
15181
|
+
function toDisplayUnits(baseAmount, exponent) {
|
|
15182
|
+
const raw = String(baseAmount ?? "").trim();
|
|
15183
|
+
if (!raw || !DECIMAL_RE.test(raw)) return "0";
|
|
15184
|
+
if (!Number.isInteger(exponent) || exponent <= 0) return raw.replace(/\..*$/, "");
|
|
15185
|
+
const negative = raw.startsWith("-");
|
|
15186
|
+
const digits = (negative ? raw.slice(1) : raw).replace(/\..*$/, "").padStart(exponent + 1, "0");
|
|
15187
|
+
const whole = digits.slice(0, digits.length - exponent).replace(/^0+(?=\d)/, "");
|
|
15188
|
+
const fraction = digits.slice(digits.length - exponent).replace(/0+$/, "");
|
|
15189
|
+
return `${negative ? "-" : ""}${whole}${fraction ? `.${fraction}` : ""}`;
|
|
15190
|
+
}
|
|
15191
|
+
function formatTokenAmount(baseAmount, denom, exponent, symbol) {
|
|
15192
|
+
if (exponent === void 0 || exponent === null) return `${baseAmount} ${denom}`;
|
|
15193
|
+
return `${toDisplayUnits(baseAmount, exponent)} ${symbol || denom}`;
|
|
15194
|
+
}
|
|
15195
|
+
|
|
12764
15196
|
// src/core/lib/actionRegistry/actions/walletFund.diff.ts
|
|
15197
|
+
var FALLBACK_DENOM = "uixo";
|
|
15198
|
+
async function describeToken(ctx, walletAddress, denom) {
|
|
15199
|
+
if (!walletAddress || !ctx.handlers?.getBalances) return {};
|
|
15200
|
+
try {
|
|
15201
|
+
const res = await ctx.handlers.getBalances(walletAddress);
|
|
15202
|
+
const match = (res?.data || []).find((b) => b.denom === denom);
|
|
15203
|
+
return { symbol: match?.tokenName, exponent: match?.exponent };
|
|
15204
|
+
} catch {
|
|
15205
|
+
return {};
|
|
15206
|
+
}
|
|
15207
|
+
}
|
|
12765
15208
|
registerDiffResolver("qi/wallet.fund", {
|
|
12766
|
-
resolver: async (inputs,
|
|
15209
|
+
resolver: async (inputs, ctx) => {
|
|
12767
15210
|
const address = String(inputs.address || "").trim();
|
|
12768
15211
|
const amount = String(inputs.amount || "250000").trim();
|
|
12769
|
-
const
|
|
12770
|
-
const
|
|
15212
|
+
const denom = String(inputs.denom || "").trim() || FALLBACK_DENOM;
|
|
15213
|
+
const fromAddress = String(inputs.fromAddress || "").trim();
|
|
15214
|
+
const signerAddress = (() => {
|
|
15215
|
+
try {
|
|
15216
|
+
return ctx.handlers?.getCurrentUser?.()?.address || "";
|
|
15217
|
+
} catch {
|
|
15218
|
+
return "";
|
|
15219
|
+
}
|
|
15220
|
+
})();
|
|
15221
|
+
const source = fromAddress || signerAddress;
|
|
15222
|
+
const { symbol, exponent } = await describeToken(ctx, source, denom);
|
|
12771
15223
|
return [
|
|
15224
|
+
{
|
|
15225
|
+
key: "from",
|
|
15226
|
+
label: "From",
|
|
15227
|
+
before: "N/A",
|
|
15228
|
+
// Naming the mechanism matters: spending another wallet's tokens is an
|
|
15229
|
+
// authz exec, and the signer should see that before they slide.
|
|
15230
|
+
after: fromAddress ? `${fromAddress} (authorized send)` : source ? `${source} (your wallet)` : "Your wallet",
|
|
15231
|
+
changeType: "replace",
|
|
15232
|
+
severity: fromAddress ? "warning" : "info"
|
|
15233
|
+
},
|
|
12772
15234
|
{
|
|
12773
15235
|
key: "recipient",
|
|
12774
15236
|
label: "Recipient",
|
|
@@ -12779,17 +15241,10 @@ registerDiffResolver("qi/wallet.fund", {
|
|
|
12779
15241
|
{
|
|
12780
15242
|
key: "amount",
|
|
12781
15243
|
label: "Amount",
|
|
12782
|
-
before: "0
|
|
12783
|
-
after: `${
|
|
15244
|
+
before: "0",
|
|
15245
|
+
after: exponent === void 0 ? `${amount} ${denom}` : `${formatTokenAmount(amount, denom, exponent, symbol)} (${amount} ${denom})`,
|
|
12784
15246
|
changeType: "replace",
|
|
12785
15247
|
severity: "info"
|
|
12786
|
-
},
|
|
12787
|
-
{
|
|
12788
|
-
key: "network",
|
|
12789
|
-
label: "Network",
|
|
12790
|
-
before: network,
|
|
12791
|
-
after: network,
|
|
12792
|
-
changeType: "unchanged"
|
|
12793
15248
|
}
|
|
12794
15249
|
];
|
|
12795
15250
|
}
|
|
@@ -13302,7 +15757,7 @@ registerDiffResolver(EVAL_ENGINE_ACTION_TYPE, {
|
|
|
13302
15757
|
key: "decisions",
|
|
13303
15758
|
label: "Decisions",
|
|
13304
15759
|
before: null,
|
|
13305
|
-
after: inputs?.allowChainEvaluation
|
|
15760
|
+
after: inputs?.allowChainEvaluation === false ? "Recorded only \u2014 nothing is submitted on chain" : inputs?.allowZeroPayoutApprovals === true ? "Submitted on chain, including approvals that pay nothing \u2014 you grant the engine evaluator rights" : "Submitted on chain, which releases payment \u2014 you grant the engine evaluator rights",
|
|
13306
15761
|
changeType: "add"
|
|
13307
15762
|
}
|
|
13308
15763
|
];
|
|
@@ -13439,19 +15894,6 @@ function shouldNotifyPending(state, blockId, pendingInvocationId, currentAssigne
|
|
|
13439
15894
|
|
|
13440
15895
|
// src/core/lib/flowEngine/runs.ts
|
|
13441
15896
|
import * as Y2 from "yjs";
|
|
13442
|
-
|
|
13443
|
-
// src/core/lib/yjsTypes.ts
|
|
13444
|
-
function getExistingYMap(yDoc, key) {
|
|
13445
|
-
if (!yDoc.share.has(key)) return void 0;
|
|
13446
|
-
return yDoc.getMap(key);
|
|
13447
|
-
}
|
|
13448
|
-
function isYMapLike(value) {
|
|
13449
|
-
if (!value || typeof value !== "object") return false;
|
|
13450
|
-
const candidate = value;
|
|
13451
|
-
return typeof candidate.get === "function" && typeof candidate.set === "function" && typeof candidate.has === "function" && typeof candidate.forEach === "function";
|
|
13452
|
-
}
|
|
13453
|
-
|
|
13454
|
-
// src/core/lib/flowEngine/runs.ts
|
|
13455
15897
|
var RUNS_MAP_KEY = "runs";
|
|
13456
15898
|
var RUNS_TERMINAL_MAP_KEY = "runsTerminal";
|
|
13457
15899
|
var LEGACY_RUNTIME_MAP_KEY2 = "runtime";
|
|
@@ -14012,7 +16454,10 @@ function resolveReferencesDetailed(input, editorDocument, options = {}) {
|
|
|
14012
16454
|
}
|
|
14013
16455
|
if (warnContext && unresolved.length > 0) {
|
|
14014
16456
|
for (const entry of unresolved) {
|
|
14015
|
-
warnOnce(
|
|
16457
|
+
warnOnce(
|
|
16458
|
+
`ref-unresolved:${warnContext}:${entry.ref}`,
|
|
16459
|
+
`[flow-config] ${warnContext}: reference ${entry.ref} did not resolve (${entry.reason}); using fallback '${fallback}'`
|
|
16460
|
+
);
|
|
14016
16461
|
}
|
|
14017
16462
|
}
|
|
14018
16463
|
return { value: result, unresolved };
|
|
@@ -14850,8 +17295,8 @@ function fnv1a322(input, seed) {
|
|
|
14850
17295
|
}
|
|
14851
17296
|
function createRunEventIdempotencyKey(kind, ...identity) {
|
|
14852
17297
|
const canonical = JSON.stringify([kind, ...identity]);
|
|
14853
|
-
const
|
|
14854
|
-
return `v1:${kind.replace(/\./g, "_")}:${
|
|
17298
|
+
const digest2 = `${fnv1a322(canonical, 2166136261)}${fnv1a322(canonical, 2654435761)}`;
|
|
17299
|
+
return `v1:${kind.replace(/\./g, "_")}:${digest2}`;
|
|
14855
17300
|
}
|
|
14856
17301
|
function boundRunActionOutput(value) {
|
|
14857
17302
|
const byteLength = jsonByteLength(value);
|
|
@@ -16410,7 +18855,6 @@ var executeNode = async ({ node, actorDid, actorType, entityRoomId, context, act
|
|
|
16410
18855
|
};
|
|
16411
18856
|
|
|
16412
18857
|
// src/core/lib/flowEngine/readBackReconciler.ts
|
|
16413
|
-
import * as Y5 from "yjs";
|
|
16414
18858
|
async function appendReadBackTimeline(eventLog, event, runtime, blockId, now) {
|
|
16415
18859
|
const result = await appendRunTimelineEvent({
|
|
16416
18860
|
eventLog,
|
|
@@ -16422,7 +18866,7 @@ async function appendReadBackTimeline(eventLog, event, runtime, blockId, now) {
|
|
|
16422
18866
|
return result.ok;
|
|
16423
18867
|
}
|
|
16424
18868
|
function isYDoc(value) {
|
|
16425
|
-
return value
|
|
18869
|
+
return isYDocLike(value);
|
|
16426
18870
|
}
|
|
16427
18871
|
function getYDoc(editorOrYDoc) {
|
|
16428
18872
|
if (!editorOrYDoc) return void 0;
|
|
@@ -16896,9 +19340,8 @@ async function reconcileActionReadBack(params) {
|
|
|
16896
19340
|
}
|
|
16897
19341
|
|
|
16898
19342
|
// src/core/lib/flowEngine/actionExecutor.ts
|
|
16899
|
-
import * as Y6 from "yjs";
|
|
16900
19343
|
function isYDoc2(value) {
|
|
16901
|
-
return value
|
|
19344
|
+
return isYDocLike(value);
|
|
16902
19345
|
}
|
|
16903
19346
|
function getYDoc2(editorOrYDoc) {
|
|
16904
19347
|
if (!editorOrYDoc) return void 0;
|
|
@@ -17146,10 +19589,35 @@ async function executeActionBlock(params) {
|
|
|
17146
19589
|
let requestedAwaitingReadBack = false;
|
|
17147
19590
|
let proofFailureReason = null;
|
|
17148
19591
|
let proofFailureOutput;
|
|
19592
|
+
let topicRecords = [];
|
|
17149
19593
|
const startedAt = now();
|
|
17150
19594
|
const previousState = runtime.get(blockId);
|
|
17151
19595
|
const attempt = (previousState.attempt || 0) + 1;
|
|
17152
19596
|
const executionId = makeExecutionId2(now);
|
|
19597
|
+
const recordTopicPhase = async (status, options = {}) => {
|
|
19598
|
+
if (!params.topic || !params.topicBridge) return void 0;
|
|
19599
|
+
try {
|
|
19600
|
+
return await params.topicBridge.recordFlowPhase({
|
|
19601
|
+
topic: params.topic,
|
|
19602
|
+
actionType,
|
|
19603
|
+
actorDid: params.actorDid,
|
|
19604
|
+
executorDid: params.executorDid || params.actorDid,
|
|
19605
|
+
executionId,
|
|
19606
|
+
status,
|
|
19607
|
+
input: inputBuild.inputs,
|
|
19608
|
+
output: options.output,
|
|
19609
|
+
semanticRecords: topicRecords,
|
|
19610
|
+
flowUri,
|
|
19611
|
+
sessionRunId,
|
|
19612
|
+
nodeId: blockId,
|
|
19613
|
+
invocationReference: options.invocationReference,
|
|
19614
|
+
traceReference: sessionRunId ? `${flowUri}/session/${sessionRunId}/execution/${executionId}` : `${flowUri}/execution/${executionId}`,
|
|
19615
|
+
error: options.error
|
|
19616
|
+
});
|
|
19617
|
+
} catch (error) {
|
|
19618
|
+
return { state: "queued", error: error instanceof Error ? error.message : "Topic receipt bridge failed" };
|
|
19619
|
+
}
|
|
19620
|
+
};
|
|
17153
19621
|
const timelineRequired = !!yDoc && !usesLegacyRuntimeCompatibility(yDoc);
|
|
17154
19622
|
if (sessionRunId && (eventLog || timelineRequired)) {
|
|
17155
19623
|
const startedLogged = await appendRunTimelineEvent({
|
|
@@ -17198,6 +19666,18 @@ async function executeActionBlock(params) {
|
|
|
17198
19666
|
executionId,
|
|
17199
19667
|
executionStartedAt: startedAt
|
|
17200
19668
|
});
|
|
19669
|
+
const runningWriteBack = await recordTopicPhase("running");
|
|
19670
|
+
if (runningWriteBack?.state === "rejected") {
|
|
19671
|
+
const message = runningWriteBack.error || "Topic Action execution was rejected by the receipt bridge.";
|
|
19672
|
+
updateRuntimeFailure(runtime, blockId, message, now);
|
|
19673
|
+
return {
|
|
19674
|
+
...buildFailureResult({ blockId, actionType, stage: "authorization", error: message, pendingInvocation: inputBuild.pendingInvocation }),
|
|
19675
|
+
executionId,
|
|
19676
|
+
runId: executionId,
|
|
19677
|
+
topicWriteBack: runningWriteBack
|
|
19678
|
+
};
|
|
19679
|
+
}
|
|
19680
|
+
const actionServices = action.type.startsWith("qi/topic.") ? params.services || {} : { ...params.services || {}, topic: void 0 };
|
|
17201
19681
|
const outcome = await executeNode({
|
|
17202
19682
|
node: flowNode,
|
|
17203
19683
|
actorDid: params.actorDid,
|
|
@@ -17223,13 +19703,16 @@ async function executeActionBlock(params) {
|
|
|
17223
19703
|
nodeId: blockId,
|
|
17224
19704
|
flowNode,
|
|
17225
19705
|
runtime,
|
|
17226
|
-
services:
|
|
19706
|
+
services: actionServices,
|
|
17227
19707
|
handlers: params.handlers,
|
|
17228
19708
|
editor,
|
|
17229
19709
|
yDoc,
|
|
17230
|
-
pendingInvocation: inputBuild.pendingInvocation
|
|
19710
|
+
pendingInvocation: inputBuild.pendingInvocation,
|
|
19711
|
+
topic: params.topic,
|
|
19712
|
+
flowRevision: params.flowRevision
|
|
17231
19713
|
});
|
|
17232
19714
|
if (result.events?.length) events.push(...result.events);
|
|
19715
|
+
if (result.topicRecords?.length) topicRecords = result.topicRecords;
|
|
17233
19716
|
if (result.completion?.state === "awaiting_readback") {
|
|
17234
19717
|
requestedAwaitingReadBack = true;
|
|
17235
19718
|
rawReadBack = result.completion.readBack;
|
|
@@ -17292,7 +19775,12 @@ async function executeActionBlock(params) {
|
|
|
17292
19775
|
invocationCid: outcome.invocationCid,
|
|
17293
19776
|
capabilityId: outcome.capabilityId,
|
|
17294
19777
|
executionId,
|
|
17295
|
-
runId: executionId
|
|
19778
|
+
runId: executionId,
|
|
19779
|
+
topicWriteBack: await recordTopicPhase(proofFailureState === "needs_verification" ? "needs_verification" : "failed", {
|
|
19780
|
+
output: proofFailureOutput,
|
|
19781
|
+
error: { code: PROOF_MISSING_CODE, message },
|
|
19782
|
+
invocationReference: outcome.invocationCid || outcome.capabilityId
|
|
19783
|
+
})
|
|
17296
19784
|
};
|
|
17297
19785
|
}
|
|
17298
19786
|
updateRuntimeFailure(runtime, blockId, message, now);
|
|
@@ -17317,6 +19805,10 @@ async function executeActionBlock(params) {
|
|
|
17317
19805
|
now
|
|
17318
19806
|
});
|
|
17319
19807
|
}
|
|
19808
|
+
const topicWriteBack2 = await recordTopicPhase(outcome.stage === "authorization" ? "rejected" : "failed", {
|
|
19809
|
+
error: { message },
|
|
19810
|
+
invocationReference: outcome.invocationCid || outcome.capabilityId
|
|
19811
|
+
});
|
|
17320
19812
|
return {
|
|
17321
19813
|
...buildFailureResult({
|
|
17322
19814
|
blockId,
|
|
@@ -17328,7 +19820,8 @@ async function executeActionBlock(params) {
|
|
|
17328
19820
|
invocationCid: outcome.invocationCid,
|
|
17329
19821
|
capabilityId: outcome.capabilityId,
|
|
17330
19822
|
executionId,
|
|
17331
|
-
runId: executionId
|
|
19823
|
+
runId: executionId,
|
|
19824
|
+
topicWriteBack: topicWriteBack2
|
|
17332
19825
|
};
|
|
17333
19826
|
}
|
|
17334
19827
|
const output = outcome.result?.payload || {};
|
|
@@ -17441,6 +19934,11 @@ async function executeActionBlock(params) {
|
|
|
17441
19934
|
});
|
|
17442
19935
|
}
|
|
17443
19936
|
const pendingInvocationRemoved = completionState === "completed" ? cleanupCompletedPendingInvocation(yDoc, blockId, inputBuild.pendingInvocation, sessionRunId) : false;
|
|
19937
|
+
const topicWriteBack = await recordTopicPhase(completionState === "completed" ? "succeeded" : completionState === "needs_verification" ? "needs_verification" : "failed", {
|
|
19938
|
+
output,
|
|
19939
|
+
...boundedOutput.exceeded ? { error: { code: "RUN_OUTPUT_TOO_LARGE", message: `Action output exceeded the ${MAX_RUN_ACTION_OUTPUT_BYTES / 1024} KiB run-head limit.` } } : {},
|
|
19940
|
+
invocationReference: outcome.invocationCid || outcome.capabilityId
|
|
19941
|
+
});
|
|
17444
19942
|
return {
|
|
17445
19943
|
success: !boundedOutput.exceeded,
|
|
17446
19944
|
stage: outcome.stage,
|
|
@@ -17458,7 +19956,8 @@ async function executeActionBlock(params) {
|
|
|
17458
19956
|
executionId,
|
|
17459
19957
|
pendingInvocationRemoved,
|
|
17460
19958
|
completionState,
|
|
17461
|
-
pendingInvocation: inputBuild.pendingInvocation
|
|
19959
|
+
pendingInvocation: inputBuild.pendingInvocation,
|
|
19960
|
+
topicWriteBack
|
|
17462
19961
|
};
|
|
17463
19962
|
}
|
|
17464
19963
|
|
|
@@ -18160,6 +20659,17 @@ function compileBlockProps(cap, registryType) {
|
|
|
18160
20659
|
var COMPILED_BLOCK_TYPE = "action";
|
|
18161
20660
|
|
|
18162
20661
|
// src/core/lib/flowCompiler/compiler.ts
|
|
20662
|
+
function stripActiveScheduleBindings(plan) {
|
|
20663
|
+
let changed = false;
|
|
20664
|
+
const capabilities = plan.capabilities.map((cap) => {
|
|
20665
|
+
if (cap.trigger?.type !== "schedule") return cap;
|
|
20666
|
+
if (cap.trigger.scheduleRef === void 0 && cap.trigger.scheduleRevision === void 0) return cap;
|
|
20667
|
+
changed = true;
|
|
20668
|
+
const { scheduleRef: _ref, scheduleRevision: _rev, ...inactive } = cap.trigger;
|
|
20669
|
+
return { ...cap, trigger: inactive };
|
|
20670
|
+
});
|
|
20671
|
+
return changed ? { ...plan, capabilities } : plan;
|
|
20672
|
+
}
|
|
18163
20673
|
function compileBaseUcanFlow(plan, registry) {
|
|
18164
20674
|
if (!Array.isArray(plan.capabilities)) {
|
|
18165
20675
|
throw new Error("BaseUcanFlow.capabilities must be an array");
|
|
@@ -18257,6 +20767,21 @@ function compileBaseUcanFlow(plan, registry) {
|
|
|
18257
20767
|
});
|
|
18258
20768
|
}
|
|
18259
20769
|
}
|
|
20770
|
+
if (trigger.type === "schedule") {
|
|
20771
|
+
if (action.eligibleForTimeTrigger !== true) {
|
|
20772
|
+
throw new Error(
|
|
20773
|
+
`Block "${nodeId}" is configured with a schedule trigger, but its action type "${action.type}" is not marked eligibleForTimeTrigger. Set eligibleForTimeTrigger: true on the action definition or change the trigger.`
|
|
20774
|
+
);
|
|
20775
|
+
}
|
|
20776
|
+
if (trigger.sourceBlockId || trigger.sources || trigger.eventName) {
|
|
20777
|
+
throw new Error(
|
|
20778
|
+
`Block "${nodeId}" has a schedule trigger carrying event-trigger fields (sourceBlockId/eventName/sources). A schedule trigger holds only scheduleRef and scheduleRevision \u2014 timing lives in the referenced ScheduleSpec, never in the block.`
|
|
20779
|
+
);
|
|
20780
|
+
}
|
|
20781
|
+
if (trigger.scheduleRevision !== void 0 && (!Number.isInteger(trigger.scheduleRevision) || trigger.scheduleRevision < 1)) {
|
|
20782
|
+
throw new Error(`Block "${nodeId}" has a schedule trigger with an invalid scheduleRevision (must be an integer >= 1).`);
|
|
20783
|
+
}
|
|
20784
|
+
}
|
|
18260
20785
|
if (trigger.type === "block.event" || trigger.type === "block.event.all") {
|
|
18261
20786
|
const refs = collectOutputRefs(cap.nb || {});
|
|
18262
20787
|
for (const ref of refs) {
|
|
@@ -18430,7 +20955,7 @@ function detectTriggerCycles(triggerEdges) {
|
|
|
18430
20955
|
}
|
|
18431
20956
|
|
|
18432
20957
|
// src/core/lib/flowCompiler/documentFragment.ts
|
|
18433
|
-
import * as
|
|
20958
|
+
import * as Y5 from "yjs";
|
|
18434
20959
|
function writeCompiledBlocksToFragment(fragment, blocks) {
|
|
18435
20960
|
const documentBlockGroup = getOrCreateDocumentBlockGroup(fragment);
|
|
18436
20961
|
for (const block of blocks) {
|
|
@@ -18449,7 +20974,7 @@ function removeBlockFromFragment(fragment, blockId) {
|
|
|
18449
20974
|
if (!blockGroup) return false;
|
|
18450
20975
|
for (let i = 0; i < blockGroup.length; i++) {
|
|
18451
20976
|
const container = blockGroup.get(i);
|
|
18452
|
-
if (container
|
|
20977
|
+
if (isYXmlElementLike(container) && container.getAttribute("id") === blockId) {
|
|
18453
20978
|
blockGroup.delete(i, 1);
|
|
18454
20979
|
return true;
|
|
18455
20980
|
}
|
|
@@ -18461,7 +20986,7 @@ function replaceBlockInFragment(fragment, block) {
|
|
|
18461
20986
|
if (!blockGroup) return false;
|
|
18462
20987
|
for (let i = 0; i < blockGroup.length; i++) {
|
|
18463
20988
|
const container = blockGroup.get(i);
|
|
18464
|
-
if (container
|
|
20989
|
+
if (isYXmlElementLike(container) && container.getAttribute("id") === block.id) {
|
|
18465
20990
|
blockGroup.delete(i, 1);
|
|
18466
20991
|
const newContainer = createBlockContainer(block);
|
|
18467
20992
|
blockGroup.insert(i, [newContainer]);
|
|
@@ -18477,7 +21002,7 @@ function swapBlocksInFragment(fragment, idA, idB) {
|
|
|
18477
21002
|
let ib = -1;
|
|
18478
21003
|
for (let i = 0; i < blockGroup.length; i++) {
|
|
18479
21004
|
const container = blockGroup.get(i);
|
|
18480
|
-
if (container
|
|
21005
|
+
if (isYXmlElementLike(container)) {
|
|
18481
21006
|
const id = container.getAttribute("id");
|
|
18482
21007
|
if (id === idA) ia = i;
|
|
18483
21008
|
else if (id === idB) ib = i;
|
|
@@ -18524,11 +21049,11 @@ function readBlocksFromFragment(fragment) {
|
|
|
18524
21049
|
const blocks = [];
|
|
18525
21050
|
for (let i = 0; i < blockGroup.length; i++) {
|
|
18526
21051
|
const container = blockGroup.get(i);
|
|
18527
|
-
if (!(container
|
|
21052
|
+
if (!isYXmlElementLike(container) || container.nodeName !== "blockContainer") continue;
|
|
18528
21053
|
const id = container.getAttribute("id");
|
|
18529
21054
|
if (typeof id !== "string" || id.length === 0) continue;
|
|
18530
21055
|
const content = container.get(0);
|
|
18531
|
-
if (!(content
|
|
21056
|
+
if (!isYXmlElementLike(content)) continue;
|
|
18532
21057
|
const props = { ...content.getAttributes() };
|
|
18533
21058
|
const textColor = container.getAttribute("textColor");
|
|
18534
21059
|
if (typeof textColor === "string" && textColor !== "default") props.textColor = textColor;
|
|
@@ -18539,12 +21064,12 @@ function readBlocksFromFragment(fragment) {
|
|
|
18539
21064
|
return blocks;
|
|
18540
21065
|
}
|
|
18541
21066
|
function createBlockContainer(block) {
|
|
18542
|
-
const blockContainer = new
|
|
21067
|
+
const blockContainer = new Y5.XmlElement("blockContainer");
|
|
18543
21068
|
const { backgroundColor: rawBackgroundColor, textColor: rawTextColor, ...contentProps } = block.props;
|
|
18544
21069
|
blockContainer.setAttribute("id", block.id);
|
|
18545
21070
|
blockContainer.setAttribute("textColor", rawTextColor || "default");
|
|
18546
21071
|
blockContainer.setAttribute("backgroundColor", rawBackgroundColor || "default");
|
|
18547
|
-
const blockContent = new
|
|
21072
|
+
const blockContent = new Y5.XmlElement(block.type);
|
|
18548
21073
|
for (const [key, value] of Object.entries(contentProps)) {
|
|
18549
21074
|
if (value !== "") {
|
|
18550
21075
|
blockContent.setAttribute(key, value);
|
|
@@ -18559,13 +21084,13 @@ function appendBlockToGroup(blockGroup, block) {
|
|
|
18559
21084
|
}
|
|
18560
21085
|
function getOrCreateDocumentBlockGroup(fragment) {
|
|
18561
21086
|
if (fragment.length === 0) {
|
|
18562
|
-
const blockGroup = new
|
|
21087
|
+
const blockGroup = new Y5.XmlElement("blockGroup");
|
|
18563
21088
|
fragment.insert(0, [blockGroup]);
|
|
18564
21089
|
return blockGroup;
|
|
18565
21090
|
}
|
|
18566
21091
|
if (fragment.length === 1) {
|
|
18567
21092
|
const rootNode = fragment.get(0);
|
|
18568
|
-
if (rootNode
|
|
21093
|
+
if (isYXmlElementLike(rootNode) && rootNode.nodeName === "blockGroup") {
|
|
18569
21094
|
return rootNode;
|
|
18570
21095
|
}
|
|
18571
21096
|
}
|
|
@@ -18575,7 +21100,7 @@ function getExistingBlockGroup(fragment) {
|
|
|
18575
21100
|
if (fragment.length === 0) return null;
|
|
18576
21101
|
if (fragment.length === 1) {
|
|
18577
21102
|
const rootNode = fragment.get(0);
|
|
18578
|
-
if (rootNode
|
|
21103
|
+
if (isYXmlElementLike(rootNode) && rootNode.nodeName === "blockGroup") {
|
|
18579
21104
|
return rootNode;
|
|
18580
21105
|
}
|
|
18581
21106
|
}
|
|
@@ -18583,13 +21108,12 @@ function getExistingBlockGroup(fragment) {
|
|
|
18583
21108
|
}
|
|
18584
21109
|
|
|
18585
21110
|
// src/core/lib/flowCompiler/readFlow.ts
|
|
18586
|
-
import * as Y8 from "yjs";
|
|
18587
21111
|
function readCompiledFlowFromYDoc(yDoc) {
|
|
18588
21112
|
const flowMeta = yDoc.getMap("qi.flow.meta");
|
|
18589
21113
|
const flowNodes = yDoc.getMap("qi.flow.nodes");
|
|
18590
21114
|
const nodes = {};
|
|
18591
21115
|
flowNodes.forEach((value, nodeId) => {
|
|
18592
|
-
if (value
|
|
21116
|
+
if (isYMapLike(value)) {
|
|
18593
21117
|
nodes[nodeId] = yMapToFlowNode(value);
|
|
18594
21118
|
}
|
|
18595
21119
|
});
|
|
@@ -18609,7 +21133,7 @@ function readCompiledFlowFromYDoc(yDoc) {
|
|
|
18609
21133
|
const flowEdges = yDoc.getMap("qi.flow.edges");
|
|
18610
21134
|
const edges = [];
|
|
18611
21135
|
flowEdges.forEach((value) => {
|
|
18612
|
-
if (value
|
|
21136
|
+
if (isYMapLike(value)) {
|
|
18613
21137
|
edges.push(yMapToEdge(value));
|
|
18614
21138
|
}
|
|
18615
21139
|
});
|
|
@@ -18806,11 +21330,11 @@ function triggerToNodeIds(trigger, nodeIdByBlockId) {
|
|
|
18806
21330
|
}
|
|
18807
21331
|
|
|
18808
21332
|
// src/core/lib/flowCompiler/setup.ts
|
|
18809
|
-
import * as
|
|
21333
|
+
import * as Y7 from "yjs";
|
|
18810
21334
|
import { MatrixProvider } from "@ixo/matrix-crdt";
|
|
18811
21335
|
|
|
18812
21336
|
// src/core/lib/flowCompiler/hydrate.ts
|
|
18813
|
-
import * as
|
|
21337
|
+
import * as Y6 from "yjs";
|
|
18814
21338
|
function hydrateYDocFromCompiledFlow(yDoc, compiled) {
|
|
18815
21339
|
yDoc.transact(() => {
|
|
18816
21340
|
const flowMeta = yDoc.getMap("qi.flow.meta");
|
|
@@ -18825,7 +21349,7 @@ function hydrateYDocFromCompiledFlow(yDoc, compiled) {
|
|
|
18825
21349
|
}
|
|
18826
21350
|
const flowEdges = yDoc.getMap("qi.flow.edges");
|
|
18827
21351
|
for (const edge of compiled.edges) {
|
|
18828
|
-
const yEdge = new
|
|
21352
|
+
const yEdge = new Y6.Map();
|
|
18829
21353
|
yEdge.set("id", edge.id);
|
|
18830
21354
|
yEdge.set("source", edge.source);
|
|
18831
21355
|
yEdge.set("target", edge.target);
|
|
@@ -18879,7 +21403,7 @@ function hydrateYDocFromMergeResult(yDoc, mergeResult) {
|
|
|
18879
21403
|
const flowEdges = yDoc.getMap("qi.flow.edges");
|
|
18880
21404
|
flowEdges.forEach((_, key) => flowEdges.delete(key));
|
|
18881
21405
|
for (const edge of merged.edges) {
|
|
18882
|
-
const yEdge = new
|
|
21406
|
+
const yEdge = new Y6.Map();
|
|
18883
21407
|
yEdge.set("id", edge.id);
|
|
18884
21408
|
yEdge.set("source", edge.source);
|
|
18885
21409
|
yEdge.set("target", edge.target);
|
|
@@ -18921,7 +21445,7 @@ function initializeRuntimeForNodes(yDoc, compiled, nodeIds, runId) {
|
|
|
18921
21445
|
});
|
|
18922
21446
|
}
|
|
18923
21447
|
function createYMapFromNode(node) {
|
|
18924
|
-
const yNode = new
|
|
21448
|
+
const yNode = new Y6.Map();
|
|
18925
21449
|
yNode.set("id", node.id);
|
|
18926
21450
|
yNode.set("blockId", node.blockId);
|
|
18927
21451
|
yNode.set("can", node.can);
|
|
@@ -18974,7 +21498,8 @@ function readFlow() {
|
|
|
18974
21498
|
}
|
|
18975
21499
|
async function setupFlowFromBaseUcan(options) {
|
|
18976
21500
|
const { plan: rawPlan, roomId, matrixClient, creatorDid, docId, templateId, strategy = "full" } = options;
|
|
18977
|
-
const
|
|
21501
|
+
const identified = rawPlan.flowId ? rawPlan : { ...rawPlan, flowId: docId || roomId };
|
|
21502
|
+
const plan = templateId ? stripActiveScheduleBindings(identified) : identified;
|
|
18978
21503
|
const incomingCompiled = compileBaseUcanFlow(plan, { getActionByCan });
|
|
18979
21504
|
const { yDoc, provider } = await connectToRoom(roomId, matrixClient, { adoptRuns: true });
|
|
18980
21505
|
let finalCompiled;
|
|
@@ -19030,7 +21555,7 @@ function applyFlowPlanToYDoc(yDoc, options) {
|
|
|
19030
21555
|
return mergeResult.merged;
|
|
19031
21556
|
}
|
|
19032
21557
|
async function connectToRoom(roomId, matrixClient, options) {
|
|
19033
|
-
const yDoc = new
|
|
21558
|
+
const yDoc = new Y7.Doc();
|
|
19034
21559
|
const client = matrixClient;
|
|
19035
21560
|
client.canSupportVoip = false;
|
|
19036
21561
|
client.clientOpts = { lazyLoadMembers: true };
|
|
@@ -19815,7 +22340,7 @@ function isRecord2(value) {
|
|
|
19815
22340
|
function isYXmlContainerLike(value) {
|
|
19816
22341
|
return value != null && typeof value === "object" && typeof value.toArray === "function";
|
|
19817
22342
|
}
|
|
19818
|
-
function
|
|
22343
|
+
function isYXmlElementLike2(value) {
|
|
19819
22344
|
return isYXmlContainerLike(value) && typeof value.nodeName === "string" && typeof value.getAttribute === "function" && typeof value.setAttribute === "function";
|
|
19820
22345
|
}
|
|
19821
22346
|
function getElementBlockId(element) {
|
|
@@ -19844,7 +22369,7 @@ function updateXmlElementProps(element, blockId, propsPatch) {
|
|
|
19844
22369
|
}
|
|
19845
22370
|
function findXmlElementById(container, blockId) {
|
|
19846
22371
|
for (const node of container.toArray()) {
|
|
19847
|
-
if (!
|
|
22372
|
+
if (!isYXmlElementLike2(node)) continue;
|
|
19848
22373
|
const id = getElementBlockId(node);
|
|
19849
22374
|
if (id === blockId) return node;
|
|
19850
22375
|
const nested = findXmlElementById(node, blockId);
|
|
@@ -19856,7 +22381,7 @@ function updateDocumentBlockProps(yDoc, blockId, propsPatch) {
|
|
|
19856
22381
|
const target = findXmlElementById(yDoc.getXmlFragment("document"), blockId);
|
|
19857
22382
|
if (!target) return false;
|
|
19858
22383
|
updateXmlElementProps(target, blockId, propsPatch);
|
|
19859
|
-
const content = target.toArray().find((node) =>
|
|
22384
|
+
const content = target.toArray().find((node) => isYXmlElementLike2(node) && node.nodeName !== "blockGroup");
|
|
19860
22385
|
if (content) {
|
|
19861
22386
|
updateXmlElementProps(content, blockId, propsPatch);
|
|
19862
22387
|
}
|
|
@@ -20290,10 +22815,17 @@ export {
|
|
|
20290
22815
|
matrixUserIdToDid,
|
|
20291
22816
|
findOrCreateDMRoom,
|
|
20292
22817
|
sendDirectMessage,
|
|
22818
|
+
canonicalActionJson,
|
|
22819
|
+
sha256Digest,
|
|
22820
|
+
MAX_TOPIC_SEMANTIC_RECORDS_PER_RECEIPT,
|
|
22821
|
+
MAX_TOPIC_SEMANTIC_RECORD_BATCH_BYTES,
|
|
22822
|
+
validateTopicSemanticRecord,
|
|
22823
|
+
validateTopicSemanticRecordBatch,
|
|
20293
22824
|
canToType,
|
|
20294
22825
|
typeToCan,
|
|
20295
22826
|
getAllCanMappings,
|
|
20296
22827
|
warnOnce,
|
|
22828
|
+
getActionPresentation,
|
|
20297
22829
|
STEP_COMPLETED_EVENT_NAME,
|
|
20298
22830
|
STEP_COMPLETED_EVENT,
|
|
20299
22831
|
doneWhenCompleted,
|
|
@@ -20312,7 +22844,16 @@ export {
|
|
|
20312
22844
|
getActionByCan,
|
|
20313
22845
|
getEventsForBlock,
|
|
20314
22846
|
getOutputSchemaForBlock,
|
|
22847
|
+
ACTION_MANIFEST_VERSION,
|
|
22848
|
+
ACTION_REGISTRY_VERSION,
|
|
20315
22849
|
generateActionManifest,
|
|
22850
|
+
actionManifestIssues,
|
|
22851
|
+
ACTION_MANIFEST_V2_SCHEMA,
|
|
22852
|
+
ActionManifestV2VerificationError,
|
|
22853
|
+
canonicalizeManifestV2Payload,
|
|
22854
|
+
computeActionManifestV2Digest,
|
|
22855
|
+
signActionManifestV2,
|
|
22856
|
+
verifyActionManifestV2,
|
|
20316
22857
|
isBlankInputValue,
|
|
20317
22858
|
getMissingActionInputs,
|
|
20318
22859
|
SERVICE_VERBS,
|
|
@@ -20361,6 +22902,7 @@ export {
|
|
|
20361
22902
|
DIFFERENT_WHEN_OTHER_MAX,
|
|
20362
22903
|
normalizeDifferentWhen,
|
|
20363
22904
|
normalizeRepeatSubmissions,
|
|
22905
|
+
FORM_SEGMENT,
|
|
20364
22906
|
extractRubricFieldCatalog,
|
|
20365
22907
|
buildRubricEnvelope,
|
|
20366
22908
|
parsePublishedRubric,
|
|
@@ -20382,6 +22924,8 @@ export {
|
|
|
20382
22924
|
parseCalendarEventCreateInputs,
|
|
20383
22925
|
serializeCalendarEventCreateInputs,
|
|
20384
22926
|
parseAttendeesField,
|
|
22927
|
+
isYMapLike,
|
|
22928
|
+
isYXmlElementLike,
|
|
20385
22929
|
FLOW_CONNECTIONS_MAP_KEY,
|
|
20386
22930
|
FLOW_CONNECTION_BINDINGS_MAP_KEY,
|
|
20387
22931
|
readFlowConnections,
|
|
@@ -20400,6 +22944,8 @@ export {
|
|
|
20400
22944
|
renderNumber,
|
|
20401
22945
|
formatCoin2 as formatCoin,
|
|
20402
22946
|
formatCoinAmount,
|
|
22947
|
+
toBaseUnits,
|
|
22948
|
+
formatTokenAmount,
|
|
20403
22949
|
DM_NOTIFICATIONS_MAP_KEY,
|
|
20404
22950
|
getDMNotificationState,
|
|
20405
22951
|
setDMNotificationRecord,
|
|
@@ -20602,4 +23148,4 @@ export {
|
|
|
20602
23148
|
executeQueuedFlowAgentCoreCommands,
|
|
20603
23149
|
FlowAgentService
|
|
20604
23150
|
};
|
|
20605
|
-
//# sourceMappingURL=chunk-
|
|
23151
|
+
//# sourceMappingURL=chunk-NEOPTPDF.js.map
|