@ixo/editor 6.31.0 → 6.31.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action-manifest.json +16640 -3129
- package/dist/{chunk-ZXNBOVAA.js → chunk-6OMM32CE.js} +2593 -439
- package/dist/chunk-6OMM32CE.js.map +1 -0
- package/dist/{chunk-NYQBGIZ2.js → chunk-B6S65337.js} +2355 -926
- package/dist/chunk-B6S65337.js.map +1 -0
- package/dist/{chunk-S7CAGFE3.js → chunk-PSZOCS5V.js} +15 -14
- package/dist/chunk-PSZOCS5V.js.map +1 -0
- package/dist/{store-CqvAkvIZ.d.ts → connections-B24zj0RI.d.ts} +340 -323
- package/dist/core/index.d.ts +314 -251
- package/dist/core/index.js +455 -2
- package/dist/core/index.js.map +1 -1
- package/dist/{graphql-client-ZY4Iw0jB.d.ts → graphql-client-BICFLCX8.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-DFPayq89.d.ts} +9378 -8728
- package/package.json +7 -5
- package/style.css.d.ts +5 -5
- package/dist/chunk-NYQBGIZ2.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,160 @@ 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.accept": { displayName: "Accept Topic Contract", description: "Accept the exact current Topic contract 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.confirm": { displayName: "Confirm Topic Outcome", description: "Confirm a proposed outcome using the stated authority" },
|
|
1103
|
+
"qi/topic.outcome.propose": { displayName: "Propose Topic Outcome", description: "Propose an evidence-backed outcome for review" },
|
|
1104
|
+
"qi/topic.status.transition": { displayName: "Change Topic Status", description: "Move the Topic between permitted lifecycle statuses" },
|
|
1105
|
+
"qi/wallet.fund": { displayName: "Fund Wallet", description: "Fund a wallet with an on-chain transfer" },
|
|
1106
|
+
"qi/wallet.generate": { displayName: "Generate Wallet", description: "Generate an IXO wallet and DID" },
|
|
1107
|
+
"qi/wallet.generateAndFund": { displayName: "Generate & Fund Wallet", description: "Generate an IXO wallet and fund it on-chain" },
|
|
1108
|
+
"qi/work.accept": { displayName: "Accept Work", description: "Accept submitted work using the stated authority" },
|
|
1109
|
+
"qi/work.assign": { displayName: "Assign Work", description: "Assign a referenced unit of work" },
|
|
1110
|
+
"qi/work.checkpoint": { displayName: "Record Work Checkpoint", description: "Record progress and evidence for work in progress" },
|
|
1111
|
+
"qi/work.dispatch": { displayName: "Dispatch Work", description: "Dispatch assigned work to its assignee" },
|
|
1112
|
+
"qi/work.submit": { displayName: "Submit Work", description: "Submit completed work for review" },
|
|
1113
|
+
"qi/xero.contact.create": { displayName: "Create Xero contact", description: "Add a customer or supplier in Xero" },
|
|
1114
|
+
"qi/xero.invoice.create": { displayName: "Create Xero invoice", description: "Draft a new Xero invoice" },
|
|
1115
|
+
"qi/xero.invoice.list": { displayName: "List Xero invoices", description: "Fetch invoices from Xero" },
|
|
1116
|
+
"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" }
|
|
1117
|
+
};
|
|
1118
|
+
function humanizeActionType(type) {
|
|
1119
|
+
const withoutNamespace = type.replace(/^qi\//, "").replace(/^oracle\.?/, "oracle ");
|
|
1120
|
+
const words = withoutNamespace.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[./_-]+/).filter(Boolean);
|
|
1121
|
+
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(" ");
|
|
1122
|
+
}
|
|
1123
|
+
function getActionPresentation(type) {
|
|
1124
|
+
const registered = PRESENTATION[type];
|
|
1125
|
+
if (registered) return registered;
|
|
1126
|
+
const displayName = humanizeActionType(type) || "Action";
|
|
1127
|
+
return { displayName, description: `Perform ${displayName.toLowerCase()} as part of this flow.` };
|
|
1128
|
+
}
|
|
1129
|
+
|
|
583
1130
|
// src/core/lib/warnOnce.ts
|
|
584
1131
|
var seen = /* @__PURE__ */ new Set();
|
|
585
1132
|
function warnOnce(key, message) {
|
|
@@ -588,8 +1135,160 @@ function warnOnce(key, message) {
|
|
|
588
1135
|
console.warn(message);
|
|
589
1136
|
}
|
|
590
1137
|
|
|
1138
|
+
// src/core/lib/actionRegistry/types.ts
|
|
1139
|
+
var TOPIC_ACTION_BASE_KINDS = ["task", "agent_task", "proposal", "evaluation", "claims", "question", "discussion", "incident"];
|
|
1140
|
+
var CollectionStateEnum = /* @__PURE__ */ ((CollectionStateEnum2) => {
|
|
1141
|
+
CollectionStateEnum2[CollectionStateEnum2["OPEN"] = 0] = "OPEN";
|
|
1142
|
+
CollectionStateEnum2[CollectionStateEnum2["PAUSED"] = 1] = "PAUSED";
|
|
1143
|
+
CollectionStateEnum2[CollectionStateEnum2["CLOSED"] = 2] = "CLOSED";
|
|
1144
|
+
return CollectionStateEnum2;
|
|
1145
|
+
})(CollectionStateEnum || {});
|
|
1146
|
+
|
|
1147
|
+
// src/core/lib/actionRegistry/topicPolicy.ts
|
|
1148
|
+
var ALL_KINDS = [...TOPIC_ACTION_BASE_KINDS];
|
|
1149
|
+
var HUMAN_OWNED = [
|
|
1150
|
+
/^qi\/human\./,
|
|
1151
|
+
/^qi\/governance\./,
|
|
1152
|
+
/^qi\/proposal\./,
|
|
1153
|
+
/^qi\/(claim|bid)\.(submit|evaluate)$/,
|
|
1154
|
+
/^qi\/(domain\.sign|entity\.|iid\.|identity\.|kyc\.)/,
|
|
1155
|
+
/^qi\/(carbon\.(harvest|retire)|wallet\.fund)/,
|
|
1156
|
+
/^qi\/xero\.(invoice|payment)\.create$/,
|
|
1157
|
+
/^qi\/settlement\./,
|
|
1158
|
+
/^qi\/topic\.(flow\.(bind|unbind)|status\.transition|contract\.accept|outcome\.confirm)$/,
|
|
1159
|
+
/^qi\/work\.accept$/,
|
|
1160
|
+
/^qi\/answer\.accept$/
|
|
1161
|
+
];
|
|
1162
|
+
var RESTRICTED = [/^qi\/oracle\.(configure|deploy|store)/i, /^qi\/(matrix\.register|sandbox\.provision|entity\.createOracle|wallet\.generate)/];
|
|
1163
|
+
function any(patterns, value) {
|
|
1164
|
+
return patterns.some((pattern) => pattern.test(value));
|
|
1165
|
+
}
|
|
1166
|
+
function requiredServices(type) {
|
|
1167
|
+
if (type.startsWith("qi/http.")) return ["http"];
|
|
1168
|
+
if (type === "qi/email.send") return ["email"];
|
|
1169
|
+
if (type === "qi/notification.push") return ["notifications"];
|
|
1170
|
+
if (/^qi\/(gmail|outlook|slack|googlecalendar|calendar|xero)\./.test(type)) return ["integrations"];
|
|
1171
|
+
if (/^qi\/flow\.run\./.test(type)) return ["flowRuns"];
|
|
1172
|
+
if (/^qi\/blueprint\./.test(type)) return ["blueprint"];
|
|
1173
|
+
if (/^qi\/bid\./.test(type)) return ["bid"];
|
|
1174
|
+
if (/^qi\/claim\./.test(type)) return ["claim"];
|
|
1175
|
+
if (type === "qi/collection.lifecycle") return ["collection"];
|
|
1176
|
+
if (type === "qi/collection.users") return ["collectionUsers"];
|
|
1177
|
+
if (/^qi\/(matrix\.dm|credential\.store)/.test(type)) return ["matrix"];
|
|
1178
|
+
if (/^qi\/(oracle\.|wallet\.|iid\.|matrix\.register|identity\.|entity\.createOracle|sandbox\.)/.test(type) || type === "qi/oracle.invoke") return ["oracle"];
|
|
1179
|
+
if (/^qi\/carbon\./.test(type)) return ["carbon"];
|
|
1180
|
+
if (/^qi\/entity\.transfer/.test(type)) return ["entity"];
|
|
1181
|
+
if (/^qi\/kyc\./.test(type)) return ["kyc"];
|
|
1182
|
+
if (/^qi\/eval\./.test(type)) return ["evalRegister", "rubric"];
|
|
1183
|
+
if (/^qi\/topic\./.test(type)) return ["topic"];
|
|
1184
|
+
if (/^qi\/agent\./.test(type)) return ["agents"];
|
|
1185
|
+
if (/^qi\/evidence\./.test(type)) return ["evidence"];
|
|
1186
|
+
if (/^qi\/evaluation\./.test(type)) return ["evaluations"];
|
|
1187
|
+
if (/^qi\/settlement\./.test(type)) return ["settlement"];
|
|
1188
|
+
if (/^qi\/incident\.escalate/.test(type)) return ["incidents"];
|
|
1189
|
+
if (/^qi\/(work|answer|incident\.mitigation)\./.test(type)) return ["topic"];
|
|
1190
|
+
if (/^qi\/(governance|proposal|domain\.)\./.test(type)) return ["portalHandlers"];
|
|
1191
|
+
return [];
|
|
1192
|
+
}
|
|
1193
|
+
function riskTier(action) {
|
|
1194
|
+
const type = action.type;
|
|
1195
|
+
if (/^qi\/(settlement\.execute|carbon\.retire)$/.test(type)) return "critical";
|
|
1196
|
+
if (/^qi\/(governance\.|claim\.evaluate|entity\.transfer|domain\.sign|xero\.payment\.create|wallet\.fund)/.test(type)) return "high";
|
|
1197
|
+
if (!action.sideEffect || /\.(list|loadBatches|card-preview|fetch)$/.test(type)) return "low";
|
|
1198
|
+
return "medium";
|
|
1199
|
+
}
|
|
1200
|
+
function sensitivePaths(type) {
|
|
1201
|
+
const input = [];
|
|
1202
|
+
const output = [];
|
|
1203
|
+
if (type.startsWith("qi/http.")) {
|
|
1204
|
+
input.push("headers.authorization", "headers.cookie", "headers.x-api-key", "body");
|
|
1205
|
+
output.push("data", "response");
|
|
1206
|
+
}
|
|
1207
|
+
if (/^qi\/(email|gmail|outlook|slack|notification|matrix\.dm)/.test(type)) {
|
|
1208
|
+
input.push("to", "cc", "bcc", "body", "template", "variables");
|
|
1209
|
+
output.push("providerResponse");
|
|
1210
|
+
}
|
|
1211
|
+
if (/^qi\/(gmail|outlook|slack|googlecalendar|calendar|xero)\./.test(type)) input.push("connection", "bindingId");
|
|
1212
|
+
if (/^qi\/(kyc|credential)\./.test(type)) {
|
|
1213
|
+
input.push("data", "credential");
|
|
1214
|
+
output.push("credential", "surveyAnswers");
|
|
1215
|
+
}
|
|
1216
|
+
if (/^qi\/(oracle\.|wallet\.|iid\.|matrix\.register|identity\.|entity\.createOracle|sandbox\.)/.test(type)) {
|
|
1217
|
+
input.push("mnemonic", "pin", "secrets", "config");
|
|
1218
|
+
output.push("mnemonic", "privateKey", "matrixAccessToken", "matrixPassword", "matrixRecoveryPhrase", "secrets");
|
|
1219
|
+
}
|
|
1220
|
+
if (type === "qi/oracle.invoke") {
|
|
1221
|
+
input.push("prompt");
|
|
1222
|
+
output.push("result");
|
|
1223
|
+
}
|
|
1224
|
+
if (/^qi\/(form|human\.form|claim|bid)\./.test(type)) {
|
|
1225
|
+
input.push("answers", "surveyAnswers", "surveyData");
|
|
1226
|
+
output.push("answers", "surveyAnswers", "surveyData");
|
|
1227
|
+
}
|
|
1228
|
+
return { sensitiveInputPaths: [...new Set(input)].sort(), sensitiveOutputPaths: [...new Set(output)].sort() };
|
|
1229
|
+
}
|
|
1230
|
+
function topicKindsAndRelevance(type) {
|
|
1231
|
+
if (any(RESTRICTED, type)) return { supportedBaseKinds: ["agent_task"], relevance: "restricted" };
|
|
1232
|
+
if (/^qi\/flow\.run\./.test(type)) return { supportedBaseKinds: ALL_KINDS, relevance: "recommended" };
|
|
1233
|
+
if (/^qi\/(oracle\.invoke|agent\.)/.test(type)) return { supportedBaseKinds: ["agent_task", "question", "task"], relevance: "recommended" };
|
|
1234
|
+
if (/^qi\/(governance\.|proposal\.|topic\.decision)/.test(type)) return { supportedBaseKinds: ["proposal", "discussion", "evaluation"], relevance: "recommended" };
|
|
1235
|
+
if (/^qi\/(eval\.|evaluation\.)/.test(type)) return { supportedBaseKinds: ["evaluation", "claims"], relevance: "recommended" };
|
|
1236
|
+
if (/^qi\/(claim\.|collection\.|settlement\.)/.test(type)) return { supportedBaseKinds: ["claims", "evaluation"], relevance: "recommended" };
|
|
1237
|
+
if (/^qi\/(work\.)/.test(type)) return { supportedBaseKinds: ["task", "discussion", "incident"], relevance: "recommended" };
|
|
1238
|
+
if (/^qi\/(evidence\.|answer\.)/.test(type)) return { supportedBaseKinds: ["question", "evaluation"], relevance: "recommended" };
|
|
1239
|
+
if (/^qi\/incident\./.test(type)) return { supportedBaseKinds: ["incident"], relevance: "recommended" };
|
|
1240
|
+
if (/^qi\/topic\./.test(type)) return { supportedBaseKinds: ALL_KINDS, relevance: "recommended" };
|
|
1241
|
+
if (/^qi\/(http\.|domain\.card-preview|calendar\.|googlecalendar\.)/.test(type))
|
|
1242
|
+
return { supportedBaseKinds: ["question", "evaluation", "agent_task", "task"], relevance: "contextual" };
|
|
1243
|
+
if (/^qi\/(email\.|gmail\.|outlook\.|slack\.|matrix\.dm|notification\.)/.test(type)) {
|
|
1244
|
+
return { supportedBaseKinds: ["task", "question", "discussion", "incident"], relevance: "contextual" };
|
|
1245
|
+
}
|
|
1246
|
+
return { supportedBaseKinds: ALL_KINDS, relevance: "contextual" };
|
|
1247
|
+
}
|
|
1248
|
+
function topicSemanticRecords(type) {
|
|
1249
|
+
if (/^qi\/(governance\.|proposal\.)/.test(type)) return ["org.ixo.topic.proposal-receipt"];
|
|
1250
|
+
if (/^qi\/claim\.submit/.test(type)) return ["org.ixo.topic.claim-submission"];
|
|
1251
|
+
if (/^qi\/claim\.evaluate/.test(type)) return ["org.ixo.topic.claim-evaluation"];
|
|
1252
|
+
if (/^qi\/evaluation\./.test(type)) return ["org.ixo.topic.evaluation-assertion"];
|
|
1253
|
+
return [];
|
|
1254
|
+
}
|
|
1255
|
+
function normalizeActionPolicy(action) {
|
|
1256
|
+
const sensitive = sensitivePaths(action.type);
|
|
1257
|
+
const topicSelection = topicKindsAndRelevance(action.type);
|
|
1258
|
+
const semanticRecords = topicSemanticRecords(action.type);
|
|
1259
|
+
const declaredTopic = action.topic;
|
|
1260
|
+
const sensitiveInputPaths = [.../* @__PURE__ */ new Set([...action.sensitiveInputPaths || [], ...sensitive.sensitiveInputPaths])].sort();
|
|
1261
|
+
const sensitiveOutputPaths = [.../* @__PURE__ */ new Set([...action.sensitiveOutputPaths || [], ...sensitive.sensitiveOutputPaths])].sort();
|
|
1262
|
+
return {
|
|
1263
|
+
executionOwner: action.executionOwner || (any(HUMAN_OWNED, action.type) ? "human" : "agent"),
|
|
1264
|
+
riskTier: action.riskTier || riskTier(action),
|
|
1265
|
+
requiredServices: [...new Set(action.requiredServices || requiredServices(action.type))].sort(),
|
|
1266
|
+
sensitiveInputPaths,
|
|
1267
|
+
sensitiveOutputPaths,
|
|
1268
|
+
topic: declaredTopic ? {
|
|
1269
|
+
...declaredTopic,
|
|
1270
|
+
semanticRecordTypes: declaredTopic.semanticRecordTypes || getTopicSemanticRecordDefinitions(declaredTopic.permittedTopicRecordTypes),
|
|
1271
|
+
permittedTopicRecordTypes: (declaredTopic.semanticRecordTypes || getTopicSemanticRecordDefinitions(declaredTopic.permittedTopicRecordTypes)).map(
|
|
1272
|
+
(definition) => definition.type
|
|
1273
|
+
)
|
|
1274
|
+
} : {
|
|
1275
|
+
...topicSelection,
|
|
1276
|
+
writeBackMode: semanticRecords.length > 0 ? "semantic-record" : "receipt-only",
|
|
1277
|
+
semanticRecordTypes: getTopicSemanticRecordDefinitions(semanticRecords),
|
|
1278
|
+
permittedTopicRecordTypes: semanticRecords,
|
|
1279
|
+
lifecycleEffect: "none",
|
|
1280
|
+
requiredTopicAbilities: ["topic/request-action", "topic/record-action"],
|
|
1281
|
+
redactionPolicy: { mode: "paths", sensitiveInputPaths, sensitiveOutputPaths }
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
|
|
591
1286
|
// src/core/lib/actionRegistry/registry.ts
|
|
592
1287
|
var actions = /* @__PURE__ */ new Map();
|
|
1288
|
+
var registryRevision = 0;
|
|
1289
|
+
function getRegistryRevision() {
|
|
1290
|
+
return registryRevision;
|
|
1291
|
+
}
|
|
593
1292
|
var STEP_COMPLETED_EVENT_NAME = "step.completed";
|
|
594
1293
|
var STEP_COMPLETED_EVENT = {
|
|
595
1294
|
name: STEP_COMPLETED_EVENT_NAME,
|
|
@@ -604,6 +1303,7 @@ var neverDone = {
|
|
|
604
1303
|
isDone: () => false
|
|
605
1304
|
};
|
|
606
1305
|
var ACTION_TYPE_ALIASES = {
|
|
1306
|
+
oracle: "qi/oracle.invoke",
|
|
607
1307
|
bid: "qi/bid.submit",
|
|
608
1308
|
claim: "qi/claim.submit",
|
|
609
1309
|
evaluateBid: "qi/bid.evaluate",
|
|
@@ -627,7 +1327,16 @@ var ACTION_TYPE_ALIASES = {
|
|
|
627
1327
|
MemberMultiSelect: "qi/pod.member-multi-select",
|
|
628
1328
|
governanceConfig: "qi/pod.governance-config",
|
|
629
1329
|
listDomainFlows: "qi/pod.list-domain-flows",
|
|
630
|
-
"matrix.dm": "qi/matrix.dm"
|
|
1330
|
+
"matrix.dm": "qi/matrix.dm",
|
|
1331
|
+
// The qi/calendar.* namespace is retired permanently (IXO-4420 §5): these
|
|
1332
|
+
// blocks were always Google Calendar via Composio, misnamed as neutral.
|
|
1333
|
+
// Aliases keep every existing document loading; because resolveActionType
|
|
1334
|
+
// checks aliases before registered types, no new canonical action can ever
|
|
1335
|
+
// be registered under these names — the IXO-native calendar (M4) takes
|
|
1336
|
+
// qi/ixo.calendar.event.* instead.
|
|
1337
|
+
"qi/calendar.event.create": "qi/googlecalendar.event.create-self",
|
|
1338
|
+
"qi/calendar.event.update": "qi/googlecalendar.event.update-self",
|
|
1339
|
+
"qi/calendar.event.list": "qi/googlecalendar.event.list-self"
|
|
631
1340
|
};
|
|
632
1341
|
var aliases = new Map(Object.entries(ACTION_TYPE_ALIASES));
|
|
633
1342
|
function resolveActionType(type) {
|
|
@@ -678,13 +1387,17 @@ function capabilityPatternCoversCan(pattern, can, options = {}) {
|
|
|
678
1387
|
return false;
|
|
679
1388
|
}
|
|
680
1389
|
function registerAction(definition) {
|
|
681
|
-
const
|
|
682
|
-
|
|
1390
|
+
const presentation = getActionPresentation(definition.type);
|
|
1391
|
+
definition.displayName = definition.displayName?.trim() || presentation.displayName;
|
|
1392
|
+
definition.description = definition.description?.trim() || presentation.description;
|
|
1393
|
+
if (definition.can) definition.can = normalizeCan(definition.can);
|
|
1394
|
+
Object.assign(definition, normalizeActionPolicy(definition));
|
|
1395
|
+
if (!definition.done) {
|
|
683
1396
|
warnOnce(`missing-done-contract:${definition.type}`, `[flow-config] action ${definition.type}: no done contract declared; defaulting to state === 'completed'`);
|
|
684
|
-
|
|
685
|
-
return;
|
|
1397
|
+
definition.done = doneWhenCompleted;
|
|
686
1398
|
}
|
|
687
|
-
actions.set(definition.type,
|
|
1399
|
+
actions.set(definition.type, definition);
|
|
1400
|
+
registryRevision += 1;
|
|
688
1401
|
}
|
|
689
1402
|
function getAction(type) {
|
|
690
1403
|
return actions.get(resolveActionType(type));
|
|
@@ -770,6 +1483,8 @@ function normalizeInputs(inputs) {
|
|
|
770
1483
|
}
|
|
771
1484
|
|
|
772
1485
|
// src/core/lib/actionRegistry/manifest.ts
|
|
1486
|
+
var ACTION_MANIFEST_VERSION = "4";
|
|
1487
|
+
var ACTION_REGISTRY_VERSION = "6.32.0-topic-actions.3";
|
|
773
1488
|
function serializeProof(action) {
|
|
774
1489
|
const proof = action.proof;
|
|
775
1490
|
if (proof === "none" || proof === void 0) return { kind: "none" };
|
|
@@ -784,42 +1499,106 @@ function serializeDone(action) {
|
|
|
784
1499
|
cardinality: action.cardinality || "once"
|
|
785
1500
|
};
|
|
786
1501
|
}
|
|
787
|
-
function
|
|
788
|
-
const
|
|
1502
|
+
function serializeAction(action, aliases2) {
|
|
1503
|
+
const can = action.can || actionTypeToCan(action.type) || "";
|
|
1504
|
+
return {
|
|
1505
|
+
type: action.type,
|
|
1506
|
+
displayName: action.displayName,
|
|
1507
|
+
description: action.description,
|
|
1508
|
+
aliases: aliases2,
|
|
1509
|
+
can,
|
|
1510
|
+
effectiveCapability: {
|
|
1511
|
+
action: can,
|
|
1512
|
+
...action.requiredCapability ? { flowExecution: action.requiredCapability } : {},
|
|
1513
|
+
topicWriteBack: [...action.topic?.requiredTopicAbilities || []].sort()
|
|
1514
|
+
},
|
|
1515
|
+
sideEffect: action.sideEffect,
|
|
1516
|
+
defaultRequiresConfirmation: action.defaultRequiresConfirmation,
|
|
1517
|
+
executionOwner: action.executionOwner || "agent",
|
|
1518
|
+
riskTier: action.riskTier || "medium",
|
|
1519
|
+
requiredServices: [...action.requiredServices || []].sort(),
|
|
1520
|
+
sensitiveInputPaths: [...action.sensitiveInputPaths || []].sort(),
|
|
1521
|
+
sensitiveOutputPaths: [...action.sensitiveOutputPaths || []].sort(),
|
|
1522
|
+
hidden: action.hiddenFromAuthoring === true,
|
|
1523
|
+
deprecated: action.deprecated === true,
|
|
1524
|
+
...action.supersededBy ? { supersededBy: action.supersededBy } : {},
|
|
1525
|
+
proof: serializeProof(action),
|
|
1526
|
+
done: serializeDone(action),
|
|
1527
|
+
inputSchema: action.inputSchema || {},
|
|
1528
|
+
outputSchema: action.outputSchema || [],
|
|
1529
|
+
events: (action.events || []).map((event) => ({
|
|
1530
|
+
name: event.name,
|
|
1531
|
+
displayName: event.displayName,
|
|
1532
|
+
description: event.description,
|
|
1533
|
+
payloadSchema: event.payloadSchema
|
|
1534
|
+
})),
|
|
1535
|
+
hasDynamicEvents: !!action.getDynamicEvents,
|
|
1536
|
+
hasDynamicOutputSchema: !!action.getDynamicOutputSchema,
|
|
1537
|
+
eligibleForEventTrigger: !!action.eligibleForEventTrigger,
|
|
1538
|
+
eligibleForTimeTrigger: !!action.eligibleForTimeTrigger,
|
|
1539
|
+
...action.scheduling ? { scheduling: action.scheduling } : {},
|
|
1540
|
+
hasCustomInputValidation: !!action.getMissingInputs,
|
|
1541
|
+
topic: action.topic
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
function contractDigestPayload(entry) {
|
|
1545
|
+
const { displayName: _displayName, description: _description, topic, ...contract } = entry;
|
|
1546
|
+
const { semanticRecordTypes, ...legacyTopic } = topic;
|
|
1547
|
+
const semanticContracts = semanticRecordTypes.map(({ displayName: _recordDisplayName, description: _recordDescription, ...definition }) => definition);
|
|
1548
|
+
return {
|
|
1549
|
+
...contract,
|
|
1550
|
+
topic: semanticContracts.length > 0 ? { ...legacyTopic, semanticRecordTypes: semanticContracts } : legacyTopic
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
function buildActionManifest() {
|
|
789
1554
|
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
|
-
|
|
1555
|
+
for (const [alias, canonical] of getAliasEntries()) {
|
|
1556
|
+
aliasesByType.set(canonical, [...aliasesByType.get(canonical) || [], alias]);
|
|
1557
|
+
}
|
|
1558
|
+
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)) }));
|
|
1559
|
+
const payload = { manifestVersion: ACTION_MANIFEST_VERSION, registryVersion: ACTION_REGISTRY_VERSION, actions: actions2 };
|
|
1560
|
+
return { ...payload, manifestDigest: sha256Digest(payload) };
|
|
1561
|
+
}
|
|
1562
|
+
var cached;
|
|
1563
|
+
function generateActionManifest() {
|
|
1564
|
+
const revision = getRegistryRevision();
|
|
1565
|
+
if (cached && cached.revision === revision) return cached.manifest;
|
|
1566
|
+
const manifest = buildActionManifest();
|
|
1567
|
+
cached = { revision, manifest };
|
|
1568
|
+
return manifest;
|
|
1569
|
+
}
|
|
1570
|
+
function actionManifestIssues(manifest = generateActionManifest()) {
|
|
1571
|
+
const issues = [];
|
|
1572
|
+
for (const action of manifest.actions) {
|
|
1573
|
+
const publicAction = !action.hidden;
|
|
1574
|
+
if (!action.can) issues.push({ actionType: action.type, code: "MISSING_CAN", message: "Action has no canonical can ability." });
|
|
1575
|
+
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)) {
|
|
1576
|
+
issues.push({ actionType: action.type, code: "MISSING_PRESENTATION", message: "Action needs a non-technical display name and description." });
|
|
1577
|
+
}
|
|
1578
|
+
if (publicAction && Object.keys(action.inputSchema).length === 0) {
|
|
1579
|
+
issues.push({ actionType: action.type, code: "MISSING_INPUT_SCHEMA", message: "Public Action has no input schema." });
|
|
1580
|
+
}
|
|
1581
|
+
if (publicAction && action.outputSchema.length === 0 && !action.hasDynamicOutputSchema) {
|
|
1582
|
+
issues.push({ actionType: action.type, code: "MISSING_OUTPUT_SCHEMA", message: "Public Action has no output schema." });
|
|
1583
|
+
}
|
|
1584
|
+
if (action.sideEffect && action.proof.kind === "none") {
|
|
1585
|
+
issues.push({ actionType: action.type, code: "SIDE_EFFECT_WITHOUT_PROOF", message: "Side-effecting Action declares no proof." });
|
|
1586
|
+
}
|
|
1587
|
+
if (!action.topic) issues.push({ actionType: action.type, code: "MISSING_TOPIC_POLICY", message: "Action has no Topic compatibility policy." });
|
|
1588
|
+
else if (action.topic.lifecycleEffect !== "none" || action.topic.supportedBaseKinds.length === 0) {
|
|
1589
|
+
issues.push({ actionType: action.type, code: "INVALID_TOPIC_POLICY", message: "Topic policy must support a base Kind and cannot imply lifecycle effects." });
|
|
1590
|
+
}
|
|
1591
|
+
if (action.topic.semanticRecordTypes.some(
|
|
1592
|
+
(definition) => !definition.type || !definition.version || !definition.displayName.trim() || !definition.description.trim() || Object.keys(definition.valueSchema).length === 0
|
|
1593
|
+
) || action.topic.permittedTopicRecordTypes.join("|") !== action.topic.semanticRecordTypes.map((definition) => definition.type).join("|")) {
|
|
1594
|
+
issues.push({
|
|
1595
|
+
actionType: action.type,
|
|
1596
|
+
code: "INVALID_SEMANTIC_RECORD_DEFINITION",
|
|
1597
|
+
message: "Topic semantic record definitions must be complete and determine the compatibility type list."
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
return issues;
|
|
823
1602
|
}
|
|
824
1603
|
|
|
825
1604
|
// src/core/lib/actionRegistry/inputRequirements.ts
|
|
@@ -938,7 +1717,8 @@ function buildServicesFromHandlers(handlers) {
|
|
|
938
1717
|
request: async (params) => {
|
|
939
1718
|
const fetchOptions = {
|
|
940
1719
|
method: params.method,
|
|
941
|
-
headers: { "Content-Type": "application/json", ...params.headers }
|
|
1720
|
+
headers: { "Content-Type": "application/json", ...params.headers },
|
|
1721
|
+
redirect: "error"
|
|
942
1722
|
};
|
|
943
1723
|
if (params.method !== "GET" && params.body) {
|
|
944
1724
|
fetchOptions.body = typeof params.body === "string" ? params.body : JSON.stringify(params.body);
|
|
@@ -952,7 +1732,9 @@ function buildServicesFromHandlers(handlers) {
|
|
|
952
1732
|
return {
|
|
953
1733
|
status: res.status,
|
|
954
1734
|
headers: responseHeaders,
|
|
955
|
-
data
|
|
1735
|
+
data,
|
|
1736
|
+
responseDigest: sha256Digest(data),
|
|
1737
|
+
requestId: sha256Digest({ url: params.url, method: params.method, status: res.status, data })
|
|
956
1738
|
};
|
|
957
1739
|
}
|
|
958
1740
|
},
|
|
@@ -1323,6 +2105,29 @@ function markXeroWorkCompletedForEditor(editor, itemId, params) {
|
|
|
1323
2105
|
|
|
1324
2106
|
// src/core/lib/flowCompiler/connections.ts
|
|
1325
2107
|
import * as Y from "yjs";
|
|
2108
|
+
|
|
2109
|
+
// src/core/lib/yjsTypes.ts
|
|
2110
|
+
function getExistingYMap(yDoc, key) {
|
|
2111
|
+
if (!yDoc.share.has(key)) return void 0;
|
|
2112
|
+
return yDoc.getMap(key);
|
|
2113
|
+
}
|
|
2114
|
+
function isYMapLike(value) {
|
|
2115
|
+
if (!value || typeof value !== "object") return false;
|
|
2116
|
+
const candidate = value;
|
|
2117
|
+
return typeof candidate.get === "function" && typeof candidate.set === "function" && typeof candidate.has === "function" && typeof candidate.forEach === "function";
|
|
2118
|
+
}
|
|
2119
|
+
function isYXmlElementLike(value) {
|
|
2120
|
+
if (!value || typeof value !== "object") return false;
|
|
2121
|
+
const candidate = value;
|
|
2122
|
+
return typeof candidate.nodeName === "string" && typeof candidate.getAttribute === "function" && typeof candidate.getAttributes === "function";
|
|
2123
|
+
}
|
|
2124
|
+
function isYDocLike(value) {
|
|
2125
|
+
if (!value || typeof value !== "object") return false;
|
|
2126
|
+
const candidate = value;
|
|
2127
|
+
return typeof candidate.getMap === "function" && typeof candidate.getXmlFragment === "function" && typeof candidate.transact === "function";
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
// src/core/lib/flowCompiler/connections.ts
|
|
1326
2131
|
var FLOW_CONNECTIONS_MAP_KEY = "qi.flow.connections";
|
|
1327
2132
|
var FLOW_CONNECTION_BINDINGS_MAP_KEY = "qi.flow.connectionBindings";
|
|
1328
2133
|
var REQUIREMENT_KEYS = ["org", "bankAccount"];
|
|
@@ -1361,13 +2166,13 @@ function removeFlowConnection(yDoc, toolkit) {
|
|
|
1361
2166
|
function setFlowConnectionOptional(yDoc, toolkit, optional) {
|
|
1362
2167
|
yDoc.transact(() => {
|
|
1363
2168
|
const entry = getConnectionsMap(yDoc).get(toolkit);
|
|
1364
|
-
if (entry
|
|
2169
|
+
if (isYMapLike(entry)) mergeOptionalFlag(entry, "optional", optional);
|
|
1365
2170
|
});
|
|
1366
2171
|
}
|
|
1367
2172
|
function setFlowConnectionRequires(yDoc, toolkit, requires) {
|
|
1368
2173
|
yDoc.transact(() => {
|
|
1369
2174
|
const entry = getConnectionsMap(yDoc).get(toolkit);
|
|
1370
|
-
if (entry
|
|
2175
|
+
if (isYMapLike(entry)) entry.set("requires", toRequirementKeys(requires));
|
|
1371
2176
|
});
|
|
1372
2177
|
}
|
|
1373
2178
|
function readFlowConnectionBindings(yDoc) {
|
|
@@ -1414,13 +2219,13 @@ function getBindingsMap(yDoc) {
|
|
|
1414
2219
|
}
|
|
1415
2220
|
function ensureEntry(map, toolkit) {
|
|
1416
2221
|
const existing = map.get(toolkit);
|
|
1417
|
-
if (existing
|
|
2222
|
+
if (isYMapLike(existing)) return existing;
|
|
1418
2223
|
const created = new Y.Map();
|
|
1419
2224
|
map.set(toolkit, created);
|
|
1420
2225
|
return created;
|
|
1421
2226
|
}
|
|
1422
2227
|
function toConnection(value) {
|
|
1423
|
-
if (!(value
|
|
2228
|
+
if (!isYMapLike(value)) return void 0;
|
|
1424
2229
|
const toolkit = value.get("toolkit");
|
|
1425
2230
|
if (typeof toolkit !== "string" || toolkit.length === 0) return void 0;
|
|
1426
2231
|
const connection = { toolkit, requires: toRequirementKeys(value.get("requires")) };
|
|
@@ -1432,7 +2237,7 @@ function toConnection(value) {
|
|
|
1432
2237
|
return connection;
|
|
1433
2238
|
}
|
|
1434
2239
|
function toBinding(value) {
|
|
1435
|
-
if (!(value
|
|
2240
|
+
if (!isYMapLike(value)) return void 0;
|
|
1436
2241
|
const toolkit = toNonEmptyString(value.get("toolkit"));
|
|
1437
2242
|
const connectedAccountId = toNonEmptyString(value.get("connectedAccountId"));
|
|
1438
2243
|
const entityDid = toNonEmptyString(value.get("entityDid"));
|
|
@@ -2212,6 +3017,182 @@ for (const spec of ACTIONS) {
|
|
|
2212
3017
|
});
|
|
2213
3018
|
}
|
|
2214
3019
|
|
|
3020
|
+
// src/core/lib/actionRegistry/actions/governance/_shared.ts
|
|
3021
|
+
var GOVERNANCE_REQUIRED_FIELDS = {
|
|
3022
|
+
"qi/governance.authz.exec": ["authzExecActionType"],
|
|
3023
|
+
"qi/governance.authz.grant": ["grantee", "msgTypeUrl"],
|
|
3024
|
+
"qi/governance.authz.revoke": ["grantee", "msgTypeUrl"],
|
|
3025
|
+
"qi/governance.chain-governance-vote": ["proposalId", "vote"],
|
|
3026
|
+
"qi/governance.contract.execute": ["address", "message"],
|
|
3027
|
+
"qi/governance.contract.instantiate": ["codeId", "label", "message"],
|
|
3028
|
+
"qi/governance.contract.manage-cw20": ["adding", "address"],
|
|
3029
|
+
"qi/governance.contract.migrate": ["contract", "codeId", "msg"],
|
|
3030
|
+
"qi/governance.contract.update-admin": ["contract", "newAdmin"],
|
|
3031
|
+
"qi/governance.custom-message": ["message"],
|
|
3032
|
+
"qi/governance.dao.accept-to-marketplace": ["did", "relayerNodeAddress", "relayerNodeDid"],
|
|
3033
|
+
"qi/governance.dao.admin-exec": ["targetCoreAddress", "msgs"],
|
|
3034
|
+
"qi/governance.dao.create-entity": ["typeUrl", "value"],
|
|
3035
|
+
"qi/governance.dao.join": ["entityDid", "memberId"],
|
|
3036
|
+
"qi/governance.dao.manage-storage": ["setting", "key", "value"],
|
|
3037
|
+
"qi/governance.dao.manage-subdaos": [],
|
|
3038
|
+
"qi/governance.dao.update-info": ["name"],
|
|
3039
|
+
"qi/governance.member-proposal": ["operation", "members"],
|
|
3040
|
+
"qi/governance.nft.burn": ["collection", "tokenId"],
|
|
3041
|
+
"qi/governance.nft.manage-collections": ["adding", "address"],
|
|
3042
|
+
"qi/governance.nft.transfer": ["collection", "tokenId", "recipient"],
|
|
3043
|
+
"qi/governance.staking.stake": ["stakeType", "amount"],
|
|
3044
|
+
"qi/governance.staking.stake-to-group": ["tokenContract", "stakingContract", "amount"],
|
|
3045
|
+
"qi/governance.settings-proposal": ["votingPeriodHours", "quorumPercent", "thresholdPercent"],
|
|
3046
|
+
"qi/governance.submission-config-proposal": ["anyoneCanPropose", "depositRequired"],
|
|
3047
|
+
"qi/governance.transaction.mint": ["recipient", "amount"],
|
|
3048
|
+
"qi/governance.transaction.send-funds": ["recipient", "denom", "amount"],
|
|
3049
|
+
"qi/governance.transaction.perform-token-swap": ["tokenSwapContractAddress", "selfPartyType", "selfPartyDenomOrAddress", "selfPartyAmount"],
|
|
3050
|
+
"qi/governance.transaction.send-group-token": ["tokenContract", "recipient", "amount"],
|
|
3051
|
+
"qi/governance.transaction.withdraw-token-swap": ["tokenSwapContractAddress"],
|
|
3052
|
+
"qi/governance.validator.actions": ["validatorActionType"]
|
|
3053
|
+
};
|
|
3054
|
+
var GOVERNANCE_FIELDS = {
|
|
3055
|
+
"qi/governance.authz.exec": ["authzExecActionType", "delegatorAddress", "validatorAddress", "validatorDstAddress", "amount", "custom"],
|
|
3056
|
+
"qi/governance.authz.grant": ["grantee", "msgTypeUrl"],
|
|
3057
|
+
"qi/governance.authz.revoke": ["grantee", "msgTypeUrl"],
|
|
3058
|
+
"qi/governance.chain-governance-vote": ["proposalId", "vote"],
|
|
3059
|
+
"qi/governance.contract.execute": ["address", "message", "funds"],
|
|
3060
|
+
"qi/governance.contract.instantiate": ["codeId", "label", "admin", "message", "funds"],
|
|
3061
|
+
"qi/governance.contract.manage-cw20": ["adding", "address"],
|
|
3062
|
+
"qi/governance.contract.migrate": ["contract", "codeId", "msg"],
|
|
3063
|
+
"qi/governance.contract.update-admin": ["contract", "newAdmin"],
|
|
3064
|
+
"qi/governance.custom-message": ["message"],
|
|
3065
|
+
"qi/governance.dao.accept-to-marketplace": ["did", "relayerNodeAddress", "relayerNodeDid"],
|
|
3066
|
+
"qi/governance.dao.admin-exec": ["targetCoreAddress", "msgs"],
|
|
3067
|
+
"qi/governance.dao.create-entity": ["typeUrl", "value"],
|
|
3068
|
+
"qi/governance.dao.join": ["entityDid", "memberId"],
|
|
3069
|
+
"qi/governance.dao.manage-storage": ["setting", "key", "value"],
|
|
3070
|
+
"qi/governance.dao.manage-subdaos": ["toAdd", "toRemove"],
|
|
3071
|
+
"qi/governance.dao.update-info": ["name", "daoDescription", "imageUrl", "automaticallyAddCw20s", "automaticallyAddCw721s"],
|
|
3072
|
+
"qi/governance.member-proposal": ["operation", "members"],
|
|
3073
|
+
"qi/governance.nft.burn": ["collection", "tokenId"],
|
|
3074
|
+
"qi/governance.nft.manage-collections": ["adding", "address"],
|
|
3075
|
+
"qi/governance.nft.transfer": ["collection", "tokenId", "recipient", "executeSmartContract", "smartContractMsg"],
|
|
3076
|
+
"qi/governance.staking.stake": ["stakeType", "validator", "toValidator", "amount"],
|
|
3077
|
+
"qi/governance.staking.stake-to-group": ["tokenContract", "stakingContract", "amount"],
|
|
3078
|
+
"qi/governance.settings-proposal": ["votingPeriodHours", "quorumPercent", "thresholdPercent", "allowRevoting"],
|
|
3079
|
+
"qi/governance.submission-config-proposal": ["anyoneCanPropose", "depositRequired", "depositAmount", "depositRefundPolicy"],
|
|
3080
|
+
"qi/governance.transaction.mint": ["recipient", "amount"],
|
|
3081
|
+
"qi/governance.transaction.send-funds": ["recipient", "denom", "amount"],
|
|
3082
|
+
"qi/governance.transaction.perform-token-swap": ["tokenSwapContractAddress", "selfPartyType", "selfPartyDenomOrAddress", "selfPartyAmount"],
|
|
3083
|
+
"qi/governance.transaction.send-group-token": ["tokenContract", "recipient", "amount"],
|
|
3084
|
+
"qi/governance.transaction.withdraw-token-swap": ["tokenSwapContractAddress"],
|
|
3085
|
+
"qi/governance.validator.actions": ["validatorActionType", "createMsg", "editMsg"]
|
|
3086
|
+
};
|
|
3087
|
+
var BOOLEAN_FIELDS = /* @__PURE__ */ new Set(["adding", "automaticallyAddCw20s", "automaticallyAddCw721s", "executeSmartContract", "anyoneCanPropose", "depositRequired", "allowRevoting"]);
|
|
3088
|
+
var NUMBER_FIELDS = /* @__PURE__ */ new Set(["codeId", "vote", "votingPeriodHours", "quorumPercent", "thresholdPercent"]);
|
|
3089
|
+
var ARRAY_FIELDS = /* @__PURE__ */ new Set(["funds", "msgs", "toAdd", "toRemove", "members"]);
|
|
3090
|
+
var OBJECT_FIELDS = /* @__PURE__ */ new Set(["value"]);
|
|
3091
|
+
function governanceProperty(name) {
|
|
3092
|
+
if (BOOLEAN_FIELDS.has(name)) return { type: "boolean" };
|
|
3093
|
+
if (NUMBER_FIELDS.has(name)) return { type: "number" };
|
|
3094
|
+
if (ARRAY_FIELDS.has(name)) return { type: "array", items: {} };
|
|
3095
|
+
if (OBJECT_FIELDS.has(name)) return { type: "object" };
|
|
3096
|
+
return { type: "string" };
|
|
3097
|
+
}
|
|
3098
|
+
function governanceInputSchema(type, extraFields = {}, requiredOverride) {
|
|
3099
|
+
const properties = {
|
|
3100
|
+
coreAddress: { type: "string", description: "DAO core contract address." },
|
|
3101
|
+
title: { type: "string", description: "Proposal title voters see." },
|
|
3102
|
+
description: { type: "string", description: "Long-form proposal description voters see." }
|
|
3103
|
+
};
|
|
3104
|
+
for (const field of GOVERNANCE_FIELDS[type] || []) properties[field] = governanceProperty(field);
|
|
3105
|
+
Object.assign(properties, extraFields);
|
|
3106
|
+
return {
|
|
3107
|
+
type: "object",
|
|
3108
|
+
required: ["coreAddress", ...requiredOverride || GOVERNANCE_REQUIRED_FIELDS[type] || []],
|
|
3109
|
+
additionalProperties: false,
|
|
3110
|
+
properties
|
|
3111
|
+
};
|
|
3112
|
+
}
|
|
3113
|
+
var STANDARD_OUTPUT_SCHEMA = [
|
|
3114
|
+
{ path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
|
|
3115
|
+
{ path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
|
|
3116
|
+
{ path: "proposalDescription", displayName: "Proposal Description", type: "string", description: "The long-form description voters see on-chain" },
|
|
3117
|
+
{ path: "status", displayName: "Proposal Status", type: "string", description: "Current proposal status (open, passed, rejected, executed, etc.)" },
|
|
3118
|
+
{ path: "proposalContractAddress", displayName: "Proposal Contract Address", type: "string", description: "The proposal module contract address" },
|
|
3119
|
+
{ path: "coreAddress", displayName: "Core Address", type: "string", description: "The DAO core contract address" },
|
|
3120
|
+
{ path: "createdAt", displayName: "Created At", type: "string", description: "ISO timestamp of proposal creation" }
|
|
3121
|
+
];
|
|
3122
|
+
function registerGovernanceProposalAction(spec) {
|
|
3123
|
+
registerAction({
|
|
3124
|
+
type: spec.type,
|
|
3125
|
+
can: spec.can,
|
|
3126
|
+
sideEffect: true,
|
|
3127
|
+
proof: { fields: ["proposalId"] },
|
|
3128
|
+
done: doneWhenCompleted,
|
|
3129
|
+
defaultRequiresConfirmation: true,
|
|
3130
|
+
requiredCapability: "flow/block/execute",
|
|
3131
|
+
inputSchema: governanceInputSchema(spec.type),
|
|
3132
|
+
outputSchema: [...STANDARD_OUTPUT_SCHEMA, ...spec.extraOutputSchema || []],
|
|
3133
|
+
run: async (inputs, ctx) => {
|
|
3134
|
+
const handlers = ctx.handlers;
|
|
3135
|
+
if (!handlers) {
|
|
3136
|
+
throw new Error("Handlers not available");
|
|
3137
|
+
}
|
|
3138
|
+
if (!handlers.getPreProposalContractAddress || !handlers.getGroupContractAddress || !handlers.getProposalContractAddress || !handlers.createProposal) {
|
|
3139
|
+
throw new Error("Governance proposal handlers not available");
|
|
3140
|
+
}
|
|
3141
|
+
const coreAddress = String(inputs.coreAddress || "").trim();
|
|
3142
|
+
if (!coreAddress) throw new Error("coreAddress is required");
|
|
3143
|
+
const actions2 = spec.buildActions(inputs);
|
|
3144
|
+
if (!actions2.length) throw new Error("The proposal must contain at least one action");
|
|
3145
|
+
const title = String(inputs.title || "").trim() || spec.defaultTitle(inputs);
|
|
3146
|
+
const description = String(inputs.description || "").trim() || (spec.defaultDescription ? spec.defaultDescription(inputs) : title);
|
|
3147
|
+
const { preProposalContractAddress } = await handlers.getPreProposalContractAddress({ coreAddress });
|
|
3148
|
+
const { groupContractAddress } = await handlers.getGroupContractAddress({ coreAddress });
|
|
3149
|
+
const { proposalContractAddress } = await handlers.getProposalContractAddress({ coreAddress });
|
|
3150
|
+
const proposalId = await handlers.createProposal({
|
|
3151
|
+
preProposalContractAddress,
|
|
3152
|
+
title,
|
|
3153
|
+
description,
|
|
3154
|
+
actions: actions2,
|
|
3155
|
+
coreAddress,
|
|
3156
|
+
groupContractAddress
|
|
3157
|
+
});
|
|
3158
|
+
if (proposalId === void 0 || proposalId === null || String(proposalId).trim() === "") {
|
|
3159
|
+
throw new Error("Proposal creation returned no proposal id. Check the handler logs.");
|
|
3160
|
+
}
|
|
3161
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3162
|
+
const output = {
|
|
3163
|
+
proposalId: String(proposalId),
|
|
3164
|
+
proposalTitle: title,
|
|
3165
|
+
proposalDescription: description,
|
|
3166
|
+
status: "open",
|
|
3167
|
+
proposalContractAddress: proposalContractAddress || "",
|
|
3168
|
+
coreAddress,
|
|
3169
|
+
createdAt,
|
|
3170
|
+
...spec.buildExtraOutput ? spec.buildExtraOutput(inputs) : {}
|
|
3171
|
+
};
|
|
3172
|
+
return {
|
|
3173
|
+
output,
|
|
3174
|
+
topicRecords: ctx.topic ? [
|
|
3175
|
+
{
|
|
3176
|
+
type: "org.ixo.topic.proposal-receipt",
|
|
3177
|
+
id: sha256Digest({ topicId: ctx.topic.topicId, proposalContractAddress, proposalId }),
|
|
3178
|
+
version: 1,
|
|
3179
|
+
value: {
|
|
3180
|
+
event: "created",
|
|
3181
|
+
actionType: spec.type,
|
|
3182
|
+
proposalId: String(proposalId),
|
|
3183
|
+
proposalContractAddress: proposalContractAddress || "",
|
|
3184
|
+
coreAddress,
|
|
3185
|
+
proposalTitle: title,
|
|
3186
|
+
proposalDescriptionDigest: sha256Digest(description),
|
|
3187
|
+
createdAt
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
] : void 0
|
|
3191
|
+
};
|
|
3192
|
+
}
|
|
3193
|
+
});
|
|
3194
|
+
}
|
|
3195
|
+
|
|
2215
3196
|
// src/core/lib/actionRegistry/actions/governance/memberProposal.ts
|
|
2216
3197
|
var VALID_OPERATIONS = ["add", "remove", "update-weight"];
|
|
2217
3198
|
function defaultTitle(operation, count) {
|
|
@@ -2233,6 +3214,7 @@ registerAction({
|
|
|
2233
3214
|
done: doneWhenCompleted,
|
|
2234
3215
|
defaultRequiresConfirmation: true,
|
|
2235
3216
|
requiredCapability: "flow/block/execute",
|
|
3217
|
+
inputSchema: governanceInputSchema("qi/governance.member-proposal"),
|
|
2236
3218
|
outputSchema: [
|
|
2237
3219
|
{ path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
|
|
2238
3220
|
{ path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
|
|
@@ -2333,6 +3315,7 @@ registerAction({
|
|
|
2333
3315
|
done: doneWhenCompleted,
|
|
2334
3316
|
defaultRequiresConfirmation: true,
|
|
2335
3317
|
requiredCapability: "flow/block/execute",
|
|
3318
|
+
inputSchema: governanceInputSchema("qi/governance.settings-proposal"),
|
|
2336
3319
|
outputSchema: [
|
|
2337
3320
|
{ path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
|
|
2338
3321
|
{ path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
|
|
@@ -2414,70 +3397,6 @@ registerAction({
|
|
|
2414
3397
|
}
|
|
2415
3398
|
});
|
|
2416
3399
|
|
|
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
3400
|
// src/core/lib/actionRegistry/actions/governance/submissionConfigProposal.ts
|
|
2482
3401
|
var REFUND_POLICIES = ["always", "only_passed", "never"];
|
|
2483
3402
|
registerGovernanceProposalAction({
|
|
@@ -2539,6 +3458,7 @@ registerAction({
|
|
|
2539
3458
|
done: doneWhenCompleted,
|
|
2540
3459
|
defaultRequiresConfirmation: true,
|
|
2541
3460
|
requiredCapability: "flow/block/execute",
|
|
3461
|
+
inputSchema: governanceInputSchema("qi/governance.transaction.send-funds"),
|
|
2542
3462
|
outputSchema: [
|
|
2543
3463
|
{ path: "proposalId", displayName: "Proposal ID", type: "string", description: "The on-chain proposal identifier" },
|
|
2544
3464
|
{ path: "proposalTitle", displayName: "Proposal Title", type: "string", description: "The proposal name voters see on-chain" },
|
|
@@ -3458,71 +4378,162 @@ registerGovernanceProposalAction({
|
|
|
3458
4378
|
});
|
|
3459
4379
|
|
|
3460
4380
|
// src/core/lib/actionRegistry/actions/httpRequest.ts
|
|
4381
|
+
var SENSITIVE_HEADERS = /* @__PURE__ */ new Set(["authorization", "cookie", "proxy-authorization", "x-api-key"]);
|
|
4382
|
+
var MUTATING_METHODS = ["POST", "PUT", "PATCH", "DELETE"];
|
|
4383
|
+
var REQUEST_METHODS = [...MUTATING_METHODS, "GET", "HEAD"];
|
|
4384
|
+
function publicHttpUrl(raw) {
|
|
4385
|
+
const value = String(raw || "").trim();
|
|
4386
|
+
let url;
|
|
4387
|
+
try {
|
|
4388
|
+
url = new URL(value);
|
|
4389
|
+
} catch {
|
|
4390
|
+
throw new Error("HTTP endpoint must be an absolute URL");
|
|
4391
|
+
}
|
|
4392
|
+
if (url.protocol !== "https:") throw new Error("HTTP Actions require an HTTPS endpoint");
|
|
4393
|
+
if (url.username || url.password) throw new Error("Credentials must not be embedded in an HTTP endpoint");
|
|
4394
|
+
const host = url.hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
4395
|
+
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") {
|
|
4396
|
+
throw new Error("HTTP endpoint must not resolve to a local, private, link-local, or metadata address");
|
|
4397
|
+
}
|
|
4398
|
+
return url.toString();
|
|
4399
|
+
}
|
|
4400
|
+
function safeHeaders(raw) {
|
|
4401
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
4402
|
+
const headers = {};
|
|
4403
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
4404
|
+
if (SENSITIVE_HEADERS.has(key.toLowerCase())) {
|
|
4405
|
+
throw new Error(`Sensitive header '${key}' must be supplied through a host-managed credential binding, not Action input`);
|
|
4406
|
+
}
|
|
4407
|
+
headers[key] = String(value);
|
|
4408
|
+
}
|
|
4409
|
+
return headers;
|
|
4410
|
+
}
|
|
4411
|
+
var INPUT_PROPERTIES = {
|
|
4412
|
+
endpoint: { type: "string", format: "uri", description: "Public HTTPS request URL. Either endpoint or url is required." },
|
|
4413
|
+
url: { type: "string", format: "uri", description: "Legacy alias for endpoint." },
|
|
4414
|
+
method: { type: "string", description: "HTTP method." },
|
|
4415
|
+
headers: { type: "object", additionalProperties: { type: "string" }, description: "Non-secret request headers. Authorization and cookies are rejected." },
|
|
4416
|
+
body: { description: "Request body for a mutating HTTP request." }
|
|
4417
|
+
};
|
|
4418
|
+
var OUTPUT_SCHEMA2 = [
|
|
4419
|
+
{ path: "requestId", displayName: "Request ID", type: "string", description: "Host invocation identifier." },
|
|
4420
|
+
{ path: "status", displayName: "HTTP Status", type: "number" },
|
|
4421
|
+
{ path: "responseDigest", displayName: "Response Digest", type: "string", description: "Digest of the response body." },
|
|
4422
|
+
{ path: "data", displayName: "Response Data", type: "object", description: "Full result retained in the Flow timeline, not copied into Topic state." },
|
|
4423
|
+
{ path: "response", displayName: "Response JSON", type: "string" },
|
|
4424
|
+
{ path: "traceReference", displayName: "Trace Reference", type: "string" }
|
|
4425
|
+
];
|
|
4426
|
+
function missingEndpoint(inputs) {
|
|
4427
|
+
return String(inputs.endpoint || inputs.url || "").trim() ? [] : ["endpoint"];
|
|
4428
|
+
}
|
|
3461
4429
|
registerAction({
|
|
3462
|
-
type: "qi/http.
|
|
3463
|
-
can: "http/
|
|
4430
|
+
type: "qi/http.fetch",
|
|
4431
|
+
can: "http/fetch",
|
|
3464
4432
|
sideEffect: false,
|
|
3465
|
-
proof: { fields: ["
|
|
4433
|
+
proof: { fields: ["responseDigest"] },
|
|
3466
4434
|
done: doneWhenCompleted,
|
|
3467
4435
|
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
4436
|
eligibleForEventTrigger: true,
|
|
3472
4437
|
inputSchema: {
|
|
3473
4438
|
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
|
-
}
|
|
4439
|
+
required: [],
|
|
4440
|
+
additionalProperties: false,
|
|
4441
|
+
properties: { ...INPUT_PROPERTIES, method: { type: "string", enum: ["GET", "HEAD"], default: "GET" } }
|
|
3482
4442
|
},
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
getMissingInputs: (inputs) => String(inputs.endpoint || inputs.url || "").trim() ? [] : ["endpoint"],
|
|
4443
|
+
getMissingInputs: missingEndpoint,
|
|
4444
|
+
outputSchema: OUTPUT_SCHEMA2,
|
|
3486
4445
|
run: async (inputs, ctx) => {
|
|
3487
|
-
const
|
|
3488
|
-
const
|
|
3489
|
-
if (
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
4446
|
+
const url = publicHttpUrl(inputs.endpoint ?? inputs.url);
|
|
4447
|
+
const method = String(inputs.method || "GET").toUpperCase();
|
|
4448
|
+
if (method !== "GET" && method !== "HEAD") throw new Error("qi/http.fetch permits GET or HEAD only");
|
|
4449
|
+
const headers = safeHeaders(inputs.headers);
|
|
4450
|
+
const service = ctx.services.http;
|
|
4451
|
+
if (service) {
|
|
4452
|
+
const result = await service.request({
|
|
4453
|
+
url,
|
|
4454
|
+
method,
|
|
4455
|
+
headers,
|
|
4456
|
+
security: { denyPrivateNetworks: true, maxRedirects: 0, stripSensitiveHeadersOnRedirect: true }
|
|
4457
|
+
});
|
|
4458
|
+
const responseDigest2 = result.responseDigest || sha256Digest(result.data);
|
|
3498
4459
|
return {
|
|
3499
4460
|
output: {
|
|
4461
|
+
requestId: result.requestId || responseDigest2,
|
|
3500
4462
|
status: result.status,
|
|
4463
|
+
responseDigest: responseDigest2,
|
|
3501
4464
|
data: result.data,
|
|
3502
|
-
response: JSON.stringify(result.data, null, 2)
|
|
4465
|
+
response: JSON.stringify(result.data, null, 2),
|
|
4466
|
+
traceReference: result.traceReference || ""
|
|
3503
4467
|
}
|
|
3504
4468
|
};
|
|
3505
4469
|
}
|
|
3506
|
-
const
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
4470
|
+
const response = await fetch(url, { method, headers, redirect: "error" });
|
|
4471
|
+
const text = method === "HEAD" ? "" : await response.text();
|
|
4472
|
+
let data = text;
|
|
4473
|
+
try {
|
|
4474
|
+
data = text ? JSON.parse(text) : {};
|
|
4475
|
+
} catch {
|
|
3512
4476
|
}
|
|
3513
|
-
const
|
|
3514
|
-
const data = await response.json().catch(() => ({}));
|
|
4477
|
+
const responseDigest = sha256Digest(data);
|
|
3515
4478
|
return {
|
|
3516
4479
|
output: {
|
|
4480
|
+
requestId: responseDigest,
|
|
3517
4481
|
status: response.status,
|
|
4482
|
+
responseDigest,
|
|
3518
4483
|
data,
|
|
3519
|
-
response: JSON.stringify(data, null, 2)
|
|
4484
|
+
response: typeof data === "string" ? data : JSON.stringify(data, null, 2),
|
|
4485
|
+
traceReference: ""
|
|
3520
4486
|
}
|
|
3521
4487
|
};
|
|
3522
4488
|
}
|
|
3523
4489
|
});
|
|
3524
|
-
|
|
3525
|
-
|
|
4490
|
+
registerAction({
|
|
4491
|
+
type: "qi/http.request",
|
|
4492
|
+
can: "http/request",
|
|
4493
|
+
sideEffect: true,
|
|
4494
|
+
proof: { fields: ["requestId"] },
|
|
4495
|
+
done: doneWhenCompleted,
|
|
4496
|
+
defaultRequiresConfirmation: true,
|
|
4497
|
+
requiredCapability: "flow/block/execute",
|
|
4498
|
+
eligibleForEventTrigger: true,
|
|
4499
|
+
inputSchema: {
|
|
4500
|
+
type: "object",
|
|
4501
|
+
required: [],
|
|
4502
|
+
additionalProperties: false,
|
|
4503
|
+
properties: {
|
|
4504
|
+
...INPUT_PROPERTIES,
|
|
4505
|
+
method: {
|
|
4506
|
+
type: "string",
|
|
4507
|
+
enum: REQUEST_METHODS,
|
|
4508
|
+
default: "GET",
|
|
4509
|
+
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."
|
|
4510
|
+
}
|
|
4511
|
+
}
|
|
4512
|
+
},
|
|
4513
|
+
getMissingInputs: missingEndpoint,
|
|
4514
|
+
outputSchema: OUTPUT_SCHEMA2,
|
|
4515
|
+
run: async (inputs, ctx) => {
|
|
4516
|
+
const service = ctx.services.http;
|
|
4517
|
+
if (!service) throw new Error("Mutating HTTP requests require the host HTTP service; native fetch is not permitted");
|
|
4518
|
+
const url = publicHttpUrl(inputs.endpoint ?? inputs.url);
|
|
4519
|
+
const method = String(inputs.method || "GET").toUpperCase();
|
|
4520
|
+
if (!REQUEST_METHODS.includes(method)) throw new Error(`qi/http.request method must be one of ${REQUEST_METHODS.join(", ")}`);
|
|
4521
|
+
const result = await service.request({
|
|
4522
|
+
url,
|
|
4523
|
+
method,
|
|
4524
|
+
headers: safeHeaders(inputs.headers),
|
|
4525
|
+
body: inputs.body,
|
|
4526
|
+
security: { denyPrivateNetworks: true, maxRedirects: 0, stripSensitiveHeadersOnRedirect: true }
|
|
4527
|
+
});
|
|
4528
|
+
const responseDigest = result.responseDigest || sha256Digest(result.data);
|
|
4529
|
+
const requestId = result.requestId || sha256Digest({ url, method, status: result.status, responseDigest });
|
|
4530
|
+
return {
|
|
4531
|
+
output: { requestId, status: result.status, responseDigest, data: result.data, response: JSON.stringify(result.data, null, 2), traceReference: result.traceReference || "" }
|
|
4532
|
+
};
|
|
4533
|
+
}
|
|
4534
|
+
});
|
|
4535
|
+
|
|
4536
|
+
// src/core/lib/actionRegistry/actions/emailSend.ts
|
|
3526
4537
|
registerAction({
|
|
3527
4538
|
type: "qi/email.send",
|
|
3528
4539
|
can: "email/send",
|
|
@@ -3602,7 +4613,7 @@ registerAction({
|
|
|
3602
4613
|
type: "qi/human.checkbox.set",
|
|
3603
4614
|
can: "human/checkbox",
|
|
3604
4615
|
sideEffect: true,
|
|
3605
|
-
proof: "
|
|
4616
|
+
proof: { fields: ["attestationId"] },
|
|
3606
4617
|
done: doneWhenCompleted,
|
|
3607
4618
|
defaultRequiresConfirmation: false,
|
|
3608
4619
|
requiredCapability: "flow/execute",
|
|
@@ -3613,9 +4624,17 @@ registerAction({
|
|
|
3613
4624
|
checked: { type: "boolean", description: "Whether the checkbox should be checked (defaults to true)." }
|
|
3614
4625
|
}
|
|
3615
4626
|
},
|
|
3616
|
-
|
|
4627
|
+
outputSchema: [
|
|
4628
|
+
{ path: "checked", displayName: "Checked", type: "boolean" },
|
|
4629
|
+
{ path: "attestationId", displayName: "Attestation ID", type: "string", description: "Proof identifier for the human checkbox attestation." },
|
|
4630
|
+
{ path: "attestedAt", displayName: "Attested At", type: "string" },
|
|
4631
|
+
{ path: "attestedBy", displayName: "Attested By", type: "string" }
|
|
4632
|
+
],
|
|
4633
|
+
run: async (inputs, ctx) => {
|
|
3617
4634
|
const checked = inputs.checked !== void 0 ? !!inputs.checked : true;
|
|
3618
|
-
|
|
4635
|
+
const attestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4636
|
+
const attestationId = sha256Digest({ action: "qi/human.checkbox.set", checked, actorDid: ctx.actorDid, flowId: ctx.flowId, nodeId: ctx.nodeId, attestedAt });
|
|
4637
|
+
return { output: { checked, attestationId, attestedAt, attestedBy: ctx.actorDid } };
|
|
3619
4638
|
}
|
|
3620
4639
|
});
|
|
3621
4640
|
|
|
@@ -3645,7 +4664,7 @@ function registerFormSubmitAction(type, can) {
|
|
|
3645
4664
|
type,
|
|
3646
4665
|
can,
|
|
3647
4666
|
sideEffect: true,
|
|
3648
|
-
proof: "
|
|
4667
|
+
proof: { fields: ["submissionId"] },
|
|
3649
4668
|
done: doneWhenCompleted,
|
|
3650
4669
|
defaultRequiresConfirmation: false,
|
|
3651
4670
|
requiredCapability: "flow/execute",
|
|
@@ -3662,7 +4681,11 @@ function registerFormSubmitAction(type, can) {
|
|
|
3662
4681
|
},
|
|
3663
4682
|
outputSchema: [
|
|
3664
4683
|
{ 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." }
|
|
4684
|
+
{ path: "answers", displayName: "Form Answers", type: "object", description: "Parsed form answers object for convenience." },
|
|
4685
|
+
{ path: "submissionId", displayName: "Submission ID", type: "string", description: "Stable proof identifier for this submission." },
|
|
4686
|
+
{ path: "answersDigest", displayName: "Answers Digest", type: "string", description: "Content digest; safe to place in a Topic receipt." },
|
|
4687
|
+
{ path: "submittedAt", displayName: "Submitted At", type: "string" },
|
|
4688
|
+
{ path: "submittedBy", displayName: "Submitted By", type: "string" }
|
|
3666
4689
|
],
|
|
3667
4690
|
events: [
|
|
3668
4691
|
{
|
|
@@ -3673,15 +4696,22 @@ function registerFormSubmitAction(type, can) {
|
|
|
3673
4696
|
pendingDisplayFields: ["answers"]
|
|
3674
4697
|
}
|
|
3675
4698
|
],
|
|
3676
|
-
run: async (inputs) => {
|
|
4699
|
+
run: async (inputs, ctx) => {
|
|
3677
4700
|
const answers = normalizeAnswers(inputs.answers ?? inputs.form?.answers);
|
|
3678
4701
|
const answersJson = JSON.stringify(answers);
|
|
4702
|
+
const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4703
|
+
const answersDigest = sha256Digest(answers);
|
|
4704
|
+
const submissionId = sha256Digest({ type, flowId: ctx.flowId, sessionRunId: ctx.sessionRunId || "", nodeId: ctx.nodeId, submittedAt, answersDigest });
|
|
3679
4705
|
return {
|
|
3680
4706
|
output: {
|
|
3681
4707
|
form: {
|
|
3682
4708
|
answers: answersJson
|
|
3683
4709
|
},
|
|
3684
|
-
answers
|
|
4710
|
+
answers,
|
|
4711
|
+
submissionId,
|
|
4712
|
+
answersDigest,
|
|
4713
|
+
submittedAt,
|
|
4714
|
+
submittedBy: ctx.actorDid
|
|
3685
4715
|
},
|
|
3686
4716
|
events: [{ name: "form.submitted", payload: { answers } }]
|
|
3687
4717
|
};
|
|
@@ -3710,20 +4740,39 @@ registerAction({
|
|
|
3710
4740
|
outputSchema: [
|
|
3711
4741
|
{ path: "runId", displayName: "Session run id", type: "string" },
|
|
3712
4742
|
{ path: "eventId", displayName: "Started event id", type: "string" },
|
|
3713
|
-
{ path: "startedAt", displayName: "Started at", type: "number" }
|
|
4743
|
+
{ path: "startedAt", displayName: "Started at", type: "number" },
|
|
4744
|
+
{ path: "sessionId", displayName: "Session ID", type: "string" },
|
|
4745
|
+
{ path: "flowRevision", displayName: "Flow revision", type: "string" },
|
|
4746
|
+
{ path: "topicBindingId", displayName: "Topic binding ID", type: "string" }
|
|
4747
|
+
],
|
|
4748
|
+
events: [
|
|
4749
|
+
{
|
|
4750
|
+
name: "flow.run.started",
|
|
4751
|
+
displayName: "Flow run started",
|
|
4752
|
+
description: "Emitted when a pinned Flow run starts; it does not change Topic status.",
|
|
4753
|
+
payloadSchema: [
|
|
4754
|
+
{ path: "runId", displayName: "Run ID", type: "string" },
|
|
4755
|
+
{ path: "topicBindingId", displayName: "Topic binding ID", type: "string" }
|
|
4756
|
+
]
|
|
4757
|
+
}
|
|
3714
4758
|
],
|
|
3715
4759
|
run: async (inputs, ctx) => {
|
|
3716
4760
|
if (!ctx.services.flowRuns?.start) {
|
|
3717
4761
|
throw new Error("flowRuns.start handler not available");
|
|
3718
4762
|
}
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
4763
|
+
const lifecycle = await ctx.services.flowRuns.start({
|
|
4764
|
+
actorDid: ctx.actorDid,
|
|
4765
|
+
flowId: ctx.flowId,
|
|
4766
|
+
flowUri: ctx.flowUri,
|
|
4767
|
+
...typeof inputs.label === "string" && inputs.label ? { label: inputs.label } : {}
|
|
4768
|
+
});
|
|
4769
|
+
const output = {
|
|
4770
|
+
...lifecycle,
|
|
4771
|
+
sessionId: lifecycle.runId,
|
|
4772
|
+
flowRevision: ctx.flowRevision || "",
|
|
4773
|
+
topicBindingId: ctx.topic?.bindingId || ""
|
|
3726
4774
|
};
|
|
4775
|
+
return { output, events: [{ name: "flow.run.started", payload: { runId: lifecycle.runId, topicBindingId: output.topicBindingId } }] };
|
|
3727
4776
|
}
|
|
3728
4777
|
});
|
|
3729
4778
|
registerAction({
|
|
@@ -3748,7 +4797,22 @@ registerAction({
|
|
|
3748
4797
|
{ path: "status", displayName: "Terminal status", type: "string" },
|
|
3749
4798
|
{ path: "eventId", displayName: "Terminal event id", type: "string" },
|
|
3750
4799
|
{ path: "closedAt", displayName: "Closed at", type: "number" },
|
|
3751
|
-
{ path: "cancelledAt", displayName: "Cancelled at", type: "number" }
|
|
4800
|
+
{ path: "cancelledAt", displayName: "Cancelled at", type: "number" },
|
|
4801
|
+
{ path: "runId", displayName: "Session run ID", type: "string" },
|
|
4802
|
+
{ path: "flowRevision", displayName: "Flow revision", type: "string" },
|
|
4803
|
+
{ path: "topicBindingId", displayName: "Topic binding ID", type: "string" }
|
|
4804
|
+
],
|
|
4805
|
+
events: [
|
|
4806
|
+
{
|
|
4807
|
+
name: "flow.run.closed",
|
|
4808
|
+
displayName: "Flow run closed",
|
|
4809
|
+
description: "Emitted when the Flow run closes; Topic resolution remains an explicit, separate Action.",
|
|
4810
|
+
payloadSchema: [
|
|
4811
|
+
{ path: "runId", displayName: "Run ID", type: "string" },
|
|
4812
|
+
{ path: "status", displayName: "Status", type: "string" },
|
|
4813
|
+
{ path: "topicBindingId", displayName: "Topic binding ID", type: "string" }
|
|
4814
|
+
]
|
|
4815
|
+
}
|
|
3752
4816
|
],
|
|
3753
4817
|
run: async (inputs, ctx) => {
|
|
3754
4818
|
if (!ctx.sessionRunId) {
|
|
@@ -3758,24 +4822,30 @@ registerAction({
|
|
|
3758
4822
|
throw new Error("flowRuns lifecycle handler not available");
|
|
3759
4823
|
}
|
|
3760
4824
|
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({
|
|
4825
|
+
const lifecycle2 = await ctx.services.flowRuns.cancel({
|
|
3773
4826
|
actorDid: ctx.actorDid,
|
|
3774
4827
|
flowId: ctx.flowId,
|
|
3775
4828
|
flowUri: ctx.flowUri,
|
|
3776
4829
|
runId: ctx.sessionRunId,
|
|
3777
|
-
|
|
3778
|
-
})
|
|
4830
|
+
...typeof inputs.reason === "string" && inputs.reason ? { reason: inputs.reason } : {}
|
|
4831
|
+
});
|
|
4832
|
+
const output2 = { ...lifecycle2, runId: ctx.sessionRunId, flowRevision: ctx.flowRevision || "", topicBindingId: ctx.topic?.bindingId || "" };
|
|
4833
|
+
return {
|
|
4834
|
+
output: output2,
|
|
4835
|
+
events: [{ name: "flow.run.closed", payload: { runId: ctx.sessionRunId, status: lifecycle2.status, topicBindingId: output2.topicBindingId } }]
|
|
4836
|
+
};
|
|
4837
|
+
}
|
|
4838
|
+
const lifecycle = await ctx.services.flowRuns.close({
|
|
4839
|
+
actorDid: ctx.actorDid,
|
|
4840
|
+
flowId: ctx.flowId,
|
|
4841
|
+
flowUri: ctx.flowUri,
|
|
4842
|
+
runId: ctx.sessionRunId,
|
|
4843
|
+
allowIncomplete: inputs.allowIncomplete === true
|
|
4844
|
+
});
|
|
4845
|
+
const output = { ...lifecycle, runId: ctx.sessionRunId, flowRevision: ctx.flowRevision || "", topicBindingId: ctx.topic?.bindingId || "" };
|
|
4846
|
+
return {
|
|
4847
|
+
output,
|
|
4848
|
+
events: [{ name: "flow.run.closed", payload: { runId: ctx.sessionRunId, status: lifecycle.status, topicBindingId: output.topicBindingId } }]
|
|
3779
4849
|
};
|
|
3780
4850
|
}
|
|
3781
4851
|
});
|
|
@@ -3804,6 +4874,10 @@ registerAction({
|
|
|
3804
4874
|
replyTo: { type: "string", description: "Reply-to address." }
|
|
3805
4875
|
}
|
|
3806
4876
|
},
|
|
4877
|
+
outputSchema: [
|
|
4878
|
+
{ path: "messageId", displayName: "Message ID", type: "string", description: "Provider or host notification identifier." },
|
|
4879
|
+
{ path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp supplied by the provider or host." }
|
|
4880
|
+
],
|
|
3807
4881
|
run: async (inputs, ctx) => {
|
|
3808
4882
|
if (!ctx.services.notify) {
|
|
3809
4883
|
throw new Error("Notification service not configured");
|
|
@@ -4457,6 +5531,22 @@ registerAction({
|
|
|
4457
5531
|
};
|
|
4458
5532
|
return {
|
|
4459
5533
|
output,
|
|
5534
|
+
topicRecords: ctx.topic ? [
|
|
5535
|
+
{
|
|
5536
|
+
type: "org.ixo.topic.claim-submission",
|
|
5537
|
+
id: sha256Digest({ topicId: ctx.topic.topicId, collectionId, claimId }),
|
|
5538
|
+
version: 1,
|
|
5539
|
+
value: {
|
|
5540
|
+
claimId,
|
|
5541
|
+
collectionId,
|
|
5542
|
+
deedDid,
|
|
5543
|
+
submittedByDid,
|
|
5544
|
+
submittedAt,
|
|
5545
|
+
transactionHash,
|
|
5546
|
+
submissionDigest: sha256Digest(surveyAnswers)
|
|
5547
|
+
}
|
|
5548
|
+
}
|
|
5549
|
+
] : void 0,
|
|
4460
5550
|
events: [
|
|
4461
5551
|
{
|
|
4462
5552
|
name: "submitted",
|
|
@@ -4811,7 +5901,7 @@ registerAction({
|
|
|
4811
5901
|
const flowId = String(ctx.flowId || ctx.flowUri || "flow");
|
|
4812
5902
|
const claimSnapshot = inputs.claimSnapshot && typeof inputs.claimSnapshot === "object" && !Array.isArray(inputs.claimSnapshot) ? inputs.claimSnapshot : void 0;
|
|
4813
5903
|
const surveyQuestions = Array.isArray(claimSnapshot?.surveyQuestions) ? claimSnapshot.surveyQuestions : Array.isArray(inputs?.surveyAnswersSchema) ? inputs.surveyAnswersSchema : [];
|
|
4814
|
-
const
|
|
5904
|
+
const idempotencyKey2 = buildXeroInvoiceWorkKey({ flowId, evaluationBlockId: ctx.nodeId, claimId });
|
|
4815
5905
|
const originalPayload = {
|
|
4816
5906
|
claim: { claimId, collectionId, deedDid },
|
|
4817
5907
|
surveyQuestions,
|
|
@@ -4827,11 +5917,11 @@ registerAction({
|
|
|
4827
5917
|
invoiceDefaults: buildXeroInvoiceDefaults(inputs.xeroInvoiceDefaults)
|
|
4828
5918
|
};
|
|
4829
5919
|
upsertXeroWorkItemForEditor(ctx.editor, {
|
|
4830
|
-
id:
|
|
5920
|
+
id: idempotencyKey2,
|
|
4831
5921
|
kind: "invoice.create",
|
|
4832
5922
|
status: "pending",
|
|
4833
5923
|
assignedBlockId: ctx.nodeId,
|
|
4834
|
-
idempotencyKey,
|
|
5924
|
+
idempotencyKey: idempotencyKey2,
|
|
4835
5925
|
source: {
|
|
4836
5926
|
claimId,
|
|
4837
5927
|
evaluationBlockId: ctx.nodeId,
|
|
@@ -4871,6 +5961,25 @@ registerAction({
|
|
|
4871
5961
|
};
|
|
4872
5962
|
return {
|
|
4873
5963
|
output,
|
|
5964
|
+
topicRecords: ctx.topic ? [
|
|
5965
|
+
{
|
|
5966
|
+
type: "org.ixo.topic.claim-evaluation",
|
|
5967
|
+
id: sha256Digest({ topicId: ctx.topic.topicId, collectionId, claimId, evaluatedAt, decision }),
|
|
5968
|
+
version: 1,
|
|
5969
|
+
value: {
|
|
5970
|
+
claimId,
|
|
5971
|
+
collectionId,
|
|
5972
|
+
deedDid,
|
|
5973
|
+
decision,
|
|
5974
|
+
evaluatedByDid,
|
|
5975
|
+
evaluatedAt,
|
|
5976
|
+
verificationProof,
|
|
5977
|
+
transactionHash,
|
|
5978
|
+
evidenceDigest: sha256Digest(surveyAnswers)
|
|
5979
|
+
},
|
|
5980
|
+
evidenceReferences: verificationProof ? [verificationProof] : []
|
|
5981
|
+
}
|
|
5982
|
+
] : void 0,
|
|
4874
5983
|
events: [{ name: eventName, payload: eventPayload }]
|
|
4875
5984
|
};
|
|
4876
5985
|
}
|
|
@@ -4953,7 +6062,23 @@ registerAction({
|
|
|
4953
6062
|
proposalContractAddress: proposalContractAddress || "",
|
|
4954
6063
|
coreAddress,
|
|
4955
6064
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4956
|
-
}
|
|
6065
|
+
},
|
|
6066
|
+
topicRecords: ctx.topic ? [
|
|
6067
|
+
{
|
|
6068
|
+
type: "org.ixo.topic.proposal-receipt",
|
|
6069
|
+
id: sha256Digest({ topicId: ctx.topic.topicId, proposalContractAddress, proposalId }),
|
|
6070
|
+
version: 1,
|
|
6071
|
+
value: {
|
|
6072
|
+
event: "created",
|
|
6073
|
+
proposalId: String(proposalId),
|
|
6074
|
+
proposalContractAddress: proposalContractAddress || "",
|
|
6075
|
+
coreAddress,
|
|
6076
|
+
title,
|
|
6077
|
+
descriptionDigest: sha256Digest(description),
|
|
6078
|
+
status: "open"
|
|
6079
|
+
}
|
|
6080
|
+
}
|
|
6081
|
+
] : void 0
|
|
4957
6082
|
};
|
|
4958
6083
|
}
|
|
4959
6084
|
});
|
|
@@ -5008,13 +6133,30 @@ registerAction({
|
|
|
5008
6133
|
rationale: rationale || void 0,
|
|
5009
6134
|
proposalContractAddress
|
|
5010
6135
|
});
|
|
6136
|
+
const votedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5011
6137
|
return {
|
|
5012
6138
|
output: {
|
|
5013
6139
|
vote,
|
|
5014
6140
|
rationale: rationale || "",
|
|
5015
6141
|
proposalId: String(proposalId),
|
|
5016
|
-
votedAt
|
|
5017
|
-
}
|
|
6142
|
+
votedAt
|
|
6143
|
+
},
|
|
6144
|
+
topicRecords: ctx.topic ? [
|
|
6145
|
+
{
|
|
6146
|
+
type: "org.ixo.topic.proposal-receipt",
|
|
6147
|
+
id: sha256Digest({ topicId: ctx.topic.topicId, proposalContractAddress, proposalId, actorDid: ctx.actorDid, votedAt }),
|
|
6148
|
+
version: 1,
|
|
6149
|
+
value: {
|
|
6150
|
+
event: "vote-cast",
|
|
6151
|
+
proposalId: String(proposalId),
|
|
6152
|
+
proposalContractAddress,
|
|
6153
|
+
vote,
|
|
6154
|
+
rationaleDigest: sha256Digest(rationale),
|
|
6155
|
+
actorDid: ctx.actorDid,
|
|
6156
|
+
votedAt
|
|
6157
|
+
}
|
|
6158
|
+
}
|
|
6159
|
+
] : void 0
|
|
5018
6160
|
};
|
|
5019
6161
|
}
|
|
5020
6162
|
});
|
|
@@ -6826,29 +7968,46 @@ registerAction({
|
|
|
6826
7968
|
|
|
6827
7969
|
// src/core/lib/actionRegistry/actions/oracle.ts
|
|
6828
7970
|
registerAction({
|
|
6829
|
-
type: "oracle",
|
|
6830
|
-
can: "oracle/
|
|
6831
|
-
sideEffect:
|
|
6832
|
-
proof: "
|
|
7971
|
+
type: "qi/oracle.invoke",
|
|
7972
|
+
can: "oracle/invoke",
|
|
7973
|
+
sideEffect: true,
|
|
7974
|
+
proof: { fields: ["resultDigest"] },
|
|
6833
7975
|
done: doneWhenCompleted,
|
|
6834
7976
|
defaultRequiresConfirmation: false,
|
|
6835
7977
|
inputSchema: {
|
|
6836
7978
|
type: "object",
|
|
6837
7979
|
required: ["prompt"],
|
|
6838
7980
|
properties: {
|
|
6839
|
-
prompt: { type: "string", description: "The prompt text sent to the
|
|
7981
|
+
prompt: { type: "string", description: "The prompt text sent to the Agent." }
|
|
6840
7982
|
}
|
|
6841
7983
|
},
|
|
6842
|
-
|
|
7984
|
+
sensitiveInputPaths: ["prompt"],
|
|
7985
|
+
sensitiveOutputPaths: ["result"],
|
|
7986
|
+
outputSchema: [
|
|
7987
|
+
{ path: "prompt", displayName: "Prompt", type: "string", description: "Legacy Flow-timeline echo; redacted from Topic receipts." },
|
|
7988
|
+
{ path: "sessionId", displayName: "Session ID", type: "string", description: "Private Oracle session identifier." },
|
|
7989
|
+
{ path: "result", displayName: "Result", type: "object", description: "Full result retained in the Flow timeline." },
|
|
7990
|
+
{ path: "resultDigest", displayName: "Result Digest", type: "string", description: "Content digest safe for a Topic receipt." },
|
|
7991
|
+
{ path: "evidenceDigest", displayName: "Evidence Digest", type: "string", description: "Digest of evidence references returned by the Oracle." }
|
|
7992
|
+
],
|
|
6843
7993
|
run: async (inputs, ctx) => {
|
|
6844
7994
|
const prompt = String(inputs.prompt || "").trim();
|
|
6845
7995
|
if (!prompt) throw new Error("prompt is required");
|
|
6846
7996
|
if (!ctx.handlers?.askCompanion) {
|
|
6847
7997
|
throw new Error("askCompanion handler is not available");
|
|
6848
7998
|
}
|
|
6849
|
-
await ctx.handlers.askCompanion(prompt);
|
|
7999
|
+
const raw = await ctx.handlers.askCompanion(prompt);
|
|
8000
|
+
const envelope = raw && typeof raw === "object" ? raw : { result: raw };
|
|
8001
|
+
const result = envelope.result ?? envelope.response ?? envelope.message ?? raw ?? null;
|
|
8002
|
+
const evidence = Array.isArray(envelope.evidenceReferences) ? envelope.evidenceReferences : Array.isArray(envelope.evidence) ? envelope.evidence : [];
|
|
6850
8003
|
return {
|
|
6851
|
-
output: {
|
|
8004
|
+
output: {
|
|
8005
|
+
prompt,
|
|
8006
|
+
sessionId: String(envelope.sessionId ?? envelope.runId ?? ""),
|
|
8007
|
+
result,
|
|
8008
|
+
resultDigest: sha256Digest(result),
|
|
8009
|
+
evidenceDigest: sha256Digest(evidence)
|
|
8010
|
+
}
|
|
6852
8011
|
};
|
|
6853
8012
|
}
|
|
6854
8013
|
});
|
|
@@ -6984,6 +8143,7 @@ registerAction({
|
|
|
6984
8143
|
});
|
|
6985
8144
|
|
|
6986
8145
|
// src/core/lib/actionRegistry/actions/walletFund.ts
|
|
8146
|
+
var DEFAULT_DENOM = "uixo";
|
|
6987
8147
|
registerAction({
|
|
6988
8148
|
type: "qi/wallet.fund",
|
|
6989
8149
|
can: "wallet/fund",
|
|
@@ -6996,20 +8156,37 @@ registerAction({
|
|
|
6996
8156
|
required: ["address"],
|
|
6997
8157
|
properties: {
|
|
6998
8158
|
address: { type: "string", description: "The IXO wallet address to fund." },
|
|
6999
|
-
amount: { type: "number", description: "Funding amount in base units (defaults to 250000)." }
|
|
8159
|
+
amount: { type: "number", description: "Funding amount in the denom\u2019s base units (defaults to 250000)." },
|
|
8160
|
+
denom: { type: "string", description: "Base denom to send, e.g. uixo. Defaults to uixo." },
|
|
8161
|
+
fromAddress: {
|
|
8162
|
+
type: "string",
|
|
8163
|
+
description: "Wallet the tokens leave. Defaults to the signed-in user\u2019s wallet; any other address must be one they can act on."
|
|
8164
|
+
}
|
|
7000
8165
|
}
|
|
7001
8166
|
},
|
|
7002
|
-
outputSchema: [
|
|
8167
|
+
outputSchema: [
|
|
8168
|
+
{ path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The funding transaction hash" },
|
|
8169
|
+
{ path: "denom", displayName: "Denom", type: "string", description: "The base denom that was sent" },
|
|
8170
|
+
{ path: "amount", displayName: "Amount", type: "string", description: "The amount sent, in the denom\u2019s base units" },
|
|
8171
|
+
{ path: "fromAddress", displayName: "From Address", type: "string", description: "The wallet the tokens left (blank when the signer\u2019s own wallet)" }
|
|
8172
|
+
],
|
|
7003
8173
|
run: async (inputs, ctx) => {
|
|
7004
8174
|
if (!ctx.services.oracle?.fundWallet) {
|
|
7005
8175
|
throw new Error("oracle.fundWallet handler not available");
|
|
7006
8176
|
}
|
|
7007
8177
|
if (!inputs.address) throw new Error("address is required");
|
|
8178
|
+
const denom = String(inputs.denom || "").trim() || DEFAULT_DENOM;
|
|
8179
|
+
const amount = Number(inputs.amount) || 25e4;
|
|
8180
|
+
if (!Number.isFinite(amount) || amount <= 0) throw new Error("amount must be greater than 0");
|
|
8181
|
+
if (!Number.isInteger(amount)) throw new Error(`amount must be a whole number of ${denom} base units`);
|
|
8182
|
+
const fromAddress = String(inputs.fromAddress || "").trim();
|
|
7008
8183
|
const result = await ctx.services.oracle.fundWallet({
|
|
7009
8184
|
address: inputs.address,
|
|
7010
|
-
amount
|
|
8185
|
+
amount,
|
|
8186
|
+
denom,
|
|
8187
|
+
...fromAddress ? { fromAddress } : {}
|
|
7011
8188
|
});
|
|
7012
|
-
return { output: result };
|
|
8189
|
+
return { output: { ...result, denom, amount: String(amount), fromAddress } };
|
|
7013
8190
|
}
|
|
7014
8191
|
});
|
|
7015
8192
|
|
|
@@ -7025,7 +8202,12 @@ registerAction({
|
|
|
7025
8202
|
type: "object",
|
|
7026
8203
|
required: [],
|
|
7027
8204
|
properties: {
|
|
7028
|
-
amount: { type: "number", description: "Funding amount in base units for the generated wallet (defaults to 250000)." }
|
|
8205
|
+
amount: { type: "number", description: "Funding amount in base units for the generated wallet (defaults to 250000)." },
|
|
8206
|
+
denom: { type: "string", description: "Base denom to send, e.g. uixo. Defaults to uixo." },
|
|
8207
|
+
fromAddress: {
|
|
8208
|
+
type: "string",
|
|
8209
|
+
description: "Wallet the funding leaves. Defaults to the signed-in user\u2019s wallet; any other address must be one they can act on."
|
|
8210
|
+
}
|
|
7029
8211
|
}
|
|
7030
8212
|
},
|
|
7031
8213
|
outputSchema: [
|
|
@@ -7033,7 +8215,8 @@ registerAction({
|
|
|
7033
8215
|
{ path: "did", displayName: "DID", type: "string", description: "The DID derived from the wallet address" },
|
|
7034
8216
|
{ path: "pubKey", displayName: "Public Key", type: "string", description: "The secp256k1 public key (hex)" },
|
|
7035
8217
|
{ 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" }
|
|
8218
|
+
{ path: "transactionHash", displayName: "Transaction Hash", type: "string", description: "The funding transaction hash" },
|
|
8219
|
+
{ path: "denom", displayName: "Denom", type: "string", description: "The base denom that was sent" }
|
|
7037
8220
|
],
|
|
7038
8221
|
run: async (inputs, ctx) => {
|
|
7039
8222
|
if (!ctx.services.oracle?.generateWallet) {
|
|
@@ -7046,9 +8229,13 @@ registerAction({
|
|
|
7046
8229
|
if (!walletResult?.address) {
|
|
7047
8230
|
throw new Error("generateWallet did not return an address");
|
|
7048
8231
|
}
|
|
8232
|
+
const denom = String(inputs.denom || "").trim() || "uixo";
|
|
8233
|
+
const fromAddress = String(inputs.fromAddress || "").trim();
|
|
7049
8234
|
const fundResult = await ctx.services.oracle.fundWallet({
|
|
7050
8235
|
address: walletResult.address,
|
|
7051
|
-
amount: inputs.amount || 25e4
|
|
8236
|
+
amount: inputs.amount || 25e4,
|
|
8237
|
+
denom,
|
|
8238
|
+
...fromAddress ? { fromAddress } : {}
|
|
7052
8239
|
});
|
|
7053
8240
|
if (!fundResult?.transactionHash) {
|
|
7054
8241
|
throw new Error("fundWallet did not return a transactionHash");
|
|
@@ -7059,7 +8246,8 @@ registerAction({
|
|
|
7059
8246
|
did: walletResult.did,
|
|
7060
8247
|
pubKey: walletResult.pubKey,
|
|
7061
8248
|
mnemonic: walletResult.mnemonic,
|
|
7062
|
-
transactionHash: fundResult.transactionHash
|
|
8249
|
+
transactionHash: fundResult.transactionHash,
|
|
8250
|
+
denom
|
|
7063
8251
|
}
|
|
7064
8252
|
};
|
|
7065
8253
|
}
|
|
@@ -8105,14 +9293,6 @@ var COLLECTION_CREATED_EVENT = {
|
|
|
8105
9293
|
pendingDisplayFields: ["collectionId", "entity"]
|
|
8106
9294
|
};
|
|
8107
9295
|
|
|
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
9296
|
// src/core/lib/actionRegistry/actions/collection/collection.ts
|
|
8117
9297
|
function normalizeQuota(quota) {
|
|
8118
9298
|
if (quota === void 0 || quota === null) return void 0;
|
|
@@ -9014,6 +10194,7 @@ async function runEvalRegister(inputs, ctx) {
|
|
|
9014
10194
|
const allowAiChecks = inputs.allowAiChecks !== false;
|
|
9015
10195
|
const allowImageChecks = allowAiChecks && inputs.allowImageChecks !== false;
|
|
9016
10196
|
const allowChainEvaluation = inputs.allowChainEvaluation !== false;
|
|
10197
|
+
const allowZeroPayoutApprovals = inputs.allowZeroPayoutApprovals === true;
|
|
9017
10198
|
if (!collectionId) throw new Error("collectionId is required");
|
|
9018
10199
|
if (!deedDid) throw new Error("deedDid (entity/deed DID) is required");
|
|
9019
10200
|
if (!ownerDid) throw new Error("ownerDid is required (pass it explicitly, or run as the collection owner)");
|
|
@@ -9052,7 +10233,7 @@ async function runEvalRegister(inputs, ctx) {
|
|
|
9052
10233
|
// (see above), and a host that copies `params` field-by-field would otherwise forward an
|
|
9053
10234
|
// explicit `undefined` as the erasing empty value.
|
|
9054
10235
|
...description !== void 0 ? { description } : {},
|
|
9055
|
-
settings: { allowAiChecks, allowImageChecks, allowChainEvaluation }
|
|
10236
|
+
settings: { allowAiChecks, allowImageChecks, allowChainEvaluation, allowZeroPayoutApprovals }
|
|
9056
10237
|
});
|
|
9057
10238
|
const registrationId = String(registration?.id || "").trim();
|
|
9058
10239
|
if (!registrationId) {
|
|
@@ -9125,8 +10306,12 @@ function canonicalJson(value) {
|
|
|
9125
10306
|
throw new Error(`canonicalJson: unsupported value of type ${typeof value}`);
|
|
9126
10307
|
}
|
|
9127
10308
|
|
|
10309
|
+
// src/core/lib/actionRegistry/actions/evalRubric/pathGrammar.ts
|
|
10310
|
+
var FORM_SEGMENT = "[A-Za-z0-9_-]+(?::[A-Za-z0-9_-]+)?";
|
|
10311
|
+
|
|
9128
10312
|
// src/core/lib/actionRegistry/actions/evalRubric/fieldCatalog.ts
|
|
9129
|
-
var SEGMENT =
|
|
10313
|
+
var SEGMENT = new RegExp(`^${FORM_SEGMENT}$`);
|
|
10314
|
+
var NAME_PATH = new RegExp(`^${FORM_SEGMENT}(?:\\.${FORM_SEGMENT})*$`);
|
|
9130
10315
|
function scalarKind(type, inputType) {
|
|
9131
10316
|
switch (type) {
|
|
9132
10317
|
case "text":
|
|
@@ -9209,7 +10394,7 @@ function extractRubricFieldCatalog(surveyTemplate, proof = "") {
|
|
|
9209
10394
|
}
|
|
9210
10395
|
const name = typeof el.name === "string" ? el.name.trim() : "";
|
|
9211
10396
|
const type = typeof el.type === "string" ? el.type : "";
|
|
9212
|
-
if (!name || !
|
|
10397
|
+
if (!name || !NAME_PATH.test(name) || seen2.has(name) || type === "html" || type === "expression") continue;
|
|
9213
10398
|
const field = extractQuestion(el, name, type);
|
|
9214
10399
|
if (!field) continue;
|
|
9215
10400
|
seen2.add(name);
|
|
@@ -9305,14 +10490,15 @@ function titleOf(el) {
|
|
|
9305
10490
|
return title || humanize2(typeof el.name === "string" ? el.name : "");
|
|
9306
10491
|
}
|
|
9307
10492
|
function humanize2(name) {
|
|
9308
|
-
|
|
10493
|
+
const bare = name.split(".").map((seg) => seg.includes(":") ? seg.split(":").pop() : seg).join(".");
|
|
10494
|
+
return bare.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
9309
10495
|
}
|
|
9310
10496
|
function stripHtml2(s) {
|
|
9311
10497
|
return s.replace(/<[^>]*>/g, "").replace(/\s+/g, " ").trim();
|
|
9312
10498
|
}
|
|
9313
10499
|
|
|
9314
10500
|
// src/core/lib/actionRegistry/actions/evalRubric/schemaGate.ts
|
|
9315
|
-
import
|
|
10501
|
+
import Ajv20202 from "ajv/dist/2020.js";
|
|
9316
10502
|
|
|
9317
10503
|
// src/core/lib/actionRegistry/actions/evalRubric/types.ts
|
|
9318
10504
|
var RUBRIC_CTX_TOKENS = [
|
|
@@ -9396,7 +10582,7 @@ async function getValidator(fetchSchema, evalEngineUrl) {
|
|
|
9396
10582
|
if (compiledValidator) return compiledValidator;
|
|
9397
10583
|
const schema = await fetchSchema(evalEngineUrl);
|
|
9398
10584
|
if (!schema || typeof schema !== "object") throw new Error("the rules service returned no schema");
|
|
9399
|
-
const validate = new
|
|
10585
|
+
const validate = new Ajv20202({ allErrors: true, strict: false }).compile(schema);
|
|
9400
10586
|
compiledValidator = validate;
|
|
9401
10587
|
return validate;
|
|
9402
10588
|
}
|
|
@@ -9494,7 +10680,7 @@ var ExpressionSyntaxError = class extends Error {
|
|
|
9494
10680
|
};
|
|
9495
10681
|
var IDENT = /[A-Za-z_][A-Za-z0-9_]*/y;
|
|
9496
10682
|
var NUMBER = /(?:\d+\.?\d*|\.\d+)/y;
|
|
9497
|
-
var FIELD_REF =
|
|
10683
|
+
var FIELD_REF = new RegExp(`\\$${FORM_SEGMENT}(?:\\.${FORM_SEGMENT}|\\[\\*\\])*`, "y");
|
|
9498
10684
|
var SIGIL_REF = /~[A-Za-z_][A-Za-z0-9_]*/y;
|
|
9499
10685
|
var CTX_REF = /ctx(?:\.[A-Za-z][A-Za-z0-9]*)+/y;
|
|
9500
10686
|
function parseExpression(src) {
|
|
@@ -9657,8 +10843,9 @@ var CTX_TOKEN_KIND = {
|
|
|
9657
10843
|
"ctx.submitter.priorApprovedCount": "number",
|
|
9658
10844
|
"ctx.collection.projectBoundary": "geo"
|
|
9659
10845
|
};
|
|
9660
|
-
var FIELD_REF2 =
|
|
9661
|
-
var ROW_REF =
|
|
10846
|
+
var FIELD_REF2 = new RegExp(`^\\$${FORM_SEGMENT}(?:\\.${FORM_SEGMENT}|\\[\\*\\])*$`);
|
|
10847
|
+
var ROW_REF = new RegExp(`^\\.${FORM_SEGMENT}$`);
|
|
10848
|
+
var FIELD_ROOT = new RegExp(`^\\$${FORM_SEGMENT}`);
|
|
9662
10849
|
var DERIVED_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
9663
10850
|
var EXT_REF = /^ext\.([A-Za-z0-9_-]+)\.(valid|score|reason)$/;
|
|
9664
10851
|
var AI_REF = /^ai\.([A-Za-z0-9_-]+)\.(valid|reason)$/;
|
|
@@ -9690,7 +10877,7 @@ function validateRubric(body, catalog, authoringCatalog) {
|
|
|
9690
10877
|
error("RUB_SCHEMA_DRIFT", `'${ref}' no longer exists on the claim form (deleted or renamed since the rules were authored)`, path);
|
|
9691
10878
|
return void 0;
|
|
9692
10879
|
}
|
|
9693
|
-
const rootName =
|
|
10880
|
+
const rootName = FIELD_ROOT.exec(ref)?.[0] ?? ref;
|
|
9694
10881
|
if (ref !== rootName && fieldIndex.has(rootName)) {
|
|
9695
10882
|
error("RUB_FIELD_PATH", `'${ref}' does not address a row/column of ${rootName}`, path);
|
|
9696
10883
|
} else {
|
|
@@ -10763,6 +11950,11 @@ registerAction({
|
|
|
10763
11950
|
allowAiChecks: { type: "boolean", default: true, description: "Engine setting: allow paid AI checks for this collection." },
|
|
10764
11951
|
allowImageChecks: { type: "boolean", default: true, description: "Engine setting: allow fake-photo detection (needs AI checks on)." },
|
|
10765
11952
|
allowChainEvaluation: { type: "boolean", default: true, description: "Engine setting: submit the decision on chain (releases payment); makes adminAddress required." },
|
|
11953
|
+
allowZeroPayoutApprovals: {
|
|
11954
|
+
type: "boolean",
|
|
11955
|
+
default: false,
|
|
11956
|
+
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."
|
|
11957
|
+
},
|
|
10766
11958
|
evaluateMaxAmount: { type: "array", description: "Per-claim payout cap on the evaluate grant, base-unit coins in the owner's denoms." },
|
|
10767
11959
|
// ---- rules (qi/eval.rubric) ----
|
|
10768
11960
|
rubric: {
|
|
@@ -10935,6 +12127,27 @@ registerAction({
|
|
|
10935
12127
|
});
|
|
10936
12128
|
|
|
10937
12129
|
// src/core/lib/actionRegistry/actions/_shared/delegatedTool.ts
|
|
12130
|
+
function delegatedToolInputSchema(schema) {
|
|
12131
|
+
return {
|
|
12132
|
+
type: "object",
|
|
12133
|
+
required: ["connection", ...schema.parameters.required],
|
|
12134
|
+
additionalProperties: false,
|
|
12135
|
+
properties: {
|
|
12136
|
+
connection: {
|
|
12137
|
+
type: "object",
|
|
12138
|
+
required: ["bindingId", "connectedAccountId", "toolkit"],
|
|
12139
|
+
additionalProperties: false,
|
|
12140
|
+
properties: {
|
|
12141
|
+
bindingId: { type: "string", minLength: 1, description: "Opaque, server-side delegated credential binding." },
|
|
12142
|
+
connectedAccountId: { type: "string" },
|
|
12143
|
+
toolkit: { type: "string" },
|
|
12144
|
+
label: { type: ["string", "null"] }
|
|
12145
|
+
}
|
|
12146
|
+
},
|
|
12147
|
+
...schema.parameters.properties
|
|
12148
|
+
}
|
|
12149
|
+
};
|
|
12150
|
+
}
|
|
10938
12151
|
function parseBoundConnection(raw) {
|
|
10939
12152
|
if (!raw || typeof raw !== "object") return null;
|
|
10940
12153
|
const c = raw;
|
|
@@ -11021,7 +12234,10 @@ async function executeDelegatedTool(ctx, opts) {
|
|
|
11021
12234
|
}
|
|
11022
12235
|
throw new Error(result.error || `${opts.toolkitLabel} action failed.`);
|
|
11023
12236
|
}
|
|
11024
|
-
return
|
|
12237
|
+
return {
|
|
12238
|
+
data: result.data ?? {},
|
|
12239
|
+
providerInvocationReceipt: result.providerInvocationReceipt
|
|
12240
|
+
};
|
|
11025
12241
|
}
|
|
11026
12242
|
|
|
11027
12243
|
// src/core/lib/actionRegistry/actions/gmail/emailSend.types.ts
|
|
@@ -11047,7 +12263,9 @@ var GMAIL_SEND_SCHEMA = {
|
|
|
11047
12263
|
var GMAIL_SEND_OUTPUT_SCHEMA = [
|
|
11048
12264
|
{ path: "messageId", displayName: "Message ID", type: "string", description: "Gmail id of the sent message" },
|
|
11049
12265
|
{ path: "threadId", displayName: "Thread ID", type: "string" },
|
|
11050
|
-
{ path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" }
|
|
12266
|
+
{ path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" },
|
|
12267
|
+
{ path: "providerReceiptId", displayName: "Provider Receipt ID", type: "string", description: "Signed integration-host invocation receipt identifier" },
|
|
12268
|
+
{ path: "providerInvocationReceipt", displayName: "Provider Receipt", type: "object", description: "Signed integration-host invocation receipt" }
|
|
11051
12269
|
];
|
|
11052
12270
|
|
|
11053
12271
|
// src/core/lib/actionRegistry/actions/gmail/emailSend.ts
|
|
@@ -11062,6 +12280,7 @@ registerAction({
|
|
|
11062
12280
|
requiredCapability: "flow/block/execute",
|
|
11063
12281
|
// Can be wired to another block's event (e.g. form submitted → send email).
|
|
11064
12282
|
eligibleForEventTrigger: true,
|
|
12283
|
+
inputSchema: delegatedToolInputSchema(GMAIL_SEND_SCHEMA),
|
|
11065
12284
|
// Mirrors executeDelegatedTool's gates: the bound connection plus the
|
|
11066
12285
|
// tool schema's required fields, so orchestrators ask before run() throws.
|
|
11067
12286
|
getMissingInputs: (inputs) => delegatedToolMissingInputs(GMAIL_SEND_SCHEMA, inputs),
|
|
@@ -11081,13 +12300,14 @@ registerAction({
|
|
|
11081
12300
|
run: async (inputs, ctx) => {
|
|
11082
12301
|
const parsed = parseDelegatedToolInputs(inputs);
|
|
11083
12302
|
const values = fieldValues(parsed);
|
|
11084
|
-
const
|
|
12303
|
+
const execution = await executeDelegatedTool(ctx, {
|
|
11085
12304
|
connection: parsed.connection,
|
|
11086
12305
|
schema: GMAIL_SEND_SCHEMA,
|
|
11087
12306
|
toolSlug: GMAIL_SEND_SLUG,
|
|
11088
12307
|
values,
|
|
11089
12308
|
toolkitLabel: "Gmail"
|
|
11090
12309
|
});
|
|
12310
|
+
const { data, providerInvocationReceipt } = execution;
|
|
11091
12311
|
const envelope = data.response_data ?? data;
|
|
11092
12312
|
const messageId = String(envelope.id ?? envelope.messageId ?? "");
|
|
11093
12313
|
const threadId = String(envelope.threadId ?? "");
|
|
@@ -11095,7 +12315,9 @@ registerAction({
|
|
|
11095
12315
|
output: {
|
|
11096
12316
|
messageId,
|
|
11097
12317
|
threadId,
|
|
11098
|
-
sentAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12318
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12319
|
+
providerReceiptId: providerInvocationReceipt?.id || "",
|
|
12320
|
+
providerInvocationReceipt
|
|
11099
12321
|
},
|
|
11100
12322
|
events: messageId ? [{ name: GMAIL_SENT_EVENT, payload: { messageId, recipient_email: values.recipient_email ?? "" } }] : void 0
|
|
11101
12323
|
};
|
|
@@ -11125,7 +12347,9 @@ var OUTLOOK_SEND_SCHEMA = {
|
|
|
11125
12347
|
};
|
|
11126
12348
|
var OUTLOOK_SEND_OUTPUT_SCHEMA = [
|
|
11127
12349
|
{ path: "messageId", displayName: "Message ID", type: "string" },
|
|
11128
|
-
{ path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" }
|
|
12350
|
+
{ path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" },
|
|
12351
|
+
{ path: "providerReceiptId", displayName: "Provider Receipt ID", type: "string", description: "Required signed proof when Outlook returns no message id" },
|
|
12352
|
+
{ path: "providerInvocationReceipt", displayName: "Provider Receipt", type: "object", description: "Signed integration-host invocation receipt" }
|
|
11129
12353
|
];
|
|
11130
12354
|
|
|
11131
12355
|
// src/core/lib/actionRegistry/actions/outlook/emailSend.ts
|
|
@@ -11133,16 +12357,15 @@ registerAction({
|
|
|
11133
12357
|
type: "qi/outlook.email.send",
|
|
11134
12358
|
can: "outlook.email/send",
|
|
11135
12359
|
sideEffect: true,
|
|
11136
|
-
// Outlook
|
|
11137
|
-
//
|
|
11138
|
-
|
|
11139
|
-
// requiring messageId, which would push every id-less send to needs_verification.
|
|
11140
|
-
proof: "none",
|
|
12360
|
+
// Outlook often returns no message id. The integration host must therefore
|
|
12361
|
+
// return a signed provider invocation receipt for proof of the side effect.
|
|
12362
|
+
proof: { fields: ["messageId", "providerReceiptId"] },
|
|
11141
12363
|
done: doneWhenCompleted,
|
|
11142
12364
|
defaultRequiresConfirmation: true,
|
|
11143
12365
|
requiredCapability: "flow/block/execute",
|
|
11144
12366
|
// Can be wired to another block's event (e.g. form submitted → send email).
|
|
11145
12367
|
eligibleForEventTrigger: true,
|
|
12368
|
+
inputSchema: delegatedToolInputSchema(OUTLOOK_SEND_SCHEMA),
|
|
11146
12369
|
// Mirrors executeDelegatedTool's gates: the bound connection plus the
|
|
11147
12370
|
// tool schema's required fields, so orchestrators ask before run() throws.
|
|
11148
12371
|
getMissingInputs: (inputs) => delegatedToolMissingInputs(OUTLOOK_SEND_SCHEMA, inputs),
|
|
@@ -11162,19 +12385,25 @@ registerAction({
|
|
|
11162
12385
|
run: async (inputs, ctx) => {
|
|
11163
12386
|
const parsed = parseDelegatedToolInputs(inputs);
|
|
11164
12387
|
const values = fieldValues(parsed);
|
|
11165
|
-
const
|
|
12388
|
+
const execution = await executeDelegatedTool(ctx, {
|
|
11166
12389
|
connection: parsed.connection,
|
|
11167
12390
|
schema: OUTLOOK_SEND_SCHEMA,
|
|
11168
12391
|
toolSlug: OUTLOOK_SEND_SLUG,
|
|
11169
12392
|
values,
|
|
11170
12393
|
toolkitLabel: "Outlook"
|
|
11171
12394
|
});
|
|
12395
|
+
const { data, providerInvocationReceipt } = execution;
|
|
11172
12396
|
const envelope = data.response_data ?? data;
|
|
11173
12397
|
const messageId = String(envelope.id ?? envelope.messageId ?? "");
|
|
12398
|
+
if (!messageId && !providerInvocationReceipt?.id) {
|
|
12399
|
+
throw new Error("Outlook returned no message id and the integration host returned no signed provider invocation receipt.");
|
|
12400
|
+
}
|
|
11174
12401
|
return {
|
|
11175
12402
|
output: {
|
|
11176
12403
|
messageId,
|
|
11177
|
-
sentAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12404
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12405
|
+
providerReceiptId: providerInvocationReceipt?.id || "",
|
|
12406
|
+
providerInvocationReceipt
|
|
11178
12407
|
},
|
|
11179
12408
|
// Outlook often returns no id, so emit unconditionally.
|
|
11180
12409
|
events: [{ name: OUTLOOK_SENT_EVENT, payload: { messageId, to_email: values.to_email ?? "" } }]
|
|
@@ -11191,155 +12420,927 @@ var SLACK_SEND_SCHEMA = {
|
|
|
11191
12420
|
description: "Post a message to a Slack channel from the template author's Slack account.",
|
|
11192
12421
|
parameters: {
|
|
11193
12422
|
type: "object",
|
|
11194
|
-
required: ["channel"],
|
|
12423
|
+
required: ["channel"],
|
|
12424
|
+
properties: {
|
|
12425
|
+
channel: { type: "string", title: "Channel", description: "Channel ID or name, e.g. #general or C0123456." },
|
|
12426
|
+
markdown_text: {
|
|
12427
|
+
type: "string",
|
|
12428
|
+
title: "Message",
|
|
12429
|
+
description: "Message text in Slack markdown. Preferred over the deprecated plain text field."
|
|
12430
|
+
},
|
|
12431
|
+
thread_ts: { type: "string", title: "Thread", description: "Optional parent message timestamp to reply within a thread." }
|
|
12432
|
+
}
|
|
12433
|
+
}
|
|
12434
|
+
};
|
|
12435
|
+
var SLACK_SEND_OUTPUT_SCHEMA = [
|
|
12436
|
+
{ path: "messageTs", displayName: "Message ts", type: "string" },
|
|
12437
|
+
{ path: "channel", displayName: "Channel", type: "string" },
|
|
12438
|
+
{ path: "sentAt", displayName: "Sent At", type: "string", description: "ISO timestamp when sent" },
|
|
12439
|
+
{ path: "providerReceiptId", displayName: "Provider Receipt ID", type: "string", description: "Signed integration-host invocation receipt identifier" },
|
|
12440
|
+
{ path: "providerInvocationReceipt", displayName: "Provider Receipt", type: "object", description: "Signed integration-host invocation receipt" }
|
|
12441
|
+
];
|
|
12442
|
+
|
|
12443
|
+
// src/core/lib/actionRegistry/actions/slack/messageSend.ts
|
|
12444
|
+
registerAction({
|
|
12445
|
+
type: "qi/slack.message.send",
|
|
12446
|
+
can: "slack.message/send",
|
|
12447
|
+
sideEffect: true,
|
|
12448
|
+
// Proof of execution: Slack returns the posted message timestamp (ts).
|
|
12449
|
+
proof: { fields: ["messageTs"] },
|
|
12450
|
+
done: doneWhenCompleted,
|
|
12451
|
+
defaultRequiresConfirmation: true,
|
|
12452
|
+
requiredCapability: "flow/block/execute",
|
|
12453
|
+
// Can be wired to another block's event (e.g. form submitted → post message).
|
|
12454
|
+
eligibleForEventTrigger: true,
|
|
12455
|
+
inputSchema: delegatedToolInputSchema(SLACK_SEND_SCHEMA),
|
|
12456
|
+
// Mirrors executeDelegatedTool's gates: the bound connection plus the
|
|
12457
|
+
// tool schema's required fields, so orchestrators ask before run() throws.
|
|
12458
|
+
getMissingInputs: (inputs) => delegatedToolMissingInputs(SLACK_SEND_SCHEMA, inputs),
|
|
12459
|
+
outputSchema: SLACK_SEND_OUTPUT_SCHEMA,
|
|
12460
|
+
events: [
|
|
12461
|
+
{
|
|
12462
|
+
name: SLACK_SENT_EVENT,
|
|
12463
|
+
displayName: "Message posted",
|
|
12464
|
+
description: "Fired after the message is posted to Slack.",
|
|
12465
|
+
payloadSchema: [
|
|
12466
|
+
{ path: "messageTs", displayName: "Message ts", type: "string" },
|
|
12467
|
+
{ path: "channel", displayName: "Channel", type: "string" }
|
|
12468
|
+
],
|
|
12469
|
+
pendingDisplayFields: ["messageTs"]
|
|
12470
|
+
}
|
|
12471
|
+
],
|
|
12472
|
+
run: async (inputs, ctx) => {
|
|
12473
|
+
const parsed = parseDelegatedToolInputs(inputs);
|
|
12474
|
+
const values = fieldValues(parsed);
|
|
12475
|
+
const execution = await executeDelegatedTool(ctx, {
|
|
12476
|
+
connection: parsed.connection,
|
|
12477
|
+
schema: SLACK_SEND_SCHEMA,
|
|
12478
|
+
toolSlug: SLACK_SEND_SLUG,
|
|
12479
|
+
values,
|
|
12480
|
+
toolkitLabel: "Slack"
|
|
12481
|
+
});
|
|
12482
|
+
const { data, providerInvocationReceipt } = execution;
|
|
12483
|
+
const envelope = data.response_data ?? data;
|
|
12484
|
+
const messageTs = String(envelope.ts ?? "");
|
|
12485
|
+
const channel = String(envelope.channel ?? values.channel ?? "");
|
|
12486
|
+
return {
|
|
12487
|
+
output: {
|
|
12488
|
+
messageTs,
|
|
12489
|
+
channel,
|
|
12490
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12491
|
+
providerReceiptId: providerInvocationReceipt?.id || "",
|
|
12492
|
+
providerInvocationReceipt
|
|
12493
|
+
},
|
|
12494
|
+
events: messageTs ? [{ name: SLACK_SENT_EVENT, payload: { messageTs, channel } }] : void 0
|
|
12495
|
+
};
|
|
12496
|
+
}
|
|
12497
|
+
});
|
|
12498
|
+
|
|
12499
|
+
// src/core/lib/actionRegistry/actions/googlecalendar/eventCreate.types.ts
|
|
12500
|
+
var GOOGLECALENDAR_CREATE_SLUG = "GOOGLECALENDAR_CREATE_EVENT";
|
|
12501
|
+
var GOOGLECALENDAR_CREATED_EVENT = "event.created";
|
|
12502
|
+
var GOOGLECALENDAR_CREATE_SCHEMA = {
|
|
12503
|
+
slug: GOOGLECALENDAR_CREATE_SLUG,
|
|
12504
|
+
name: "Create Event",
|
|
12505
|
+
description: "Create an event on the template author's Google Calendar.",
|
|
12506
|
+
parameters: {
|
|
12507
|
+
type: "object",
|
|
12508
|
+
required: ["start_datetime"],
|
|
12509
|
+
properties: {
|
|
12510
|
+
start_datetime: { type: "string", title: "Start time", description: "ISO 8601, e.g. 2026-05-12T09:00:00." },
|
|
12511
|
+
summary: { type: "string", title: "Title" },
|
|
12512
|
+
description: { type: "string", title: "Description" },
|
|
12513
|
+
location: { type: "string", title: "Location" },
|
|
12514
|
+
timezone: { type: "string", title: "Timezone", description: "IANA name, e.g. Europe/London." },
|
|
12515
|
+
attendees: { type: "array", title: "Attendees", description: "Comma-separated email addresses." },
|
|
12516
|
+
calendar_id: { type: "string", title: "Calendar", description: "Use 'primary' for the author's main calendar." },
|
|
12517
|
+
event_duration_minutes: { type: "number", title: "Duration (minutes)", description: "Defaults to the calendar default if blank." }
|
|
12518
|
+
}
|
|
12519
|
+
}
|
|
12520
|
+
};
|
|
12521
|
+
var GOOGLECALENDAR_CREATE_OUTPUT_SCHEMA = [
|
|
12522
|
+
{ path: "eventId", displayName: "Event ID", type: "string" },
|
|
12523
|
+
{ path: "htmlLink", displayName: "Event link", type: "string" },
|
|
12524
|
+
{ path: "summary", displayName: "Summary", type: "string" },
|
|
12525
|
+
{ path: "startIso", displayName: "Start", type: "string" },
|
|
12526
|
+
{ path: "providerReceiptId", displayName: "Provider Receipt ID", type: "string", description: "Signed integration-host invocation receipt identifier" },
|
|
12527
|
+
{ path: "providerInvocationReceipt", displayName: "Provider Receipt", type: "object", description: "Signed integration-host invocation receipt" }
|
|
12528
|
+
];
|
|
12529
|
+
|
|
12530
|
+
// src/core/lib/actionRegistry/actions/googlecalendar/eventCreate.ts
|
|
12531
|
+
registerAction({
|
|
12532
|
+
type: "qi/googlecalendar.event.create",
|
|
12533
|
+
can: "googlecalendar.event/create",
|
|
12534
|
+
sideEffect: true,
|
|
12535
|
+
// Proof of execution: the created event's id. Matches qi/calendar.event.create.
|
|
12536
|
+
proof: { fields: ["eventId"] },
|
|
12537
|
+
done: doneWhenCompleted,
|
|
12538
|
+
defaultRequiresConfirmation: true,
|
|
12539
|
+
requiredCapability: "flow/block/execute",
|
|
12540
|
+
eligibleForEventTrigger: true,
|
|
12541
|
+
inputSchema: delegatedToolInputSchema(GOOGLECALENDAR_CREATE_SCHEMA),
|
|
12542
|
+
// Mirrors executeDelegatedTool's gates: the bound connection plus the
|
|
12543
|
+
// tool schema's required fields, so orchestrators ask before run() throws.
|
|
12544
|
+
getMissingInputs: (inputs) => delegatedToolMissingInputs(GOOGLECALENDAR_CREATE_SCHEMA, inputs),
|
|
12545
|
+
outputSchema: GOOGLECALENDAR_CREATE_OUTPUT_SCHEMA,
|
|
12546
|
+
events: [
|
|
12547
|
+
{
|
|
12548
|
+
name: GOOGLECALENDAR_CREATED_EVENT,
|
|
12549
|
+
displayName: "Calendar event created",
|
|
12550
|
+
description: "Fired after the event is created on the author\u2019s calendar.",
|
|
12551
|
+
payloadSchema: [
|
|
12552
|
+
{ path: "eventId", displayName: "Event ID", type: "string" },
|
|
12553
|
+
{ path: "htmlLink", displayName: "Event link", type: "string" },
|
|
12554
|
+
{ path: "summary", displayName: "Summary", type: "string" }
|
|
12555
|
+
],
|
|
12556
|
+
pendingDisplayFields: ["summary", "eventId"]
|
|
12557
|
+
}
|
|
12558
|
+
],
|
|
12559
|
+
run: async (inputs, ctx) => {
|
|
12560
|
+
const parsed = parseDelegatedToolInputs(inputs);
|
|
12561
|
+
const values = fieldValues(parsed);
|
|
12562
|
+
const execution = await executeDelegatedTool(ctx, {
|
|
12563
|
+
connection: parsed.connection,
|
|
12564
|
+
schema: GOOGLECALENDAR_CREATE_SCHEMA,
|
|
12565
|
+
toolSlug: GOOGLECALENDAR_CREATE_SLUG,
|
|
12566
|
+
values,
|
|
12567
|
+
toolkitLabel: "Google Calendar"
|
|
12568
|
+
});
|
|
12569
|
+
const { data, providerInvocationReceipt } = execution;
|
|
12570
|
+
const envelope = data.response_data ?? data;
|
|
12571
|
+
const eventId = String(envelope.id ?? "");
|
|
12572
|
+
const htmlLink = String(envelope.htmlLink ?? "");
|
|
12573
|
+
const summary = String(envelope.summary ?? values.summary ?? "");
|
|
12574
|
+
const start = envelope.start;
|
|
12575
|
+
const startIso = String(start?.dateTime ?? start?.date ?? values.start_datetime ?? "");
|
|
12576
|
+
return {
|
|
12577
|
+
output: { eventId, htmlLink, summary, startIso, providerReceiptId: providerInvocationReceipt?.id || "", providerInvocationReceipt },
|
|
12578
|
+
events: eventId ? [{ name: GOOGLECALENDAR_CREATED_EVENT, payload: { eventId, htmlLink, summary } }] : void 0
|
|
12579
|
+
};
|
|
12580
|
+
}
|
|
12581
|
+
});
|
|
12582
|
+
|
|
12583
|
+
// src/core/lib/actionRegistry/actions/topicActions.ts
|
|
12584
|
+
var ALL_KINDS2 = ["task", "agent_task", "proposal", "evaluation", "claims", "question", "discussion", "incident"];
|
|
12585
|
+
function topicMetadata(supportedBaseKinds, permittedTopicRecordTypes, requiredTopicAbilities, relevance = "recommended", sensitiveInputPaths = [], sensitiveOutputPaths = []) {
|
|
12586
|
+
const semanticRecordTypes = getTopicSemanticRecordDefinitions(permittedTopicRecordTypes);
|
|
12587
|
+
if (semanticRecordTypes.length !== permittedTopicRecordTypes.length) {
|
|
12588
|
+
throw new Error(`Topic Action metadata names a semantic record type without a complete definition`);
|
|
12589
|
+
}
|
|
12590
|
+
return {
|
|
12591
|
+
supportedBaseKinds,
|
|
12592
|
+
relevance,
|
|
12593
|
+
writeBackMode: permittedTopicRecordTypes.length > 0 ? "semantic-record" : "receipt-only",
|
|
12594
|
+
semanticRecordTypes,
|
|
12595
|
+
permittedTopicRecordTypes: semanticRecordTypes.map((definition) => definition.type),
|
|
12596
|
+
lifecycleEffect: "none",
|
|
12597
|
+
requiredTopicAbilities,
|
|
12598
|
+
redactionPolicy: { mode: "paths", sensitiveInputPaths, sensitiveOutputPaths }
|
|
12599
|
+
};
|
|
12600
|
+
}
|
|
12601
|
+
function requiredString(value, name) {
|
|
12602
|
+
const normalized = String(value || "").trim();
|
|
12603
|
+
if (!normalized) throw new Error(`${name} is required`);
|
|
12604
|
+
return normalized;
|
|
12605
|
+
}
|
|
12606
|
+
function requiredArray(value, name) {
|
|
12607
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) throw new Error(`${name} must be an array of strings`);
|
|
12608
|
+
return value.map(String);
|
|
12609
|
+
}
|
|
12610
|
+
function topicContext(ctx) {
|
|
12611
|
+
if (!ctx.topic) throw new Error("This Action requires a revision-bound Topic execution context");
|
|
12612
|
+
return ctx.topic;
|
|
12613
|
+
}
|
|
12614
|
+
function topicService(ctx) {
|
|
12615
|
+
topicContext(ctx);
|
|
12616
|
+
if (!ctx.services.topic) throw new Error("The host did not grant the capability-checked Topic service");
|
|
12617
|
+
return ctx.services.topic;
|
|
12618
|
+
}
|
|
12619
|
+
function idempotencyKey(actionType, inputs, ctx) {
|
|
12620
|
+
const explicit = String(inputs.idempotencyKey || "").trim();
|
|
12621
|
+
if (explicit) return explicit;
|
|
12622
|
+
const topic = topicContext(ctx);
|
|
12623
|
+
return sha256Digest({ actionType, topicId: topic.topicId, topicRevision: topic.topicRevision, requestId: topic.requestId, inputs });
|
|
12624
|
+
}
|
|
12625
|
+
function semanticRecord(type, value, ctx) {
|
|
12626
|
+
const topic = topicContext(ctx);
|
|
12627
|
+
const digest2 = sha256Digest(value);
|
|
12628
|
+
return {
|
|
12629
|
+
digest: digest2,
|
|
12630
|
+
record: {
|
|
12631
|
+
type,
|
|
12632
|
+
id: sha256Digest({ type, topicId: topic.topicId, requestId: topic.requestId, digest: digest2 }),
|
|
12633
|
+
version: 1,
|
|
12634
|
+
value,
|
|
12635
|
+
evidenceReferences: Array.isArray(value.evidenceReferences) ? value.evidenceReferences : void 0
|
|
12636
|
+
}
|
|
12637
|
+
};
|
|
12638
|
+
}
|
|
12639
|
+
function registerTopicOperation(spec) {
|
|
12640
|
+
registerAction({
|
|
12641
|
+
type: spec.type,
|
|
12642
|
+
can: spec.can,
|
|
12643
|
+
sideEffect: true,
|
|
12644
|
+
proof: { fields: ["operationId"] },
|
|
12645
|
+
done: doneWhenCompleted,
|
|
12646
|
+
defaultRequiresConfirmation: spec.confirmation === true,
|
|
12647
|
+
requiredCapability: "flow/block/execute",
|
|
12648
|
+
executionOwner: spec.owner || "agent",
|
|
12649
|
+
hiddenFromAuthoring: spec.hidden,
|
|
12650
|
+
riskTier: spec.confirmation ? "high" : "medium",
|
|
12651
|
+
requiredServices: ["topic"],
|
|
12652
|
+
topic: topicMetadata(spec.kinds || ALL_KINDS2, [], [spec.ability]),
|
|
12653
|
+
inputSchema: spec.inputSchema,
|
|
12654
|
+
outputSchema: [
|
|
12655
|
+
{ path: "operationId", displayName: "Topic operation ID", type: "string" },
|
|
12656
|
+
{ path: "topicRevision", displayName: "Topic revision", type: "string" },
|
|
12657
|
+
{ path: "proofReference", displayName: "Operation proof", type: "string" }
|
|
12658
|
+
],
|
|
12659
|
+
run: async (inputs, ctx) => {
|
|
12660
|
+
const service = topicService(ctx);
|
|
12661
|
+
const payload = await spec.buildPayload(inputs, ctx);
|
|
12662
|
+
return {
|
|
12663
|
+
output: await service.appendOperation({
|
|
12664
|
+
context: topicContext(ctx),
|
|
12665
|
+
actorDid: ctx.actorDid,
|
|
12666
|
+
operationType: spec.operationType,
|
|
12667
|
+
payload,
|
|
12668
|
+
idempotencyKey: idempotencyKey(spec.type, inputs, ctx)
|
|
12669
|
+
})
|
|
12670
|
+
};
|
|
12671
|
+
}
|
|
12672
|
+
});
|
|
12673
|
+
}
|
|
12674
|
+
registerTopicOperation({
|
|
12675
|
+
type: "qi/topic.flow.bind",
|
|
12676
|
+
can: "topic/flow.bind",
|
|
12677
|
+
operationType: "bind-flow",
|
|
12678
|
+
confirmation: true,
|
|
12679
|
+
owner: "human",
|
|
12680
|
+
ability: "topic/bind-flow",
|
|
12681
|
+
inputSchema: {
|
|
12682
|
+
type: "object",
|
|
12683
|
+
required: ["flowUri", "flowRevision", "flowDigest", "actionManifestDigest", "controllerDid", "role", "startPolicy", "triggerPolicy", "receiptPolicy"],
|
|
12684
|
+
additionalProperties: false,
|
|
12685
|
+
properties: {
|
|
12686
|
+
bindingId: { type: "string" },
|
|
12687
|
+
flowUri: { type: "string" },
|
|
12688
|
+
flowRevision: { type: "string" },
|
|
12689
|
+
flowDigest: { type: "string", pattern: "^sha256:" },
|
|
12690
|
+
actionManifestDigest: { type: "string", pattern: "^sha256:" },
|
|
12691
|
+
controllerDid: { type: "string", pattern: "^did:" },
|
|
12692
|
+
role: { type: "string", enum: ["primary", "supporting"] },
|
|
12693
|
+
startPolicy: { type: "string", enum: ["manual", "on-topic-active", "scheduled", "event"] },
|
|
12694
|
+
triggerPolicy: { type: "object" },
|
|
12695
|
+
receiptPolicy: { type: "string", enum: ["all", "terminal"] },
|
|
12696
|
+
capabilityReferences: { type: "array", minItems: 2, uniqueItems: true, items: { type: "string" } },
|
|
12697
|
+
idempotencyKey: { type: "string" }
|
|
12698
|
+
}
|
|
12699
|
+
},
|
|
12700
|
+
buildPayload: (inputs, ctx) => {
|
|
12701
|
+
const topic = topicContext(ctx);
|
|
12702
|
+
return {
|
|
12703
|
+
binding: {
|
|
12704
|
+
version: 1,
|
|
12705
|
+
bindingId: String(inputs.bindingId || sha256Digest({ topicId: topic.topicId, flowUri: inputs.flowUri, flowRevision: inputs.flowRevision })),
|
|
12706
|
+
topicId: topic.topicId,
|
|
12707
|
+
flowUri: requiredString(inputs.flowUri, "flowUri"),
|
|
12708
|
+
flowRevision: requiredString(inputs.flowRevision, "flowRevision"),
|
|
12709
|
+
flowDigest: requiredString(inputs.flowDigest, "flowDigest"),
|
|
12710
|
+
actionManifestDigest: requiredString(inputs.actionManifestDigest, "actionManifestDigest"),
|
|
12711
|
+
controllerDid: requiredString(inputs.controllerDid, "controllerDid"),
|
|
12712
|
+
role: requiredString(inputs.role, "role"),
|
|
12713
|
+
startPolicy: requiredString(inputs.startPolicy, "startPolicy"),
|
|
12714
|
+
triggerPolicy: inputs.triggerPolicy || {},
|
|
12715
|
+
receiptPolicy: requiredString(inputs.receiptPolicy, "receiptPolicy"),
|
|
12716
|
+
status: "active",
|
|
12717
|
+
capability: topic.topicCapabilityReference,
|
|
12718
|
+
capabilityReferences: Array.isArray(inputs.capabilityReferences) ? inputs.capabilityReferences.map(String) : [],
|
|
12719
|
+
createdBy: ctx.actorDid,
|
|
12720
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
12721
|
+
}
|
|
12722
|
+
};
|
|
12723
|
+
}
|
|
12724
|
+
});
|
|
12725
|
+
registerTopicOperation({
|
|
12726
|
+
type: "qi/topic.flow.unbind",
|
|
12727
|
+
can: "topic/flow.unbind",
|
|
12728
|
+
operationType: "unbind-flow",
|
|
12729
|
+
confirmation: true,
|
|
12730
|
+
owner: "human",
|
|
12731
|
+
ability: "topic/bind-flow",
|
|
12732
|
+
inputSchema: {
|
|
12733
|
+
type: "object",
|
|
12734
|
+
required: ["bindingId"],
|
|
12735
|
+
additionalProperties: false,
|
|
12736
|
+
properties: { bindingId: { type: "string" }, reason: { type: "string" }, idempotencyKey: { type: "string" } }
|
|
12737
|
+
},
|
|
12738
|
+
buildPayload: (inputs) => ({ bindingId: requiredString(inputs.bindingId, "bindingId"), reason: String(inputs.reason || "") })
|
|
12739
|
+
});
|
|
12740
|
+
registerTopicOperation({
|
|
12741
|
+
type: "qi/topic.action.request",
|
|
12742
|
+
can: "topic/action.request",
|
|
12743
|
+
operationType: "request-action",
|
|
12744
|
+
ability: "topic/request-action",
|
|
12745
|
+
inputSchema: {
|
|
12746
|
+
type: "object",
|
|
12747
|
+
required: ["actionType", "actionContractDigest", "inputDigest"],
|
|
12748
|
+
additionalProperties: false,
|
|
12749
|
+
properties: {
|
|
12750
|
+
actionType: { type: "string" },
|
|
12751
|
+
actionContractDigest: { type: "string", pattern: "^sha256:" },
|
|
12752
|
+
inputDigest: { type: "string", pattern: "^sha256:" },
|
|
12753
|
+
inputReference: { type: "string" },
|
|
12754
|
+
requestId: { type: "string" },
|
|
12755
|
+
safeInputSummary: { type: "object" },
|
|
12756
|
+
executorPreference: { type: "string", enum: ["qi-flow", "qiforge", "mcp"] },
|
|
12757
|
+
bindingId: { type: "string" },
|
|
12758
|
+
confirmationPolicy: { type: "string", enum: ["inherit", "required"] },
|
|
12759
|
+
idempotencyKey: { type: "string" }
|
|
12760
|
+
}
|
|
12761
|
+
},
|
|
12762
|
+
buildPayload: (inputs, ctx) => {
|
|
12763
|
+
const actionType = requiredString(inputs.actionType, "actionType");
|
|
12764
|
+
if (actionType === "qi/topic.action.request") throw new Error("A Topic Action request cannot recursively request itself");
|
|
12765
|
+
const target = getAction(actionType);
|
|
12766
|
+
if (!target) throw new Error(`Unknown Action type '${actionType}'`);
|
|
12767
|
+
const manifestEntry = generateActionManifest().actions.find((entry) => entry.type === target.type);
|
|
12768
|
+
const suppliedDigest = requiredString(inputs.actionContractDigest, "actionContractDigest");
|
|
12769
|
+
if (!manifestEntry || manifestEntry.contractDigest !== suppliedDigest) throw new Error("Action contract digest does not match the live registry");
|
|
12770
|
+
const kind = topicContext(ctx).kind;
|
|
12771
|
+
const baseKind = kind.source === "standard" ? kind.kind : kind.baseKind;
|
|
12772
|
+
if (!target.topic?.supportedBaseKinds.includes(baseKind)) throw new Error(`Action '${actionType}' does not support Topic base Kind '${baseKind}'`);
|
|
12773
|
+
const topic = topicContext(ctx);
|
|
12774
|
+
const inputDigest = requiredString(inputs.inputDigest, "inputDigest");
|
|
12775
|
+
const derived = (purpose) => sha256Digest({ purpose, parentRequestId: topic.requestId, topicId: topic.topicId, actionType: target.type, inputDigest });
|
|
12776
|
+
return {
|
|
12777
|
+
request: {
|
|
12778
|
+
version: 1,
|
|
12779
|
+
requestId: String(inputs.requestId || derived("topic-action-request")),
|
|
12780
|
+
topicId: topic.topicId,
|
|
12781
|
+
topicRevision: topic.topicRevision,
|
|
12782
|
+
actionType: target.type,
|
|
12783
|
+
actionContractDigest: suppliedDigest,
|
|
12784
|
+
executor: inputs.executorPreference || "qi-flow",
|
|
12785
|
+
inputs: { digest: inputDigest, ...inputs.inputReference ? { ref: String(inputs.inputReference) } : {} },
|
|
12786
|
+
requestedBy: ctx.actorDid,
|
|
12787
|
+
requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12788
|
+
idempotencyKey: String(inputs.idempotencyKey || derived("topic-action-idempotency")),
|
|
12789
|
+
confirmation: target.defaultRequiresConfirmation || inputs.confirmationPolicy === "required" ? "required" : topic.confirmationReference ? "confirmed" : "not-required",
|
|
12790
|
+
...inputs.bindingId ? { flowBindingId: String(inputs.bindingId) } : {},
|
|
12791
|
+
capability: topic.topicCapabilityReference
|
|
12792
|
+
}
|
|
12793
|
+
};
|
|
12794
|
+
}
|
|
12795
|
+
});
|
|
12796
|
+
registerTopicOperation({
|
|
12797
|
+
type: "qi/topic.action.receipt.record",
|
|
12798
|
+
can: "topic/action.receipt.record",
|
|
12799
|
+
operationType: "record-action-receipt",
|
|
12800
|
+
ability: "topic/record-action",
|
|
12801
|
+
hidden: true,
|
|
12802
|
+
inputSchema: {
|
|
12803
|
+
type: "object",
|
|
12804
|
+
required: ["receipt"],
|
|
12805
|
+
additionalProperties: false,
|
|
12806
|
+
properties: { receipt: { type: "object" }, idempotencyKey: { type: "string" } }
|
|
12807
|
+
},
|
|
12808
|
+
buildPayload: (inputs, ctx) => {
|
|
12809
|
+
if (!inputs.receipt || typeof inputs.receipt !== "object" || Array.isArray(inputs.receipt)) throw new Error("receipt must be an ActionReceiptV2 object");
|
|
12810
|
+
const receipt = inputs.receipt;
|
|
12811
|
+
if (receipt.topicId !== topicContext(ctx).topicId) throw new Error("Receipt Topic does not match the execution context");
|
|
12812
|
+
if (receipt.version !== 2 || !receipt.signature || !receipt.issuerDid) throw new Error("Receipt requires a v2 issuer signature");
|
|
12813
|
+
const receiptAction = receipt.action;
|
|
12814
|
+
const action = getAction(String(receiptAction?.type || ""));
|
|
12815
|
+
const manifestEntry = action && generateActionManifest().actions.find((entry) => entry.type === action.type);
|
|
12816
|
+
if (!manifestEntry || manifestEntry.contractDigest !== receiptAction?.contractDigest) throw new Error("Receipt Action contract digest does not match the live registry");
|
|
12817
|
+
return { receipt };
|
|
12818
|
+
}
|
|
12819
|
+
});
|
|
12820
|
+
registerTopicOperation({
|
|
12821
|
+
type: "qi/topic.action.cancel",
|
|
12822
|
+
can: "topic/action.cancel",
|
|
12823
|
+
operationType: "cancel-action",
|
|
12824
|
+
ability: "topic/cancel-action",
|
|
12825
|
+
inputSchema: {
|
|
12826
|
+
type: "object",
|
|
12827
|
+
required: ["requestId", "reason"],
|
|
12828
|
+
additionalProperties: false,
|
|
12829
|
+
properties: { requestId: { type: "string" }, reason: { type: "string" }, idempotencyKey: { type: "string" } }
|
|
12830
|
+
},
|
|
12831
|
+
buildPayload: (inputs) => ({ requestId: requiredString(inputs.requestId, "requestId"), reason: requiredString(inputs.reason, "reason") })
|
|
12832
|
+
});
|
|
12833
|
+
registerTopicOperation({
|
|
12834
|
+
type: "qi/topic.status.transition",
|
|
12835
|
+
can: "topic/status.transition",
|
|
12836
|
+
operationType: "change-status",
|
|
12837
|
+
confirmation: true,
|
|
12838
|
+
owner: "human",
|
|
12839
|
+
ability: "topic/change-status",
|
|
12840
|
+
inputSchema: {
|
|
12841
|
+
type: "object",
|
|
12842
|
+
required: ["from", "to", "reason"],
|
|
12843
|
+
additionalProperties: false,
|
|
12844
|
+
properties: { from: { type: "string" }, to: { type: "string" }, reason: { type: "string" }, idempotencyKey: { type: "string" } }
|
|
12845
|
+
},
|
|
12846
|
+
buildPayload: async (inputs, ctx) => {
|
|
12847
|
+
const from = requiredString(inputs.from, "from");
|
|
12848
|
+
const to = requiredString(inputs.to, "to");
|
|
12849
|
+
if (to === "resolved") {
|
|
12850
|
+
const topic = topicContext(ctx);
|
|
12851
|
+
const projection = await topicService(ctx).readProjection?.({ topicId: topic.topicId, topicRevision: topic.topicRevision, requestId: topic.requestId });
|
|
12852
|
+
if (!projection) throw new Error("Resolution requires a current Topic projection so completion policy can be verified");
|
|
12853
|
+
const completion = projection.completion;
|
|
12854
|
+
const outcome = projection.outcome;
|
|
12855
|
+
if (completion?.requiresOutcomeRecord === true && !outcome?.outcomeRecordId) throw new Error("Topic policy requires an accepted outcome record before resolution");
|
|
12856
|
+
}
|
|
12857
|
+
return { from, to, reason: requiredString(inputs.reason, "reason") };
|
|
12858
|
+
}
|
|
12859
|
+
});
|
|
12860
|
+
registerTopicOperation({
|
|
12861
|
+
type: "qi/topic.contract.accept",
|
|
12862
|
+
can: "topic/contract.accept",
|
|
12863
|
+
operationType: "accept-contract",
|
|
12864
|
+
confirmation: true,
|
|
12865
|
+
owner: "human",
|
|
12866
|
+
ability: "topic/accept-contract",
|
|
12867
|
+
inputSchema: {
|
|
12868
|
+
type: "object",
|
|
12869
|
+
required: ["contractRevision", "contractDigest", "confirmationReference"],
|
|
12870
|
+
additionalProperties: false,
|
|
12871
|
+
properties: {
|
|
12872
|
+
contractRevision: { type: "string" },
|
|
12873
|
+
contractDigest: { type: "string", pattern: "^sha256:" },
|
|
12874
|
+
confirmationReference: { type: "string" },
|
|
12875
|
+
idempotencyKey: { type: "string" }
|
|
12876
|
+
}
|
|
12877
|
+
},
|
|
12878
|
+
buildPayload: (inputs, ctx) => {
|
|
12879
|
+
const revision = requiredString(inputs.contractRevision, "contractRevision");
|
|
12880
|
+
const digest2 = requiredString(inputs.contractDigest, "contractDigest");
|
|
12881
|
+
if (revision !== topicContext(ctx).contract.revision || digest2 !== topicContext(ctx).contract.digest)
|
|
12882
|
+
throw new Error("Contract acceptance must target the exact effective revision and digest");
|
|
12883
|
+
return { contractRevision: revision, contractDigest: digest2, confirmationReference: requiredString(inputs.confirmationReference, "confirmationReference") };
|
|
12884
|
+
}
|
|
12885
|
+
});
|
|
12886
|
+
registerTopicOperation({
|
|
12887
|
+
type: "qi/topic.outcome.propose",
|
|
12888
|
+
can: "topic/outcome.propose",
|
|
12889
|
+
operationType: "update-contract",
|
|
12890
|
+
ability: "topic/update-contract",
|
|
12891
|
+
inputSchema: {
|
|
12892
|
+
type: "object",
|
|
12893
|
+
required: ["statement"],
|
|
12894
|
+
additionalProperties: false,
|
|
12895
|
+
properties: { statement: { type: "string" }, evidenceReferences: { type: "array", items: { type: "string" } }, idempotencyKey: { type: "string" } }
|
|
12896
|
+
},
|
|
12897
|
+
buildPayload: (inputs) => ({
|
|
12898
|
+
patch: {
|
|
12899
|
+
outcome: {
|
|
12900
|
+
statement: requiredString(inputs.statement, "statement"),
|
|
12901
|
+
status: "proposed",
|
|
12902
|
+
evidenceReferences: requiredArray(inputs.evidenceReferences || [], "evidenceReferences")
|
|
12903
|
+
}
|
|
12904
|
+
}
|
|
12905
|
+
})
|
|
12906
|
+
});
|
|
12907
|
+
registerTopicOperation({
|
|
12908
|
+
type: "qi/topic.outcome.confirm",
|
|
12909
|
+
can: "topic/outcome.confirm",
|
|
12910
|
+
operationType: "update-contract",
|
|
12911
|
+
confirmation: true,
|
|
12912
|
+
owner: "human",
|
|
12913
|
+
ability: "topic/update-contract",
|
|
12914
|
+
inputSchema: {
|
|
12915
|
+
type: "object",
|
|
12916
|
+
required: ["proposedOutcomeRecordId", "confirmationAuthorityDid", "confirmationReference"],
|
|
12917
|
+
additionalProperties: false,
|
|
12918
|
+
properties: {
|
|
12919
|
+
proposedOutcomeRecordId: { type: "string" },
|
|
12920
|
+
confirmationAuthorityDid: { type: "string", pattern: "^did:" },
|
|
12921
|
+
confirmationReference: { type: "string" },
|
|
12922
|
+
idempotencyKey: { type: "string" }
|
|
12923
|
+
}
|
|
12924
|
+
},
|
|
12925
|
+
buildPayload: (inputs) => ({
|
|
12926
|
+
patch: {
|
|
12927
|
+
outcome: {
|
|
12928
|
+
status: "achieved",
|
|
12929
|
+
outcomeRecordId: requiredString(inputs.proposedOutcomeRecordId, "proposedOutcomeRecordId"),
|
|
12930
|
+
confirmedBy: requiredString(inputs.confirmationAuthorityDid, "confirmationAuthorityDid"),
|
|
12931
|
+
confirmationReference: requiredString(inputs.confirmationReference, "confirmationReference")
|
|
12932
|
+
}
|
|
12933
|
+
}
|
|
12934
|
+
})
|
|
12935
|
+
});
|
|
12936
|
+
registerTopicOperation({
|
|
12937
|
+
type: "qi/topic.decision.record",
|
|
12938
|
+
can: "topic/decision.record",
|
|
12939
|
+
operationType: "record-decision",
|
|
12940
|
+
ability: "topic/record-decision",
|
|
12941
|
+
inputSchema: {
|
|
12942
|
+
type: "object",
|
|
12943
|
+
required: ["decision", "authorityDid", "rationale"],
|
|
12944
|
+
additionalProperties: false,
|
|
12945
|
+
properties: {
|
|
12946
|
+
decision: { type: "string" },
|
|
12947
|
+
authorityDid: { type: "string", pattern: "^did:" },
|
|
12948
|
+
rationale: { type: "string" },
|
|
12949
|
+
alternatives: { type: "array", items: { type: "string" } },
|
|
12950
|
+
receiptReferences: { type: "array", items: { type: "string" } },
|
|
12951
|
+
evidenceReferences: { type: "array", items: { type: "string" } },
|
|
12952
|
+
idempotencyKey: { type: "string" }
|
|
12953
|
+
}
|
|
12954
|
+
},
|
|
12955
|
+
buildPayload: (inputs) => ({
|
|
12956
|
+
decision: requiredString(inputs.decision, "decision"),
|
|
12957
|
+
authorityDid: requiredString(inputs.authorityDid, "authorityDid"),
|
|
12958
|
+
rationale: requiredString(inputs.rationale, "rationale"),
|
|
12959
|
+
alternatives: requiredArray(inputs.alternatives || [], "alternatives"),
|
|
12960
|
+
receiptReferences: requiredArray(inputs.receiptReferences || [], "receiptReferences"),
|
|
12961
|
+
evidenceReferences: requiredArray(inputs.evidenceReferences || [], "evidenceReferences")
|
|
12962
|
+
})
|
|
12963
|
+
});
|
|
12964
|
+
registerTopicOperation({
|
|
12965
|
+
type: "qi/topic.context.link",
|
|
12966
|
+
can: "topic/context.link",
|
|
12967
|
+
operationType: "link-context",
|
|
12968
|
+
ability: "topic/link-context",
|
|
12969
|
+
inputSchema: {
|
|
12970
|
+
type: "object",
|
|
12971
|
+
required: ["contextType", "id"],
|
|
12972
|
+
additionalProperties: false,
|
|
12973
|
+
properties: {
|
|
12974
|
+
contextType: { type: "string", enum: ["ixo.resource", "ixo.flow", "matrix.conversation", "ixo.entity", "ixo.service"] },
|
|
12975
|
+
id: { type: "string" },
|
|
12976
|
+
label: { type: "string" },
|
|
12977
|
+
reference: { type: "string" },
|
|
12978
|
+
idempotencyKey: { type: "string" }
|
|
12979
|
+
}
|
|
12980
|
+
},
|
|
12981
|
+
buildPayload: (inputs) => ({
|
|
12982
|
+
type: requiredString(inputs.contextType, "contextType"),
|
|
12983
|
+
id: requiredString(inputs.id, "id"),
|
|
12984
|
+
label: String(inputs.label || ""),
|
|
12985
|
+
reference: String(inputs.reference || "")
|
|
12986
|
+
})
|
|
12987
|
+
});
|
|
12988
|
+
registerTopicOperation({
|
|
12989
|
+
type: "qi/topic.file.attach-reference",
|
|
12990
|
+
can: "topic/file.attach-reference",
|
|
12991
|
+
operationType: "attach-files",
|
|
12992
|
+
ability: "topic/attach-files",
|
|
12993
|
+
inputSchema: {
|
|
12994
|
+
type: "object",
|
|
12995
|
+
required: ["resource", "fileId", "version", "cid", "contentHash", "path", "name", "mimeType", "size"],
|
|
12996
|
+
additionalProperties: false,
|
|
12997
|
+
properties: {
|
|
12998
|
+
resource: { type: "string" },
|
|
12999
|
+
fileId: { type: "string" },
|
|
13000
|
+
version: { type: "number" },
|
|
13001
|
+
cid: { type: "string" },
|
|
13002
|
+
contentHash: { type: "string" },
|
|
13003
|
+
path: { type: "string" },
|
|
13004
|
+
name: { type: "string" },
|
|
13005
|
+
mimeType: { type: "string" },
|
|
13006
|
+
size: { type: "number" },
|
|
13007
|
+
idempotencyKey: { type: "string" }
|
|
13008
|
+
}
|
|
13009
|
+
},
|
|
13010
|
+
buildPayload: (inputs) => {
|
|
13011
|
+
if ("bytes" in inputs || "content" in inputs || "capability" in inputs)
|
|
13012
|
+
throw new Error("Topic file Actions accept pinned references only; bytes and access grants are forbidden");
|
|
13013
|
+
return {
|
|
13014
|
+
attachments: [
|
|
13015
|
+
{
|
|
13016
|
+
provider: "ixo.vfs",
|
|
13017
|
+
resource: inputs.resource,
|
|
13018
|
+
fileId: inputs.fileId,
|
|
13019
|
+
version: inputs.version,
|
|
13020
|
+
cid: inputs.cid,
|
|
13021
|
+
contentHash: inputs.contentHash,
|
|
13022
|
+
path: inputs.path,
|
|
13023
|
+
name: inputs.name,
|
|
13024
|
+
mimeType: inputs.mimeType,
|
|
13025
|
+
size: inputs.size
|
|
13026
|
+
}
|
|
13027
|
+
]
|
|
13028
|
+
};
|
|
13029
|
+
}
|
|
13030
|
+
});
|
|
13031
|
+
function registerSemanticAction(spec) {
|
|
13032
|
+
registerAction({
|
|
13033
|
+
type: spec.type,
|
|
13034
|
+
can: spec.can,
|
|
13035
|
+
sideEffect: true,
|
|
13036
|
+
proof: { fields: ["recordDigest"] },
|
|
13037
|
+
done: doneWhenCompleted,
|
|
13038
|
+
defaultRequiresConfirmation: spec.confirmation === true,
|
|
13039
|
+
requiredCapability: "flow/block/execute",
|
|
13040
|
+
executionOwner: spec.owner || "agent",
|
|
13041
|
+
riskTier: spec.riskTier,
|
|
13042
|
+
requiredServices: spec.requiredServices || ["topic"],
|
|
13043
|
+
sensitiveInputPaths: spec.sensitiveInputPaths,
|
|
13044
|
+
sensitiveOutputPaths: spec.sensitiveOutputPaths,
|
|
13045
|
+
topic: topicMetadata(spec.kinds, [spec.recordType], ["topic/request-action", "topic/record-action"], "recommended", spec.sensitiveInputPaths, spec.sensitiveOutputPaths),
|
|
13046
|
+
inputSchema: spec.inputSchema,
|
|
13047
|
+
outputSchema: [
|
|
13048
|
+
{ path: "recordId", displayName: "Semantic record ID", type: "string" },
|
|
13049
|
+
{ path: "recordType", displayName: "Semantic record type", type: "string" },
|
|
13050
|
+
{ path: "recordDigest", displayName: "Semantic record digest", type: "string" },
|
|
13051
|
+
{ path: "record", displayName: "Semantic record", type: "object" }
|
|
13052
|
+
],
|
|
13053
|
+
run: async (inputs, ctx) => {
|
|
13054
|
+
topicContext(ctx);
|
|
13055
|
+
const value = await spec.execute(inputs, ctx);
|
|
13056
|
+
const { record, digest: digest2 } = semanticRecord(spec.recordType, value, ctx);
|
|
13057
|
+
return { output: { recordId: record.id, recordType: record.type, recordDigest: digest2, record: value }, topicRecords: [record] };
|
|
13058
|
+
}
|
|
13059
|
+
});
|
|
13060
|
+
}
|
|
13061
|
+
var WORK_PHASES = {
|
|
13062
|
+
"qi/work.assign": "requested",
|
|
13063
|
+
"qi/work.dispatch": "dispatched",
|
|
13064
|
+
"qi/work.checkpoint": "in_progress",
|
|
13065
|
+
"qi/work.submit": "ready_for_review",
|
|
13066
|
+
"qi/work.accept": "completed"
|
|
13067
|
+
};
|
|
13068
|
+
for (const [type, phase] of Object.entries(WORK_PHASES)) {
|
|
13069
|
+
registerSemanticAction({
|
|
13070
|
+
type,
|
|
13071
|
+
can: type.replace("qi/", "").replace(".", "/"),
|
|
13072
|
+
kinds: ["task", "discussion", "incident"],
|
|
13073
|
+
recordType: "org.ixo.topic.work-event",
|
|
13074
|
+
confirmation: type === "qi/work.accept",
|
|
13075
|
+
owner: type === "qi/work.accept" ? "human" : "agent",
|
|
13076
|
+
inputSchema: {
|
|
13077
|
+
type: "object",
|
|
13078
|
+
required: ["workId", "resourceType"],
|
|
13079
|
+
additionalProperties: false,
|
|
13080
|
+
properties: {
|
|
13081
|
+
workId: { type: "string" },
|
|
13082
|
+
resourceType: { type: "string" },
|
|
13083
|
+
assigneeDid: { type: "string" },
|
|
13084
|
+
note: { type: "string" },
|
|
13085
|
+
artifactReferences: { type: "array", items: { type: "string" } },
|
|
13086
|
+
evidenceReferences: { type: "array", items: { type: "string" } }
|
|
13087
|
+
}
|
|
13088
|
+
},
|
|
13089
|
+
execute: (inputs, ctx) => ({
|
|
13090
|
+
workId: requiredString(inputs.workId, "workId"),
|
|
13091
|
+
resourceType: requiredString(inputs.resourceType, "resourceType"),
|
|
13092
|
+
phase,
|
|
13093
|
+
assigneeDid: String(inputs.assigneeDid || ""),
|
|
13094
|
+
note: String(inputs.note || ""),
|
|
13095
|
+
artifactReferences: requiredArray(inputs.artifactReferences || [], "artifactReferences"),
|
|
13096
|
+
evidenceReferences: requiredArray(inputs.evidenceReferences || [], "evidenceReferences"),
|
|
13097
|
+
actorDid: ctx.actorDid,
|
|
13098
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13099
|
+
})
|
|
13100
|
+
});
|
|
13101
|
+
}
|
|
13102
|
+
registerSemanticAction({
|
|
13103
|
+
type: "qi/agent.invoke",
|
|
13104
|
+
can: "agent/invoke",
|
|
13105
|
+
kinds: ["agent_task", "question", "task"],
|
|
13106
|
+
recordType: "org.ixo.topic.agent-result",
|
|
13107
|
+
requiredServices: ["agents"],
|
|
13108
|
+
sensitiveInputPaths: ["prompt", "toolPolicy"],
|
|
13109
|
+
sensitiveOutputPaths: ["record.result"],
|
|
13110
|
+
inputSchema: {
|
|
13111
|
+
type: "object",
|
|
13112
|
+
required: ["agentDid", "capsuleReference", "toolPolicy"],
|
|
13113
|
+
additionalProperties: false,
|
|
11195
13114
|
properties: {
|
|
11196
|
-
|
|
11197
|
-
|
|
11198
|
-
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
},
|
|
11202
|
-
|
|
13115
|
+
agentDid: { type: "string", pattern: "^did:" },
|
|
13116
|
+
capsuleReference: { type: "string" },
|
|
13117
|
+
toolPolicy: { type: "object" },
|
|
13118
|
+
budget: { type: "object" },
|
|
13119
|
+
deadline: { type: "string" },
|
|
13120
|
+
executorPreference: { type: "string", enum: ["qi-flow", "qiforge", "mcp"] },
|
|
13121
|
+
prompt: { type: "string" }
|
|
11203
13122
|
}
|
|
13123
|
+
},
|
|
13124
|
+
execute: async (inputs, ctx) => {
|
|
13125
|
+
if (!ctx.services.agents) throw new Error("Agent runtime service is not configured");
|
|
13126
|
+
const result = await ctx.services.agents.invoke({
|
|
13127
|
+
agentDid: requiredString(inputs.agentDid, "agentDid"),
|
|
13128
|
+
capsuleReference: requiredString(inputs.capsuleReference, "capsuleReference"),
|
|
13129
|
+
toolPolicy: inputs.toolPolicy || {},
|
|
13130
|
+
budget: inputs.budget,
|
|
13131
|
+
deadline: inputs.deadline,
|
|
13132
|
+
executorPreference: inputs.executorPreference,
|
|
13133
|
+
prompt: inputs.prompt
|
|
13134
|
+
});
|
|
13135
|
+
return {
|
|
13136
|
+
sessionId: result.sessionId,
|
|
13137
|
+
result: result.result,
|
|
13138
|
+
resultDigest: sha256Digest(result.result),
|
|
13139
|
+
evidenceReferences: result.evidenceReferences || [],
|
|
13140
|
+
evidenceDigest: sha256Digest(result.evidenceReferences || []),
|
|
13141
|
+
providerReceiptReference: result.providerReceiptReference
|
|
13142
|
+
};
|
|
11204
13143
|
}
|
|
11205
|
-
};
|
|
11206
|
-
|
|
11207
|
-
|
|
11208
|
-
|
|
11209
|
-
|
|
11210
|
-
|
|
11211
|
-
|
|
11212
|
-
|
|
11213
|
-
|
|
11214
|
-
|
|
11215
|
-
|
|
11216
|
-
|
|
11217
|
-
|
|
11218
|
-
|
|
11219
|
-
|
|
11220
|
-
|
|
11221
|
-
|
|
11222
|
-
|
|
11223
|
-
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11228
|
-
|
|
11229
|
-
{
|
|
11230
|
-
|
|
11231
|
-
|
|
11232
|
-
|
|
11233
|
-
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
pendingDisplayFields: ["messageTs"]
|
|
11238
|
-
}
|
|
11239
|
-
],
|
|
11240
|
-
run: async (inputs, ctx) => {
|
|
11241
|
-
const parsed = parseDelegatedToolInputs(inputs);
|
|
11242
|
-
const values = fieldValues(parsed);
|
|
11243
|
-
const data = await executeDelegatedTool(ctx, {
|
|
11244
|
-
connection: parsed.connection,
|
|
11245
|
-
schema: SLACK_SEND_SCHEMA,
|
|
11246
|
-
toolSlug: SLACK_SEND_SLUG,
|
|
11247
|
-
values,
|
|
11248
|
-
toolkitLabel: "Slack"
|
|
13144
|
+
});
|
|
13145
|
+
registerSemanticAction({
|
|
13146
|
+
type: "qi/agent.cancel",
|
|
13147
|
+
can: "agent/cancel",
|
|
13148
|
+
kinds: ["agent_task", "question", "task"],
|
|
13149
|
+
recordType: "org.ixo.topic.agent-cancellation",
|
|
13150
|
+
requiredServices: ["agents"],
|
|
13151
|
+
inputSchema: { type: "object", required: ["sessionId"], additionalProperties: false, properties: { sessionId: { type: "string" }, reason: { type: "string" } } },
|
|
13152
|
+
execute: async (inputs, ctx) => {
|
|
13153
|
+
if (!ctx.services.agents) throw new Error("Agent runtime service is not configured");
|
|
13154
|
+
return ctx.services.agents.cancel({ sessionId: requiredString(inputs.sessionId, "sessionId"), reason: String(inputs.reason || "") });
|
|
13155
|
+
}
|
|
13156
|
+
});
|
|
13157
|
+
registerSemanticAction({
|
|
13158
|
+
type: "qi/evidence.collect",
|
|
13159
|
+
can: "evidence/collect",
|
|
13160
|
+
kinds: ["question", "evaluation"],
|
|
13161
|
+
recordType: "org.ixo.topic.evidence",
|
|
13162
|
+
requiredServices: ["evidence"],
|
|
13163
|
+
sensitiveOutputPaths: ["record.evidence"],
|
|
13164
|
+
inputSchema: {
|
|
13165
|
+
type: "object",
|
|
13166
|
+
required: ["question", "sourceReferences"],
|
|
13167
|
+
additionalProperties: false,
|
|
13168
|
+
properties: { question: { type: "string" }, sourceReferences: { type: "array", items: { type: "string" } }, constraints: { type: "object" } }
|
|
13169
|
+
},
|
|
13170
|
+
execute: async (inputs, ctx) => {
|
|
13171
|
+
if (!ctx.services.evidence) throw new Error("Evidence service is not configured");
|
|
13172
|
+
const result = await ctx.services.evidence.collect({
|
|
13173
|
+
question: requiredString(inputs.question, "question"),
|
|
13174
|
+
sourceReferences: requiredArray(inputs.sourceReferences, "sourceReferences"),
|
|
13175
|
+
constraints: inputs.constraints
|
|
11249
13176
|
});
|
|
11250
|
-
const envelope = data.response_data ?? data;
|
|
11251
|
-
const messageTs = String(envelope.ts ?? "");
|
|
11252
|
-
const channel = String(envelope.channel ?? values.channel ?? "");
|
|
11253
13177
|
return {
|
|
11254
|
-
|
|
11255
|
-
|
|
11256
|
-
|
|
11257
|
-
|
|
11258
|
-
|
|
11259
|
-
events: messageTs ? [{ name: SLACK_SENT_EVENT, payload: { messageTs, channel } }] : void 0
|
|
13178
|
+
question: inputs.question,
|
|
13179
|
+
evidence: result.evidence,
|
|
13180
|
+
provenance: result.provenance,
|
|
13181
|
+
evidenceReferences: result.evidenceReferences,
|
|
13182
|
+
evidenceDigest: sha256Digest(result.evidence)
|
|
11260
13183
|
};
|
|
11261
13184
|
}
|
|
11262
13185
|
});
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
|
|
11266
|
-
|
|
11267
|
-
|
|
11268
|
-
|
|
11269
|
-
|
|
11270
|
-
|
|
11271
|
-
|
|
13186
|
+
for (const accepted of [false, true]) {
|
|
13187
|
+
registerSemanticAction({
|
|
13188
|
+
type: accepted ? "qi/answer.accept" : "qi/answer.propose",
|
|
13189
|
+
can: accepted ? "answer/accept" : "answer/propose",
|
|
13190
|
+
kinds: ["question"],
|
|
13191
|
+
recordType: accepted ? "org.ixo.topic.accepted-answer" : "org.ixo.topic.proposed-answer",
|
|
13192
|
+
confirmation: accepted,
|
|
13193
|
+
owner: accepted ? "human" : "agent",
|
|
13194
|
+
inputSchema: {
|
|
13195
|
+
type: "object",
|
|
13196
|
+
required: accepted ? ["answer", "acceptanceAuthorityDid", "proposedAnswerRecordId"] : ["answer"],
|
|
13197
|
+
additionalProperties: false,
|
|
13198
|
+
properties: {
|
|
13199
|
+
answer: { type: "string" },
|
|
13200
|
+
proposedAnswerRecordId: { type: "string" },
|
|
13201
|
+
acceptanceAuthorityDid: { type: "string", pattern: "^did:" },
|
|
13202
|
+
evidenceReferences: { type: "array", items: { type: "string" } },
|
|
13203
|
+
limitations: { type: "string" }
|
|
13204
|
+
}
|
|
13205
|
+
},
|
|
13206
|
+
execute: (inputs, ctx) => ({
|
|
13207
|
+
answer: requiredString(inputs.answer, "answer"),
|
|
13208
|
+
status: accepted ? "accepted" : "proposed",
|
|
13209
|
+
proposedAnswerRecordId: String(inputs.proposedAnswerRecordId || ""),
|
|
13210
|
+
authorityDid: accepted ? requiredString(inputs.acceptanceAuthorityDid, "acceptanceAuthorityDid") : ctx.actorDid,
|
|
13211
|
+
evidenceReferences: requiredArray(inputs.evidenceReferences || [], "evidenceReferences"),
|
|
13212
|
+
limitations: String(inputs.limitations || ""),
|
|
13213
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13214
|
+
})
|
|
13215
|
+
});
|
|
13216
|
+
}
|
|
13217
|
+
for (const review of [false, true]) {
|
|
13218
|
+
registerSemanticAction({
|
|
13219
|
+
type: review ? "qi/evaluation.review" : "qi/evaluation.run",
|
|
13220
|
+
can: review ? "evaluation/review" : "evaluation/run",
|
|
13221
|
+
kinds: ["evaluation", "claims"],
|
|
13222
|
+
recordType: review ? "org.ixo.topic.evaluation-review" : "org.ixo.topic.evaluation-assertion",
|
|
13223
|
+
requiredServices: ["evaluations"],
|
|
13224
|
+
confirmation: review,
|
|
13225
|
+
owner: review ? "human" : "agent",
|
|
13226
|
+
inputSchema: {
|
|
13227
|
+
type: "object",
|
|
13228
|
+
required: review ? ["assertionReference", "methodologyRevision", "rubricRevision"] : ["subjectReference", "methodologyRevision", "rubricRevision"],
|
|
13229
|
+
additionalProperties: true,
|
|
13230
|
+
properties: {
|
|
13231
|
+
subjectReference: { type: "string" },
|
|
13232
|
+
assertionReference: { type: "string" },
|
|
13233
|
+
methodologyRevision: { type: "string" },
|
|
13234
|
+
rubricRevision: { type: "string" },
|
|
13235
|
+
evidenceReferences: { type: "array", items: { type: "string" } }
|
|
13236
|
+
}
|
|
13237
|
+
},
|
|
13238
|
+
execute: async (inputs, ctx) => {
|
|
13239
|
+
if (!ctx.services.evaluations) throw new Error("Evaluation runtime service is not configured");
|
|
13240
|
+
const result = review ? await ctx.services.evaluations.review(inputs) : await ctx.services.evaluations.run(inputs);
|
|
13241
|
+
const value = "assertion" in result ? result.assertion : result.review;
|
|
13242
|
+
return {
|
|
13243
|
+
providerResult: value,
|
|
13244
|
+
assertionId: "assertionId" in result ? result.assertionId : void 0,
|
|
13245
|
+
reviewId: "reviewId" in result ? result.reviewId : void 0,
|
|
13246
|
+
methodologyRevision: inputs.methodologyRevision,
|
|
13247
|
+
rubricRevision: inputs.rubricRevision,
|
|
13248
|
+
evaluatorDid: ctx.actorDid,
|
|
13249
|
+
evidenceReferences: result.evidenceReferences,
|
|
13250
|
+
signature: result.signature
|
|
13251
|
+
};
|
|
13252
|
+
}
|
|
13253
|
+
});
|
|
13254
|
+
}
|
|
13255
|
+
registerSemanticAction({
|
|
13256
|
+
type: "qi/settlement.execute",
|
|
13257
|
+
can: "settlement/execute",
|
|
13258
|
+
kinds: ["claims"],
|
|
13259
|
+
recordType: "org.ixo.topic.settlement-record",
|
|
13260
|
+
confirmation: true,
|
|
13261
|
+
owner: "human",
|
|
13262
|
+
riskTier: "critical",
|
|
13263
|
+
requiredServices: ["settlement"],
|
|
13264
|
+
inputSchema: {
|
|
11272
13265
|
type: "object",
|
|
11273
|
-
required: ["
|
|
13266
|
+
required: ["approvedClaimReference", "amount", "asset", "recipient", "policyReference", "confirmationReference"],
|
|
13267
|
+
additionalProperties: false,
|
|
11274
13268
|
properties: {
|
|
11275
|
-
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
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." }
|
|
13269
|
+
approvedClaimReference: { type: "string" },
|
|
13270
|
+
amount: { type: "string" },
|
|
13271
|
+
asset: { type: "string" },
|
|
13272
|
+
recipient: { type: "string" },
|
|
13273
|
+
policyReference: { type: "string" },
|
|
13274
|
+
confirmationReference: { type: "string" }
|
|
11283
13275
|
}
|
|
13276
|
+
},
|
|
13277
|
+
execute: async (inputs, ctx) => {
|
|
13278
|
+
if (!ctx.services.settlement) throw new Error("Settlement service is not configured");
|
|
13279
|
+
return ctx.services.settlement.execute({
|
|
13280
|
+
approvedClaimReference: requiredString(inputs.approvedClaimReference, "approvedClaimReference"),
|
|
13281
|
+
amount: requiredString(inputs.amount, "amount"),
|
|
13282
|
+
asset: requiredString(inputs.asset, "asset"),
|
|
13283
|
+
recipient: requiredString(inputs.recipient, "recipient"),
|
|
13284
|
+
policyReference: requiredString(inputs.policyReference, "policyReference"),
|
|
13285
|
+
confirmationReference: requiredString(inputs.confirmationReference, "confirmationReference")
|
|
13286
|
+
});
|
|
11284
13287
|
}
|
|
11285
|
-
};
|
|
11286
|
-
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
11295
|
-
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
|
|
11301
|
-
|
|
11302
|
-
|
|
11303
|
-
eligibleForEventTrigger: true,
|
|
11304
|
-
// Mirrors executeDelegatedTool's gates: the bound connection plus the
|
|
11305
|
-
// tool schema's required fields, so orchestrators ask before run() throws.
|
|
11306
|
-
getMissingInputs: (inputs) => delegatedToolMissingInputs(GOOGLECALENDAR_CREATE_SCHEMA, inputs),
|
|
11307
|
-
outputSchema: GOOGLECALENDAR_CREATE_OUTPUT_SCHEMA,
|
|
11308
|
-
events: [
|
|
11309
|
-
{
|
|
11310
|
-
name: GOOGLECALENDAR_CREATED_EVENT,
|
|
11311
|
-
displayName: "Calendar event created",
|
|
11312
|
-
description: "Fired after the event is created on the author\u2019s calendar.",
|
|
11313
|
-
payloadSchema: [
|
|
11314
|
-
{ path: "eventId", displayName: "Event ID", type: "string" },
|
|
11315
|
-
{ path: "htmlLink", displayName: "Event link", type: "string" },
|
|
11316
|
-
{ path: "summary", displayName: "Summary", type: "string" }
|
|
11317
|
-
],
|
|
11318
|
-
pendingDisplayFields: ["summary", "eventId"]
|
|
13288
|
+
});
|
|
13289
|
+
registerSemanticAction({
|
|
13290
|
+
type: "qi/incident.escalate",
|
|
13291
|
+
can: "incident/escalate",
|
|
13292
|
+
kinds: ["incident"],
|
|
13293
|
+
recordType: "org.ixo.topic.incident-escalation",
|
|
13294
|
+
confirmation: true,
|
|
13295
|
+
requiredServices: ["incidents"],
|
|
13296
|
+
inputSchema: {
|
|
13297
|
+
type: "object",
|
|
13298
|
+
required: ["severity", "affectedResources", "recipients", "summary"],
|
|
13299
|
+
additionalProperties: false,
|
|
13300
|
+
properties: {
|
|
13301
|
+
severity: { type: "string", enum: ["low", "medium", "high", "critical"] },
|
|
13302
|
+
affectedResources: { type: "array", items: { type: "string" } },
|
|
13303
|
+
recipients: { type: "array", items: { type: "string" } },
|
|
13304
|
+
evidenceReferences: { type: "array", items: { type: "string" } },
|
|
13305
|
+
summary: { type: "string" }
|
|
11319
13306
|
}
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
const
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
|
|
11327
|
-
|
|
11328
|
-
|
|
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 ?? "");
|
|
11337
|
-
return {
|
|
11338
|
-
output: { eventId, htmlLink, summary, startIso },
|
|
11339
|
-
events: eventId ? [{ name: GOOGLECALENDAR_CREATED_EVENT, payload: { eventId, htmlLink, summary } }] : void 0
|
|
13307
|
+
},
|
|
13308
|
+
execute: async (inputs, ctx) => {
|
|
13309
|
+
if (!ctx.services.incidents) throw new Error("Incident service is not configured");
|
|
13310
|
+
const payload = {
|
|
13311
|
+
severity: requiredString(inputs.severity, "severity"),
|
|
13312
|
+
affectedResources: requiredArray(inputs.affectedResources, "affectedResources"),
|
|
13313
|
+
recipients: requiredArray(inputs.recipients, "recipients"),
|
|
13314
|
+
evidenceReferences: requiredArray(inputs.evidenceReferences || [], "evidenceReferences"),
|
|
13315
|
+
summary: requiredString(inputs.summary, "summary")
|
|
11340
13316
|
};
|
|
13317
|
+
return { ...payload, ...await ctx.services.incidents.escalate(payload) };
|
|
11341
13318
|
}
|
|
11342
13319
|
});
|
|
13320
|
+
registerSemanticAction({
|
|
13321
|
+
type: "qi/incident.mitigation.record",
|
|
13322
|
+
can: "incident/mitigation.record",
|
|
13323
|
+
kinds: ["incident"],
|
|
13324
|
+
recordType: "org.ixo.topic.incident-mitigation",
|
|
13325
|
+
inputSchema: {
|
|
13326
|
+
type: "object",
|
|
13327
|
+
required: ["mitigation", "affectedResources"],
|
|
13328
|
+
additionalProperties: false,
|
|
13329
|
+
properties: {
|
|
13330
|
+
mitigation: { type: "string" },
|
|
13331
|
+
affectedResources: { type: "array", items: { type: "string" } },
|
|
13332
|
+
evidenceReferences: { type: "array", items: { type: "string" } },
|
|
13333
|
+
occurredAt: { type: "string" }
|
|
13334
|
+
}
|
|
13335
|
+
},
|
|
13336
|
+
execute: (inputs, ctx) => ({
|
|
13337
|
+
mitigation: requiredString(inputs.mitigation, "mitigation"),
|
|
13338
|
+
affectedResources: requiredArray(inputs.affectedResources, "affectedResources"),
|
|
13339
|
+
evidenceReferences: requiredArray(inputs.evidenceReferences || [], "evidenceReferences"),
|
|
13340
|
+
recordedBy: ctx.actorDid,
|
|
13341
|
+
occurredAt: String(inputs.occurredAt || (/* @__PURE__ */ new Date()).toISOString())
|
|
13342
|
+
})
|
|
13343
|
+
});
|
|
11343
13344
|
|
|
11344
13345
|
// src/core/lib/actionRegistry/actions/calendar/eventCreate.types.ts
|
|
11345
13346
|
var EMPTY = {
|
|
@@ -11394,7 +13395,13 @@ function parseAttendeesField(raw) {
|
|
|
11394
13395
|
// src/core/lib/actionRegistry/actions/calendar/eventCreate.ts
|
|
11395
13396
|
var CALENDAR_EVENT_CREATE_SLUG = "GOOGLECALENDAR_CREATE_EVENT";
|
|
11396
13397
|
registerAction({
|
|
11397
|
-
|
|
13398
|
+
// Provider-explicit identity (IXO-4420 §5): this block is Google Calendar
|
|
13399
|
+
// via Composio with a per-runner pinned connection — `-self` distinguishes
|
|
13400
|
+
// it from the delegated `qi/googlecalendar.event.create`. The retired
|
|
13401
|
+
// `qi/calendar.event.create` name resolves here via ACTION_TYPE_ALIASES.
|
|
13402
|
+
// The `can` deliberately keeps its historical value so existing UCAN grants
|
|
13403
|
+
// keep matching; `qi/ixo.calendar.event.*` (M4) gets its own can namespace.
|
|
13404
|
+
type: "qi/googlecalendar.event.create-self",
|
|
11398
13405
|
can: "calendar.event/create",
|
|
11399
13406
|
sideEffect: true,
|
|
11400
13407
|
proof: { fields: ["eventId"] },
|
|
@@ -11503,7 +13510,9 @@ registerAction({
|
|
|
11503
13510
|
var CALENDAR_EVENT_UPDATE_SLUG = "GOOGLECALENDAR_UPDATE_EVENT";
|
|
11504
13511
|
var CALENDAR_EVENT_GET_SLUG = "GOOGLECALENDAR_EVENTS_GET";
|
|
11505
13512
|
registerAction({
|
|
11506
|
-
|
|
13513
|
+
// Provider-explicit identity (IXO-4420 §5); `qi/calendar.event.update` is
|
|
13514
|
+
// a permanent alias. `can` keeps its historical value — see eventCreate.ts.
|
|
13515
|
+
type: "qi/googlecalendar.event.update-self",
|
|
11507
13516
|
can: "calendar.event/update",
|
|
11508
13517
|
sideEffect: true,
|
|
11509
13518
|
proof: { fields: ["eventId"] },
|
|
@@ -11614,7 +13623,9 @@ registerAction({
|
|
|
11614
13623
|
// src/core/lib/actionRegistry/actions/calendar/eventList.ts
|
|
11615
13624
|
var CALENDAR_EVENT_LIST_SLUG = "GOOGLECALENDAR_EVENTS_LIST";
|
|
11616
13625
|
registerAction({
|
|
11617
|
-
|
|
13626
|
+
// Provider-explicit identity (IXO-4420 §5); `qi/calendar.event.list` is
|
|
13627
|
+
// a permanent alias. `can` keeps its historical value — see eventCreate.ts.
|
|
13628
|
+
type: "qi/googlecalendar.event.list-self",
|
|
11618
13629
|
can: "calendar.event/list",
|
|
11619
13630
|
sideEffect: false,
|
|
11620
13631
|
proof: "none",
|
|
@@ -12424,7 +14435,7 @@ registerDiffResolver("evaluateClaim", {
|
|
|
12424
14435
|
});
|
|
12425
14436
|
|
|
12426
14437
|
// src/core/lib/actionRegistry/actions/calendar/eventCreate.diff.ts
|
|
12427
|
-
registerDiffResolver("qi/
|
|
14438
|
+
registerDiffResolver("qi/googlecalendar.event.create-self", {
|
|
12428
14439
|
resolver: async (inputs, _ctx) => {
|
|
12429
14440
|
const attendees = parseAttendeesField(String(inputs.attendees || ""));
|
|
12430
14441
|
const calendarId = String(inputs.calendar_id || "").trim() || "primary";
|
|
@@ -12505,7 +14516,7 @@ registerDiffResolver("qi/calendar.event.create", {
|
|
|
12505
14516
|
});
|
|
12506
14517
|
|
|
12507
14518
|
// src/core/lib/actionRegistry/actions/calendar/eventUpdate.diff.ts
|
|
12508
|
-
registerDiffResolver("qi/
|
|
14519
|
+
registerDiffResolver("qi/googlecalendar.event.update-self", {
|
|
12509
14520
|
resolver: async (inputs, ctx) => {
|
|
12510
14521
|
const connection = inputs.connection || {};
|
|
12511
14522
|
const connectedAccountId = connection.connectedAccountId;
|
|
@@ -12761,14 +14772,79 @@ registerDiffResolver("qi/xero.payment.create", {
|
|
|
12761
14772
|
}
|
|
12762
14773
|
});
|
|
12763
14774
|
|
|
14775
|
+
// src/core/utils/tokenAmount.ts
|
|
14776
|
+
var DECIMAL_RE = /^-?\d*(\.\d*)?$/;
|
|
14777
|
+
function toBaseUnits(displayAmount, exponent) {
|
|
14778
|
+
const raw = String(displayAmount ?? "").trim();
|
|
14779
|
+
if (!raw) return { ok: false, error: "Enter an amount" };
|
|
14780
|
+
if (!DECIMAL_RE.test(raw)) return { ok: false, error: `\u201C${raw}\u201D is not a valid amount` };
|
|
14781
|
+
if (raw.startsWith("-")) return { ok: false, error: "Amount must be greater than 0" };
|
|
14782
|
+
if (!Number.isInteger(exponent) || exponent < 0) return { ok: false, error: `Unknown decimals for this token` };
|
|
14783
|
+
const [whole = "", fraction = ""] = raw.split(".");
|
|
14784
|
+
if (fraction.length > exponent) {
|
|
14785
|
+
return {
|
|
14786
|
+
ok: false,
|
|
14787
|
+
error: exponent === 0 ? "This token has no decimal places \u2014 enter a whole number" : `This token has at most ${exponent} decimal places`
|
|
14788
|
+
};
|
|
14789
|
+
}
|
|
14790
|
+
const shifted = `${whole}${fraction.padEnd(exponent, "0")}`.replace(/^0+(?=\d)/, "");
|
|
14791
|
+
const value = shifted === "" ? "0" : shifted;
|
|
14792
|
+
if (value === "0") return { ok: false, error: "Amount must be greater than 0" };
|
|
14793
|
+
return { ok: true, value };
|
|
14794
|
+
}
|
|
14795
|
+
function toDisplayUnits(baseAmount, exponent) {
|
|
14796
|
+
const raw = String(baseAmount ?? "").trim();
|
|
14797
|
+
if (!raw || !DECIMAL_RE.test(raw)) return "0";
|
|
14798
|
+
if (!Number.isInteger(exponent) || exponent <= 0) return raw.replace(/\..*$/, "");
|
|
14799
|
+
const negative = raw.startsWith("-");
|
|
14800
|
+
const digits = (negative ? raw.slice(1) : raw).replace(/\..*$/, "").padStart(exponent + 1, "0");
|
|
14801
|
+
const whole = digits.slice(0, digits.length - exponent).replace(/^0+(?=\d)/, "");
|
|
14802
|
+
const fraction = digits.slice(digits.length - exponent).replace(/0+$/, "");
|
|
14803
|
+
return `${negative ? "-" : ""}${whole}${fraction ? `.${fraction}` : ""}`;
|
|
14804
|
+
}
|
|
14805
|
+
function formatTokenAmount(baseAmount, denom, exponent, symbol) {
|
|
14806
|
+
if (exponent === void 0 || exponent === null) return `${baseAmount} ${denom}`;
|
|
14807
|
+
return `${toDisplayUnits(baseAmount, exponent)} ${symbol || denom}`;
|
|
14808
|
+
}
|
|
14809
|
+
|
|
12764
14810
|
// src/core/lib/actionRegistry/actions/walletFund.diff.ts
|
|
14811
|
+
var FALLBACK_DENOM = "uixo";
|
|
14812
|
+
async function describeToken(ctx, walletAddress, denom) {
|
|
14813
|
+
if (!walletAddress || !ctx.handlers?.getBalances) return {};
|
|
14814
|
+
try {
|
|
14815
|
+
const res = await ctx.handlers.getBalances(walletAddress);
|
|
14816
|
+
const match = (res?.data || []).find((b) => b.denom === denom);
|
|
14817
|
+
return { symbol: match?.tokenName, exponent: match?.exponent };
|
|
14818
|
+
} catch {
|
|
14819
|
+
return {};
|
|
14820
|
+
}
|
|
14821
|
+
}
|
|
12765
14822
|
registerDiffResolver("qi/wallet.fund", {
|
|
12766
|
-
resolver: async (inputs,
|
|
14823
|
+
resolver: async (inputs, ctx) => {
|
|
12767
14824
|
const address = String(inputs.address || "").trim();
|
|
12768
14825
|
const amount = String(inputs.amount || "250000").trim();
|
|
12769
|
-
const
|
|
12770
|
-
const
|
|
14826
|
+
const denom = String(inputs.denom || "").trim() || FALLBACK_DENOM;
|
|
14827
|
+
const fromAddress = String(inputs.fromAddress || "").trim();
|
|
14828
|
+
const signerAddress = (() => {
|
|
14829
|
+
try {
|
|
14830
|
+
return ctx.handlers?.getCurrentUser?.()?.address || "";
|
|
14831
|
+
} catch {
|
|
14832
|
+
return "";
|
|
14833
|
+
}
|
|
14834
|
+
})();
|
|
14835
|
+
const source = fromAddress || signerAddress;
|
|
14836
|
+
const { symbol, exponent } = await describeToken(ctx, source, denom);
|
|
12771
14837
|
return [
|
|
14838
|
+
{
|
|
14839
|
+
key: "from",
|
|
14840
|
+
label: "From",
|
|
14841
|
+
before: "N/A",
|
|
14842
|
+
// Naming the mechanism matters: spending another wallet's tokens is an
|
|
14843
|
+
// authz exec, and the signer should see that before they slide.
|
|
14844
|
+
after: fromAddress ? `${fromAddress} (authorized send)` : source ? `${source} (your wallet)` : "Your wallet",
|
|
14845
|
+
changeType: "replace",
|
|
14846
|
+
severity: fromAddress ? "warning" : "info"
|
|
14847
|
+
},
|
|
12772
14848
|
{
|
|
12773
14849
|
key: "recipient",
|
|
12774
14850
|
label: "Recipient",
|
|
@@ -12779,17 +14855,10 @@ registerDiffResolver("qi/wallet.fund", {
|
|
|
12779
14855
|
{
|
|
12780
14856
|
key: "amount",
|
|
12781
14857
|
label: "Amount",
|
|
12782
|
-
before: "0
|
|
12783
|
-
after: `${
|
|
14858
|
+
before: "0",
|
|
14859
|
+
after: exponent === void 0 ? `${amount} ${denom}` : `${formatTokenAmount(amount, denom, exponent, symbol)} (${amount} ${denom})`,
|
|
12784
14860
|
changeType: "replace",
|
|
12785
14861
|
severity: "info"
|
|
12786
|
-
},
|
|
12787
|
-
{
|
|
12788
|
-
key: "network",
|
|
12789
|
-
label: "Network",
|
|
12790
|
-
before: network,
|
|
12791
|
-
after: network,
|
|
12792
|
-
changeType: "unchanged"
|
|
12793
14862
|
}
|
|
12794
14863
|
];
|
|
12795
14864
|
}
|
|
@@ -13302,7 +15371,7 @@ registerDiffResolver(EVAL_ENGINE_ACTION_TYPE, {
|
|
|
13302
15371
|
key: "decisions",
|
|
13303
15372
|
label: "Decisions",
|
|
13304
15373
|
before: null,
|
|
13305
|
-
after: inputs?.allowChainEvaluation
|
|
15374
|
+
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
15375
|
changeType: "add"
|
|
13307
15376
|
}
|
|
13308
15377
|
];
|
|
@@ -13439,19 +15508,6 @@ function shouldNotifyPending(state, blockId, pendingInvocationId, currentAssigne
|
|
|
13439
15508
|
|
|
13440
15509
|
// src/core/lib/flowEngine/runs.ts
|
|
13441
15510
|
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
15511
|
var RUNS_MAP_KEY = "runs";
|
|
13456
15512
|
var RUNS_TERMINAL_MAP_KEY = "runsTerminal";
|
|
13457
15513
|
var LEGACY_RUNTIME_MAP_KEY2 = "runtime";
|
|
@@ -14012,7 +16068,10 @@ function resolveReferencesDetailed(input, editorDocument, options = {}) {
|
|
|
14012
16068
|
}
|
|
14013
16069
|
if (warnContext && unresolved.length > 0) {
|
|
14014
16070
|
for (const entry of unresolved) {
|
|
14015
|
-
warnOnce(
|
|
16071
|
+
warnOnce(
|
|
16072
|
+
`ref-unresolved:${warnContext}:${entry.ref}`,
|
|
16073
|
+
`[flow-config] ${warnContext}: reference ${entry.ref} did not resolve (${entry.reason}); using fallback '${fallback}'`
|
|
16074
|
+
);
|
|
14016
16075
|
}
|
|
14017
16076
|
}
|
|
14018
16077
|
return { value: result, unresolved };
|
|
@@ -14850,8 +16909,8 @@ function fnv1a322(input, seed) {
|
|
|
14850
16909
|
}
|
|
14851
16910
|
function createRunEventIdempotencyKey(kind, ...identity) {
|
|
14852
16911
|
const canonical = JSON.stringify([kind, ...identity]);
|
|
14853
|
-
const
|
|
14854
|
-
return `v1:${kind.replace(/\./g, "_")}:${
|
|
16912
|
+
const digest2 = `${fnv1a322(canonical, 2166136261)}${fnv1a322(canonical, 2654435761)}`;
|
|
16913
|
+
return `v1:${kind.replace(/\./g, "_")}:${digest2}`;
|
|
14855
16914
|
}
|
|
14856
16915
|
function boundRunActionOutput(value) {
|
|
14857
16916
|
const byteLength = jsonByteLength(value);
|
|
@@ -16410,7 +18469,6 @@ var executeNode = async ({ node, actorDid, actorType, entityRoomId, context, act
|
|
|
16410
18469
|
};
|
|
16411
18470
|
|
|
16412
18471
|
// src/core/lib/flowEngine/readBackReconciler.ts
|
|
16413
|
-
import * as Y5 from "yjs";
|
|
16414
18472
|
async function appendReadBackTimeline(eventLog, event, runtime, blockId, now) {
|
|
16415
18473
|
const result = await appendRunTimelineEvent({
|
|
16416
18474
|
eventLog,
|
|
@@ -16422,7 +18480,7 @@ async function appendReadBackTimeline(eventLog, event, runtime, blockId, now) {
|
|
|
16422
18480
|
return result.ok;
|
|
16423
18481
|
}
|
|
16424
18482
|
function isYDoc(value) {
|
|
16425
|
-
return value
|
|
18483
|
+
return isYDocLike(value);
|
|
16426
18484
|
}
|
|
16427
18485
|
function getYDoc(editorOrYDoc) {
|
|
16428
18486
|
if (!editorOrYDoc) return void 0;
|
|
@@ -16896,9 +18954,8 @@ async function reconcileActionReadBack(params) {
|
|
|
16896
18954
|
}
|
|
16897
18955
|
|
|
16898
18956
|
// src/core/lib/flowEngine/actionExecutor.ts
|
|
16899
|
-
import * as Y6 from "yjs";
|
|
16900
18957
|
function isYDoc2(value) {
|
|
16901
|
-
return value
|
|
18958
|
+
return isYDocLike(value);
|
|
16902
18959
|
}
|
|
16903
18960
|
function getYDoc2(editorOrYDoc) {
|
|
16904
18961
|
if (!editorOrYDoc) return void 0;
|
|
@@ -17146,10 +19203,35 @@ async function executeActionBlock(params) {
|
|
|
17146
19203
|
let requestedAwaitingReadBack = false;
|
|
17147
19204
|
let proofFailureReason = null;
|
|
17148
19205
|
let proofFailureOutput;
|
|
19206
|
+
let topicRecords = [];
|
|
17149
19207
|
const startedAt = now();
|
|
17150
19208
|
const previousState = runtime.get(blockId);
|
|
17151
19209
|
const attempt = (previousState.attempt || 0) + 1;
|
|
17152
19210
|
const executionId = makeExecutionId2(now);
|
|
19211
|
+
const recordTopicPhase = async (status, options = {}) => {
|
|
19212
|
+
if (!params.topic || !params.topicBridge) return void 0;
|
|
19213
|
+
try {
|
|
19214
|
+
return await params.topicBridge.recordFlowPhase({
|
|
19215
|
+
topic: params.topic,
|
|
19216
|
+
actionType,
|
|
19217
|
+
actorDid: params.actorDid,
|
|
19218
|
+
executorDid: params.executorDid || params.actorDid,
|
|
19219
|
+
executionId,
|
|
19220
|
+
status,
|
|
19221
|
+
input: inputBuild.inputs,
|
|
19222
|
+
output: options.output,
|
|
19223
|
+
semanticRecords: topicRecords,
|
|
19224
|
+
flowUri,
|
|
19225
|
+
sessionRunId,
|
|
19226
|
+
nodeId: blockId,
|
|
19227
|
+
invocationReference: options.invocationReference,
|
|
19228
|
+
traceReference: sessionRunId ? `${flowUri}/session/${sessionRunId}/execution/${executionId}` : `${flowUri}/execution/${executionId}`,
|
|
19229
|
+
error: options.error
|
|
19230
|
+
});
|
|
19231
|
+
} catch (error) {
|
|
19232
|
+
return { state: "queued", error: error instanceof Error ? error.message : "Topic receipt bridge failed" };
|
|
19233
|
+
}
|
|
19234
|
+
};
|
|
17153
19235
|
const timelineRequired = !!yDoc && !usesLegacyRuntimeCompatibility(yDoc);
|
|
17154
19236
|
if (sessionRunId && (eventLog || timelineRequired)) {
|
|
17155
19237
|
const startedLogged = await appendRunTimelineEvent({
|
|
@@ -17198,6 +19280,18 @@ async function executeActionBlock(params) {
|
|
|
17198
19280
|
executionId,
|
|
17199
19281
|
executionStartedAt: startedAt
|
|
17200
19282
|
});
|
|
19283
|
+
const runningWriteBack = await recordTopicPhase("running");
|
|
19284
|
+
if (runningWriteBack?.state === "rejected") {
|
|
19285
|
+
const message = runningWriteBack.error || "Topic Action execution was rejected by the receipt bridge.";
|
|
19286
|
+
updateRuntimeFailure(runtime, blockId, message, now);
|
|
19287
|
+
return {
|
|
19288
|
+
...buildFailureResult({ blockId, actionType, stage: "authorization", error: message, pendingInvocation: inputBuild.pendingInvocation }),
|
|
19289
|
+
executionId,
|
|
19290
|
+
runId: executionId,
|
|
19291
|
+
topicWriteBack: runningWriteBack
|
|
19292
|
+
};
|
|
19293
|
+
}
|
|
19294
|
+
const actionServices = action.type.startsWith("qi/topic.") ? params.services || {} : { ...params.services || {}, topic: void 0 };
|
|
17201
19295
|
const outcome = await executeNode({
|
|
17202
19296
|
node: flowNode,
|
|
17203
19297
|
actorDid: params.actorDid,
|
|
@@ -17223,13 +19317,16 @@ async function executeActionBlock(params) {
|
|
|
17223
19317
|
nodeId: blockId,
|
|
17224
19318
|
flowNode,
|
|
17225
19319
|
runtime,
|
|
17226
|
-
services:
|
|
19320
|
+
services: actionServices,
|
|
17227
19321
|
handlers: params.handlers,
|
|
17228
19322
|
editor,
|
|
17229
19323
|
yDoc,
|
|
17230
|
-
pendingInvocation: inputBuild.pendingInvocation
|
|
19324
|
+
pendingInvocation: inputBuild.pendingInvocation,
|
|
19325
|
+
topic: params.topic,
|
|
19326
|
+
flowRevision: params.flowRevision
|
|
17231
19327
|
});
|
|
17232
19328
|
if (result.events?.length) events.push(...result.events);
|
|
19329
|
+
if (result.topicRecords?.length) topicRecords = result.topicRecords;
|
|
17233
19330
|
if (result.completion?.state === "awaiting_readback") {
|
|
17234
19331
|
requestedAwaitingReadBack = true;
|
|
17235
19332
|
rawReadBack = result.completion.readBack;
|
|
@@ -17292,7 +19389,12 @@ async function executeActionBlock(params) {
|
|
|
17292
19389
|
invocationCid: outcome.invocationCid,
|
|
17293
19390
|
capabilityId: outcome.capabilityId,
|
|
17294
19391
|
executionId,
|
|
17295
|
-
runId: executionId
|
|
19392
|
+
runId: executionId,
|
|
19393
|
+
topicWriteBack: await recordTopicPhase(proofFailureState === "needs_verification" ? "needs_verification" : "failed", {
|
|
19394
|
+
output: proofFailureOutput,
|
|
19395
|
+
error: { code: PROOF_MISSING_CODE, message },
|
|
19396
|
+
invocationReference: outcome.invocationCid || outcome.capabilityId
|
|
19397
|
+
})
|
|
17296
19398
|
};
|
|
17297
19399
|
}
|
|
17298
19400
|
updateRuntimeFailure(runtime, blockId, message, now);
|
|
@@ -17317,6 +19419,10 @@ async function executeActionBlock(params) {
|
|
|
17317
19419
|
now
|
|
17318
19420
|
});
|
|
17319
19421
|
}
|
|
19422
|
+
const topicWriteBack2 = await recordTopicPhase(outcome.stage === "authorization" ? "rejected" : "failed", {
|
|
19423
|
+
error: { message },
|
|
19424
|
+
invocationReference: outcome.invocationCid || outcome.capabilityId
|
|
19425
|
+
});
|
|
17320
19426
|
return {
|
|
17321
19427
|
...buildFailureResult({
|
|
17322
19428
|
blockId,
|
|
@@ -17328,7 +19434,8 @@ async function executeActionBlock(params) {
|
|
|
17328
19434
|
invocationCid: outcome.invocationCid,
|
|
17329
19435
|
capabilityId: outcome.capabilityId,
|
|
17330
19436
|
executionId,
|
|
17331
|
-
runId: executionId
|
|
19437
|
+
runId: executionId,
|
|
19438
|
+
topicWriteBack: topicWriteBack2
|
|
17332
19439
|
};
|
|
17333
19440
|
}
|
|
17334
19441
|
const output = outcome.result?.payload || {};
|
|
@@ -17441,6 +19548,11 @@ async function executeActionBlock(params) {
|
|
|
17441
19548
|
});
|
|
17442
19549
|
}
|
|
17443
19550
|
const pendingInvocationRemoved = completionState === "completed" ? cleanupCompletedPendingInvocation(yDoc, blockId, inputBuild.pendingInvocation, sessionRunId) : false;
|
|
19551
|
+
const topicWriteBack = await recordTopicPhase(completionState === "completed" ? "succeeded" : completionState === "needs_verification" ? "needs_verification" : "failed", {
|
|
19552
|
+
output,
|
|
19553
|
+
...boundedOutput.exceeded ? { error: { code: "RUN_OUTPUT_TOO_LARGE", message: `Action output exceeded the ${MAX_RUN_ACTION_OUTPUT_BYTES / 1024} KiB run-head limit.` } } : {},
|
|
19554
|
+
invocationReference: outcome.invocationCid || outcome.capabilityId
|
|
19555
|
+
});
|
|
17444
19556
|
return {
|
|
17445
19557
|
success: !boundedOutput.exceeded,
|
|
17446
19558
|
stage: outcome.stage,
|
|
@@ -17458,7 +19570,8 @@ async function executeActionBlock(params) {
|
|
|
17458
19570
|
executionId,
|
|
17459
19571
|
pendingInvocationRemoved,
|
|
17460
19572
|
completionState,
|
|
17461
|
-
pendingInvocation: inputBuild.pendingInvocation
|
|
19573
|
+
pendingInvocation: inputBuild.pendingInvocation,
|
|
19574
|
+
topicWriteBack
|
|
17462
19575
|
};
|
|
17463
19576
|
}
|
|
17464
19577
|
|
|
@@ -18160,6 +20273,17 @@ function compileBlockProps(cap, registryType) {
|
|
|
18160
20273
|
var COMPILED_BLOCK_TYPE = "action";
|
|
18161
20274
|
|
|
18162
20275
|
// src/core/lib/flowCompiler/compiler.ts
|
|
20276
|
+
function stripActiveScheduleBindings(plan) {
|
|
20277
|
+
let changed = false;
|
|
20278
|
+
const capabilities = plan.capabilities.map((cap) => {
|
|
20279
|
+
if (cap.trigger?.type !== "schedule") return cap;
|
|
20280
|
+
if (cap.trigger.scheduleRef === void 0 && cap.trigger.scheduleRevision === void 0) return cap;
|
|
20281
|
+
changed = true;
|
|
20282
|
+
const { scheduleRef: _ref, scheduleRevision: _rev, ...inactive } = cap.trigger;
|
|
20283
|
+
return { ...cap, trigger: inactive };
|
|
20284
|
+
});
|
|
20285
|
+
return changed ? { ...plan, capabilities } : plan;
|
|
20286
|
+
}
|
|
18163
20287
|
function compileBaseUcanFlow(plan, registry) {
|
|
18164
20288
|
if (!Array.isArray(plan.capabilities)) {
|
|
18165
20289
|
throw new Error("BaseUcanFlow.capabilities must be an array");
|
|
@@ -18257,6 +20381,21 @@ function compileBaseUcanFlow(plan, registry) {
|
|
|
18257
20381
|
});
|
|
18258
20382
|
}
|
|
18259
20383
|
}
|
|
20384
|
+
if (trigger.type === "schedule") {
|
|
20385
|
+
if (action.eligibleForTimeTrigger !== true) {
|
|
20386
|
+
throw new Error(
|
|
20387
|
+
`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.`
|
|
20388
|
+
);
|
|
20389
|
+
}
|
|
20390
|
+
if (trigger.sourceBlockId || trigger.sources || trigger.eventName) {
|
|
20391
|
+
throw new Error(
|
|
20392
|
+
`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.`
|
|
20393
|
+
);
|
|
20394
|
+
}
|
|
20395
|
+
if (trigger.scheduleRevision !== void 0 && (!Number.isInteger(trigger.scheduleRevision) || trigger.scheduleRevision < 1)) {
|
|
20396
|
+
throw new Error(`Block "${nodeId}" has a schedule trigger with an invalid scheduleRevision (must be an integer >= 1).`);
|
|
20397
|
+
}
|
|
20398
|
+
}
|
|
18260
20399
|
if (trigger.type === "block.event" || trigger.type === "block.event.all") {
|
|
18261
20400
|
const refs = collectOutputRefs(cap.nb || {});
|
|
18262
20401
|
for (const ref of refs) {
|
|
@@ -18430,7 +20569,7 @@ function detectTriggerCycles(triggerEdges) {
|
|
|
18430
20569
|
}
|
|
18431
20570
|
|
|
18432
20571
|
// src/core/lib/flowCompiler/documentFragment.ts
|
|
18433
|
-
import * as
|
|
20572
|
+
import * as Y5 from "yjs";
|
|
18434
20573
|
function writeCompiledBlocksToFragment(fragment, blocks) {
|
|
18435
20574
|
const documentBlockGroup = getOrCreateDocumentBlockGroup(fragment);
|
|
18436
20575
|
for (const block of blocks) {
|
|
@@ -18449,7 +20588,7 @@ function removeBlockFromFragment(fragment, blockId) {
|
|
|
18449
20588
|
if (!blockGroup) return false;
|
|
18450
20589
|
for (let i = 0; i < blockGroup.length; i++) {
|
|
18451
20590
|
const container = blockGroup.get(i);
|
|
18452
|
-
if (container
|
|
20591
|
+
if (isYXmlElementLike(container) && container.getAttribute("id") === blockId) {
|
|
18453
20592
|
blockGroup.delete(i, 1);
|
|
18454
20593
|
return true;
|
|
18455
20594
|
}
|
|
@@ -18461,7 +20600,7 @@ function replaceBlockInFragment(fragment, block) {
|
|
|
18461
20600
|
if (!blockGroup) return false;
|
|
18462
20601
|
for (let i = 0; i < blockGroup.length; i++) {
|
|
18463
20602
|
const container = blockGroup.get(i);
|
|
18464
|
-
if (container
|
|
20603
|
+
if (isYXmlElementLike(container) && container.getAttribute("id") === block.id) {
|
|
18465
20604
|
blockGroup.delete(i, 1);
|
|
18466
20605
|
const newContainer = createBlockContainer(block);
|
|
18467
20606
|
blockGroup.insert(i, [newContainer]);
|
|
@@ -18477,7 +20616,7 @@ function swapBlocksInFragment(fragment, idA, idB) {
|
|
|
18477
20616
|
let ib = -1;
|
|
18478
20617
|
for (let i = 0; i < blockGroup.length; i++) {
|
|
18479
20618
|
const container = blockGroup.get(i);
|
|
18480
|
-
if (container
|
|
20619
|
+
if (isYXmlElementLike(container)) {
|
|
18481
20620
|
const id = container.getAttribute("id");
|
|
18482
20621
|
if (id === idA) ia = i;
|
|
18483
20622
|
else if (id === idB) ib = i;
|
|
@@ -18524,11 +20663,11 @@ function readBlocksFromFragment(fragment) {
|
|
|
18524
20663
|
const blocks = [];
|
|
18525
20664
|
for (let i = 0; i < blockGroup.length; i++) {
|
|
18526
20665
|
const container = blockGroup.get(i);
|
|
18527
|
-
if (!(container
|
|
20666
|
+
if (!isYXmlElementLike(container) || container.nodeName !== "blockContainer") continue;
|
|
18528
20667
|
const id = container.getAttribute("id");
|
|
18529
20668
|
if (typeof id !== "string" || id.length === 0) continue;
|
|
18530
20669
|
const content = container.get(0);
|
|
18531
|
-
if (!(content
|
|
20670
|
+
if (!isYXmlElementLike(content)) continue;
|
|
18532
20671
|
const props = { ...content.getAttributes() };
|
|
18533
20672
|
const textColor = container.getAttribute("textColor");
|
|
18534
20673
|
if (typeof textColor === "string" && textColor !== "default") props.textColor = textColor;
|
|
@@ -18539,12 +20678,12 @@ function readBlocksFromFragment(fragment) {
|
|
|
18539
20678
|
return blocks;
|
|
18540
20679
|
}
|
|
18541
20680
|
function createBlockContainer(block) {
|
|
18542
|
-
const blockContainer = new
|
|
20681
|
+
const blockContainer = new Y5.XmlElement("blockContainer");
|
|
18543
20682
|
const { backgroundColor: rawBackgroundColor, textColor: rawTextColor, ...contentProps } = block.props;
|
|
18544
20683
|
blockContainer.setAttribute("id", block.id);
|
|
18545
20684
|
blockContainer.setAttribute("textColor", rawTextColor || "default");
|
|
18546
20685
|
blockContainer.setAttribute("backgroundColor", rawBackgroundColor || "default");
|
|
18547
|
-
const blockContent = new
|
|
20686
|
+
const blockContent = new Y5.XmlElement(block.type);
|
|
18548
20687
|
for (const [key, value] of Object.entries(contentProps)) {
|
|
18549
20688
|
if (value !== "") {
|
|
18550
20689
|
blockContent.setAttribute(key, value);
|
|
@@ -18559,13 +20698,13 @@ function appendBlockToGroup(blockGroup, block) {
|
|
|
18559
20698
|
}
|
|
18560
20699
|
function getOrCreateDocumentBlockGroup(fragment) {
|
|
18561
20700
|
if (fragment.length === 0) {
|
|
18562
|
-
const blockGroup = new
|
|
20701
|
+
const blockGroup = new Y5.XmlElement("blockGroup");
|
|
18563
20702
|
fragment.insert(0, [blockGroup]);
|
|
18564
20703
|
return blockGroup;
|
|
18565
20704
|
}
|
|
18566
20705
|
if (fragment.length === 1) {
|
|
18567
20706
|
const rootNode = fragment.get(0);
|
|
18568
|
-
if (rootNode
|
|
20707
|
+
if (isYXmlElementLike(rootNode) && rootNode.nodeName === "blockGroup") {
|
|
18569
20708
|
return rootNode;
|
|
18570
20709
|
}
|
|
18571
20710
|
}
|
|
@@ -18575,7 +20714,7 @@ function getExistingBlockGroup(fragment) {
|
|
|
18575
20714
|
if (fragment.length === 0) return null;
|
|
18576
20715
|
if (fragment.length === 1) {
|
|
18577
20716
|
const rootNode = fragment.get(0);
|
|
18578
|
-
if (rootNode
|
|
20717
|
+
if (isYXmlElementLike(rootNode) && rootNode.nodeName === "blockGroup") {
|
|
18579
20718
|
return rootNode;
|
|
18580
20719
|
}
|
|
18581
20720
|
}
|
|
@@ -18583,13 +20722,12 @@ function getExistingBlockGroup(fragment) {
|
|
|
18583
20722
|
}
|
|
18584
20723
|
|
|
18585
20724
|
// src/core/lib/flowCompiler/readFlow.ts
|
|
18586
|
-
import * as Y8 from "yjs";
|
|
18587
20725
|
function readCompiledFlowFromYDoc(yDoc) {
|
|
18588
20726
|
const flowMeta = yDoc.getMap("qi.flow.meta");
|
|
18589
20727
|
const flowNodes = yDoc.getMap("qi.flow.nodes");
|
|
18590
20728
|
const nodes = {};
|
|
18591
20729
|
flowNodes.forEach((value, nodeId) => {
|
|
18592
|
-
if (value
|
|
20730
|
+
if (isYMapLike(value)) {
|
|
18593
20731
|
nodes[nodeId] = yMapToFlowNode(value);
|
|
18594
20732
|
}
|
|
18595
20733
|
});
|
|
@@ -18609,7 +20747,7 @@ function readCompiledFlowFromYDoc(yDoc) {
|
|
|
18609
20747
|
const flowEdges = yDoc.getMap("qi.flow.edges");
|
|
18610
20748
|
const edges = [];
|
|
18611
20749
|
flowEdges.forEach((value) => {
|
|
18612
|
-
if (value
|
|
20750
|
+
if (isYMapLike(value)) {
|
|
18613
20751
|
edges.push(yMapToEdge(value));
|
|
18614
20752
|
}
|
|
18615
20753
|
});
|
|
@@ -18806,11 +20944,11 @@ function triggerToNodeIds(trigger, nodeIdByBlockId) {
|
|
|
18806
20944
|
}
|
|
18807
20945
|
|
|
18808
20946
|
// src/core/lib/flowCompiler/setup.ts
|
|
18809
|
-
import * as
|
|
20947
|
+
import * as Y7 from "yjs";
|
|
18810
20948
|
import { MatrixProvider } from "@ixo/matrix-crdt";
|
|
18811
20949
|
|
|
18812
20950
|
// src/core/lib/flowCompiler/hydrate.ts
|
|
18813
|
-
import * as
|
|
20951
|
+
import * as Y6 from "yjs";
|
|
18814
20952
|
function hydrateYDocFromCompiledFlow(yDoc, compiled) {
|
|
18815
20953
|
yDoc.transact(() => {
|
|
18816
20954
|
const flowMeta = yDoc.getMap("qi.flow.meta");
|
|
@@ -18825,7 +20963,7 @@ function hydrateYDocFromCompiledFlow(yDoc, compiled) {
|
|
|
18825
20963
|
}
|
|
18826
20964
|
const flowEdges = yDoc.getMap("qi.flow.edges");
|
|
18827
20965
|
for (const edge of compiled.edges) {
|
|
18828
|
-
const yEdge = new
|
|
20966
|
+
const yEdge = new Y6.Map();
|
|
18829
20967
|
yEdge.set("id", edge.id);
|
|
18830
20968
|
yEdge.set("source", edge.source);
|
|
18831
20969
|
yEdge.set("target", edge.target);
|
|
@@ -18879,7 +21017,7 @@ function hydrateYDocFromMergeResult(yDoc, mergeResult) {
|
|
|
18879
21017
|
const flowEdges = yDoc.getMap("qi.flow.edges");
|
|
18880
21018
|
flowEdges.forEach((_, key) => flowEdges.delete(key));
|
|
18881
21019
|
for (const edge of merged.edges) {
|
|
18882
|
-
const yEdge = new
|
|
21020
|
+
const yEdge = new Y6.Map();
|
|
18883
21021
|
yEdge.set("id", edge.id);
|
|
18884
21022
|
yEdge.set("source", edge.source);
|
|
18885
21023
|
yEdge.set("target", edge.target);
|
|
@@ -18921,7 +21059,7 @@ function initializeRuntimeForNodes(yDoc, compiled, nodeIds, runId) {
|
|
|
18921
21059
|
});
|
|
18922
21060
|
}
|
|
18923
21061
|
function createYMapFromNode(node) {
|
|
18924
|
-
const yNode = new
|
|
21062
|
+
const yNode = new Y6.Map();
|
|
18925
21063
|
yNode.set("id", node.id);
|
|
18926
21064
|
yNode.set("blockId", node.blockId);
|
|
18927
21065
|
yNode.set("can", node.can);
|
|
@@ -18974,7 +21112,8 @@ function readFlow() {
|
|
|
18974
21112
|
}
|
|
18975
21113
|
async function setupFlowFromBaseUcan(options) {
|
|
18976
21114
|
const { plan: rawPlan, roomId, matrixClient, creatorDid, docId, templateId, strategy = "full" } = options;
|
|
18977
|
-
const
|
|
21115
|
+
const identified = rawPlan.flowId ? rawPlan : { ...rawPlan, flowId: docId || roomId };
|
|
21116
|
+
const plan = templateId ? stripActiveScheduleBindings(identified) : identified;
|
|
18978
21117
|
const incomingCompiled = compileBaseUcanFlow(plan, { getActionByCan });
|
|
18979
21118
|
const { yDoc, provider } = await connectToRoom(roomId, matrixClient, { adoptRuns: true });
|
|
18980
21119
|
let finalCompiled;
|
|
@@ -19030,7 +21169,7 @@ function applyFlowPlanToYDoc(yDoc, options) {
|
|
|
19030
21169
|
return mergeResult.merged;
|
|
19031
21170
|
}
|
|
19032
21171
|
async function connectToRoom(roomId, matrixClient, options) {
|
|
19033
|
-
const yDoc = new
|
|
21172
|
+
const yDoc = new Y7.Doc();
|
|
19034
21173
|
const client = matrixClient;
|
|
19035
21174
|
client.canSupportVoip = false;
|
|
19036
21175
|
client.clientOpts = { lazyLoadMembers: true };
|
|
@@ -19815,7 +21954,7 @@ function isRecord2(value) {
|
|
|
19815
21954
|
function isYXmlContainerLike(value) {
|
|
19816
21955
|
return value != null && typeof value === "object" && typeof value.toArray === "function";
|
|
19817
21956
|
}
|
|
19818
|
-
function
|
|
21957
|
+
function isYXmlElementLike2(value) {
|
|
19819
21958
|
return isYXmlContainerLike(value) && typeof value.nodeName === "string" && typeof value.getAttribute === "function" && typeof value.setAttribute === "function";
|
|
19820
21959
|
}
|
|
19821
21960
|
function getElementBlockId(element) {
|
|
@@ -19844,7 +21983,7 @@ function updateXmlElementProps(element, blockId, propsPatch) {
|
|
|
19844
21983
|
}
|
|
19845
21984
|
function findXmlElementById(container, blockId) {
|
|
19846
21985
|
for (const node of container.toArray()) {
|
|
19847
|
-
if (!
|
|
21986
|
+
if (!isYXmlElementLike2(node)) continue;
|
|
19848
21987
|
const id = getElementBlockId(node);
|
|
19849
21988
|
if (id === blockId) return node;
|
|
19850
21989
|
const nested = findXmlElementById(node, blockId);
|
|
@@ -19856,7 +21995,7 @@ function updateDocumentBlockProps(yDoc, blockId, propsPatch) {
|
|
|
19856
21995
|
const target = findXmlElementById(yDoc.getXmlFragment("document"), blockId);
|
|
19857
21996
|
if (!target) return false;
|
|
19858
21997
|
updateXmlElementProps(target, blockId, propsPatch);
|
|
19859
|
-
const content = target.toArray().find((node) =>
|
|
21998
|
+
const content = target.toArray().find((node) => isYXmlElementLike2(node) && node.nodeName !== "blockGroup");
|
|
19860
21999
|
if (content) {
|
|
19861
22000
|
updateXmlElementProps(content, blockId, propsPatch);
|
|
19862
22001
|
}
|
|
@@ -20290,10 +22429,17 @@ export {
|
|
|
20290
22429
|
matrixUserIdToDid,
|
|
20291
22430
|
findOrCreateDMRoom,
|
|
20292
22431
|
sendDirectMessage,
|
|
22432
|
+
canonicalActionJson,
|
|
22433
|
+
sha256Digest,
|
|
22434
|
+
MAX_TOPIC_SEMANTIC_RECORDS_PER_RECEIPT,
|
|
22435
|
+
MAX_TOPIC_SEMANTIC_RECORD_BATCH_BYTES,
|
|
22436
|
+
validateTopicSemanticRecord,
|
|
22437
|
+
validateTopicSemanticRecordBatch,
|
|
20293
22438
|
canToType,
|
|
20294
22439
|
typeToCan,
|
|
20295
22440
|
getAllCanMappings,
|
|
20296
22441
|
warnOnce,
|
|
22442
|
+
getActionPresentation,
|
|
20297
22443
|
STEP_COMPLETED_EVENT_NAME,
|
|
20298
22444
|
STEP_COMPLETED_EVENT,
|
|
20299
22445
|
doneWhenCompleted,
|
|
@@ -20312,7 +22458,10 @@ export {
|
|
|
20312
22458
|
getActionByCan,
|
|
20313
22459
|
getEventsForBlock,
|
|
20314
22460
|
getOutputSchemaForBlock,
|
|
22461
|
+
ACTION_MANIFEST_VERSION,
|
|
22462
|
+
ACTION_REGISTRY_VERSION,
|
|
20315
22463
|
generateActionManifest,
|
|
22464
|
+
actionManifestIssues,
|
|
20316
22465
|
isBlankInputValue,
|
|
20317
22466
|
getMissingActionInputs,
|
|
20318
22467
|
SERVICE_VERBS,
|
|
@@ -20361,6 +22510,7 @@ export {
|
|
|
20361
22510
|
DIFFERENT_WHEN_OTHER_MAX,
|
|
20362
22511
|
normalizeDifferentWhen,
|
|
20363
22512
|
normalizeRepeatSubmissions,
|
|
22513
|
+
FORM_SEGMENT,
|
|
20364
22514
|
extractRubricFieldCatalog,
|
|
20365
22515
|
buildRubricEnvelope,
|
|
20366
22516
|
parsePublishedRubric,
|
|
@@ -20382,6 +22532,8 @@ export {
|
|
|
20382
22532
|
parseCalendarEventCreateInputs,
|
|
20383
22533
|
serializeCalendarEventCreateInputs,
|
|
20384
22534
|
parseAttendeesField,
|
|
22535
|
+
isYMapLike,
|
|
22536
|
+
isYXmlElementLike,
|
|
20385
22537
|
FLOW_CONNECTIONS_MAP_KEY,
|
|
20386
22538
|
FLOW_CONNECTION_BINDINGS_MAP_KEY,
|
|
20387
22539
|
readFlowConnections,
|
|
@@ -20400,6 +22552,8 @@ export {
|
|
|
20400
22552
|
renderNumber,
|
|
20401
22553
|
formatCoin2 as formatCoin,
|
|
20402
22554
|
formatCoinAmount,
|
|
22555
|
+
toBaseUnits,
|
|
22556
|
+
formatTokenAmount,
|
|
20403
22557
|
DM_NOTIFICATIONS_MAP_KEY,
|
|
20404
22558
|
getDMNotificationState,
|
|
20405
22559
|
setDMNotificationRecord,
|
|
@@ -20602,4 +22756,4 @@ export {
|
|
|
20602
22756
|
executeQueuedFlowAgentCoreCommands,
|
|
20603
22757
|
FlowAgentService
|
|
20604
22758
|
};
|
|
20605
|
-
//# sourceMappingURL=chunk-
|
|
22759
|
+
//# sourceMappingURL=chunk-6OMM32CE.js.map
|