@wowok/agent-mcp 2.6.0 → 2.6.1
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/README.md +5 -3
- package/dist/extensions/capability-manifest.d.ts +125 -0
- package/dist/extensions/capability-manifest.js +594 -0
- package/dist/extensions/constraint-registry.d.ts +24 -0
- package/dist/extensions/constraint-registry.js +196 -0
- package/dist/extensions/index.d.ts +12 -0
- package/dist/extensions/index.js +6 -0
- package/dist/extensions/metric-registry.d.ts +26 -0
- package/dist/extensions/metric-registry.js +257 -0
- package/dist/extensions/mode-evaluator.d.ts +15 -0
- package/dist/extensions/mode-evaluator.js +170 -0
- package/dist/extensions/modes.d.ts +2 -0
- package/dist/extensions/modes.js +407 -0
- package/dist/extensions/registry.d.ts +48 -0
- package/dist/extensions/registry.js +629 -0
- package/dist/extensions/types.d.ts +218 -0
- package/dist/extensions/types.js +1 -0
- package/dist/knowledge/deployment-scanner.d.ts +3 -0
- package/dist/knowledge/deployment-scanner.js +64 -3
- package/dist/knowledge/guard-risk.d.ts +13 -0
- package/dist/knowledge/guard-risk.js +57 -0
- package/dist/knowledge/guard-templates.js +278 -0
- package/dist/knowledge/machine-templates.js +20 -1
- package/dist/knowledge/service-confirm.d.ts +14 -5
- package/dist/knowledge/service-confirm.js +116 -8
- package/dist/knowledge/tool-constraints.js +6 -2
- package/dist/project/deployment-bridge.d.ts +1 -1
- package/dist/project/deployment-bridge.js +27 -4
- package/dist/project/deployment-doc.d.ts +3 -0
- package/dist/project/deployment-doc.js +72 -10
- package/dist/project/evaluation.d.ts +2 -0
- package/dist/project/evaluation.js +569 -84
- package/dist/project/graph-builder.d.ts +4 -1
- package/dist/project/graph-builder.js +126 -61
- package/dist/project/graph.d.ts +1 -0
- package/dist/project/handlers.d.ts +219 -5
- package/dist/project/handlers.js +819 -9
- package/dist/project/stage-gate.d.ts +4 -0
- package/dist/project/stage-gate.js +64 -5
- package/dist/project/task-tracker.d.ts +26 -0
- package/dist/project/task-tracker.js +78 -0
- package/dist/safety/preview.js +16 -0
- package/dist/schema/call/allocation.d.ts +16 -16
- package/dist/schema/call/base.d.ts +21 -13
- package/dist/schema/call/base.js +27 -6
- package/dist/schema/call/bridge.d.ts +5 -5
- package/dist/schema/call/bridge.js +3 -1
- package/dist/schema/call/demand.d.ts +23 -31
- package/dist/schema/call/guard.js +1 -1
- package/dist/schema/call/machine.d.ts +402 -376
- package/dist/schema/call/order.d.ts +149 -228
- package/dist/schema/call/order.js +7 -3
- package/dist/schema/call/payment.d.ts +183 -3
- package/dist/schema/call/payment.js +21 -3
- package/dist/schema/call/personal.d.ts +241 -52
- package/dist/schema/call/progress.d.ts +53 -61
- package/dist/schema/call/progress.js +18 -4
- package/dist/schema/call/repository.d.ts +23 -31
- package/dist/schema/call/semantic.d.ts +1 -1
- package/dist/schema/call/semantic.js +30 -1
- package/dist/schema/call/service.d.ts +95 -119
- package/dist/schema/call/service.js +22 -1
- package/dist/schema/common/index.d.ts +11 -2
- package/dist/schema/common/index.js +43 -14
- package/dist/schema/local/index.d.ts +32 -35
- package/dist/schema/local/index.js +23 -5
- package/dist/schema/messenger/index.d.ts +274 -46
- package/dist/schema/operations.d.ts +680 -539
- package/dist/schema/operations.js +22 -0
- package/dist/schema/project/index.d.ts +1796 -80
- package/dist/schema/project/index.js +304 -14
- package/dist/schema/query/index.d.ts +705 -349
- package/dist/schema/query/index.js +164 -31
- package/dist/schema/schema-query/index.d.ts +15 -3
- package/dist/schema/schema-query/index.js +23 -5
- package/dist/schema/utils/node-parser.js +7 -4
- package/dist/schema-query/index.d.ts +7 -1
- package/dist/schema-query/index.js +204 -4
- package/dist/schemas/account_operation.output.json +7 -1
- package/dist/schemas/account_operation.schema.json +2 -2
- package/dist/schemas/bridge_operation.output.json +6 -0
- package/dist/schemas/bridge_operation.schema.json +1 -1
- package/dist/schemas/guard-templates.json +379 -0
- package/dist/schemas/guard2file.schema.json +1 -1
- package/dist/schemas/index.json +1 -1
- package/dist/schemas/local_info_operation.output.json +6 -0
- package/dist/schemas/local_mark_operation.output.json +7 -1
- package/dist/schemas/local_mark_operation.schema.json +1 -1
- package/dist/schemas/machineNode2file.schema.json +1 -1
- package/dist/schemas/messenger_operation.schema.json +4 -4
- package/dist/schemas/onchain_events.output.json +1 -1
- package/dist/schemas/onchain_operations.schema.json +348 -296
- package/dist/schemas/onchain_operations_allocation.schema.json +34 -25
- package/dist/schemas/onchain_operations_arbitration.schema.json +8 -8
- package/dist/schemas/onchain_operations_contact.schema.json +8 -8
- package/dist/schemas/onchain_operations_demand.schema.json +8 -8
- package/dist/schemas/onchain_operations_gen_passport.schema.json +14 -14
- package/dist/schemas/onchain_operations_gen_proof.schema.json +2 -2
- package/dist/schemas/onchain_operations_guard.schema.json +1 -1
- package/dist/schemas/onchain_operations_machine.schema.json +41 -33
- package/dist/schemas/onchain_operations_order.schema.json +71 -77
- package/dist/schemas/onchain_operations_payment.schema.json +141 -111
- package/dist/schemas/onchain_operations_permission.schema.json +2 -2
- package/dist/schemas/onchain_operations_personal.schema.json +7 -7
- package/dist/schemas/onchain_operations_progress.schema.json +8 -8
- package/dist/schemas/onchain_operations_proof.schema.json +7 -7
- package/dist/schemas/onchain_operations_repository.schema.json +8 -8
- package/dist/schemas/onchain_operations_reward.schema.json +10 -10
- package/dist/schemas/onchain_operations_service.schema.json +46 -35
- package/dist/schemas/onchain_operations_treasury.schema.json +8 -8
- package/dist/schemas/onchain_table_data.output.json +33 -25
- package/dist/schemas/onchain_table_data.schema.json +12 -12
- package/dist/schemas/project_operation.output.json +1175 -23
- package/dist/schemas/project_operation.schema.json +40 -4
- package/dist/schemas/query_toolkit.output.json +104 -76
- package/dist/schemas/query_toolkit.schema.json +9 -9
- package/dist/schemas/schema_query.output.json +7 -3
- package/dist/schemas/schema_query.schema.json +17 -3
- package/dist/tools/handlers/local.js +20 -5
- package/dist/tools/handlers/onchain.js +23 -0
- package/dist/tools/handlers/project.js +52 -3
- package/dist/tools/handlers/query.js +27 -0
- package/dist/tools/handlers/schema-query.js +19 -0
- package/dist/tools/handlers/task-status.d.ts +170 -0
- package/dist/tools/handlers/task-status.js +55 -0
- package/dist/tools/handlers/wip.js +47 -1
- package/dist/tools/index.js +211 -7
- package/dist/tools/retry.d.ts +8 -0
- package/dist/tools/retry.js +85 -0
- package/dist/tools/wip-deploy-assist.d.ts +28 -0
- package/dist/tools/wip-deploy-assist.js +278 -0
- package/package.json +2 -2
- package/dist/schemas/guard-node-examples.md +0 -199
|
@@ -740,6 +740,284 @@ export const GUARD_TEMPLATES = [
|
|
|
740
740
|
example_use_case: "Food delivery: if delivery delayed > 30min at node 'delayed' (history count=5), restaurant Treasury auto-compensates $5 to customer with PaymentInfo(for_object=order, for_guard=this, index=5, remark='delayed'). Customer submits Payment via Passport, guard verifies index matches current history count AND remark matches current node name.",
|
|
741
741
|
verifier_constraint_level: "level3_scene_combined",
|
|
742
742
|
},
|
|
743
|
+
{
|
|
744
|
+
id: "tpl_forward_content_subscription_deliver",
|
|
745
|
+
scene_id: "machine_forward_guard",
|
|
746
|
+
name: "Content subscription delivery verification (Substack-style)",
|
|
747
|
+
description: "Forward guard for content subscription delivery flow. Verifies that (1) the order is at the expected pre-delivery node " +
|
|
748
|
+
"(e.g. 'Ordered'), AND (2) the Signer holds the author's permission index on the bound Permission object. " +
|
|
749
|
+
"Prevents unauthorized parties from publishing content on behalf of the author. " +
|
|
750
|
+
"Designed for Substack-style content subscription: Paid → ContentPublished → Completed (success path), " +
|
|
751
|
+
"with Cancelled and Disputed branches using separate guards (tpl_forward_content_subscription_cancel, " +
|
|
752
|
+
"tpl_forward_content_subscription_refund).",
|
|
753
|
+
recommended_patterns: ["P06", "P09"],
|
|
754
|
+
parameters: [
|
|
755
|
+
{
|
|
756
|
+
name: "expected_pre_delivery_node",
|
|
757
|
+
type: "string",
|
|
758
|
+
description: "Expected current node name BEFORE delivery (e.g. 'Ordered')",
|
|
759
|
+
required: true,
|
|
760
|
+
default: "Ordered",
|
|
761
|
+
constraint: "Must exactly match the name defined in Machine nodes (case-sensitive). This is the node from which the 'Deliver' forward advances.",
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
name: "permission_object",
|
|
765
|
+
type: "address",
|
|
766
|
+
description: "Permission object address bound to the Machine (author role lives here)",
|
|
767
|
+
required: true,
|
|
768
|
+
constraint: "Must be the same Permission object bound to the Machine. Use name (not address) if Permission is not yet created.",
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
name: "author_permission_index",
|
|
772
|
+
type: "number",
|
|
773
|
+
description: "Permission index reserved for the author role (who can publish content)",
|
|
774
|
+
required: true,
|
|
775
|
+
default: "1000",
|
|
776
|
+
constraint: "Must match the index configured in the Permission object. Substack convention: 1000=author, 1500=arbitrator, reader uses namedOperator='' (no specific operator).",
|
|
777
|
+
},
|
|
778
|
+
],
|
|
779
|
+
example_table: [
|
|
780
|
+
{
|
|
781
|
+
identifier: 0,
|
|
782
|
+
b_submission: true,
|
|
783
|
+
value_type: "Address",
|
|
784
|
+
object_type: "Order",
|
|
785
|
+
name: "Order object address (submitted by verifier at runtime)",
|
|
786
|
+
binding_constraint: "Must be an Order belonging to the bound Service; progress.current must match expected_pre_delivery_node",
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
identifier: 1,
|
|
790
|
+
b_submission: false,
|
|
791
|
+
value_type: "String",
|
|
792
|
+
value: "${expected_pre_delivery_node}",
|
|
793
|
+
name: "Expected pre-delivery node name (e.g. 'Ordered')",
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
identifier: 2,
|
|
797
|
+
b_submission: false,
|
|
798
|
+
value_type: "Address",
|
|
799
|
+
value: "${permission_object}",
|
|
800
|
+
name: "Permission object address (bound to Machine, holds author role)",
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
identifier: 3,
|
|
804
|
+
b_submission: false,
|
|
805
|
+
value_type: "U64",
|
|
806
|
+
value: "${author_permission_index}",
|
|
807
|
+
name: "Author permission index (e.g. 1000)",
|
|
808
|
+
},
|
|
809
|
+
],
|
|
810
|
+
example_root: {
|
|
811
|
+
type: "logic_and",
|
|
812
|
+
nodes: [
|
|
813
|
+
{
|
|
814
|
+
type: "logic_string_nocase_equal",
|
|
815
|
+
nodes: [
|
|
816
|
+
{ type: "query", query: "progress.current", object: { identifier: 0, convert_witness: "OrderProgress" }, parameters: [] },
|
|
817
|
+
{ type: "identifier", identifier: 1 },
|
|
818
|
+
],
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
type: "logic_equal",
|
|
822
|
+
nodes: [
|
|
823
|
+
{ type: "context", context: "Signer" },
|
|
824
|
+
{
|
|
825
|
+
type: "query",
|
|
826
|
+
query: "permission.permitted",
|
|
827
|
+
object: { identifier: 2 },
|
|
828
|
+
parameters: [{ type: "context", context: "Signer" }, { type: "identifier", identifier: 3 }],
|
|
829
|
+
},
|
|
830
|
+
],
|
|
831
|
+
},
|
|
832
|
+
],
|
|
833
|
+
},
|
|
834
|
+
applicable_industries: ["subscription", "content", "newsletter", "media"],
|
|
835
|
+
relevant_risks: ["R-C2-01", "R-C2-03", "R-X1-08"],
|
|
836
|
+
creation_notes: [
|
|
837
|
+
"VERIFIED QUERY NAMES: progress.current (id 1253, String) — current node name; permission.permitted (Permission object query, returns Address of permitted operator for the given index)",
|
|
838
|
+
"SUBSTACK DEPLOYMENT: this guard was used in Substack v2 Service. Original v1 Service was published without arbitrations — had to be recreated as v2 with arbitrations bound BEFORE publish (F-S-21).",
|
|
839
|
+
"AUTHOR ROLE: permission index 1000 = author (publish + cancel). namedOperator='' for reader role (open — any subscriber). Arbitrator uses index 1500.",
|
|
840
|
+
"FORWARD DESIGN: 'Deliver' forward advances from 'Ordered' → 'ContentPublished'. Threshold=1, weight=1, permissionIndex=1000 (author only).",
|
|
841
|
+
"GUARD-FIRST-WINS: when 'Ordered' has competing forwards (Deliver|Cancel|Dispute), each with its own guard, the first guard to verify wins. Subscribers cannot double-claim by triggering multiple forwards.",
|
|
842
|
+
"IMMUTABILITY: Forward.guard cannot be modified after Machine publish. Verify all 3 guards (deliver/cancel/refund) are correctly bound BEFORE publishing the Machine.",
|
|
843
|
+
"CIRCULAR REF: if Permission is not yet created, use its name (not address) in value field — Guard resolves names at evaluation time, not at creation time.",
|
|
844
|
+
],
|
|
845
|
+
example_use_case: "Substack newsletter: subscriber pays $5/month. Author publishes content → 'Deliver' forward advances Ordered→ContentPublished. " +
|
|
846
|
+
"This guard verifies (1) order is at 'Ordered' node (not already delivered), AND (2) Signer holds permission index 1000 on author's Permission object. " +
|
|
847
|
+
"Prevents a malicious party from publishing fake content. Subscriber cannot trigger 'Deliver' (lacks permission).",
|
|
848
|
+
verifier_constraint_level: "level2_identity_set",
|
|
849
|
+
},
|
|
850
|
+
{
|
|
851
|
+
id: "tpl_forward_content_subscription_cancel",
|
|
852
|
+
scene_id: "machine_forward_guard",
|
|
853
|
+
name: "Content subscription cancel verification (Substack-style)",
|
|
854
|
+
description: "Forward guard for content subscription cancel flow. Verifies that (1) the order is at the expected pre-cancel node " +
|
|
855
|
+
"(e.g. 'Ordered'), AND (2) the Signer is the order owner (subscriber). Allows subscriber to cancel within grace period " +
|
|
856
|
+
"for full refund. Designed for Substack-style: Paid → Cancelled (terminal-cancel), with refund via Allocation template.",
|
|
857
|
+
recommended_patterns: ["P06", "P09"],
|
|
858
|
+
parameters: [
|
|
859
|
+
{
|
|
860
|
+
name: "expected_pre_cancel_node",
|
|
861
|
+
type: "string",
|
|
862
|
+
description: "Expected current node name BEFORE cancel (e.g. 'Ordered')",
|
|
863
|
+
required: true,
|
|
864
|
+
default: "Ordered",
|
|
865
|
+
constraint: "Must exactly match the name defined in Machine nodes (case-sensitive).",
|
|
866
|
+
},
|
|
867
|
+
],
|
|
868
|
+
example_table: [
|
|
869
|
+
{
|
|
870
|
+
identifier: 0,
|
|
871
|
+
b_submission: true,
|
|
872
|
+
value_type: "Address",
|
|
873
|
+
object_type: "Order",
|
|
874
|
+
name: "Order object address (submitted by verifier at runtime)",
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
identifier: 1,
|
|
878
|
+
b_submission: false,
|
|
879
|
+
value_type: "String",
|
|
880
|
+
value: "${expected_pre_cancel_node}",
|
|
881
|
+
name: "Expected pre-cancel node name (e.g. 'Ordered')",
|
|
882
|
+
},
|
|
883
|
+
],
|
|
884
|
+
example_root: {
|
|
885
|
+
type: "logic_and",
|
|
886
|
+
nodes: [
|
|
887
|
+
{
|
|
888
|
+
type: "logic_string_nocase_equal",
|
|
889
|
+
nodes: [
|
|
890
|
+
{ type: "query", query: "progress.current", object: { identifier: 0, convert_witness: "OrderProgress" }, parameters: [] },
|
|
891
|
+
{ type: "identifier", identifier: 1 },
|
|
892
|
+
],
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
type: "logic_equal",
|
|
896
|
+
nodes: [
|
|
897
|
+
{ type: "context", context: "Signer" },
|
|
898
|
+
{ type: "query", query: "order.owner", object: { identifier: 0 }, parameters: [] },
|
|
899
|
+
],
|
|
900
|
+
},
|
|
901
|
+
],
|
|
902
|
+
},
|
|
903
|
+
applicable_industries: ["subscription", "content", "newsletter", "media"],
|
|
904
|
+
relevant_risks: ["R-C2-01", "R-C2-03"],
|
|
905
|
+
creation_notes: [
|
|
906
|
+
"VERIFIED QUERY NAMES: progress.current (id 1253, String); order.owner (id 1202, Address) — order holder (subscriber).",
|
|
907
|
+
"SUBSCRIBER-ONLY: only the order owner can cancel — prevents third-party griefing. Author cannot cancel on behalf of subscriber.",
|
|
908
|
+
"GRACE PERIOD: this guard does NOT enforce a time window. If a grace period is needed (e.g. cancel within 24h of payment), add a third condition: context(Clock) <= order.time + grace_period.",
|
|
909
|
+
"REFUND ALLOCATION: pair this guard with an Allocation template that refunds 100% to the subscriber (order.owner). Trigger node = 'Cancelled'.",
|
|
910
|
+
"GUARD-FIRST-WINS: when 'Ordered' has competing forwards (Deliver|Cancel|Dispute), each with its own guard, the first guard to verify wins.",
|
|
911
|
+
],
|
|
912
|
+
example_use_case: "Substack newsletter: subscriber pays $5, then changes mind before content is published. 'Cancel' forward advances Ordered→Cancelled. " +
|
|
913
|
+
"This guard verifies (1) order is at 'Ordered' (not yet delivered), AND (2) Signer is the order owner. " +
|
|
914
|
+
"Author cannot cancel on behalf of subscriber. Refund triggers automatically via Allocation on 'Cancelled' node.",
|
|
915
|
+
verifier_constraint_level: "level2_identity_set",
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
id: "tpl_forward_content_subscription_refund",
|
|
919
|
+
scene_id: "machine_forward_guard",
|
|
920
|
+
name: "Content subscription refund verification (Substack-style, arbitration-triggered)",
|
|
921
|
+
description: "Forward guard for content subscription dispute/refund flow. Verifies that (1) the order is at the expected pre-dispute node " +
|
|
922
|
+
"(e.g. 'Disputed'), AND (2) the Signer holds the arbitrator's permission index on the bound Permission object. " +
|
|
923
|
+
"Only the arbitrator can advance Disputed→Refunded, ensuring dispute resolution is impartial. " +
|
|
924
|
+
"Designed for Substack-style: Paid → Disputed → Refunded (terminal-dispute).",
|
|
925
|
+
recommended_patterns: ["P06", "P09"],
|
|
926
|
+
parameters: [
|
|
927
|
+
{
|
|
928
|
+
name: "expected_pre_refund_node",
|
|
929
|
+
type: "string",
|
|
930
|
+
description: "Expected current node name BEFORE refund (e.g. 'Disputed')",
|
|
931
|
+
required: true,
|
|
932
|
+
default: "Disputed",
|
|
933
|
+
constraint: "Must exactly match the name defined in Machine nodes (case-sensitive).",
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
name: "permission_object",
|
|
937
|
+
type: "address",
|
|
938
|
+
description: "Permission object address bound to the Machine (arbitrator role lives here)",
|
|
939
|
+
required: true,
|
|
940
|
+
constraint: "Must be the same Permission object bound to the Machine. Use name (not address) if Permission is not yet created.",
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
name: "arbitrator_permission_index",
|
|
944
|
+
type: "number",
|
|
945
|
+
description: "Permission index reserved for the arbitrator role (who can resolve disputes)",
|
|
946
|
+
required: true,
|
|
947
|
+
default: "1500",
|
|
948
|
+
constraint: "Must match the index configured in the Permission object. Substack convention: 1000=author, 1500=arbitrator.",
|
|
949
|
+
},
|
|
950
|
+
],
|
|
951
|
+
example_table: [
|
|
952
|
+
{
|
|
953
|
+
identifier: 0,
|
|
954
|
+
b_submission: true,
|
|
955
|
+
value_type: "Address",
|
|
956
|
+
object_type: "Order",
|
|
957
|
+
name: "Order object address (submitted by verifier at runtime)",
|
|
958
|
+
},
|
|
959
|
+
{
|
|
960
|
+
identifier: 1,
|
|
961
|
+
b_submission: false,
|
|
962
|
+
value_type: "String",
|
|
963
|
+
value: "${expected_pre_refund_node}",
|
|
964
|
+
name: "Expected pre-refund node name (e.g. 'Disputed')",
|
|
965
|
+
},
|
|
966
|
+
{
|
|
967
|
+
identifier: 2,
|
|
968
|
+
b_submission: false,
|
|
969
|
+
value_type: "Address",
|
|
970
|
+
value: "${permission_object}",
|
|
971
|
+
name: "Permission object address (bound to Machine, holds arbitrator role)",
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
identifier: 3,
|
|
975
|
+
b_submission: false,
|
|
976
|
+
value_type: "U64",
|
|
977
|
+
value: "${arbitrator_permission_index}",
|
|
978
|
+
name: "Arbitrator permission index (e.g. 1500)",
|
|
979
|
+
},
|
|
980
|
+
],
|
|
981
|
+
example_root: {
|
|
982
|
+
type: "logic_and",
|
|
983
|
+
nodes: [
|
|
984
|
+
{
|
|
985
|
+
type: "logic_string_nocase_equal",
|
|
986
|
+
nodes: [
|
|
987
|
+
{ type: "query", query: "progress.current", object: { identifier: 0, convert_witness: "OrderProgress" }, parameters: [] },
|
|
988
|
+
{ type: "identifier", identifier: 1 },
|
|
989
|
+
],
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
type: "logic_equal",
|
|
993
|
+
nodes: [
|
|
994
|
+
{ type: "context", context: "Signer" },
|
|
995
|
+
{
|
|
996
|
+
type: "query",
|
|
997
|
+
query: "permission.permitted",
|
|
998
|
+
object: { identifier: 2 },
|
|
999
|
+
parameters: [{ type: "context", context: "Signer" }, { type: "identifier", identifier: 3 }],
|
|
1000
|
+
},
|
|
1001
|
+
],
|
|
1002
|
+
},
|
|
1003
|
+
],
|
|
1004
|
+
},
|
|
1005
|
+
applicable_industries: ["subscription", "content", "newsletter", "media"],
|
|
1006
|
+
relevant_risks: ["R-C2-01", "R-C2-03", "R-X1-08"],
|
|
1007
|
+
creation_notes: [
|
|
1008
|
+
"VERIFIED QUERY NAMES: progress.current (id 1253, String); permission.permitted (Permission object query, returns Address of permitted operator for the given index).",
|
|
1009
|
+
"ARBITRATOR-ONLY: only the arbitrator (permission index 1500) can advance Disputed→Refunded. Neither author nor subscriber can self-resolve.",
|
|
1010
|
+
"ARBITRATIONS BINDING: Service MUST have arbitrations bound BEFORE publish (F-S-21 — post-publish immutable). ConfirmGate C-S-19 + QS11 will warn if publish is attempted without arbitrations.",
|
|
1011
|
+
"DISPUTE ENTRY: 'Dispute' forward (Ordered→Disputed) is triggered by subscriber (order.owner). After Disputed, only arbitrator can resolve.",
|
|
1012
|
+
"REFUND ALLOCATION: pair this guard with an Allocation template that refunds 100% to the subscriber (order.owner). Trigger node = 'Refunded'.",
|
|
1013
|
+
"GUARD-FIRST-WINS: when 'Disputed' has competing forwards (Resolve-Refund|Resolve-Reject), each with its own guard. First guard to verify wins.",
|
|
1014
|
+
],
|
|
1015
|
+
example_use_case: "Substack newsletter: subscriber disputes content quality. Subscriber triggers 'Dispute' forward (Ordered→Disputed). " +
|
|
1016
|
+
"Arbitrator reviews evidence and triggers 'Resolve-Refund' forward (Disputed→Refunded). " +
|
|
1017
|
+
"This guard verifies (1) order is at 'Disputed' (dispute properly opened), AND (2) Signer holds arbitrator permission index 1500. " +
|
|
1018
|
+
"Author cannot self-resolve. Subscriber cannot self-refund.",
|
|
1019
|
+
verifier_constraint_level: "level2_identity_set",
|
|
1020
|
+
},
|
|
743
1021
|
];
|
|
744
1022
|
export function getTemplatesByScene(sceneId) {
|
|
745
1023
|
return GUARD_TEMPLATES.filter((t) => t.scene_id === sceneId);
|
|
@@ -1316,14 +1316,33 @@ export const MACHINE_TEMPLATES = [
|
|
|
1316
1316
|
"Repository records subscription status for cross-Machine verification",
|
|
1317
1317
|
"Renewal Allocation triggers on Renewed node (not Active — avoids double-charge)",
|
|
1318
1318
|
"Note: consensus_repositories is declarative-only (NOT auto-passed to Progress); use repository_ref parameter for actual Repository object — see 06 §14.4",
|
|
1319
|
+
"VARIANT — Content subscription (Substack-style): replace time-lock Renew/Expire with content-delivery flow:",
|
|
1320
|
+
" 6-node variant: Paid (entry) → ContentPublished → Completed (terminal-success)",
|
|
1321
|
+
" Paid → Cancelled (terminal-cancel) | Paid → Disputed → Refunded (terminal-dispute)",
|
|
1322
|
+
" Permission: author=index 1000 (publish + cancel), arbitrator=index 1500 (resolve dispute), reader=namedOperator='' (open role, no specific operator)",
|
|
1323
|
+
" Guards: deliver_guard (verify author permission + content hash), cancel_guard (verify subscriber request), refund_guard (verify arbitration outcome)",
|
|
1324
|
+
" Allocators: deliver=100% to author, cancel=100% refund to subscriber, dispute=100% refund to subscriber (Guard-first-wins pattern)",
|
|
1325
|
+
" First-Forward-wins: when Paid has competing forwards (Deliver|Cancel|Dispute), the first triggered forward wins; subscribers cannot double-claim",
|
|
1326
|
+
"CRITICAL — v1→v2 lesson: arbitrations MUST be added BEFORE Service.publish (post-publish, arbitrations become immutable and cannot be added — service.move F-S-21)",
|
|
1327
|
+
" If you accidentally published without arbitrations, you MUST create a new Service v2 with arbitrations bound before publishing (the v1 Service must be paused/abandoned)",
|
|
1328
|
+
" ConfirmGate (C-S-19, QS11) will warn before publish without arbitrations — do not bypass this warning unless explicitly intending no-arbitration operation",
|
|
1329
|
+
"Build order (content variant): Permission → Machine (unpublished, with initial forward) → 3 Guards → 3 Allocators (template on Service) → Service (unpublished, bind machine+buy_guard+order_allocators+arbitrations) → Publish Service → Test order (3 paths: normal/cancel/dispute)",
|
|
1319
1330
|
],
|
|
1320
1331
|
example_use_case: "Monthly SaaS subscription: $10/month. Customer can renew anytime after day 25. " +
|
|
1321
|
-
"Auto-expire on day 35 if not renewed. Cancel anytime
|
|
1332
|
+
"Auto-expire on day 35 if not renewed. Cancel anytime.\n\n" +
|
|
1333
|
+
"VARIANT — Content subscription (Substack-style): $5/month newsletter. Author publishes content → " +
|
|
1334
|
+
"subscriber reads. Subscriber can cancel anytime for full refund. Disputes resolved by arbitrator " +
|
|
1335
|
+
"(100% refund to subscriber if dispute upheld). 6-node Machine, 3 Guards, 3 Allocators. " +
|
|
1336
|
+
"Tested on testnet with 3 order paths: normal delivery, cancel, dispute.",
|
|
1322
1337
|
publish_checklist: [
|
|
1323
1338
|
"Time-lock Guards tested on testnet (both TimeUnlock + AutoExpire)",
|
|
1324
1339
|
"Competing Pair semantics verified (Renew vs Expire vs Cancel)",
|
|
1325
1340
|
"Repository write tested (subscription status queryable)",
|
|
1326
1341
|
"Renewal loop tested: Paid -> Active -> RenewEligible -> Renewed -> Active",
|
|
1342
|
+
"Content variant: initial node (prev_node='') MUST have at least one forward (FIX-002) — empty forwards=[] causes Progress to be permanently stuck",
|
|
1343
|
+
"Content variant: arbitrations added BEFORE publish (cannot add post-publish per F-S-21)",
|
|
1344
|
+
"Content variant: 3 order paths tested on testnet — normal delivery, cancel-within-grace, dispute-resolution",
|
|
1345
|
+
"Content variant: machineNode2file export as backup (covers all 6 nodes)",
|
|
1327
1346
|
],
|
|
1328
1347
|
},
|
|
1329
1348
|
];
|
|
@@ -35,13 +35,18 @@ export interface ServiceConfirmationResponse {
|
|
|
35
35
|
question_id: string;
|
|
36
36
|
response: "yes" | "no" | "skip";
|
|
37
37
|
}
|
|
38
|
+
export type ServiceEnvironmentCheckLevel = "block" | "warning" | "info";
|
|
39
|
+
export interface ServiceEnvironmentCheck {
|
|
40
|
+
name: string;
|
|
41
|
+
passed: boolean;
|
|
42
|
+
message: string;
|
|
43
|
+
level?: ServiceEnvironmentCheckLevel;
|
|
44
|
+
}
|
|
38
45
|
export interface ServiceEnvironmentResult {
|
|
39
46
|
passed: boolean;
|
|
40
|
-
checks:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
message: string;
|
|
44
|
-
}[];
|
|
47
|
+
checks: ServiceEnvironmentCheck[];
|
|
48
|
+
warnings_count: number;
|
|
49
|
+
infos_count: number;
|
|
45
50
|
}
|
|
46
51
|
export interface ServiceDecisionRecord {
|
|
47
52
|
timestamp: string;
|
|
@@ -138,6 +143,10 @@ export interface ServiceConfirmGateInput {
|
|
|
138
143
|
current_order_allocators_set?: boolean;
|
|
139
144
|
skip_user_confirmation?: boolean;
|
|
140
145
|
skip_environment_check?: boolean;
|
|
146
|
+
current_machine_node_count?: number;
|
|
147
|
+
current_machine_initial_forward_ok?: boolean;
|
|
148
|
+
current_machine_bPublished?: boolean;
|
|
149
|
+
current_arbitrators_authorized?: boolean;
|
|
141
150
|
}
|
|
142
151
|
export declare const SERVICE_PUBLISH_CHECKLIST: ServiceChecklistItem[];
|
|
143
152
|
export declare const SERVICE_CONFIRMATION_QUESTIONS: ServiceConfirmationQuestion[];
|
|
@@ -19,6 +19,7 @@ export const SERVICE_PUBLISH_CHECKLIST = [
|
|
|
19
19
|
{ id: "C-S-16", description: "Address fields valid (0x prefix, length >= 40)", passed: false, message: "" },
|
|
20
20
|
{ id: "C-S-17", description: "Publish operation double-confirmed (bPublished irreversible)", passed: false, message: "" },
|
|
21
21
|
{ id: "C-S-18", description: "Ownership transfer double-confirmed (IRREVERSIBLE)", passed: false, message: "" },
|
|
22
|
+
{ id: "C-S-19", description: "Publish: arbitrations strongly recommended (cannot add post-publish, F-S-21)", passed: false, message: "" },
|
|
22
23
|
];
|
|
23
24
|
function runStaticChecklist(input) {
|
|
24
25
|
const items = SERVICE_PUBLISH_CHECKLIST.map(c => ({ ...c }));
|
|
@@ -227,7 +228,24 @@ function runStaticChecklist(input) {
|
|
|
227
228
|
else {
|
|
228
229
|
items.find(i => i.id === "C-S-18").passed = true;
|
|
229
230
|
}
|
|
230
|
-
|
|
231
|
+
if (data.publish) {
|
|
232
|
+
const currentArbCount = input.current_arbitrations_count ?? 0;
|
|
233
|
+
const arbFromInput = data.arbitrations && "objects" in data.arbitrations ? data.arbitrations.objects.length : 0;
|
|
234
|
+
const arbCleared = data.arbitrations && data.arbitrations.op === "clear";
|
|
235
|
+
const totalArb = arbCleared ? 0 : currentArbCount + arbFromInput;
|
|
236
|
+
const cs19 = items.find(i => i.id === "C-S-19");
|
|
237
|
+
if (totalArb === 0) {
|
|
238
|
+
cs19.passed = false;
|
|
239
|
+
cs19.message = "Pending user confirmation in Layer 3 (QS11) — arbitrations cannot be added post-publish";
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
cs19.passed = true;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
items.find(i => i.id === "C-S-19").passed = true;
|
|
247
|
+
}
|
|
248
|
+
const allPassed = items.filter(i => i.id !== "C-S-17" && i.id !== "C-S-18" && i.id !== "C-S-19").every(i => i.passed);
|
|
231
249
|
return { all_passed: allPassed, items };
|
|
232
250
|
}
|
|
233
251
|
function adaptRiskAssessment(input) {
|
|
@@ -327,6 +345,16 @@ export const SERVICE_CONFIRMATION_QUESTIONS = [
|
|
|
327
345
|
question: "Have you exported the current Service JSON as a backup before this operation?",
|
|
328
346
|
requires_explicit_yes: false,
|
|
329
347
|
},
|
|
348
|
+
{
|
|
349
|
+
id: "QS11",
|
|
350
|
+
question: "⚠️ Service is about to be published WITHOUT arbitrations.\n\n" +
|
|
351
|
+
"After publishing, arbitrations CANNOT be added (contract restriction, F-S-21).\n" +
|
|
352
|
+
"Dispute resolution will NOT be available for this service.\n\n" +
|
|
353
|
+
"Recommended: Set arbitrations BEFORE publishing:\n" +
|
|
354
|
+
' "arbitrations": {"op": "add", "objects": ["your_arbitration_object"]}\n\n' +
|
|
355
|
+
"Continue with publish anyway?",
|
|
356
|
+
requires_explicit_yes: true,
|
|
357
|
+
},
|
|
330
358
|
];
|
|
331
359
|
function processUserConfirmation(input, assessment) {
|
|
332
360
|
const questions = [...SERVICE_CONFIRMATION_QUESTIONS];
|
|
@@ -350,6 +378,18 @@ function processUserConfirmation(input, assessment) {
|
|
|
350
378
|
irrelevant.push("QS8");
|
|
351
379
|
if (!data.order_new)
|
|
352
380
|
irrelevant.push("QS9");
|
|
381
|
+
if (!data.publish) {
|
|
382
|
+
irrelevant.push("QS11");
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
const currentArbCount = input.current_arbitrations_count ?? 0;
|
|
386
|
+
const arbFromInput = data.arbitrations && "objects" in data.arbitrations ? data.arbitrations.objects.length : 0;
|
|
387
|
+
const arbCleared = data.arbitrations && data.arbitrations.op === "clear";
|
|
388
|
+
const totalArb = arbCleared ? 0 : currentArbCount + arbFromInput;
|
|
389
|
+
if (totalArb > 0) {
|
|
390
|
+
irrelevant.push("QS11");
|
|
391
|
+
}
|
|
392
|
+
}
|
|
353
393
|
const filtered = questions.filter(q => !irrelevant.includes(q.id));
|
|
354
394
|
if (input.skip_user_confirmation) {
|
|
355
395
|
return {
|
|
@@ -413,6 +453,40 @@ function verifyEnvironment(input) {
|
|
|
413
453
|
message: "Publish blocked: compensation_fund > 0 but no arbitrations (E_ARBITRATION_NOT_SET_WITH_COMPENSATION_FUND)",
|
|
414
454
|
});
|
|
415
455
|
}
|
|
456
|
+
if (data.machine) {
|
|
457
|
+
if (input.current_machine_node_count !== undefined && input.current_machine_node_count === 0) {
|
|
458
|
+
checks.push({
|
|
459
|
+
name: "machine_has_nodes",
|
|
460
|
+
passed: false,
|
|
461
|
+
message: "Publish blocked: bound Machine has 0 nodes — Progress will be permanently stuck",
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
if (input.current_machine_initial_forward_ok === false) {
|
|
465
|
+
checks.push({
|
|
466
|
+
name: "machine_initial_forward",
|
|
467
|
+
passed: false,
|
|
468
|
+
message: "Publish blocked: bound Machine's initial node (prev_node='') has no forward — Progress cannot advance from '' (FIX-002 violated)",
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
if (input.current_machine_bPublished === false) {
|
|
472
|
+
checks.push({
|
|
473
|
+
name: "machine_not_published",
|
|
474
|
+
passed: true,
|
|
475
|
+
level: "warning",
|
|
476
|
+
message: "Warning: bound Machine is not yet published. Orders cannot advance until the Machine is published. Publish the Machine after this Service to enable order workflow.",
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
const arbCountForPerm = input.current_arbitrations_count ?? 0;
|
|
481
|
+
const arbFromInputForPerm = data.arbitrations && "objects" in data.arbitrations ? data.arbitrations.objects.length : 0;
|
|
482
|
+
if ((arbCountForPerm + arbFromInputForPerm) > 0 && input.current_arbitrators_authorized === false) {
|
|
483
|
+
checks.push({
|
|
484
|
+
name: "arbitrators_permission_authorized",
|
|
485
|
+
passed: true,
|
|
486
|
+
level: "info",
|
|
487
|
+
message: "Info: ensure arbitrators have been granted the required permission index in the Permission object. Without authorization, dispute resolution Guards will reject arbitrator signatures (Permission denied).",
|
|
488
|
+
});
|
|
489
|
+
}
|
|
416
490
|
}
|
|
417
491
|
if (input.current_bPublished) {
|
|
418
492
|
if (data.machine !== undefined) {
|
|
@@ -445,7 +519,9 @@ function verifyEnvironment(input) {
|
|
|
445
519
|
});
|
|
446
520
|
}
|
|
447
521
|
const allPassed = checks.every(c => c.passed);
|
|
448
|
-
|
|
522
|
+
const warnings_count = checks.filter(c => c.level === "warning").length;
|
|
523
|
+
const infos_count = checks.filter(c => c.level === "info").length;
|
|
524
|
+
return { passed: allPassed, checks, warnings_count, infos_count };
|
|
449
525
|
}
|
|
450
526
|
export const SERVICE_CONFIRM_VERSION = "1.0.0";
|
|
451
527
|
export function runServiceConfirmGate(input) {
|
|
@@ -458,7 +534,7 @@ export function runServiceConfirmGate(input) {
|
|
|
458
534
|
status: "blocked_checklist",
|
|
459
535
|
checklist_result: checklist,
|
|
460
536
|
confirmation_text: generateConfirmationText(input, checklist),
|
|
461
|
-
block_reason: `Checklist failed: ${checklist.items.filter(i => !i.passed && i.id !== "C-S-17" && i.id !== "C-S-18").map(i => i.id).join(", ")}`,
|
|
537
|
+
block_reason: `Checklist failed: ${checklist.items.filter(i => !i.passed && i.id !== "C-S-17" && i.id !== "C-S-18" && i.id !== "C-S-19").map(i => i.id).join(", ")}`,
|
|
462
538
|
decision_record: {
|
|
463
539
|
timestamp: new Date().toISOString(),
|
|
464
540
|
gate_version: SERVICE_CONFIRM_VERSION,
|
|
@@ -507,6 +583,15 @@ export function runServiceConfirmGate(input) {
|
|
|
507
583
|
cs18.message = cs18.passed ? "" : "Transfer not confirmed by user (QS2)";
|
|
508
584
|
}
|
|
509
585
|
}
|
|
586
|
+
const cs19 = checklist.items.find(i => i.id === "C-S-19");
|
|
587
|
+
if (cs19 && !cs19.passed) {
|
|
588
|
+
const qs11Asked = userConf.questions_asked.some(q => q.id === "QS11");
|
|
589
|
+
if (qs11Asked) {
|
|
590
|
+
const qs11 = userConf.responses.find(r => r.question_id === "QS11");
|
|
591
|
+
cs19.passed = qs11?.response === "yes";
|
|
592
|
+
cs19.message = cs19.passed ? "" : "User declined publish without arbitrations (QS11)";
|
|
593
|
+
}
|
|
594
|
+
}
|
|
510
595
|
if (input.service_json.publish) {
|
|
511
596
|
const cs17 = checklist.items.find(i => i.id === "C-S-17");
|
|
512
597
|
if (cs17 && !cs17.passed) {
|
|
@@ -549,23 +634,45 @@ export function runServiceConfirmGate(input) {
|
|
|
549
634
|
};
|
|
550
635
|
}
|
|
551
636
|
}
|
|
637
|
+
if (cs19 && !cs19.passed) {
|
|
638
|
+
const qs11Asked = userConf.questions_asked.some(q => q.id === "QS11");
|
|
639
|
+
if (qs11Asked) {
|
|
640
|
+
layersFailed.push("L3_user");
|
|
641
|
+
return {
|
|
642
|
+
status: "blocked_user",
|
|
643
|
+
checklist_result: checklist,
|
|
644
|
+
risk_result: assessment,
|
|
645
|
+
user_confirmation: userConf,
|
|
646
|
+
confirmation_text: generateConfirmationText(input, checklist, assessment),
|
|
647
|
+
block_reason: "User did not confirm publish without arbitrations (QS11) — set arbitrations or explicitly confirm",
|
|
648
|
+
decision_record: {
|
|
649
|
+
timestamp: new Date().toISOString(),
|
|
650
|
+
gate_version: SERVICE_CONFIRM_VERSION,
|
|
651
|
+
final_status: "blocked_user",
|
|
652
|
+
layers_passed: layersPassed,
|
|
653
|
+
layers_failed: layersFailed,
|
|
654
|
+
},
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
}
|
|
552
658
|
}
|
|
553
659
|
layersPassed.push("L3_user");
|
|
660
|
+
let envResult;
|
|
554
661
|
if (input.skip_environment_check) {
|
|
555
662
|
layersPassed.push("L4_environment");
|
|
556
663
|
}
|
|
557
664
|
else {
|
|
558
|
-
|
|
559
|
-
if (!
|
|
665
|
+
envResult = verifyEnvironment(input);
|
|
666
|
+
if (!envResult.passed) {
|
|
560
667
|
layersFailed.push("L4_environment");
|
|
561
668
|
return {
|
|
562
669
|
status: "blocked_environment",
|
|
563
670
|
checklist_result: checklist,
|
|
564
671
|
risk_result: assessment,
|
|
565
672
|
user_confirmation: userConf,
|
|
566
|
-
environment_result:
|
|
673
|
+
environment_result: envResult,
|
|
567
674
|
confirmation_text: generateConfirmationText(input, checklist, assessment),
|
|
568
|
-
block_reason: `Environment check failed: ${
|
|
675
|
+
block_reason: `Environment check failed: ${envResult.checks.filter(c => !c.passed).map(c => c.name).join(", ")}`,
|
|
569
676
|
decision_record: {
|
|
570
677
|
timestamp: new Date().toISOString(),
|
|
571
678
|
gate_version: SERVICE_CONFIRM_VERSION,
|
|
@@ -582,6 +689,7 @@ export function runServiceConfirmGate(input) {
|
|
|
582
689
|
checklist_result: checklist,
|
|
583
690
|
risk_result: assessment,
|
|
584
691
|
user_confirmation: userConf,
|
|
692
|
+
environment_result: envResult,
|
|
585
693
|
confirmation_text: generateConfirmationText(input, checklist, assessment),
|
|
586
694
|
decision_record: {
|
|
587
695
|
timestamp: new Date().toISOString(),
|
|
@@ -670,7 +778,7 @@ export function progressiveServiceCheck(input) {
|
|
|
670
778
|
return {
|
|
671
779
|
stage: "checklist",
|
|
672
780
|
status: "fail",
|
|
673
|
-
message: `Checklist failed: ${checklist.items.filter(i => !i.passed && i.id !== "C-S-17" && i.id !== "C-S-18").map(i => i.id).join(", ")}`,
|
|
781
|
+
message: `Checklist failed: ${checklist.items.filter(i => !i.passed && i.id !== "C-S-17" && i.id !== "C-S-18" && i.id !== "C-S-19").map(i => i.id).join(", ")}`,
|
|
674
782
|
next_action: "Fix failing checklist items",
|
|
675
783
|
};
|
|
676
784
|
}
|
|
@@ -33,9 +33,13 @@ export const TOOL_CONSTRAINTS = [
|
|
|
33
33
|
return true;
|
|
34
34
|
if (!d?.publish)
|
|
35
35
|
return true;
|
|
36
|
-
|
|
36
|
+
if (Array.isArray(d?.node?.nodes) && d.node.nodes.length > 0)
|
|
37
|
+
return true;
|
|
38
|
+
if (typeof d?.node?.json_or_markdown_file === "string" && d.node.json_or_markdown_file.length > 0)
|
|
39
|
+
return true;
|
|
40
|
+
return false;
|
|
37
41
|
},
|
|
38
|
-
message: () => "Publishing a Machine without nodes — a workflow template requires at least one node. Consider adding nodes before publish, or publish later after adding nodes.",
|
|
42
|
+
message: () => "Publishing a Machine without nodes — a workflow template requires at least one node. Provide nodes via inline `node.nodes` array OR `node.json_or_markdown_file` path (file contains JSON or Markdown node definitions). Consider adding nodes before publish, or publish later after adding nodes.",
|
|
39
43
|
},
|
|
40
44
|
],
|
|
41
45
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DeploymentDocValidationResult } from "./deployment-doc.js";
|
|
2
|
-
export declare function generateDeploymentDocFromProject(projectId: string, network: "testnet" | "mainnet"): Promise<{
|
|
2
|
+
export declare function generateDeploymentDocFromProject(projectId: string, network: "testnet" | "mainnet", detailLevel?: "summary" | "detailed" | "full"): Promise<{
|
|
3
3
|
markdown: string;
|
|
4
4
|
validation: DeploymentDocValidationResult;
|
|
5
5
|
}>;
|