@wowok/agent-mcp 2.5.3 → 2.5.4
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/customer/index.d.ts +2 -2
- package/dist/customer/index.js +1 -1
- package/dist/customer/info-puzzle.d.ts +39 -2
- package/dist/customer/info-puzzle.js +370 -29
- package/dist/customer/risk-assessment.js +51 -14
- package/dist/customer/types.d.ts +85 -0
- package/dist/knowledge/machine-ledger.js +32 -29
- package/dist/knowledge/machine-risk.js +23 -0
- package/dist/knowledge/machine-templates.js +118 -86
- package/dist/knowledge/reward-templates.d.ts +1 -1
- package/dist/knowledge/reward-templates.js +77 -5
- package/dist/knowledge/trust-metrics.d.ts +4 -0
- package/dist/knowledge/trust-metrics.js +67 -7
- package/dist/schema/call/arbitration.d.ts +54 -54
- package/dist/schema/call/demand.d.ts +48 -48
- package/dist/schema/operations.d.ts +76 -76
- package/dist/schema/project/index.d.ts +12 -12
- package/dist/schema/trust/index.d.ts +10 -10
- package/dist/schemas/index.json +1 -1
- package/package.json +1 -1
package/dist/customer/types.d.ts
CHANGED
|
@@ -28,6 +28,9 @@ export interface WorkflowAnalysis {
|
|
|
28
28
|
guard_gated_forwards: number;
|
|
29
29
|
has_refund_path: boolean;
|
|
30
30
|
has_arbitration_path: boolean;
|
|
31
|
+
has_arbitration_binding: boolean;
|
|
32
|
+
has_wonder_node: boolean;
|
|
33
|
+
has_dispute_anti_pattern: boolean;
|
|
31
34
|
has_milestone_nodes: boolean;
|
|
32
35
|
dead_end_nodes: string[];
|
|
33
36
|
terminal_nodes: string[];
|
|
@@ -77,6 +80,82 @@ export interface MarketContext {
|
|
|
77
80
|
price_percentile: number;
|
|
78
81
|
supply_demand_ratio: number;
|
|
79
82
|
}
|
|
83
|
+
export type UserMetricEvaluatorType = "threshold" | "ratio" | "boolean" | "presence" | "regex_match" | "custom_query";
|
|
84
|
+
export interface UserMetric {
|
|
85
|
+
name: string;
|
|
86
|
+
label: string;
|
|
87
|
+
evaluator: UserMetricEvaluatorType;
|
|
88
|
+
params: {
|
|
89
|
+
path?: string;
|
|
90
|
+
threshold?: number | string;
|
|
91
|
+
invert?: boolean;
|
|
92
|
+
min?: number;
|
|
93
|
+
max?: number;
|
|
94
|
+
pattern?: string;
|
|
95
|
+
query_key?: string;
|
|
96
|
+
};
|
|
97
|
+
weight: number;
|
|
98
|
+
advice_on_fail?: string;
|
|
99
|
+
advice_on_pass?: string;
|
|
100
|
+
}
|
|
101
|
+
export interface UserMetricsConfig {
|
|
102
|
+
version: number;
|
|
103
|
+
source_file?: string;
|
|
104
|
+
metrics: UserMetric[];
|
|
105
|
+
adjustment_cap?: number;
|
|
106
|
+
}
|
|
107
|
+
export interface UserMetricResult {
|
|
108
|
+
name: string;
|
|
109
|
+
label: string;
|
|
110
|
+
passed: boolean;
|
|
111
|
+
score: number;
|
|
112
|
+
observed_value?: string | number | boolean;
|
|
113
|
+
weight: number;
|
|
114
|
+
advice?: string;
|
|
115
|
+
}
|
|
116
|
+
export interface UserMetricsResult {
|
|
117
|
+
metrics: UserMetricResult[];
|
|
118
|
+
score: number;
|
|
119
|
+
adjustment: number;
|
|
120
|
+
source_file?: string;
|
|
121
|
+
evaluated_at: number;
|
|
122
|
+
}
|
|
123
|
+
export interface ServiceNestingEvaluation {
|
|
124
|
+
depth_reached: number;
|
|
125
|
+
truncated: boolean;
|
|
126
|
+
bindings: {
|
|
127
|
+
machine?: {
|
|
128
|
+
object_id: string;
|
|
129
|
+
published: boolean;
|
|
130
|
+
risk_level: "low" | "medium" | "high" | "critical";
|
|
131
|
+
issues: string[];
|
|
132
|
+
};
|
|
133
|
+
arbitrations: Array<{
|
|
134
|
+
object_id: string;
|
|
135
|
+
voting_guard_count: number;
|
|
136
|
+
fee_balance: string;
|
|
137
|
+
paused: boolean;
|
|
138
|
+
risk_level: "low" | "medium" | "high" | "critical";
|
|
139
|
+
issues: string[];
|
|
140
|
+
}>;
|
|
141
|
+
rewards: Array<{
|
|
142
|
+
object_id: string;
|
|
143
|
+
balance: string;
|
|
144
|
+
guard_count: number;
|
|
145
|
+
risk_level: "low" | "medium" | "high" | "critical";
|
|
146
|
+
issues: string[];
|
|
147
|
+
}>;
|
|
148
|
+
repositories: Array<{
|
|
149
|
+
object_id: string;
|
|
150
|
+
risk_level: "low" | "medium" | "high" | "critical";
|
|
151
|
+
issues: string[];
|
|
152
|
+
}>;
|
|
153
|
+
};
|
|
154
|
+
overall_risk_level: "low" | "medium" | "high" | "critical";
|
|
155
|
+
overall_risk_score: number;
|
|
156
|
+
total_bindings_evaluated: number;
|
|
157
|
+
evaluated_at: number;
|
|
158
|
+
}
|
|
80
159
|
export interface InfoPuzzle {
|
|
81
160
|
service_id: string;
|
|
82
161
|
service_basics: ServiceBasics;
|
|
@@ -85,6 +164,8 @@ export interface InfoPuzzle {
|
|
|
85
164
|
trust_signals?: TrustSignals;
|
|
86
165
|
merchant_behavior?: MerchantBehavior;
|
|
87
166
|
market_context?: MarketContext;
|
|
167
|
+
user_metrics?: UserMetricsResult;
|
|
168
|
+
service_nesting?: ServiceNestingEvaluation;
|
|
88
169
|
completeness: number;
|
|
89
170
|
gaps: string[];
|
|
90
171
|
assembled_at: number;
|
|
@@ -107,8 +188,12 @@ export interface RiskDimensionScore {
|
|
|
107
188
|
export type RiskLevel = "low" | "medium_low" | "medium_high" | "high";
|
|
108
189
|
export interface RiskScore {
|
|
109
190
|
total: number;
|
|
191
|
+
base_total: number;
|
|
110
192
|
level: RiskLevel;
|
|
111
193
|
dimensions: RiskDimensionScore[];
|
|
194
|
+
user_metrics_adjustment?: number;
|
|
195
|
+
service_nesting_risk?: number;
|
|
196
|
+
service_nesting_level?: "low" | "medium" | "high" | "critical";
|
|
112
197
|
red_flags: string[];
|
|
113
198
|
advice: string[];
|
|
114
199
|
}
|
|
@@ -29,25 +29,24 @@ export const MACHINE_SCENES = [
|
|
|
29
29
|
id: "ecommerce_with_exceptions",
|
|
30
30
|
name: "E-commerce Flow with Exception Branches",
|
|
31
31
|
industry: ["ecommerce"],
|
|
32
|
-
scenario: "Paid (entry) → Ship → {Receive → Complete | Lost
|
|
32
|
+
scenario: "Paid (entry) → Ship → {Receive → Complete | Lost (terminal) | Return → Refund}",
|
|
33
33
|
participants: [
|
|
34
|
-
{ role: "customer", permission_type: "OrderHolder", involved_in: ["Receive", "Return"] },
|
|
35
|
-
{ role: "merchant", permission_type: "Permission", involved_in: ["Ship", "Refund"] },
|
|
36
|
-
{ role: "arbitrator", permission_type: "NamedOperator", involved_in: ["Arbitration"] },
|
|
34
|
+
{ role: "customer", permission_type: "OrderHolder", involved_in: ["Receive", "Return", "Lost"] },
|
|
35
|
+
{ role: "merchant", permission_type: "Permission", involved_in: ["Ship", "Refund", "Lost"] },
|
|
37
36
|
],
|
|
38
37
|
recommended_patterns: ["P-M-SEQ", "P-M-COMPETING"],
|
|
39
38
|
recommended_topology: ["P-M-LINEAR", "P-M-COMPETING"],
|
|
40
39
|
guard_requirements: [
|
|
41
40
|
{ forward: "Lost", scene: "machine_forward_guard", purpose: "Dual-sig to confirm lost" },
|
|
42
|
-
{ forward: "Arbitration", scene: "arbitration_usage_guard", purpose: "Verify arbitration eligibility" },
|
|
43
41
|
],
|
|
44
|
-
allocation_integration: "Complete->merchant | Refund->customer |
|
|
42
|
+
allocation_integration: "Complete->merchant | Refund->customer | Lost->escrow or partial refund (Allocator only). Disputes are NOT modeled here — they flow through Order + Arbitration object off-Machine.",
|
|
45
43
|
special_constraints: [
|
|
46
44
|
"First node 'Paid' represents post-payment state — buy() completes payment atomically before Progress is created",
|
|
47
45
|
"No separate 'Order' or 'Pay' nodes needed — payment is handled by Service.buy(), not Machine",
|
|
48
|
-
"Lost node threshold=2 (customer + merchant dual-sig)",
|
|
49
|
-
"Competing Pair: Receive vs Lost (first-wins)",
|
|
50
|
-
"Arbitration
|
|
46
|
+
"Lost node threshold=2 (customer + merchant dual-sig) — Lost is a TERMINAL node, not a path to arbitration",
|
|
47
|
+
"Competing Pair: Receive vs Lost vs Return (first-wins)",
|
|
48
|
+
"DISPUTE INDEPENDENCE: arbitration is NOT a Machine node. If a Lost-terminal order later needs dispute resolution, the customer files arbitration::dispute(order, arbitration) against the Arbitration object bound to the Service — completely off-Machine. Arbitrators live in Arbitration.voting_guard, never in Permission or NamedOperator.",
|
|
49
|
+
"Refund Allocation must use sharing.who=OrderHolder (refund to customer, NOT merchant)",
|
|
51
50
|
],
|
|
52
51
|
first_node_semantics: "Paid — order has been created and paid via Service.buy(); Machine starts at post-payment state",
|
|
53
52
|
mutable_after_publish: false,
|
|
@@ -207,31 +206,32 @@ export const MACHINE_SCENES = [
|
|
|
207
206
|
m_rounds_focus: ["M1", "M3", "M8"],
|
|
208
207
|
},
|
|
209
208
|
{
|
|
210
|
-
id: "
|
|
211
|
-
name: "
|
|
212
|
-
industry: ["ecommerce", "service", "rental"],
|
|
213
|
-
scenario: "
|
|
209
|
+
id: "wonder_feedback",
|
|
210
|
+
name: "Wonder Feedback Flow (Post-Completion Rating)",
|
|
211
|
+
industry: ["ecommerce", "service", "rental", "subscription", "education"],
|
|
212
|
+
scenario: "Paid (entry) → ... → Completed → {Wonder | NoWonder} (rating terminals)",
|
|
214
213
|
participants: [
|
|
215
|
-
{ role: "
|
|
216
|
-
{ role: "
|
|
217
|
-
{ role: "system", permission_type: "Permission", involved_in: ["Execute Ruling"] },
|
|
214
|
+
{ role: "customer", permission_type: "OrderHolder", involved_in: ["Wonder", "NoWonder"] },
|
|
215
|
+
{ role: "merchant", permission_type: "Permission", involved_in: ["Completed"] },
|
|
218
216
|
],
|
|
219
|
-
recommended_patterns: ["P-M-
|
|
220
|
-
recommended_topology: ["P-M-LINEAR"],
|
|
217
|
+
recommended_patterns: ["P-M-SEQ", "P-M-COMPETING"],
|
|
218
|
+
recommended_topology: ["P-M-LINEAR", "P-M-COMPETING"],
|
|
221
219
|
guard_requirements: [
|
|
222
|
-
{ forward: "
|
|
223
|
-
{ forward: "
|
|
220
|
+
{ forward: "Praise", scene: "machine_forward_guard", purpose: "Verify Progress.current == 'Completed' (rating only after completion)" },
|
|
221
|
+
{ forward: "Complain", scene: "machine_forward_guard", purpose: "Verify Progress.current == 'Completed' (rating only after completion)" },
|
|
224
222
|
],
|
|
225
|
-
allocation_integration: "
|
|
223
|
+
allocation_integration: "Completed -> merchant allocation (terminal); Wonder/NoWonder are reputation terminals — no fund allocation. Reward object (independent) claims fire when Progress.current == 'wonder'",
|
|
226
224
|
special_constraints: [
|
|
227
|
-
"Arbitration object
|
|
228
|
-
"
|
|
229
|
-
"
|
|
230
|
-
"
|
|
225
|
+
"Dispute MUST NOT be modeled as machine nodes — disputes flow through Order + Arbitration object, independent of this Machine",
|
|
226
|
+
"Wonder/NoWonder are POST-completion terminals: order is already settled when customer rates",
|
|
227
|
+
"Both Wonder and NoWonder forwards have named_operator='' (OrderHolder) — rating is a customer-only action",
|
|
228
|
+
"Reward object (if bound) MUST use a Guard verifying Progress.current == 'wonder' AND order.holder == signer — no Permission needed for reward claim",
|
|
229
|
+
"Competing Pair: Praise vs Complain (first-wins; one rating per order)",
|
|
230
|
+
"No dispute/refund/arbitration nodes in this Machine — those flows live off-Machine",
|
|
231
231
|
],
|
|
232
|
-
first_node_semantics: "
|
|
232
|
+
first_node_semantics: "Paid — order has been created and paid via Service.buy(); Machine starts at post-payment state, same as e-commerce baseline",
|
|
233
233
|
mutable_after_publish: false,
|
|
234
|
-
m_rounds_focus: ["M1", "
|
|
234
|
+
m_rounds_focus: ["M1", "M2", "M3", "M5", "M8"],
|
|
235
235
|
},
|
|
236
236
|
{
|
|
237
237
|
id: "subscription",
|
|
@@ -281,8 +281,11 @@ export function inferSceneFromFlow(description) {
|
|
|
281
281
|
if (lower.includes("supply chain") || lower.includes("cross-machine")) {
|
|
282
282
|
return findScene("cross_machine_supply_chain");
|
|
283
283
|
}
|
|
284
|
-
if (lower.includes("
|
|
285
|
-
|
|
284
|
+
if (lower.includes("wonder") || lower.includes("praise") || lower.includes("complain")
|
|
285
|
+
|| lower.includes("thumbs_up") || lower.includes("thumbs_down")
|
|
286
|
+
|| lower.includes("rating") || lower.includes("feedback")
|
|
287
|
+
|| lower.includes("好评") || lower.includes("差评")) {
|
|
288
|
+
return findScene("wonder_feedback");
|
|
286
289
|
}
|
|
287
290
|
if (lower.includes("reward") || lower.includes("incentive")) {
|
|
288
291
|
return findScene("reward_incentive");
|
|
@@ -109,6 +109,29 @@ export const MACHINE_RISK_RULES = [
|
|
|
109
109
|
auto_fixable: false,
|
|
110
110
|
related_constraint: "MC-C-04",
|
|
111
111
|
},
|
|
112
|
+
{
|
|
113
|
+
id: "R-M1-11",
|
|
114
|
+
name: "Dispute anti-pattern — machine illegally models dispute flow",
|
|
115
|
+
category: "structural",
|
|
116
|
+
severity: "critical",
|
|
117
|
+
description: "Machine contains nodes named 'disputed', 'refunded', 'arb', 'arbitration', " +
|
|
118
|
+
"'dispute', or 'refund'. This is a CRITICAL design anti-pattern because " +
|
|
119
|
+
"dispute is INDEPENDENT of the machine workflow. Dispute flows through: " +
|
|
120
|
+
"Order → Arbitration.dispute() → Arb object → arbitration() ruling → " +
|
|
121
|
+
"arb_withdraw by order holder. Arbitrators are configured in " +
|
|
122
|
+
"Arbitration.voting_guard, NOT in Permission. Merchants who model " +
|
|
123
|
+
"dispute in the machine (e.g., 'disputed → refunded' forward guarded " +
|
|
124
|
+
"by Permission index 1500) create dead-end paths because no one has " +
|
|
125
|
+
"Permission 1500 — arbitrators are NOT Permission holders.",
|
|
126
|
+
detection: "Match node names against regex /^(disputed|refunded|arb|arbitration|dispute|refund)$/i",
|
|
127
|
+
recommendation: "Remove all dispute/refund nodes from the machine. Instead: (1) bind an " +
|
|
128
|
+
"Arbitration object to the Service via Service.arbitrations, (2) configure " +
|
|
129
|
+
"arbitrators via Arbitration.voting_guard_add, (3) deposit compensation_fund " +
|
|
130
|
+
"to the Service. Disputes are then filed via arbitration::dispute(order, " +
|
|
131
|
+
"arbitration) — completely independent of the machine workflow.",
|
|
132
|
+
auto_fixable: false,
|
|
133
|
+
related_constraint: "dispute_independence_principle",
|
|
134
|
+
},
|
|
112
135
|
{
|
|
113
136
|
id: "R-M2-01",
|
|
114
137
|
name: "Guard not created",
|
|
@@ -993,142 +993,174 @@ export const MACHINE_TEMPLATES = [
|
|
|
993
993
|
],
|
|
994
994
|
},
|
|
995
995
|
{
|
|
996
|
-
id: "
|
|
997
|
-
scene_id: "
|
|
998
|
-
name: "
|
|
999
|
-
description: "
|
|
1000
|
-
"
|
|
1001
|
-
|
|
1002
|
-
|
|
996
|
+
id: "tpl_machine_wonder_feedback",
|
|
997
|
+
scene_id: "wonder_feedback",
|
|
998
|
+
name: "Wonder Feedback Flow (Post-Completion Rating)",
|
|
999
|
+
description: "Post-completion rating flow: Paid (entry) → Shipped → Received → Completed → {Wonder | NoWonder}. " +
|
|
1000
|
+
"After the order reaches 'Completed' (terminal business state, funds already allocated to merchant), " +
|
|
1001
|
+
"the OrderHolder (namedOperator='') can advance Progress to 'wonder' (positive review) or 'no_wonder' " +
|
|
1002
|
+
"(negative review). A bound Reward object's claim_guard verifies Progress.current == 'wonder' before " +
|
|
1003
|
+
"paying out — reward claim needs NO Permission. Dispute is NEVER modeled here: it flows through " +
|
|
1004
|
+
"Order → Arbitration.dispute() → Arb → arbitration() ruling → arb_withdraw, fully off-Machine.",
|
|
1005
|
+
recommended_patterns: ["P-M-SEQ", "P-M-COMPETING"],
|
|
1006
|
+
recommended_topology: ["P-M-LINEAR", "P-M-COMPETING"],
|
|
1003
1007
|
parameters: [
|
|
1004
1008
|
{
|
|
1005
|
-
name: "
|
|
1006
|
-
type: "
|
|
1009
|
+
name: "merchant_permission",
|
|
1010
|
+
type: "permission_ref",
|
|
1007
1011
|
required: true,
|
|
1008
|
-
description: "
|
|
1012
|
+
description: "Merchant Permission object (index 2 = merchant, index 3 = admin)",
|
|
1009
1013
|
},
|
|
1010
1014
|
{
|
|
1011
|
-
name: "
|
|
1012
|
-
type: "
|
|
1015
|
+
name: "wonder_node_name",
|
|
1016
|
+
type: "string",
|
|
1013
1017
|
required: true,
|
|
1014
|
-
default: "
|
|
1015
|
-
description: "
|
|
1018
|
+
default: "wonder",
|
|
1019
|
+
description: "Node name for positive feedback (好评) terminal",
|
|
1020
|
+
constraint: "Must match /^(wonder|praise|thumbs_up)$/i OR be a custom name; must NOT match dispute anti-pattern regex",
|
|
1016
1021
|
},
|
|
1017
1022
|
{
|
|
1018
|
-
name: "
|
|
1019
|
-
type: "
|
|
1023
|
+
name: "no_wonder_node_name",
|
|
1024
|
+
type: "string",
|
|
1020
1025
|
required: true,
|
|
1021
|
-
default: "
|
|
1022
|
-
description: "
|
|
1026
|
+
default: "no_wonder",
|
|
1027
|
+
description: "Node name for negative feedback (差评) terminal",
|
|
1028
|
+
constraint: "Must match /^(no_wonder|complain|thumbs_down)$/i OR be a custom name; must NOT match dispute anti-pattern regex",
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
name: "reward_ref",
|
|
1032
|
+
type: "reward_ref",
|
|
1033
|
+
required: false,
|
|
1034
|
+
description: "Reward object address (optional). If bound, its claim_guard should verify Progress.current == wonder_node_name AND order.holder == signer. Use wonder_praise or wonder_feedback reward template.",
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
name: "rating_time_limit_days",
|
|
1038
|
+
type: "number",
|
|
1039
|
+
required: false,
|
|
1040
|
+
default: "30",
|
|
1041
|
+
description: "Days after completion during which the customer can rate (enforced by Guard on Praise/Complain forwards via context(Clock))",
|
|
1042
|
+
constraint: "Must be > 0; reasonable range 7-90",
|
|
1023
1043
|
},
|
|
1024
1044
|
],
|
|
1025
1045
|
example_nodes: [
|
|
1026
|
-
{ name: "
|
|
1027
|
-
{ name: "
|
|
1028
|
-
{ name: "
|
|
1029
|
-
{ name: "
|
|
1030
|
-
{ name: "
|
|
1046
|
+
{ name: "Paid", description: "Entry: order created and paid via Service.buy(); Machine starts here" },
|
|
1047
|
+
{ name: "Shipped", description: "Merchant dispatched goods/service" },
|
|
1048
|
+
{ name: "Received", description: "Customer confirmed receipt" },
|
|
1049
|
+
{ name: "Completed", description: "Terminal business state — triggers merchant Allocation. Funds settled." },
|
|
1050
|
+
{ name: "${wonder_node_name}", description: "Positive feedback terminal (好评). OrderHolder-only. Reward-eligible." },
|
|
1051
|
+
{ name: "${no_wonder_node_name}", description: "Negative feedback terminal (差评). OrderHolder-only. Optional Repository write for analytics." },
|
|
1031
1052
|
],
|
|
1032
1053
|
example_pairs: [
|
|
1033
1054
|
{
|
|
1034
1055
|
prev_node: "",
|
|
1035
|
-
next_node: "
|
|
1056
|
+
next_node: "Paid",
|
|
1036
1057
|
threshold: 1,
|
|
1037
|
-
forwards: [
|
|
1038
|
-
{
|
|
1039
|
-
name: "OpenDispute",
|
|
1040
|
-
weight: 1,
|
|
1041
|
-
named_operator: "",
|
|
1042
|
-
guard: "arb_usage_guard",
|
|
1043
|
-
retained_submission: "dispute_record",
|
|
1044
|
-
},
|
|
1045
|
-
],
|
|
1058
|
+
forwards: [{ name: "Enter", weight: 1, named_operator: "" }],
|
|
1046
1059
|
},
|
|
1047
1060
|
{
|
|
1048
|
-
prev_node: "
|
|
1049
|
-
next_node: "
|
|
1061
|
+
prev_node: "Paid",
|
|
1062
|
+
next_node: "Shipped",
|
|
1050
1063
|
threshold: 1,
|
|
1051
|
-
forwards: [{ name: "
|
|
1064
|
+
forwards: [{ name: "Ship", weight: 1, permission_index: 2, named_operator: "" }],
|
|
1052
1065
|
},
|
|
1053
1066
|
{
|
|
1054
|
-
prev_node: "
|
|
1055
|
-
next_node: "
|
|
1067
|
+
prev_node: "Shipped",
|
|
1068
|
+
next_node: "Received",
|
|
1069
|
+
threshold: 1,
|
|
1070
|
+
forwards: [{ name: "ConfirmReceive", weight: 1, named_operator: "" }],
|
|
1071
|
+
},
|
|
1072
|
+
{
|
|
1073
|
+
prev_node: "Received",
|
|
1074
|
+
next_node: "Completed",
|
|
1056
1075
|
threshold: 1,
|
|
1057
|
-
forwards: [{ name: "
|
|
1076
|
+
forwards: [{ name: "Finish", weight: 1, permission_index: 2, named_operator: "" }],
|
|
1058
1077
|
},
|
|
1059
1078
|
{
|
|
1060
|
-
prev_node: "
|
|
1061
|
-
next_node: "
|
|
1062
|
-
threshold:
|
|
1079
|
+
prev_node: "Completed",
|
|
1080
|
+
next_node: "${wonder_node_name}",
|
|
1081
|
+
threshold: 1,
|
|
1063
1082
|
forwards: [
|
|
1064
1083
|
{
|
|
1065
|
-
name: "
|
|
1066
|
-
weight:
|
|
1067
|
-
|
|
1068
|
-
guard: "
|
|
1069
|
-
|
|
1070
|
-
{
|
|
1071
|
-
name: "MemberVote",
|
|
1072
|
-
weight: 8,
|
|
1073
|
-
named_operator: "arbitrator",
|
|
1074
|
-
guard: "arb_voting_guard",
|
|
1084
|
+
name: "Praise",
|
|
1085
|
+
weight: 1,
|
|
1086
|
+
named_operator: "",
|
|
1087
|
+
guard: "rating_window_guard",
|
|
1088
|
+
retained_submission: "rating_record",
|
|
1075
1089
|
},
|
|
1076
1090
|
],
|
|
1077
1091
|
},
|
|
1078
1092
|
{
|
|
1079
|
-
prev_node: "
|
|
1080
|
-
next_node: "
|
|
1093
|
+
prev_node: "Completed",
|
|
1094
|
+
next_node: "${no_wonder_node_name}",
|
|
1081
1095
|
threshold: 1,
|
|
1082
|
-
forwards: [
|
|
1096
|
+
forwards: [
|
|
1097
|
+
{
|
|
1098
|
+
name: "Complain",
|
|
1099
|
+
weight: 1,
|
|
1100
|
+
named_operator: "",
|
|
1101
|
+
guard: "rating_window_guard",
|
|
1102
|
+
retained_submission: "rating_record",
|
|
1103
|
+
},
|
|
1104
|
+
],
|
|
1083
1105
|
},
|
|
1084
1106
|
],
|
|
1085
1107
|
required_guards: [
|
|
1086
1108
|
{
|
|
1087
|
-
guard_template_id: "
|
|
1088
|
-
bound_to_forward: "
|
|
1089
|
-
purpose: "Verify
|
|
1090
|
-
},
|
|
1091
|
-
{
|
|
1092
|
-
guard_template_id: "tpl_voting_guard_numeric_weight",
|
|
1093
|
-
bound_to_forward: "ChiefVote",
|
|
1094
|
-
purpose: "Verify voting eligibility + weight extraction",
|
|
1109
|
+
guard_template_id: "tpl_forward_node_check",
|
|
1110
|
+
bound_to_forward: "Praise",
|
|
1111
|
+
purpose: "Verify Progress.current == 'Completed' (rating only allowed after completion) AND context(Clock) <= progress.entry_time + rating_time_limit_days",
|
|
1095
1112
|
},
|
|
1096
1113
|
{
|
|
1097
|
-
guard_template_id: "
|
|
1098
|
-
bound_to_forward: "
|
|
1099
|
-
purpose: "Verify
|
|
1114
|
+
guard_template_id: "tpl_forward_node_check",
|
|
1115
|
+
bound_to_forward: "Complain",
|
|
1116
|
+
purpose: "Verify Progress.current == 'Completed' (rating only allowed after completion) AND context(Clock) <= progress.entry_time + rating_time_limit_days",
|
|
1100
1117
|
},
|
|
1101
1118
|
],
|
|
1102
1119
|
required_permissions: [
|
|
1103
|
-
{ index: 2, name: "
|
|
1104
|
-
{ index: 3, name: "admin", description: "
|
|
1120
|
+
{ index: 2, name: "merchant", description: "Merchant role for Ship + Finish (NOT for rating — rating is OrderHolder-only via named_operator='')" },
|
|
1121
|
+
{ index: 3, name: "admin", description: "Admin override (optional)" },
|
|
1105
1122
|
],
|
|
1106
1123
|
required_allocators: [
|
|
1107
1124
|
{
|
|
1108
|
-
name: "
|
|
1109
|
-
trigger_node: "
|
|
1110
|
-
description: "
|
|
1125
|
+
name: "MerchantAllocation",
|
|
1126
|
+
trigger_node: "Completed",
|
|
1127
|
+
description: "100% to merchant Treasury or personal address. Fires at Completion — NOT at Wonder/NoWonder (those are reputation-only terminals with no fund flow).",
|
|
1111
1128
|
},
|
|
1112
1129
|
],
|
|
1113
|
-
forkable_fields: ["
|
|
1114
|
-
applicable_industries: ["
|
|
1130
|
+
forkable_fields: ["wonder_node_name", "no_wonder_node_name", "rating_time_limit_days", "additional_rating_nodes"],
|
|
1131
|
+
applicable_industries: ["ecommerce", "service", "rental", "subscription", "education"],
|
|
1115
1132
|
relevant_risks: [
|
|
1116
|
-
"R-M1-
|
|
1117
|
-
"R-
|
|
1133
|
+
"R-M1-01", "R-M1-02", "R-M1-08", "R-M1-11",
|
|
1134
|
+
"R-M2-01", "R-M2-05",
|
|
1135
|
+
"R-M3-02", "R-M3-04",
|
|
1136
|
+
"R-M4-01", "R-M5-03",
|
|
1118
1137
|
],
|
|
1119
1138
|
creation_notes: [
|
|
1120
|
-
"
|
|
1121
|
-
"
|
|
1122
|
-
"
|
|
1123
|
-
"
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
"
|
|
1139
|
+
"DISPUTE INDEPENDENCE: This Machine MUST NOT contain 'disputed', 'refunded', 'arb', 'arbitration' nodes (R-M1-11 critical). Disputes are filed off-Machine via arbitration::dispute(order, arbitration).",
|
|
1140
|
+
"Build order: Permission → Machine (unpublished) → Guards → Bind guards → Publish → Bind to Service → Bind Reward (optional)",
|
|
1141
|
+
"First node 'Paid' represents post-payment state — Service.buy() completes payment atomically before Progress is created",
|
|
1142
|
+
"No separate 'Order' or 'Pay' nodes needed — payment is handled by Service.buy(), not Machine",
|
|
1143
|
+
"Wonder/NoWonder forwards have named_operator='' (empty) — this delegates to the OrderHolder (customer). No Permission index needed for rating.",
|
|
1144
|
+
"Completed node triggers MerchantAllocation — funds are settled BEFORE the rating window opens. Rating does NOT affect fund flow.",
|
|
1145
|
+
"Competing Pair: Praise vs Complain (first-wins) — one rating per order, enforced by Guard + retained_submission audit trail.",
|
|
1146
|
+
"Reward object (if bound) is INDEPENDENT of Allocator: Reward.claim() requires Guard+passport, not Permission. Guard checks Progress.current == 'wonder' AND order.holder == signer.",
|
|
1147
|
+
"rating_window_guard enforces time limit: context(Clock) <= progress.entry_time + rating_time_limit_days. After window closes, no rating possible (Completed becomes true terminal).",
|
|
1148
|
+
"Optional: bind a Repository to record feedback node for analytics. Repository write happens via Guard's data extraction (NOT auto-passed — see 06 §14.4).",
|
|
1149
|
+
],
|
|
1150
|
+
example_use_case: "Photography service: Paid → Shipped (booking confirmed) → Received (session delivered) → Completed " +
|
|
1151
|
+
"(photos delivered, funds released to photographer) → customer advances to 'wonder' (loved the work) " +
|
|
1152
|
+
"or 'no_wonder' (didn't love it). Bound Reward pays 1 WOW to customers who advance to 'wonder'. " +
|
|
1153
|
+
"Merchant's wonder/no_wonder ratio becomes an on-chain reputation signal. Disputes (if any) are " +
|
|
1154
|
+
"filed separately via Arbitration object — never enter this Machine.",
|
|
1127
1155
|
publish_checklist: [
|
|
1128
|
-
"
|
|
1129
|
-
"
|
|
1130
|
-
"
|
|
1131
|
-
"
|
|
1156
|
+
"All 6 nodes verified: no 'disputed'/'refunded'/'arb'/'arbitration' names (R-M1-11 PASS)",
|
|
1157
|
+
"Entry Pair exists (prev_node='' targets 'Paid')",
|
|
1158
|
+
"Competing Pair tested: Praise vs Complain — first-wins semantics verified on testnet",
|
|
1159
|
+
"rating_window_guard tested: rating blocked after time limit expires",
|
|
1160
|
+
"MerchantAllocation fires ONLY at Completed (NOT at Wonder/NoWonder)",
|
|
1161
|
+
"Reward object (if bound): claim_guard tested — Progress.current == 'wonder' required, claim fails at 'no_wonder' or 'Completed'",
|
|
1162
|
+
"named_operator='' on Praise/Complain forwards verified (OrderHolder-only operation)",
|
|
1163
|
+
"machineNode2file export as backup",
|
|
1132
1164
|
],
|
|
1133
1165
|
},
|
|
1134
1166
|
{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type RewardTemplateType = "first_order" | "referral" | "loyalty" | "flash_sale" | "bulk_discount" | "milestone" | "cashback";
|
|
1
|
+
export type RewardTemplateType = "first_order" | "referral" | "loyalty" | "flash_sale" | "bulk_discount" | "milestone" | "cashback" | "wonder_praise" | "wonder_feedback";
|
|
2
2
|
export interface RewardTemplate {
|
|
3
3
|
type: RewardTemplateType;
|
|
4
4
|
name: string;
|
|
@@ -92,6 +92,68 @@ export const REWARD_TEMPLATES = [
|
|
|
92
92
|
example: "Service reaches the 100-order milestone; the first 50 customers each receive 5 WOW extra reward.",
|
|
93
93
|
relevantGuardOps: ["query", "repository_read"],
|
|
94
94
|
},
|
|
95
|
+
{
|
|
96
|
+
type: "wonder_praise",
|
|
97
|
+
name: "Wonder-node praise reward (post-completion positive feedback)",
|
|
98
|
+
description: "Rewards customers who advance their order Progress to the 'wonder' node after normal completion — " +
|
|
99
|
+
"a user-driven positive-feedback mechanism. The machine design pattern is: " +
|
|
100
|
+
"place_order → booked → in_progress → completed → (wonder | no_wonder). " +
|
|
101
|
+
"After the order reaches 'completed' (terminal business state), the OrderHolder " +
|
|
102
|
+
"(namedOperator='') can advance Progress to 'wonder' (positive review) or 'no_wonder' " +
|
|
103
|
+
"(negative review). The Reward's claim_guard verifies Progress.current == 'wonder' " +
|
|
104
|
+
"AND order.service == this Service, so only satisfied customers can claim. " +
|
|
105
|
+
"This creates an on-chain reputation signal that benefits both the merchant " +
|
|
106
|
+
"(public proof of quality) and the customer (monetary reward for honest feedback).",
|
|
107
|
+
guardCondition: "Guard verifies: (1) Progress.current == 'wonder' (customer advanced to praise node), " +
|
|
108
|
+
"(2) Progress.task == order_address AND order.service == this Service, " +
|
|
109
|
+
"(3) Progress.machine == service.machine (snapshot match), " +
|
|
110
|
+
"(4) claimer is the order holder (order.holder == signer). " +
|
|
111
|
+
"Recommended Guard ops: query Progress object, query Order object, " +
|
|
112
|
+
"verify Progress.current field equals 'wonder'.",
|
|
113
|
+
amountRange: { min: "0.1WOW", max: "20WOW" },
|
|
114
|
+
allocationStrategy: "fixed",
|
|
115
|
+
parameters: [
|
|
116
|
+
{ name: "amount", type: "string", description: "Reward amount per praise (e.g., '1WOW')", required: true },
|
|
117
|
+
{ name: "budget", type: "string", description: "Total budget (e.g., '500WOW')", required: true },
|
|
118
|
+
{ name: "expiration_time", type: "number", description: "Claim expiration timestamp (ms); 0 = no expiration", required: false },
|
|
119
|
+
{ name: "max_claims_per_user", type: "number", description: "Max claims per user (default 1, prevents duplicate praise rewards)", required: false },
|
|
120
|
+
],
|
|
121
|
+
example: "A photography service machine: place_order → booked → in_progress → completed → wonder. " +
|
|
122
|
+
"After delivery, the customer advances Progress to 'wonder' if satisfied. " +
|
|
123
|
+
"The Reward's Guard verifies Progress.current == 'wonder', and the customer claims 1 WOW. " +
|
|
124
|
+
"Merchant benefits from on-chain proof of customer satisfaction.",
|
|
125
|
+
relevantGuardOps: ["query", "progress_node_check"],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
type: "wonder_feedback",
|
|
129
|
+
name: "Wonder-node feedback collection (covers wonder & no_wonder)",
|
|
130
|
+
description: "A feedback-collection Reward that pays a small amount for EITHER positive (wonder) OR " +
|
|
131
|
+
"negative (no_wonder) feedback, encouraging customers to leave honest reviews. " +
|
|
132
|
+
"The machine design pattern is the same as wonder_praise: " +
|
|
133
|
+
"place_order → booked → in_progress → completed → (wonder | no_wonder). " +
|
|
134
|
+
"The Reward's claim_guard verifies Progress.current ∈ {'wonder', 'no_wonder'} — both nodes " +
|
|
135
|
+
"trigger the reward. This is useful for merchants who want to gather feedback signal " +
|
|
136
|
+
"regardless of sentiment, e.g., for product improvement or public reputation metrics. " +
|
|
137
|
+
"Pair with a Repository that records the feedback node for historical analysis.",
|
|
138
|
+
guardCondition: "Guard verifies: (1) Progress.current ∈ {'wonder', 'no_wonder'}, " +
|
|
139
|
+
"(2) Progress.task == order_address AND order.service == this Service, " +
|
|
140
|
+
"(3) claimer is the order holder. " +
|
|
141
|
+
"The Guard should query the Progress object and check that current node is one of the " +
|
|
142
|
+
"two feedback nodes. Optional: write the feedback node to a Repository for analytics.",
|
|
143
|
+
amountRange: { min: "0.05WOW", max: "5WOW" },
|
|
144
|
+
allocationStrategy: "fixed",
|
|
145
|
+
parameters: [
|
|
146
|
+
{ name: "amount", type: "string", description: "Reward amount per feedback (e.g., '0.5WOW')", required: true },
|
|
147
|
+
{ name: "budget", type: "string", description: "Total budget (e.g., '200WOW')", required: true },
|
|
148
|
+
{ name: "feedback_nodes", type: "string", description: "Comma-separated list of feedback node names (default: 'wonder,no_wonder')", required: false },
|
|
149
|
+
{ name: "expiration_time", type: "number", description: "Claim expiration timestamp (ms); 0 = no expiration", required: false },
|
|
150
|
+
],
|
|
151
|
+
example: "A freelance design service: place_order → ... → completed → (wonder | no_wonder). " +
|
|
152
|
+
"Customer advances to 'wonder' (liked the work) or 'no_wonder' (didn't like it). " +
|
|
153
|
+
"Both trigger a 0.5 WOW feedback reward. Merchant tracks the wonder/no_wonder ratio " +
|
|
154
|
+
"in a Repository to identify quality issues and improve service.",
|
|
155
|
+
relevantGuardOps: ["query", "progress_node_check", "repository_write"],
|
|
156
|
+
},
|
|
95
157
|
];
|
|
96
158
|
const TEMPLATE_INDEX = new Map(REWARD_TEMPLATES.map((t) => [t.type, t]));
|
|
97
159
|
export function getRewardTemplate(type) {
|
|
@@ -102,22 +164,32 @@ export function listRewardTemplates() {
|
|
|
102
164
|
}
|
|
103
165
|
export function suggestRewardTemplate(intent) {
|
|
104
166
|
const lower = intent.toLowerCase();
|
|
167
|
+
if (lower.includes("wonder") || lower.includes("praise") || lower.includes("thumbs_up")
|
|
168
|
+
|| lower.includes("好评") || lower.includes("positive review")
|
|
169
|
+
|| lower.includes("satisfaction reward")) {
|
|
170
|
+
return getRewardTemplate("wonder_praise");
|
|
171
|
+
}
|
|
172
|
+
if (lower.includes("feedback") || lower.includes("review collection")
|
|
173
|
+
|| lower.includes("no_wonder") || lower.includes("thumbs_down")
|
|
174
|
+
|| lower.includes("差评") || lower.includes("both sentiment")) {
|
|
175
|
+
return getRewardTemplate("wonder_feedback");
|
|
176
|
+
}
|
|
105
177
|
if (lower.includes("first order") || lower.includes("new") || lower.includes("first")) {
|
|
106
178
|
return getRewardTemplate("first_order");
|
|
107
179
|
}
|
|
108
|
-
if (lower.includes("referral") || lower.includes("viral")
|
|
180
|
+
if (lower.includes("referral") || lower.includes("viral")) {
|
|
109
181
|
return getRewardTemplate("referral");
|
|
110
182
|
}
|
|
111
|
-
if (lower.includes("repeat") || lower.includes("loyalty")
|
|
183
|
+
if (lower.includes("repeat") || lower.includes("loyalty")) {
|
|
112
184
|
return getRewardTemplate("loyalty");
|
|
113
185
|
}
|
|
114
|
-
if (lower.includes("limited-time") || lower.includes("flash")
|
|
186
|
+
if (lower.includes("limited-time") || lower.includes("flash")) {
|
|
115
187
|
return getRewardTemplate("flash_sale");
|
|
116
188
|
}
|
|
117
|
-
if (lower.includes("cashback")
|
|
189
|
+
if (lower.includes("cashback")) {
|
|
118
190
|
return getRewardTemplate("cashback");
|
|
119
191
|
}
|
|
120
|
-
if (lower.includes("milestone")
|
|
192
|
+
if (lower.includes("milestone")) {
|
|
121
193
|
return getRewardTemplate("milestone");
|
|
122
194
|
}
|
|
123
195
|
return undefined;
|