@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.
Files changed (133) hide show
  1. package/README.md +5 -3
  2. package/dist/extensions/capability-manifest.d.ts +125 -0
  3. package/dist/extensions/capability-manifest.js +594 -0
  4. package/dist/extensions/constraint-registry.d.ts +24 -0
  5. package/dist/extensions/constraint-registry.js +196 -0
  6. package/dist/extensions/index.d.ts +12 -0
  7. package/dist/extensions/index.js +6 -0
  8. package/dist/extensions/metric-registry.d.ts +26 -0
  9. package/dist/extensions/metric-registry.js +257 -0
  10. package/dist/extensions/mode-evaluator.d.ts +15 -0
  11. package/dist/extensions/mode-evaluator.js +170 -0
  12. package/dist/extensions/modes.d.ts +2 -0
  13. package/dist/extensions/modes.js +407 -0
  14. package/dist/extensions/registry.d.ts +48 -0
  15. package/dist/extensions/registry.js +629 -0
  16. package/dist/extensions/types.d.ts +218 -0
  17. package/dist/extensions/types.js +1 -0
  18. package/dist/knowledge/deployment-scanner.d.ts +3 -0
  19. package/dist/knowledge/deployment-scanner.js +64 -3
  20. package/dist/knowledge/guard-risk.d.ts +13 -0
  21. package/dist/knowledge/guard-risk.js +57 -0
  22. package/dist/knowledge/guard-templates.js +278 -0
  23. package/dist/knowledge/machine-templates.js +20 -1
  24. package/dist/knowledge/service-confirm.d.ts +14 -5
  25. package/dist/knowledge/service-confirm.js +116 -8
  26. package/dist/knowledge/tool-constraints.js +6 -2
  27. package/dist/project/deployment-bridge.d.ts +1 -1
  28. package/dist/project/deployment-bridge.js +27 -4
  29. package/dist/project/deployment-doc.d.ts +3 -0
  30. package/dist/project/deployment-doc.js +72 -10
  31. package/dist/project/evaluation.d.ts +2 -0
  32. package/dist/project/evaluation.js +569 -84
  33. package/dist/project/graph-builder.d.ts +4 -1
  34. package/dist/project/graph-builder.js +126 -61
  35. package/dist/project/graph.d.ts +1 -0
  36. package/dist/project/handlers.d.ts +219 -5
  37. package/dist/project/handlers.js +819 -9
  38. package/dist/project/stage-gate.d.ts +4 -0
  39. package/dist/project/stage-gate.js +64 -5
  40. package/dist/project/task-tracker.d.ts +26 -0
  41. package/dist/project/task-tracker.js +78 -0
  42. package/dist/safety/preview.js +16 -0
  43. package/dist/schema/call/allocation.d.ts +16 -16
  44. package/dist/schema/call/base.d.ts +21 -13
  45. package/dist/schema/call/base.js +27 -6
  46. package/dist/schema/call/bridge.d.ts +5 -5
  47. package/dist/schema/call/bridge.js +3 -1
  48. package/dist/schema/call/demand.d.ts +23 -31
  49. package/dist/schema/call/guard.js +1 -1
  50. package/dist/schema/call/machine.d.ts +402 -376
  51. package/dist/schema/call/order.d.ts +149 -228
  52. package/dist/schema/call/order.js +7 -3
  53. package/dist/schema/call/payment.d.ts +183 -3
  54. package/dist/schema/call/payment.js +21 -3
  55. package/dist/schema/call/personal.d.ts +241 -52
  56. package/dist/schema/call/progress.d.ts +53 -61
  57. package/dist/schema/call/progress.js +18 -4
  58. package/dist/schema/call/repository.d.ts +23 -31
  59. package/dist/schema/call/semantic.d.ts +1 -1
  60. package/dist/schema/call/semantic.js +30 -1
  61. package/dist/schema/call/service.d.ts +95 -119
  62. package/dist/schema/call/service.js +22 -1
  63. package/dist/schema/common/index.d.ts +11 -2
  64. package/dist/schema/common/index.js +43 -14
  65. package/dist/schema/local/index.d.ts +32 -35
  66. package/dist/schema/local/index.js +23 -5
  67. package/dist/schema/messenger/index.d.ts +274 -46
  68. package/dist/schema/operations.d.ts +680 -539
  69. package/dist/schema/operations.js +22 -0
  70. package/dist/schema/project/index.d.ts +1796 -80
  71. package/dist/schema/project/index.js +304 -14
  72. package/dist/schema/query/index.d.ts +705 -349
  73. package/dist/schema/query/index.js +164 -31
  74. package/dist/schema/schema-query/index.d.ts +15 -3
  75. package/dist/schema/schema-query/index.js +23 -5
  76. package/dist/schema/utils/node-parser.js +7 -4
  77. package/dist/schema-query/index.d.ts +7 -1
  78. package/dist/schema-query/index.js +204 -4
  79. package/dist/schemas/account_operation.output.json +7 -1
  80. package/dist/schemas/account_operation.schema.json +2 -2
  81. package/dist/schemas/bridge_operation.output.json +6 -0
  82. package/dist/schemas/bridge_operation.schema.json +1 -1
  83. package/dist/schemas/guard-templates.json +379 -0
  84. package/dist/schemas/guard2file.schema.json +1 -1
  85. package/dist/schemas/index.json +1 -1
  86. package/dist/schemas/local_info_operation.output.json +6 -0
  87. package/dist/schemas/local_mark_operation.output.json +7 -1
  88. package/dist/schemas/local_mark_operation.schema.json +1 -1
  89. package/dist/schemas/machineNode2file.schema.json +1 -1
  90. package/dist/schemas/messenger_operation.schema.json +4 -4
  91. package/dist/schemas/onchain_events.output.json +1 -1
  92. package/dist/schemas/onchain_operations.schema.json +348 -296
  93. package/dist/schemas/onchain_operations_allocation.schema.json +34 -25
  94. package/dist/schemas/onchain_operations_arbitration.schema.json +8 -8
  95. package/dist/schemas/onchain_operations_contact.schema.json +8 -8
  96. package/dist/schemas/onchain_operations_demand.schema.json +8 -8
  97. package/dist/schemas/onchain_operations_gen_passport.schema.json +14 -14
  98. package/dist/schemas/onchain_operations_gen_proof.schema.json +2 -2
  99. package/dist/schemas/onchain_operations_guard.schema.json +1 -1
  100. package/dist/schemas/onchain_operations_machine.schema.json +41 -33
  101. package/dist/schemas/onchain_operations_order.schema.json +71 -77
  102. package/dist/schemas/onchain_operations_payment.schema.json +141 -111
  103. package/dist/schemas/onchain_operations_permission.schema.json +2 -2
  104. package/dist/schemas/onchain_operations_personal.schema.json +7 -7
  105. package/dist/schemas/onchain_operations_progress.schema.json +8 -8
  106. package/dist/schemas/onchain_operations_proof.schema.json +7 -7
  107. package/dist/schemas/onchain_operations_repository.schema.json +8 -8
  108. package/dist/schemas/onchain_operations_reward.schema.json +10 -10
  109. package/dist/schemas/onchain_operations_service.schema.json +46 -35
  110. package/dist/schemas/onchain_operations_treasury.schema.json +8 -8
  111. package/dist/schemas/onchain_table_data.output.json +33 -25
  112. package/dist/schemas/onchain_table_data.schema.json +12 -12
  113. package/dist/schemas/project_operation.output.json +1175 -23
  114. package/dist/schemas/project_operation.schema.json +40 -4
  115. package/dist/schemas/query_toolkit.output.json +104 -76
  116. package/dist/schemas/query_toolkit.schema.json +9 -9
  117. package/dist/schemas/schema_query.output.json +7 -3
  118. package/dist/schemas/schema_query.schema.json +17 -3
  119. package/dist/tools/handlers/local.js +20 -5
  120. package/dist/tools/handlers/onchain.js +23 -0
  121. package/dist/tools/handlers/project.js +52 -3
  122. package/dist/tools/handlers/query.js +27 -0
  123. package/dist/tools/handlers/schema-query.js +19 -0
  124. package/dist/tools/handlers/task-status.d.ts +170 -0
  125. package/dist/tools/handlers/task-status.js +55 -0
  126. package/dist/tools/handlers/wip.js +47 -1
  127. package/dist/tools/index.js +211 -7
  128. package/dist/tools/retry.d.ts +8 -0
  129. package/dist/tools/retry.js +85 -0
  130. package/dist/tools/wip-deploy-assist.d.ts +28 -0
  131. package/dist/tools/wip-deploy-assist.js +278 -0
  132. package/package.json +2 -2
  133. 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
- name: string;
42
- passed: boolean;
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
- const allPassed = items.filter(i => i.id !== "C-S-17" && i.id !== "C-S-18").every(i => i.passed);
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
- return { passed: allPassed, checks };
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
- const env = verifyEnvironment(input);
559
- if (!env.passed) {
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: env,
673
+ environment_result: envResult,
567
674
  confirmation_text: generateConfirmationText(input, checklist, assessment),
568
- block_reason: `Environment check failed: ${env.checks.filter(c => !c.passed).map(c => c.name).join(", ")}`,
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
- return Array.isArray(d?.node?.nodes) && d.node.nodes.length > 0;
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
  }>;