@wowok/agent-mcp 2.5.3 → 2.5.5
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 +33 -26
- 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-confirm.d.ts +3 -0
- package/dist/knowledge/reward-confirm.js +30 -0
- package/dist/knowledge/reward-puzzle.d.ts +1 -0
- package/dist/knowledge/reward-puzzle.js +12 -0
- package/dist/knowledge/reward-risk.d.ts +4 -1
- package/dist/knowledge/reward-risk.js +85 -3
- package/dist/knowledge/reward-templates.d.ts +1 -1
- package/dist/knowledge/reward-templates.js +77 -5
- package/dist/knowledge/tools-reference.js +5 -4
- package/dist/knowledge/trust-metrics.d.ts +4 -0
- package/dist/knowledge/trust-metrics.js +67 -7
- package/dist/project/evaluation.d.ts +53 -0
- package/dist/project/evaluation.js +853 -0
- package/dist/project/graph-builder.d.ts +43 -0
- package/dist/project/graph-builder.js +572 -0
- package/dist/project/handlers.d.ts +152 -0
- package/dist/project/handlers.js +458 -0
- package/dist/project/index.d.ts +1 -0
- package/dist/project/index.js +24 -1
- package/dist/project/query.d.ts +1 -0
- package/dist/project/query.js +2 -0
- package/dist/project/semantic-assembler.d.ts +93 -0
- package/dist/project/semantic-assembler.js +345 -0
- package/dist/schema/call/arbitration.d.ts +54 -54
- package/dist/schema/call/base.js +1 -1
- package/dist/schema/call/demand.d.ts +48 -48
- package/dist/schema/operations.d.ts +76 -76
- package/dist/schema/project/index.d.ts +1558 -3885
- package/dist/schema/project/index.js +357 -961
- package/dist/schema/trust/index.d.ts +24 -24
- package/dist/schemas/bridge_operation.schema.json +1 -1
- package/dist/schemas/guard2file.schema.json +1 -1
- package/dist/schemas/index.json +2 -2
- package/dist/schemas/machineNode2file.schema.json +1 -1
- package/dist/schemas/onchain_operations.schema.json +5 -5
- package/dist/schemas/onchain_operations_allocation.schema.json +1 -1
- package/dist/schemas/onchain_operations_arbitration.schema.json +1 -1
- package/dist/schemas/onchain_operations_contact.schema.json +1 -1
- package/dist/schemas/onchain_operations_demand.schema.json +1 -1
- package/dist/schemas/onchain_operations_gen_passport.schema.json +2 -2
- 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 +1 -1
- package/dist/schemas/onchain_operations_order.schema.json +1 -1
- package/dist/schemas/onchain_operations_payment.schema.json +1 -1
- package/dist/schemas/onchain_operations_permission.schema.json +1 -1
- package/dist/schemas/onchain_operations_personal.schema.json +1 -1
- package/dist/schemas/onchain_operations_progress.schema.json +1 -1
- package/dist/schemas/onchain_operations_proof.schema.json +1 -1
- package/dist/schemas/onchain_operations_repository.schema.json +1 -1
- package/dist/schemas/onchain_operations_reward.schema.json +1 -1
- package/dist/schemas/onchain_operations_service.schema.json +1 -1
- package/dist/schemas/onchain_operations_treasury.schema.json +1 -1
- package/dist/schemas/project_operation.output.json +975 -2471
- package/dist/schemas/project_operation.schema.json +207 -534
- package/dist/tools/handlers/onchain.js +97 -19
- package/dist/tools/handlers/project.js +34 -1046
- package/dist/tools/index.js +84 -55
- package/package.json +2 -2
|
@@ -81,6 +81,7 @@ export interface RewardJsonForConfirm {
|
|
|
81
81
|
guard_expiration_time?: number | null;
|
|
82
82
|
owner_receive?: unknown;
|
|
83
83
|
um?: string | null;
|
|
84
|
+
guard_expected_progress_state?: string;
|
|
84
85
|
}
|
|
85
86
|
export interface RewardConfirmGateInput {
|
|
86
87
|
reward_json: RewardJsonForConfirm;
|
|
@@ -97,6 +98,8 @@ export interface RewardConfirmGateInput {
|
|
|
97
98
|
passport_submission_value_types?: Record<number, string>;
|
|
98
99
|
passport_identifier_is_address?: Record<number, boolean>;
|
|
99
100
|
guard_store_from_id_compatible?: Record<string, boolean>;
|
|
101
|
+
user_intent_phrases?: string[];
|
|
102
|
+
machine_has_wonder_node?: boolean;
|
|
100
103
|
skip_user_confirmation?: boolean;
|
|
101
104
|
skip_environment_check?: boolean;
|
|
102
105
|
}
|
|
@@ -11,6 +11,7 @@ export const REWARD_OPERATION_CHECKLIST = [
|
|
|
11
11
|
{ id: "C-R-08", description: "claim: Passport is provided (SDK W_ERROR if missing)", passed: false, message: "" },
|
|
12
12
|
{ id: "C-R-09", description: "owner_receive (156): user double-confirms blast radius", passed: false, message: "" },
|
|
13
13
|
{ id: "C-R-10", description: "guard_expiration_time_add (155): new time > current clock", passed: false, message: "" },
|
|
14
|
+
{ id: "C-R-11", description: "guard_add: Guard's progress.current expected value matches business intent (praise→wonder, completion→completed)", passed: false, message: "" },
|
|
14
15
|
];
|
|
15
16
|
function runStaticChecklist(input) {
|
|
16
17
|
const items = REWARD_OPERATION_CHECKLIST.map(c => ({ ...c }));
|
|
@@ -133,6 +134,22 @@ function runStaticChecklist(input) {
|
|
|
133
134
|
else {
|
|
134
135
|
cr10.passed = true;
|
|
135
136
|
}
|
|
137
|
+
const cr11 = items.find(i => i.id === "C-R-11");
|
|
138
|
+
if (data.guard_expected_progress_state && input.user_intent_phrases && input.user_intent_phrases.length > 0) {
|
|
139
|
+
const intentLower = input.user_intent_phrases.join(" ").toLowerCase();
|
|
140
|
+
const praiseKeywords = ["好评", "praise", "wonder", "thumbs_up", "positive review", "satisfaction reward", "好评奖励"];
|
|
141
|
+
const isPraiseIntent = praiseKeywords.some(kw => intentLower.includes(kw.toLowerCase()));
|
|
142
|
+
if (isPraiseIntent && input.machine_has_wonder_node !== false && data.guard_expected_progress_state !== "wonder") {
|
|
143
|
+
cr11.passed = false;
|
|
144
|
+
cr11.message = `intent=praise but Guard checks "${data.guard_expected_progress_state}" instead of "wonder" — R-R-6-01 violation`;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
cr11.passed = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
cr11.passed = true;
|
|
152
|
+
}
|
|
136
153
|
return {
|
|
137
154
|
all_passed: items.filter(i => i.id !== "C-R-09").every(i => i.passed),
|
|
138
155
|
items,
|
|
@@ -162,6 +179,9 @@ function adaptRiskAssessment(input) {
|
|
|
162
179
|
passport_submission_value_types: input.passport_submission_value_types,
|
|
163
180
|
passport_identifier_is_address: input.passport_identifier_is_address,
|
|
164
181
|
guard_store_from_id_compatible: input.guard_store_from_id_compatible,
|
|
182
|
+
user_intent_phrases: input.user_intent_phrases,
|
|
183
|
+
guard_expected_progress_state: input.reward_json.guard_expected_progress_state,
|
|
184
|
+
machine_has_wonder_node: input.machine_has_wonder_node,
|
|
165
185
|
};
|
|
166
186
|
return assessRewardRisks(ctx);
|
|
167
187
|
}
|
|
@@ -201,6 +221,14 @@ export const REWARD_CONFIRMATION_QUESTIONS = [
|
|
|
201
221
|
requires_explicit_yes: false,
|
|
202
222
|
related_risk_ids: ["R-R-5-02"],
|
|
203
223
|
},
|
|
224
|
+
{
|
|
225
|
+
id: "QRW7",
|
|
226
|
+
question: "⚠️ For guard_add with progress.current check: does the Guard's expected progress node match the business intent? " +
|
|
227
|
+
"If intent is '好评/praise', Guard must check 'wonder' (NOT 'completed'). " +
|
|
228
|
+
"Premature claim at wrong node drains reward pool — Guard is immutable after creation.",
|
|
229
|
+
requires_explicit_yes: true,
|
|
230
|
+
related_risk_ids: ["R-R-6-01", "R-R-6-02"],
|
|
231
|
+
},
|
|
204
232
|
];
|
|
205
233
|
function processUserConfirmation(input, assessment) {
|
|
206
234
|
const questions = [...REWARD_CONFIRMATION_QUESTIONS];
|
|
@@ -217,6 +245,8 @@ function processUserConfirmation(input, assessment) {
|
|
|
217
245
|
continue;
|
|
218
246
|
if (q.id === "QRW6" && !data.coin_add)
|
|
219
247
|
continue;
|
|
248
|
+
if (q.id === "QRW7" && (!data.guard_add || !data.guard_expected_progress_state))
|
|
249
|
+
continue;
|
|
220
250
|
filtered.push(q);
|
|
221
251
|
}
|
|
222
252
|
if (input.skip_user_confirmation) {
|
|
@@ -48,6 +48,7 @@ export interface RewardConstraintChecklist {
|
|
|
48
48
|
F_R_06_recipient_amount_valid_pairing: boolean;
|
|
49
49
|
F_R_07_coin_type_matches_reward_t: boolean;
|
|
50
50
|
F_R_08_passport_required_for_claim: boolean;
|
|
51
|
+
F_R_09_progress_node_matches_intent: boolean;
|
|
51
52
|
}
|
|
52
53
|
export type RewardPuzzleDimension = "A" | "B" | "C" | "D" | "E" | "F";
|
|
53
54
|
export interface RewardPuzzle {
|
|
@@ -29,6 +29,7 @@ export function initPuzzleFromIntent(intent) {
|
|
|
29
29
|
F_R_06_recipient_amount_valid_pairing: false,
|
|
30
30
|
F_R_07_coin_type_matches_reward_t: false,
|
|
31
31
|
F_R_08_passport_required_for_claim: false,
|
|
32
|
+
F_R_09_progress_node_matches_intent: true,
|
|
32
33
|
};
|
|
33
34
|
return puzzle;
|
|
34
35
|
}
|
|
@@ -177,6 +178,17 @@ export function validateConstraints(puzzle) {
|
|
|
177
178
|
message: "claim requires Passport — SDK W_ERROR if missing",
|
|
178
179
|
});
|
|
179
180
|
}
|
|
181
|
+
if (puzzle.guard && puzzle.intent && puzzle.constraints) {
|
|
182
|
+
if (!puzzle.constraints.F_R_09_progress_node_matches_intent) {
|
|
183
|
+
violations.push({
|
|
184
|
+
constraint_id: "F-R-09",
|
|
185
|
+
severity: "HIGH",
|
|
186
|
+
message: "Guard's expected progress.current value does not match business intent — " +
|
|
187
|
+
"praise reward (好评/wonder) must check 'wonder' node, not 'completed'. " +
|
|
188
|
+
"Guard is immutable after creation; mismatch requires full rebuild.",
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
180
192
|
return violations;
|
|
181
193
|
}
|
|
182
194
|
export function generateConfirmationText(puzzle) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type RewardScene, type RewardAmountType } from "./reward-ledger.js";
|
|
2
2
|
export type RewardRiskLevel = "CRITICAL" | "HIGH" | "MEDIUM" | "LOW";
|
|
3
|
-
export type RewardRiskCategory = "structural" | "semantic" | "permission" | "lifecycle" | "cross_object";
|
|
3
|
+
export type RewardRiskCategory = "structural" | "semantic" | "permission" | "lifecycle" | "cross_object" | "business_semantic";
|
|
4
4
|
export interface RewardRisk {
|
|
5
5
|
id: string;
|
|
6
6
|
level: RewardRiskLevel;
|
|
@@ -65,6 +65,9 @@ export interface RewardRiskCheckContext {
|
|
|
65
65
|
passport_submission_value_types?: Record<number, string>;
|
|
66
66
|
passport_identifier_is_address?: Record<number, boolean>;
|
|
67
67
|
guard_store_from_id_compatible?: Record<string, boolean>;
|
|
68
|
+
user_intent_phrases?: string[];
|
|
69
|
+
guard_expected_progress_state?: string;
|
|
70
|
+
machine_has_wonder_node?: boolean;
|
|
68
71
|
}
|
|
69
72
|
export declare const REWARD_RISK_VERSION = "1.0.0";
|
|
70
73
|
export declare function getAllRewardRiskRules(): RewardRiskRule[];
|
|
@@ -469,12 +469,94 @@ const R_R_5_03 = {
|
|
|
469
469
|
return null;
|
|
470
470
|
},
|
|
471
471
|
};
|
|
472
|
+
const R_R_6_01 = {
|
|
473
|
+
id: "R-R-6-01",
|
|
474
|
+
category: "business_semantic",
|
|
475
|
+
trigger: "intent mentions praise/好评 but Guard checks non-wonder progress node",
|
|
476
|
+
check: (ctx) => {
|
|
477
|
+
if (!ctx.user_intent_phrases || ctx.user_intent_phrases.length === 0)
|
|
478
|
+
return null;
|
|
479
|
+
if (!ctx.guard_expected_progress_state)
|
|
480
|
+
return null;
|
|
481
|
+
const intentLower = ctx.user_intent_phrases.join(" ").toLowerCase();
|
|
482
|
+
const praiseKeywords = [
|
|
483
|
+
"好评", "praise", "wonder", "thumbs_up",
|
|
484
|
+
"positive review", "satisfaction reward", "好评奖励",
|
|
485
|
+
];
|
|
486
|
+
const isPraiseIntent = praiseKeywords.some(kw => intentLower.includes(kw.toLowerCase()));
|
|
487
|
+
if (!isPraiseIntent)
|
|
488
|
+
return null;
|
|
489
|
+
if (ctx.machine_has_wonder_node === false)
|
|
490
|
+
return null;
|
|
491
|
+
const expected = ctx.guard_expected_progress_state;
|
|
492
|
+
if (expected !== "wonder") {
|
|
493
|
+
return {
|
|
494
|
+
id: "R-R-6-01",
|
|
495
|
+
level: "HIGH",
|
|
496
|
+
category: "business_semantic",
|
|
497
|
+
title: `Guard checks progress=="${expected}" but intent indicates praise reward (wonder node)`,
|
|
498
|
+
description: `User intent mentions praise/好评/wonder, which maps to the 'wonder' progress node ` +
|
|
499
|
+
`(customer gives positive review AFTER service completion). But the Guard's table bakes ` +
|
|
500
|
+
`expected state="${expected}", allowing reward claim at the wrong lifecycle stage. ` +
|
|
501
|
+
`This violates the business semantics: "好评奖励" should only be claimable AFTER the ` +
|
|
502
|
+
`customer advances to the 'wonder' node, not before. ` +
|
|
503
|
+
`Guard is immutable — fixing this requires creating a new Guard + Reward pair.`,
|
|
504
|
+
affected_stakeholders: ["owner", "service_provider", "claimer"],
|
|
505
|
+
scenario: "Designer builds Guard checking 'completed' instead of 'wonder' for a praise reward",
|
|
506
|
+
mitigation: "Set the Guard table's expected progress state to 'wonder' (the positive-review node). " +
|
|
507
|
+
"The correct pattern: ordered → in_service → completed → wonder (praise) triggers reward claim. " +
|
|
508
|
+
"Reference: reward-templates.ts wonder_praise template.",
|
|
509
|
+
evidence: `intent="${intentLower}", expected_state="${expected}", expected_wonder=true`,
|
|
510
|
+
auto_fixable: false,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
return null;
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
const R_R_6_02 = {
|
|
517
|
+
id: "R-R-6-02",
|
|
518
|
+
category: "business_semantic",
|
|
519
|
+
trigger: "Guard checks wonder node but intent does not mention praise",
|
|
520
|
+
check: (ctx) => {
|
|
521
|
+
if (!ctx.user_intent_phrases || ctx.user_intent_phrases.length === 0)
|
|
522
|
+
return null;
|
|
523
|
+
if (!ctx.guard_expected_progress_state)
|
|
524
|
+
return null;
|
|
525
|
+
if (ctx.guard_expected_progress_state !== "wonder")
|
|
526
|
+
return null;
|
|
527
|
+
const intentLower = ctx.user_intent_phrases.join(" ").toLowerCase();
|
|
528
|
+
const praiseKeywords = [
|
|
529
|
+
"好评", "praise", "wonder", "thumbs_up",
|
|
530
|
+
"positive review", "satisfaction reward", "好评奖励",
|
|
531
|
+
];
|
|
532
|
+
const isPraiseIntent = praiseKeywords.some(kw => intentLower.includes(kw.toLowerCase()));
|
|
533
|
+
if (isPraiseIntent)
|
|
534
|
+
return null;
|
|
535
|
+
return {
|
|
536
|
+
id: "R-R-6-02",
|
|
537
|
+
level: "MEDIUM",
|
|
538
|
+
category: "business_semantic",
|
|
539
|
+
title: "Guard checks wonder node but intent does not mention praise",
|
|
540
|
+
description: `Guard's expected progress state is "wonder" (positive-review node), but the user intent ` +
|
|
541
|
+
`does not mention any praise/好评 keyword. This may be a copy-paste from a wonder_praise ` +
|
|
542
|
+
`template without adjusting the Guard for the actual business semantics. If this is a ` +
|
|
543
|
+
`general feedback reward (wonder_feedback), this is fine. Otherwise, verify the node choice.`,
|
|
544
|
+
affected_stakeholders: ["owner"],
|
|
545
|
+
scenario: "Designer copies wonder_praise template but actually wants a completion-based reward",
|
|
546
|
+
mitigation: "Verify: if intent is praise reward, add '好评/praise' to description. " +
|
|
547
|
+
"If intent is completion-based reward, change Guard to check 'completed' node.",
|
|
548
|
+
evidence: `expected_state="wonder", intent="${intentLower}" (no praise keyword)`,
|
|
549
|
+
auto_fixable: false,
|
|
550
|
+
};
|
|
551
|
+
},
|
|
552
|
+
};
|
|
472
553
|
const ALL_RULES = [
|
|
473
554
|
R_R_1_01, R_R_1_02, R_R_1_03,
|
|
474
555
|
R_R_2_01, R_R_2_02, R_R_2_03, R_R_2_04,
|
|
475
556
|
R_R_3_01, R_R_3_02, R_R_3_03,
|
|
476
557
|
R_R_4_01, R_R_4_02, R_R_4_03, R_R_4_04,
|
|
477
558
|
R_R_5_01, R_R_5_02, R_R_5_03,
|
|
559
|
+
R_R_6_01, R_R_6_02,
|
|
478
560
|
];
|
|
479
561
|
export const REWARD_RISK_VERSION = "1.0.0";
|
|
480
562
|
export function getAllRewardRiskRules() {
|
|
@@ -529,13 +611,13 @@ export const REWARD_STAKEHOLDERS = [
|
|
|
529
611
|
export function getRewardRiskRulesByStakeholder(stakeholder) {
|
|
530
612
|
return ALL_RULES.filter(rule => {
|
|
531
613
|
if (stakeholder === "owner") {
|
|
532
|
-
return ["structural", "permission", "lifecycle"].includes(rule.category);
|
|
614
|
+
return ["structural", "permission", "lifecycle", "business_semantic"].includes(rule.category);
|
|
533
615
|
}
|
|
534
616
|
if (stakeholder === "claimer") {
|
|
535
|
-
return rule.id.startsWith("R-R-4-0") || rule.id === "R-R-5-01" || rule.id === "R-R-1-02";
|
|
617
|
+
return rule.id.startsWith("R-R-4-0") || rule.id === "R-R-5-01" || rule.id === "R-R-1-02" || rule.id === "R-R-6-01";
|
|
536
618
|
}
|
|
537
619
|
if (stakeholder === "service_provider") {
|
|
538
|
-
return rule.id === "R-R-4-02" || rule.id === "R-R-3-01" || rule.id === "R-R-3-02";
|
|
620
|
+
return rule.id === "R-R-4-02" || rule.id === "R-R-3-01" || rule.id === "R-R-3-02" || rule.id === "R-R-6-01";
|
|
539
621
|
}
|
|
540
622
|
if (stakeholder === "system") {
|
|
541
623
|
return rule.category === "cross_object";
|
|
@@ -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;
|
|
@@ -105,11 +105,12 @@ export const TOOLS_REFERENCE = [
|
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
name: "project_operation",
|
|
108
|
-
summary: "Project
|
|
109
|
-
actions: ["list_projects", "
|
|
108
|
+
summary: "Project entity management (SQLite) + 3-pathway graph construction + dual rule-based/LLM-assisted evaluation engine.",
|
|
109
|
+
actions: ["list_projects", "create_project", "create_project_from_onchain", "update_project", "get_project_detail", "add_object", "remove_object", "create_version", "build_graph", "get_graph", "evaluate_project", "save_evaluation", "compare_projects", "refresh_objects", "get_object_diff", "assemble_context"],
|
|
110
110
|
notes: [
|
|
111
|
-
"
|
|
112
|
-
"
|
|
111
|
+
"GLM6: Projects are first-class entities with SQLite persistence, rigid project-object relationships.",
|
|
112
|
+
"Evaluation: risk/trust/completeness are rule-based (computed locally). value/worth_buying require LLM assistance (returns a prompt).",
|
|
113
|
+
"Graph: 3 pathways — internal references (configurable depth), Registrar inbound (O(1)), potential relationships.",
|
|
113
114
|
],
|
|
114
115
|
},
|
|
115
116
|
{
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export interface ServiceTrustMetrics {
|
|
2
2
|
arbitrationScore?: number;
|
|
3
3
|
arbitrationCases?: number;
|
|
4
|
+
arbitrationVotingGuardCount?: number;
|
|
5
|
+
arbitrationFeeBalance?: bigint;
|
|
6
|
+
arbitrationPaused?: boolean;
|
|
4
7
|
reviewStats?: {
|
|
5
8
|
likes: number;
|
|
6
9
|
dislikes: number;
|
|
@@ -11,6 +14,7 @@ export interface ServiceTrustMetrics {
|
|
|
11
14
|
completedRate?: number;
|
|
12
15
|
avgCompletionTime?: number;
|
|
13
16
|
compensationFundBalance?: bigint;
|
|
17
|
+
typicalOrderAmount?: bigint;
|
|
14
18
|
hasPublishedEvents?: boolean;
|
|
15
19
|
hasMachineBound?: boolean;
|
|
16
20
|
hasBuyGuard?: boolean;
|
|
@@ -38,16 +38,51 @@ export function computeServiceTrustScore(metrics) {
|
|
|
38
38
|
}
|
|
39
39
|
function computeArbitrationDimension(metrics) {
|
|
40
40
|
const maxScore = 30;
|
|
41
|
-
if (metrics.arbitrationScore === undefined) {
|
|
42
|
-
return {
|
|
41
|
+
if (metrics.arbitrationScore === undefined && metrics.arbitrationVotingGuardCount === undefined) {
|
|
42
|
+
return {
|
|
43
|
+
name: "arbitration",
|
|
44
|
+
score: maxScore * 0.25,
|
|
45
|
+
maxScore,
|
|
46
|
+
description: "Arbitration trust score (no data; default low because arbitration is critical)",
|
|
47
|
+
humanReadable: "Arbitration: no data (default low — critical missing)",
|
|
48
|
+
};
|
|
43
49
|
}
|
|
44
|
-
|
|
50
|
+
let score = 0;
|
|
51
|
+
const parts = [];
|
|
52
|
+
if (metrics.arbitrationScore !== undefined) {
|
|
53
|
+
const baseScore = Math.round((metrics.arbitrationScore / 100) * 10);
|
|
54
|
+
score += baseScore;
|
|
55
|
+
parts.push(`score ${metrics.arbitrationScore}/100`);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
score += 3;
|
|
59
|
+
}
|
|
60
|
+
if (metrics.arbitrationVotingGuardCount !== undefined && metrics.arbitrationVotingGuardCount > 0) {
|
|
61
|
+
score += 5;
|
|
62
|
+
parts.push(`${metrics.arbitrationVotingGuardCount} voting_guard(s)`);
|
|
63
|
+
}
|
|
64
|
+
if (metrics.arbitrationFeeBalance !== undefined && metrics.arbitrationFeeBalance > 0n) {
|
|
65
|
+
score += 5;
|
|
66
|
+
parts.push("fee funded");
|
|
67
|
+
}
|
|
68
|
+
if (metrics.arbitrationPaused === false) {
|
|
69
|
+
score += 5;
|
|
70
|
+
parts.push("active");
|
|
71
|
+
}
|
|
72
|
+
else if (metrics.arbitrationPaused === true) {
|
|
73
|
+
parts.push("paused");
|
|
74
|
+
}
|
|
75
|
+
if (metrics.arbitrationCases !== undefined && metrics.arbitrationCases > 0) {
|
|
76
|
+
score += 5;
|
|
77
|
+
parts.push(`${metrics.arbitrationCases} case(s)`);
|
|
78
|
+
}
|
|
79
|
+
score = Math.min(maxScore, score);
|
|
45
80
|
return {
|
|
46
81
|
name: "arbitration",
|
|
47
82
|
score,
|
|
48
83
|
maxScore,
|
|
49
|
-
description: `Arbitration
|
|
50
|
-
humanReadable: `Arbitration
|
|
84
|
+
description: `Arbitration config: ${parts.join(", ")}`,
|
|
85
|
+
humanReadable: `Arbitration: ${parts.join(", ")}`,
|
|
51
86
|
};
|
|
52
87
|
}
|
|
53
88
|
function computeReviewDimension(metrics) {
|
|
@@ -94,6 +129,31 @@ function computeFundDimension(metrics) {
|
|
|
94
129
|
if (metrics.compensationFundBalance === undefined || metrics.compensationFundBalance === 0n) {
|
|
95
130
|
return { name: "fund_safety", score: 0, maxScore, description: "No compensation fund", humanReadable: "Compensation fund: none" };
|
|
96
131
|
}
|
|
132
|
+
const fund = metrics.compensationFundBalance;
|
|
133
|
+
if (metrics.typicalOrderAmount !== undefined && metrics.typicalOrderAmount > 0n) {
|
|
134
|
+
const ratio = Number((fund * 100n) / metrics.typicalOrderAmount) / 100;
|
|
135
|
+
let score;
|
|
136
|
+
let humanReadable;
|
|
137
|
+
if (ratio >= 10) {
|
|
138
|
+
score = maxScore;
|
|
139
|
+
humanReadable = `Compensation fund: ${ratio.toFixed(1)}x order amount (excellent)`;
|
|
140
|
+
}
|
|
141
|
+
else if (ratio >= 1) {
|
|
142
|
+
score = 10 + Math.round((ratio - 1) / 9 * 5);
|
|
143
|
+
humanReadable = `Compensation fund: ${ratio.toFixed(1)}x order amount (covers ≥1 payout)`;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
score = Math.round(ratio * 10);
|
|
147
|
+
humanReadable = `Compensation fund: ${ratio.toFixed(1)}x order amount (insufficient)`;
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
name: "fund_safety",
|
|
151
|
+
score: Math.min(maxScore, score),
|
|
152
|
+
maxScore,
|
|
153
|
+
description: `Fund ${fund.toString()} / typical order ${metrics.typicalOrderAmount.toString()} = ${ratio.toFixed(2)}x`,
|
|
154
|
+
humanReadable,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
97
157
|
const threshold = 1000000000000n;
|
|
98
158
|
const ratio = Number(metrics.compensationFundBalance * 100n / threshold) / 100;
|
|
99
159
|
const score = Math.min(maxScore, Math.round(ratio * maxScore));
|
|
@@ -101,8 +161,8 @@ function computeFundDimension(metrics) {
|
|
|
101
161
|
name: "fund_safety",
|
|
102
162
|
score,
|
|
103
163
|
maxScore,
|
|
104
|
-
description: `Compensation fund ${metrics.compensationFundBalance} smallest unit`,
|
|
105
|
-
humanReadable: `Compensation fund:
|
|
164
|
+
description: `Compensation fund ${metrics.compensationFundBalance} smallest unit (absolute threshold fallback)`,
|
|
165
|
+
humanReadable: `Compensation fund: ${ratio.toFixed(2)}x absolute threshold`,
|
|
106
166
|
};
|
|
107
167
|
}
|
|
108
168
|
function computeTransparencyDimension(metrics) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Project, ProjectObject, GraphEdge, ObjectCacheEntry, EvaluationType } from "@wowok/wowok";
|
|
2
|
+
import type { ReputationData } from "./graph-builder.js";
|
|
3
|
+
export interface CriterionScore {
|
|
4
|
+
name: string;
|
|
5
|
+
score: number;
|
|
6
|
+
weight: number;
|
|
7
|
+
rationale: string;
|
|
8
|
+
}
|
|
9
|
+
export interface EvaluationFinding {
|
|
10
|
+
criterion: string;
|
|
11
|
+
severity: "critical" | "warning" | "info";
|
|
12
|
+
message: string;
|
|
13
|
+
deduction: number;
|
|
14
|
+
}
|
|
15
|
+
export interface EvaluationResult {
|
|
16
|
+
evaluationType: EvaluationType;
|
|
17
|
+
score: number;
|
|
18
|
+
summary: string;
|
|
19
|
+
criterionScores: CriterionScore[];
|
|
20
|
+
findings: EvaluationFinding[];
|
|
21
|
+
strengths: string[];
|
|
22
|
+
needsLlm: boolean;
|
|
23
|
+
llmPrompt?: string;
|
|
24
|
+
evaluatedAt: number;
|
|
25
|
+
}
|
|
26
|
+
export interface EvaluationInput {
|
|
27
|
+
project: Project;
|
|
28
|
+
objects: ProjectObject[];
|
|
29
|
+
edges: GraphEdge[];
|
|
30
|
+
objectCache: Map<string, ObjectCacheEntry>;
|
|
31
|
+
reputation?: Map<string, ReputationData>;
|
|
32
|
+
}
|
|
33
|
+
export declare function runEvaluation(input: EvaluationInput, evaluationType: EvaluationType, llmPrompt?: string): EvaluationResult;
|
|
34
|
+
export interface ProjectComparisonEntry {
|
|
35
|
+
projectId: string;
|
|
36
|
+
projectName: string;
|
|
37
|
+
score: number | null;
|
|
38
|
+
summary: string | null;
|
|
39
|
+
evaluatedAt: number | null;
|
|
40
|
+
}
|
|
41
|
+
export interface ProjectComparison {
|
|
42
|
+
evaluationType: EvaluationType;
|
|
43
|
+
entries: ProjectComparisonEntry[];
|
|
44
|
+
ranking: ProjectComparisonEntry[];
|
|
45
|
+
bestProjectId: string | null;
|
|
46
|
+
}
|
|
47
|
+
export declare function compareProjects(entries: Array<{
|
|
48
|
+
projectId: string;
|
|
49
|
+
projectName: string;
|
|
50
|
+
score: number | null;
|
|
51
|
+
summary: string | null;
|
|
52
|
+
evaluatedAt: number | null;
|
|
53
|
+
}>, evaluationType: EvaluationType): ProjectComparison;
|