@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
package/README.md
CHANGED
|
@@ -25,40 +25,47 @@ Docs: [https://github.com/wowok-ai/docs/](https://github.com/wowok-ai/docs/)
|
|
|
25
25
|
}
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
### 2.
|
|
28
|
+
### 2. Install WoWok Skills (Recommended)
|
|
29
29
|
|
|
30
|
-
**Skills significantly reduce the learning curve and usage difficulty of the MCP Server.**
|
|
30
|
+
**Skills significantly reduce the learning curve and usage difficulty of the MCP Server.** They provide structured guidance for AI assistants on complex system building, tool usage, and safety protocols.
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
+
# Personal (global) — available in all sessions:
|
|
33
34
|
npm install -g @wowok/skills
|
|
35
|
+
|
|
36
|
+
# Project (team sharing) — commit to git for collaboration:
|
|
37
|
+
cd your-project
|
|
38
|
+
wowok-skills init # Claude Code (default)
|
|
39
|
+
wowok-skills init --target agents # Trae IDE
|
|
40
|
+
wowok-skills init --target all # All 6 supported clients
|
|
34
41
|
```
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
**Supported clients:** Claude Code, OpenAI Codex, Trae IDE, CodeBuddy, Cursor, GitHub Copilot.
|
|
44
|
+
|
|
45
|
+
#### Available Skills
|
|
46
|
+
|
|
47
|
+
**Always loaded** (3 skills — foundational layer):
|
|
37
48
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
-
|
|
49
|
+
| Skill | Purpose |
|
|
50
|
+
|-------|---------|
|
|
51
|
+
| `wowok-tools` | MCP tool reference — 17 sub-tools, schema-gated execution, constraints |
|
|
52
|
+
| `wowok-safety` | Safety protocol — dry-run → confirm → execute, immutability rules |
|
|
53
|
+
| `wowok-output` | Output processing — address resolution, name mapping, data visualization |
|
|
41
54
|
|
|
42
|
-
|
|
55
|
+
**On-demand** (11 skills — loaded by context):
|
|
43
56
|
|
|
44
57
|
| Skill | Role | Purpose |
|
|
45
58
|
|-------|------|---------|
|
|
46
|
-
| `wowok-
|
|
47
|
-
| `wowok-
|
|
48
|
-
| `wowok-
|
|
49
|
-
| `wowok-arbitrator` | ⚖️ Arbitrator | Arbitration service — dispute resolution, voting
|
|
50
|
-
| `wowok-guard` | 🛠️ Shared | Guard design
|
|
51
|
-
| `wowok-messenger` | 🛠️ Shared | Encrypted messaging — E2E communication, WTS evidence,
|
|
52
|
-
| `wowok-
|
|
53
|
-
| `wowok-
|
|
54
|
-
| `wowok-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
- **Use `wowok-provider`** + `wowok-machine` when user is a merchant/service provider
|
|
60
|
-
- **Use `wowok-arbitrator`** when user operates dispute resolution services
|
|
61
|
-
- **Use `wowok-tools`** + `wowok-safety`** + `wowok-output` (always loaded) for all roles
|
|
62
|
-
- **Use `wowok-guard`** when designing complex validation rules
|
|
63
|
-
|
|
64
|
-
> **Note:** For detailed installation instructions for different AI clients (Claude Code, OpenAI Codex, Trae IDE, Cursor, etc.), see the [WoWok Skills](https://github.com/wowok-ai/skills) repository.
|
|
59
|
+
| `wowok-provider` | 🏪 Provider | Service provider guide — create Service, Machine, Allocators, order fulfillment |
|
|
60
|
+
| `wowok-machine` | 🏪 Provider | Workflow design — state machines, node/pair/forward graph, publish lifecycle |
|
|
61
|
+
| `wowok-order` | 👤 Customer | Order lifecycle — due diligence, consensus, order creation, progress, arbitration |
|
|
62
|
+
| `wowok-arbitrator` | ⚖️ Arbitrator | Arbitration service — dispute resolution, weighted voting, fee management |
|
|
63
|
+
| `wowok-guard` | 🛠️ Shared | Guard design — programmable trust rules, 4 data sources, 33 constraints |
|
|
64
|
+
| `wowok-messenger` | 🛠️ Shared | Encrypted messaging — E2E communication, WTS evidence, anti-spam |
|
|
65
|
+
| `wowok-onboard` | 🆕 New User | First-touch onboarding — 10-round dialogue from zero to first published Service |
|
|
66
|
+
| `wowok-scenario` | 🛠️ Shared | Industry mode templates — freelance, rental, education, travel presets |
|
|
67
|
+
| `wowok-planner` | 🛠️ Shared | Planning — converts natural language intent into Object Dependency Graph (ODG) |
|
|
68
|
+
| `wowok-auditor` | 🛠️ Shared | Pre-publish audit — Guard completeness, Machine soundness, 32 audit checks |
|
|
69
|
+
| `wowok-distill` | 🏪 Provider | Distillation review — review AI-generated improvement proposals |
|
|
70
|
+
|
|
71
|
+
> **Tip:** `wowok-tools` + `wowok-safety` + `wowok-output` are always loaded and serve all roles. Add role-specific skills (`wowok-provider`, `wowok-order`, `wowok-arbitrator`) as needed.
|
package/dist/customer/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { CUSTOMER_INTELLIGENCE_VERSION, RISK_LEVELS, RISK_DIMENSION_MAX, type PuzzleDepth, type ServiceBasics, type WorkflowAnalysis, type FundSafety, type TrustSignals, type MerchantBehavior, type MarketContext, type InfoPuzzle, type RiskDimensionName, type RiskCheck, type RiskDimensionScore, type RiskLevel, type RiskScore, type UserPreferences, type ReminderPriority, type Reminder, type GameScenario, type GameStrategy, type CustomerAdvice, } from "./types.js";
|
|
2
|
-
export { assembleServiceBasics, assembleWorkflowAnalysis, assembleFundSafety, assembleTrustSignals, assembleMerchantBehavior, assembleMarketContext, assembleInfoPuzzle, type ServiceRawData, type MachineNodeRaw, type ReviewRaw, type EventRaw, type PuzzleRawData, } from "./info-puzzle.js";
|
|
1
|
+
export { CUSTOMER_INTELLIGENCE_VERSION, RISK_LEVELS, RISK_DIMENSION_MAX, type PuzzleDepth, type ServiceBasics, type WorkflowAnalysis, type FundSafety, type TrustSignals, type MerchantBehavior, type MarketContext, type InfoPuzzle, type RiskDimensionName, type RiskCheck, type RiskDimensionScore, type RiskLevel, type RiskScore, type UserPreferences, type ReminderPriority, type Reminder, type GameScenario, type GameStrategy, type CustomerAdvice, type UserMetricEvaluatorType, type UserMetric, type UserMetricsConfig, type UserMetricResult, type UserMetricsResult, type ServiceNestingEvaluation, } from "./types.js";
|
|
2
|
+
export { assembleServiceBasics, assembleWorkflowAnalysis, assembleFundSafety, assembleTrustSignals, assembleMerchantBehavior, assembleMarketContext, assembleInfoPuzzle, assembleUserMetrics, evaluateServiceNesting, type ServiceRawData, type MachineNodeRaw, type ReviewRaw, type EventRaw, type PuzzleRawData, type ServiceNestingRawData, } from "./info-puzzle.js";
|
|
3
3
|
export { riskLevelFromScore, riskLevelLabel, riskLevelEmoji, assessRisk, passesRiskThreshold, } from "./risk-assessment.js";
|
|
4
4
|
export { getIndustryRiskChecks, getIndustryNeeds, checkIndustryNeeds, assessIndustryRisks, inferIndustry, type IndustryNeedTier, type IndustryNeed, type IndustryRiskCheck, type IndustryRiskResult, } from "./industry-risks.js";
|
|
5
5
|
export { getDefaultPreferences, getIndustryTemplate, inferPreferences, matchServiceToPreferences, mergePreferences, validatePreferences, SUPPORTED_INDUSTRIES, type Industry, type IndustryTemplate, type OrderHistoryEntry, type PreferenceInference, type ServiceForMatching, type PreferenceMatchResult, } from "./user-preferences.js";
|
package/dist/customer/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { CUSTOMER_INTELLIGENCE_VERSION, RISK_LEVELS, RISK_DIMENSION_MAX, } from "./types.js";
|
|
2
|
-
export { assembleServiceBasics, assembleWorkflowAnalysis, assembleFundSafety, assembleTrustSignals, assembleMerchantBehavior, assembleMarketContext, assembleInfoPuzzle, } from "./info-puzzle.js";
|
|
2
|
+
export { assembleServiceBasics, assembleWorkflowAnalysis, assembleFundSafety, assembleTrustSignals, assembleMerchantBehavior, assembleMarketContext, assembleInfoPuzzle, assembleUserMetrics, evaluateServiceNesting, } from "./info-puzzle.js";
|
|
3
3
|
export { riskLevelFromScore, riskLevelLabel, riskLevelEmoji, assessRisk, passesRiskThreshold, } from "./risk-assessment.js";
|
|
4
4
|
export { getIndustryRiskChecks, getIndustryNeeds, checkIndustryNeeds, assessIndustryRisks, inferIndustry, } from "./industry-risks.js";
|
|
5
5
|
export { getDefaultPreferences, getIndustryTemplate, inferPreferences, matchServiceToPreferences, mergePreferences, validatePreferences, SUPPORTED_INDUSTRIES, } from "./user-preferences.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InfoPuzzle, ServiceBasics, WorkflowAnalysis, FundSafety, TrustSignals, MerchantBehavior, MarketContext, PuzzleDepth } from "./types.js";
|
|
1
|
+
import type { InfoPuzzle, ServiceBasics, WorkflowAnalysis, FundSafety, TrustSignals, MerchantBehavior, MarketContext, PuzzleDepth, UserMetricsConfig, UserMetricsResult, ServiceNestingEvaluation } from "./types.js";
|
|
2
2
|
import { CUSTOMER_INTELLIGENCE_VERSION } from "./types.js";
|
|
3
3
|
export interface ServiceRawData {
|
|
4
4
|
name?: string;
|
|
@@ -60,7 +60,7 @@ export interface EventRaw {
|
|
|
60
60
|
data?: Record<string, unknown>;
|
|
61
61
|
}
|
|
62
62
|
export declare function assembleServiceBasics(serviceId: string, raw: ServiceRawData, wipVerified?: boolean): ServiceBasics;
|
|
63
|
-
export declare function assembleWorkflowAnalysis(nodes: MachineNodeRaw[], isPublished: boolean, isPaused: boolean): WorkflowAnalysis;
|
|
63
|
+
export declare function assembleWorkflowAnalysis(nodes: MachineNodeRaw[], isPublished: boolean, isPaused: boolean, serviceRaw?: ServiceRawData): WorkflowAnalysis;
|
|
64
64
|
export declare function assembleFundSafety(raw: ServiceRawData, orderAmount: bigint): FundSafety;
|
|
65
65
|
export declare function assembleTrustSignals(service: ServiceRawData, reviews: ReviewRaw[], trustScore: {
|
|
66
66
|
score: number;
|
|
@@ -97,6 +97,43 @@ export interface PuzzleRawData {
|
|
|
97
97
|
has_dispute: boolean;
|
|
98
98
|
is_repeat: boolean;
|
|
99
99
|
}>;
|
|
100
|
+
user_metrics_config?: UserMetricsConfig;
|
|
101
|
+
user_metrics_context?: Record<string, unknown>;
|
|
102
|
+
service_nesting_raw?: ServiceNestingRawData;
|
|
103
|
+
service_nesting?: ServiceNestingEvaluation;
|
|
100
104
|
}
|
|
101
105
|
export declare function assembleInfoPuzzle(serviceId: string, raw: PuzzleRawData, depth?: PuzzleDepth): InfoPuzzle;
|
|
106
|
+
export declare function assembleUserMetrics(config: UserMetricsConfig, context: Record<string, unknown>): UserMetricsResult;
|
|
107
|
+
export interface ServiceNestingRawData {
|
|
108
|
+
service_id: string;
|
|
109
|
+
depth?: number;
|
|
110
|
+
maxDepth?: number;
|
|
111
|
+
machine?: {
|
|
112
|
+
object_id: string;
|
|
113
|
+
published: boolean;
|
|
114
|
+
paused: boolean;
|
|
115
|
+
has_dispute_anti_pattern?: boolean;
|
|
116
|
+
has_no_entry?: boolean;
|
|
117
|
+
has_no_terminal?: boolean;
|
|
118
|
+
structural_issue_count?: number;
|
|
119
|
+
};
|
|
120
|
+
arbitrations?: Array<{
|
|
121
|
+
object_id: string;
|
|
122
|
+
voting_guard_count: number;
|
|
123
|
+
fee_balance: bigint;
|
|
124
|
+
paused: boolean;
|
|
125
|
+
}>;
|
|
126
|
+
rewards?: Array<{
|
|
127
|
+
object_id: string;
|
|
128
|
+
balance: bigint;
|
|
129
|
+
guard_count: number;
|
|
130
|
+
expiration_time?: number;
|
|
131
|
+
}>;
|
|
132
|
+
repositories?: Array<{
|
|
133
|
+
object_id: string;
|
|
134
|
+
has_write_guard: boolean;
|
|
135
|
+
is_empty: boolean;
|
|
136
|
+
}>;
|
|
137
|
+
}
|
|
138
|
+
export declare function evaluateServiceNesting(raw: ServiceNestingRawData): ServiceNestingEvaluation | undefined;
|
|
102
139
|
export { CUSTOMER_INTELLIGENCE_VERSION };
|
|
@@ -22,17 +22,24 @@ export function assembleServiceBasics(serviceId, raw, wipVerified = false) {
|
|
|
22
22
|
wip_verified: wipVerified,
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
-
export function assembleWorkflowAnalysis(nodes, isPublished, isPaused) {
|
|
25
|
+
export function assembleWorkflowAnalysis(nodes, isPublished, isPaused, serviceRaw) {
|
|
26
26
|
const riskSignals = [];
|
|
27
27
|
const terminalNodes = [];
|
|
28
28
|
const deadEndNodes = [];
|
|
29
29
|
let userOperable = 0;
|
|
30
30
|
let providerOnly = 0;
|
|
31
31
|
let guardGated = 0;
|
|
32
|
-
let hasRefundPath = false;
|
|
33
|
-
let hasArbitrationPath = false;
|
|
34
32
|
let hasMilestone = false;
|
|
33
|
+
let hasWonderNode = false;
|
|
34
|
+
let hasDisputeAntiPattern = false;
|
|
35
|
+
const antiPatternNodeRegex = /^(disputed|refunded|arb|arbitration|dispute|refund)$/i;
|
|
36
|
+
const wonderNodeRegex = /^(wonder|no_wonder|praise|complain|thumbs_up|thumbs_down|好评|差评)$/i;
|
|
35
37
|
const totalForwards = nodes.reduce((sum, n) => sum + (n.forwards?.length ?? 0), 0);
|
|
38
|
+
for (const node of nodes) {
|
|
39
|
+
if (antiPatternNodeRegex.test(node.name.trim())) {
|
|
40
|
+
hasDisputeAntiPattern = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
36
43
|
for (const node of nodes) {
|
|
37
44
|
const outForwards = node.forwards ?? [];
|
|
38
45
|
if (outForwards.length === 0) {
|
|
@@ -55,15 +62,11 @@ export function assembleWorkflowAnalysis(nodes, isPublished, isPaused) {
|
|
|
55
62
|
else {
|
|
56
63
|
providerOnly++;
|
|
57
64
|
}
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
hasRefundPath = true;
|
|
62
|
-
}
|
|
63
|
-
if (fwdName.includes("arb") || target.includes("arb")) {
|
|
64
|
-
hasArbitrationPath = true;
|
|
65
|
+
const target = (fwd.to ?? "").trim().toLowerCase();
|
|
66
|
+
if (wonderNodeRegex.test(target)) {
|
|
67
|
+
hasWonderNode = true;
|
|
65
68
|
}
|
|
66
|
-
if (
|
|
69
|
+
if (target.includes("milestone")) {
|
|
67
70
|
hasMilestone = true;
|
|
68
71
|
}
|
|
69
72
|
}
|
|
@@ -77,10 +80,18 @@ export function assembleWorkflowAnalysis(nodes, isPublished, isPaused) {
|
|
|
77
80
|
}
|
|
78
81
|
}
|
|
79
82
|
}
|
|
83
|
+
const hasArbitrationBinding = (serviceRaw?.arbitrations?.length ?? 0) > 0;
|
|
84
|
+
const compBalance = serviceRaw?.compensation_fund?.balance ?? 0n;
|
|
85
|
+
const hasArbRefundPath = hasArbitrationBinding && compBalance > 0n;
|
|
86
|
+
const allocators = serviceRaw?.order_allocators?.[0]?.allocators ?? [];
|
|
87
|
+
const hasRefundAllocator = allocators.some((a) => a.surplus_to === "order" || a.surplus_to === "Order");
|
|
88
|
+
const hasRefundPath = hasArbRefundPath || hasRefundAllocator;
|
|
80
89
|
if (!hasRefundPath)
|
|
81
90
|
riskSignals.push("no_refund_path");
|
|
82
|
-
if (!
|
|
83
|
-
riskSignals.push("
|
|
91
|
+
if (!hasArbitrationBinding)
|
|
92
|
+
riskSignals.push("no_arbitration_binding");
|
|
93
|
+
if (hasDisputeAntiPattern)
|
|
94
|
+
riskSignals.push("dispute_anti_pattern");
|
|
84
95
|
if (userOperable === 0)
|
|
85
96
|
riskSignals.push("no_user_operable_path");
|
|
86
97
|
if (deadEndNodes.length > 0)
|
|
@@ -94,7 +105,10 @@ export function assembleWorkflowAnalysis(nodes, isPublished, isPaused) {
|
|
|
94
105
|
provider_only_forwards: providerOnly,
|
|
95
106
|
guard_gated_forwards: guardGated,
|
|
96
107
|
has_refund_path: hasRefundPath,
|
|
97
|
-
has_arbitration_path:
|
|
108
|
+
has_arbitration_path: hasArbitrationBinding,
|
|
109
|
+
has_arbitration_binding: hasArbitrationBinding,
|
|
110
|
+
has_wonder_node: hasWonderNode,
|
|
111
|
+
has_dispute_anti_pattern: hasDisputeAntiPattern,
|
|
98
112
|
has_milestone_nodes: hasMilestone,
|
|
99
113
|
dead_end_nodes: deadEndNodes,
|
|
100
114
|
terminal_nodes: terminalNodes,
|
|
@@ -251,7 +265,7 @@ export function assembleInfoPuzzle(serviceId, raw, depth = "evaluate") {
|
|
|
251
265
|
let trustSignals;
|
|
252
266
|
if (depth === "evaluate" || depth === "preorder" || depth === "monitor") {
|
|
253
267
|
if (raw.machine_nodes) {
|
|
254
|
-
workflowAnalysis = assembleWorkflowAnalysis(raw.machine_nodes, raw.machine_published ?? false, raw.machine_paused ?? false);
|
|
268
|
+
workflowAnalysis = assembleWorkflowAnalysis(raw.machine_nodes, raw.machine_published ?? false, raw.machine_paused ?? false, raw.service);
|
|
255
269
|
}
|
|
256
270
|
else {
|
|
257
271
|
gaps.push("machine_workflow");
|
|
@@ -283,19 +297,14 @@ export function assembleInfoPuzzle(serviceId, raw, depth = "evaluate") {
|
|
|
283
297
|
gaps.push("market_context");
|
|
284
298
|
}
|
|
285
299
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
trustSignals,
|
|
295
|
-
merchantBehavior,
|
|
296
|
-
marketContext,
|
|
297
|
-
].filter((d) => d !== undefined && d !== null).length;
|
|
298
|
-
const completeness = Math.min(1, actualDimensions / expectedDimensions);
|
|
300
|
+
let userMetrics;
|
|
301
|
+
if (raw.user_metrics_config && raw.user_metrics_config.metrics.length > 0) {
|
|
302
|
+
userMetrics = assembleUserMetrics(raw.user_metrics_config, raw.user_metrics_context ?? {});
|
|
303
|
+
}
|
|
304
|
+
let serviceNesting = raw.service_nesting;
|
|
305
|
+
if (!serviceNesting && raw.service_nesting_raw) {
|
|
306
|
+
serviceNesting = evaluateServiceNesting(raw.service_nesting_raw);
|
|
307
|
+
}
|
|
299
308
|
return {
|
|
300
309
|
service_id: serviceId,
|
|
301
310
|
service_basics: serviceBasics,
|
|
@@ -304,9 +313,341 @@ export function assembleInfoPuzzle(serviceId, raw, depth = "evaluate") {
|
|
|
304
313
|
trust_signals: trustSignals,
|
|
305
314
|
merchant_behavior: merchantBehavior,
|
|
306
315
|
market_context: marketContext,
|
|
307
|
-
|
|
316
|
+
user_metrics: userMetrics,
|
|
317
|
+
service_nesting: serviceNesting,
|
|
318
|
+
completeness: computeCompleteness(depth, [
|
|
319
|
+
serviceBasics,
|
|
320
|
+
workflowAnalysis,
|
|
321
|
+
fundSafety,
|
|
322
|
+
trustSignals,
|
|
323
|
+
merchantBehavior,
|
|
324
|
+
marketContext,
|
|
325
|
+
userMetrics,
|
|
326
|
+
serviceNesting,
|
|
327
|
+
]),
|
|
308
328
|
gaps,
|
|
309
329
|
assembled_at: Date.now(),
|
|
310
330
|
};
|
|
311
331
|
}
|
|
332
|
+
export function assembleUserMetrics(config, context) {
|
|
333
|
+
const cap = config.adjustment_cap ?? 10;
|
|
334
|
+
const results = [];
|
|
335
|
+
const totalWeight = config.metrics.reduce((sum, m) => sum + Math.max(0, m.weight), 0);
|
|
336
|
+
for (const metric of config.metrics) {
|
|
337
|
+
const normalizedWeight = totalWeight > 0 ? Math.max(0, metric.weight) / totalWeight : 0;
|
|
338
|
+
const observed = resolvePath(context, metric.params.path ?? metric.name);
|
|
339
|
+
const { passed, score, observed_value } = evaluateMetric(metric, observed);
|
|
340
|
+
results.push({
|
|
341
|
+
name: metric.name,
|
|
342
|
+
label: metric.label,
|
|
343
|
+
passed,
|
|
344
|
+
score,
|
|
345
|
+
observed_value: formatObservedValue(observed),
|
|
346
|
+
weight: normalizedWeight,
|
|
347
|
+
advice: passed
|
|
348
|
+
? metric.advice_on_pass
|
|
349
|
+
: metric.advice_on_fail,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
const aggregated = results.reduce((sum, r) => sum + r.score * r.weight, 0);
|
|
353
|
+
const score = Math.round(aggregated * 100);
|
|
354
|
+
const adjustment = Math.round(((score - 50) / 50) * cap);
|
|
355
|
+
return {
|
|
356
|
+
metrics: results,
|
|
357
|
+
score,
|
|
358
|
+
adjustment,
|
|
359
|
+
source_file: config.source_file,
|
|
360
|
+
evaluated_at: Date.now(),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
function resolvePath(obj, path) {
|
|
364
|
+
if (!path)
|
|
365
|
+
return undefined;
|
|
366
|
+
const parts = path.split(".");
|
|
367
|
+
let current = obj;
|
|
368
|
+
for (const part of parts) {
|
|
369
|
+
if (current === null || current === undefined)
|
|
370
|
+
return undefined;
|
|
371
|
+
if (typeof current !== "object")
|
|
372
|
+
return undefined;
|
|
373
|
+
current = current[part];
|
|
374
|
+
}
|
|
375
|
+
return current;
|
|
376
|
+
}
|
|
377
|
+
function evaluateMetric(metric, observed) {
|
|
378
|
+
const p = metric.params;
|
|
379
|
+
switch (metric.evaluator) {
|
|
380
|
+
case "threshold": {
|
|
381
|
+
const v = typeof observed === "number" ? observed
|
|
382
|
+
: typeof observed === "string" ? Number(observed)
|
|
383
|
+
: typeof observed === "bigint" ? Number(observed)
|
|
384
|
+
: NaN;
|
|
385
|
+
const threshold = typeof p.threshold === "number" ? p.threshold
|
|
386
|
+
: typeof p.threshold === "string" ? Number(p.threshold)
|
|
387
|
+
: NaN;
|
|
388
|
+
if (isNaN(v) || isNaN(threshold)) {
|
|
389
|
+
return { passed: false, score: 0, observed_value: observed };
|
|
390
|
+
}
|
|
391
|
+
const passed = p.invert ? v <= threshold : v >= threshold;
|
|
392
|
+
const margin = Math.abs(threshold) * 0.5 || 1;
|
|
393
|
+
const rawScore = p.invert
|
|
394
|
+
? (threshold - v) / margin + 1
|
|
395
|
+
: (v - threshold) / margin + 1;
|
|
396
|
+
const score = Math.max(0, Math.min(1, rawScore));
|
|
397
|
+
return { passed, score, observed_value: observed };
|
|
398
|
+
}
|
|
399
|
+
case "ratio": {
|
|
400
|
+
const v = typeof observed === "number" ? observed
|
|
401
|
+
: typeof observed === "string" ? Number(observed)
|
|
402
|
+
: NaN;
|
|
403
|
+
const min = p.min ?? 0;
|
|
404
|
+
const max = p.max ?? 1;
|
|
405
|
+
if (isNaN(v)) {
|
|
406
|
+
return { passed: false, score: 0, observed_value: observed };
|
|
407
|
+
}
|
|
408
|
+
const passed = v >= min && v <= max;
|
|
409
|
+
const range = max - min;
|
|
410
|
+
const mid = (min + max) / 2;
|
|
411
|
+
const score = range > 0 ? Math.max(0, 1 - Math.abs(v - mid) / (range / 2)) : 1;
|
|
412
|
+
return { passed, score, observed_value: observed };
|
|
413
|
+
}
|
|
414
|
+
case "boolean": {
|
|
415
|
+
const passed = !!observed;
|
|
416
|
+
return { passed, score: passed ? 1 : 0, observed_value: observed };
|
|
417
|
+
}
|
|
418
|
+
case "presence": {
|
|
419
|
+
const present = observed !== undefined && observed !== null && observed !== "";
|
|
420
|
+
return { passed: present, score: present ? 1 : 0, observed_value: observed };
|
|
421
|
+
}
|
|
422
|
+
case "regex_match": {
|
|
423
|
+
if (typeof observed !== "string" || !p.pattern) {
|
|
424
|
+
return { passed: false, score: 0, observed_value: observed };
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
const regex = new RegExp(p.pattern);
|
|
428
|
+
const passed = regex.test(observed);
|
|
429
|
+
return { passed, score: passed ? 1 : 0, observed_value: observed };
|
|
430
|
+
}
|
|
431
|
+
catch {
|
|
432
|
+
return { passed: false, score: 0, observed_value: observed };
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
case "custom_query": {
|
|
436
|
+
return { passed: false, score: 0.5, observed_value: observed };
|
|
437
|
+
}
|
|
438
|
+
default:
|
|
439
|
+
return { passed: false, score: 0, observed_value: observed };
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function formatObservedValue(v) {
|
|
443
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean")
|
|
444
|
+
return v;
|
|
445
|
+
if (typeof v === "bigint")
|
|
446
|
+
return v.toString();
|
|
447
|
+
if (v === undefined || v === null)
|
|
448
|
+
return undefined;
|
|
449
|
+
try {
|
|
450
|
+
return JSON.stringify(v);
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
return String(v);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
function computeCompleteness(depth, dimensions) {
|
|
457
|
+
const requiredCount = depth === "browse" ? 1
|
|
458
|
+
: depth === "evaluate" ? 4
|
|
459
|
+
: depth === "preorder" ? 6
|
|
460
|
+
: 5;
|
|
461
|
+
const requiredDimensions = dimensions.slice(0, requiredCount);
|
|
462
|
+
const presentCount = requiredDimensions.filter((d) => d !== undefined && d !== null).length;
|
|
463
|
+
return Math.min(1, presentCount / requiredCount);
|
|
464
|
+
}
|
|
465
|
+
export function evaluateServiceNesting(raw) {
|
|
466
|
+
const depth = raw.depth ?? 0;
|
|
467
|
+
const maxDepth = raw.maxDepth ?? 3;
|
|
468
|
+
const truncated = depth >= maxDepth;
|
|
469
|
+
const bindings = {
|
|
470
|
+
arbitrations: [],
|
|
471
|
+
rewards: [],
|
|
472
|
+
repositories: [],
|
|
473
|
+
};
|
|
474
|
+
let totalEvaluated = 0;
|
|
475
|
+
if (raw.machine) {
|
|
476
|
+
const m = raw.machine;
|
|
477
|
+
const issues = [];
|
|
478
|
+
let score = 0;
|
|
479
|
+
if (m.published)
|
|
480
|
+
score += 10;
|
|
481
|
+
else
|
|
482
|
+
issues.push("Machine not published");
|
|
483
|
+
if (m.paused) {
|
|
484
|
+
score += 40;
|
|
485
|
+
issues.push("Machine paused");
|
|
486
|
+
}
|
|
487
|
+
if (m.has_no_entry) {
|
|
488
|
+
score += 50;
|
|
489
|
+
issues.push("No entry point (R-M1-01)");
|
|
490
|
+
}
|
|
491
|
+
if (m.has_no_terminal) {
|
|
492
|
+
score += 50;
|
|
493
|
+
issues.push("No terminal node (R-M1-02)");
|
|
494
|
+
}
|
|
495
|
+
if (m.has_dispute_anti_pattern) {
|
|
496
|
+
score += 60;
|
|
497
|
+
issues.push("Dispute anti-pattern (R-M1-11): machine illegally models dispute flow");
|
|
498
|
+
}
|
|
499
|
+
const structIssues = m.structural_issue_count ?? 0;
|
|
500
|
+
if (structIssues > 0) {
|
|
501
|
+
score += structIssues * 15;
|
|
502
|
+
issues.push(`${structIssues} structural issue(s) detected (cycles/dead-ends/orphans)`);
|
|
503
|
+
}
|
|
504
|
+
score = Math.min(100, score);
|
|
505
|
+
bindings.machine = {
|
|
506
|
+
object_id: m.object_id,
|
|
507
|
+
published: m.published,
|
|
508
|
+
risk_level: scoreToNestingLevel(score),
|
|
509
|
+
issues,
|
|
510
|
+
};
|
|
511
|
+
totalEvaluated++;
|
|
512
|
+
}
|
|
513
|
+
for (const arb of raw.arbitrations ?? []) {
|
|
514
|
+
const issues = [];
|
|
515
|
+
let score = 0;
|
|
516
|
+
if (arb.voting_guard_count === 0) {
|
|
517
|
+
score += 50;
|
|
518
|
+
issues.push("No voting_guard configured — no arbitrators can vote");
|
|
519
|
+
}
|
|
520
|
+
else if (arb.voting_guard_count <= 2) {
|
|
521
|
+
score += 30;
|
|
522
|
+
issues.push("Few arbitrators (<=2) — limited dispute resolution capacity");
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
score += 10;
|
|
526
|
+
}
|
|
527
|
+
if (arb.paused) {
|
|
528
|
+
score += 40;
|
|
529
|
+
issues.push("Arbitration paused — disputes cannot be filed");
|
|
530
|
+
}
|
|
531
|
+
if (arb.fee_balance === 0n) {
|
|
532
|
+
score += 30;
|
|
533
|
+
issues.push("No compensation fund balance — rulings cannot pay out");
|
|
534
|
+
}
|
|
535
|
+
score = Math.min(100, score);
|
|
536
|
+
bindings.arbitrations.push({
|
|
537
|
+
object_id: arb.object_id,
|
|
538
|
+
voting_guard_count: arb.voting_guard_count,
|
|
539
|
+
fee_balance: arb.fee_balance.toString(),
|
|
540
|
+
paused: arb.paused,
|
|
541
|
+
risk_level: scoreToNestingLevel(score),
|
|
542
|
+
issues,
|
|
543
|
+
});
|
|
544
|
+
totalEvaluated++;
|
|
545
|
+
}
|
|
546
|
+
for (const r of raw.rewards ?? []) {
|
|
547
|
+
const issues = [];
|
|
548
|
+
let score = 0;
|
|
549
|
+
if (r.balance === 0n) {
|
|
550
|
+
score += 60;
|
|
551
|
+
issues.push("Reward fund empty — claims will fail");
|
|
552
|
+
}
|
|
553
|
+
if (r.guard_count === 0) {
|
|
554
|
+
score += 50;
|
|
555
|
+
issues.push("No claim_guard — anyone could claim (or no one can, depending on default)");
|
|
556
|
+
}
|
|
557
|
+
if (r.expiration_time && r.expiration_time > 0 && r.expiration_time < Date.now()) {
|
|
558
|
+
score += 40;
|
|
559
|
+
issues.push("Reward expired — claims no longer possible");
|
|
560
|
+
}
|
|
561
|
+
score = Math.min(100, score);
|
|
562
|
+
bindings.rewards.push({
|
|
563
|
+
object_id: r.object_id,
|
|
564
|
+
balance: r.balance.toString(),
|
|
565
|
+
guard_count: r.guard_count,
|
|
566
|
+
risk_level: scoreToNestingLevel(score),
|
|
567
|
+
issues,
|
|
568
|
+
});
|
|
569
|
+
totalEvaluated++;
|
|
570
|
+
}
|
|
571
|
+
for (const repo of raw.repositories ?? []) {
|
|
572
|
+
const issues = [];
|
|
573
|
+
let score = 0;
|
|
574
|
+
if (!repo.has_write_guard) {
|
|
575
|
+
score += 40;
|
|
576
|
+
issues.push("No write_guard — Repository is open to arbitrary writes");
|
|
577
|
+
}
|
|
578
|
+
if (repo.is_empty) {
|
|
579
|
+
score += 20;
|
|
580
|
+
issues.push("Repository empty — no historical data for evaluation");
|
|
581
|
+
}
|
|
582
|
+
score = Math.min(100, score);
|
|
583
|
+
bindings.repositories.push({
|
|
584
|
+
object_id: repo.object_id,
|
|
585
|
+
risk_level: scoreToNestingLevel(score),
|
|
586
|
+
issues,
|
|
587
|
+
});
|
|
588
|
+
totalEvaluated++;
|
|
589
|
+
}
|
|
590
|
+
if (totalEvaluated === 0) {
|
|
591
|
+
return undefined;
|
|
592
|
+
}
|
|
593
|
+
const machineScore = bindings.machine ? nestingLevelToScore(bindings.machine.risk_level) : 0;
|
|
594
|
+
const machineWeight = bindings.machine ? 35 : 0;
|
|
595
|
+
const arbScore = bindings.arbitrations.length > 0
|
|
596
|
+
? Math.max(...bindings.arbitrations.map((a) => nestingLevelToScore(a.risk_level)))
|
|
597
|
+
: 0;
|
|
598
|
+
const arbWeight = bindings.arbitrations.length > 0 ? 30 : 0;
|
|
599
|
+
const rewardScore = bindings.rewards.length > 0
|
|
600
|
+
? Math.max(...bindings.rewards.map((r) => nestingLevelToScore(r.risk_level)))
|
|
601
|
+
: 0;
|
|
602
|
+
const rewardWeight = bindings.rewards.length > 0 ? 20 : 0;
|
|
603
|
+
const repoScore = bindings.repositories.length > 0
|
|
604
|
+
? Math.max(...bindings.repositories.map((r) => nestingLevelToScore(r.risk_level)))
|
|
605
|
+
: 0;
|
|
606
|
+
const repoWeight = bindings.repositories.length > 0 ? 15 : 0;
|
|
607
|
+
const totalWeight = machineWeight + arbWeight + rewardWeight + repoWeight;
|
|
608
|
+
const overallScore = totalWeight > 0
|
|
609
|
+
? Math.round((machineScore * machineWeight + arbScore * arbWeight
|
|
610
|
+
+ rewardScore * rewardWeight + repoScore * repoWeight) / totalWeight)
|
|
611
|
+
: 0;
|
|
612
|
+
const allLevels = [];
|
|
613
|
+
if (bindings.machine)
|
|
614
|
+
allLevels.push(bindings.machine.risk_level);
|
|
615
|
+
for (const a of bindings.arbitrations)
|
|
616
|
+
allLevels.push(a.risk_level);
|
|
617
|
+
for (const r of bindings.rewards)
|
|
618
|
+
allLevels.push(r.risk_level);
|
|
619
|
+
for (const r of bindings.repositories)
|
|
620
|
+
allLevels.push(r.risk_level);
|
|
621
|
+
const overallLevel = allLevels.reduce(highestLevel, "low");
|
|
622
|
+
return {
|
|
623
|
+
depth_reached: depth,
|
|
624
|
+
truncated,
|
|
625
|
+
bindings,
|
|
626
|
+
overall_risk_level: overallLevel,
|
|
627
|
+
overall_risk_score: overallScore,
|
|
628
|
+
total_bindings_evaluated: totalEvaluated,
|
|
629
|
+
evaluated_at: Date.now(),
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
function scoreToNestingLevel(score) {
|
|
633
|
+
if (score >= 75)
|
|
634
|
+
return "critical";
|
|
635
|
+
if (score >= 50)
|
|
636
|
+
return "high";
|
|
637
|
+
if (score >= 25)
|
|
638
|
+
return "medium";
|
|
639
|
+
return "low";
|
|
640
|
+
}
|
|
641
|
+
function nestingLevelToScore(level) {
|
|
642
|
+
switch (level) {
|
|
643
|
+
case "critical": return 90;
|
|
644
|
+
case "high": return 65;
|
|
645
|
+
case "medium": return 40;
|
|
646
|
+
case "low": return 10;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
function highestLevel(a, b) {
|
|
650
|
+
const order = { low: 0, medium: 1, high: 2, critical: 3 };
|
|
651
|
+
return order[a] >= order[b] ? a : b;
|
|
652
|
+
}
|
|
312
653
|
export { CUSTOMER_INTELLIGENCE_VERSION };
|