@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.
Files changed (69) hide show
  1. package/README.md +33 -26
  2. package/dist/customer/index.d.ts +2 -2
  3. package/dist/customer/index.js +1 -1
  4. package/dist/customer/info-puzzle.d.ts +39 -2
  5. package/dist/customer/info-puzzle.js +370 -29
  6. package/dist/customer/risk-assessment.js +51 -14
  7. package/dist/customer/types.d.ts +85 -0
  8. package/dist/knowledge/machine-ledger.js +32 -29
  9. package/dist/knowledge/machine-risk.js +23 -0
  10. package/dist/knowledge/machine-templates.js +118 -86
  11. package/dist/knowledge/reward-confirm.d.ts +3 -0
  12. package/dist/knowledge/reward-confirm.js +30 -0
  13. package/dist/knowledge/reward-puzzle.d.ts +1 -0
  14. package/dist/knowledge/reward-puzzle.js +12 -0
  15. package/dist/knowledge/reward-risk.d.ts +4 -1
  16. package/dist/knowledge/reward-risk.js +85 -3
  17. package/dist/knowledge/reward-templates.d.ts +1 -1
  18. package/dist/knowledge/reward-templates.js +77 -5
  19. package/dist/knowledge/tools-reference.js +5 -4
  20. package/dist/knowledge/trust-metrics.d.ts +4 -0
  21. package/dist/knowledge/trust-metrics.js +67 -7
  22. package/dist/project/evaluation.d.ts +53 -0
  23. package/dist/project/evaluation.js +853 -0
  24. package/dist/project/graph-builder.d.ts +43 -0
  25. package/dist/project/graph-builder.js +572 -0
  26. package/dist/project/handlers.d.ts +152 -0
  27. package/dist/project/handlers.js +458 -0
  28. package/dist/project/index.d.ts +1 -0
  29. package/dist/project/index.js +24 -1
  30. package/dist/project/query.d.ts +1 -0
  31. package/dist/project/query.js +2 -0
  32. package/dist/project/semantic-assembler.d.ts +93 -0
  33. package/dist/project/semantic-assembler.js +345 -0
  34. package/dist/schema/call/arbitration.d.ts +54 -54
  35. package/dist/schema/call/base.js +1 -1
  36. package/dist/schema/call/demand.d.ts +48 -48
  37. package/dist/schema/operations.d.ts +76 -76
  38. package/dist/schema/project/index.d.ts +1558 -3885
  39. package/dist/schema/project/index.js +357 -961
  40. package/dist/schema/trust/index.d.ts +24 -24
  41. package/dist/schemas/bridge_operation.schema.json +1 -1
  42. package/dist/schemas/guard2file.schema.json +1 -1
  43. package/dist/schemas/index.json +2 -2
  44. package/dist/schemas/machineNode2file.schema.json +1 -1
  45. package/dist/schemas/onchain_operations.schema.json +5 -5
  46. package/dist/schemas/onchain_operations_allocation.schema.json +1 -1
  47. package/dist/schemas/onchain_operations_arbitration.schema.json +1 -1
  48. package/dist/schemas/onchain_operations_contact.schema.json +1 -1
  49. package/dist/schemas/onchain_operations_demand.schema.json +1 -1
  50. package/dist/schemas/onchain_operations_gen_passport.schema.json +2 -2
  51. package/dist/schemas/onchain_operations_gen_proof.schema.json +2 -2
  52. package/dist/schemas/onchain_operations_guard.schema.json +1 -1
  53. package/dist/schemas/onchain_operations_machine.schema.json +1 -1
  54. package/dist/schemas/onchain_operations_order.schema.json +1 -1
  55. package/dist/schemas/onchain_operations_payment.schema.json +1 -1
  56. package/dist/schemas/onchain_operations_permission.schema.json +1 -1
  57. package/dist/schemas/onchain_operations_personal.schema.json +1 -1
  58. package/dist/schemas/onchain_operations_progress.schema.json +1 -1
  59. package/dist/schemas/onchain_operations_proof.schema.json +1 -1
  60. package/dist/schemas/onchain_operations_repository.schema.json +1 -1
  61. package/dist/schemas/onchain_operations_reward.schema.json +1 -1
  62. package/dist/schemas/onchain_operations_service.schema.json +1 -1
  63. package/dist/schemas/onchain_operations_treasury.schema.json +1 -1
  64. package/dist/schemas/project_operation.output.json +975 -2471
  65. package/dist/schemas/project_operation.schema.json +207 -534
  66. package/dist/tools/handlers/onchain.js +97 -19
  67. package/dist/tools/handlers/project.js +34 -1046
  68. package/dist/tools/index.js +84 -55
  69. 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. Use WoWok Skills (Recommended)
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
- Skills provide structured guidance for AI assistants:
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
- - **Complex system building** — Dependency chains, build order, step-by-step patterns
39
- - **Tool usage failures** — Correct parameter formats, tool selection, error recovery
40
- - **Safety & authorization** — User confirmation for important operations
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
- #### Available Skills (Organized by Role)
55
+ **On-demand** (11 skills — loaded by context):
43
56
 
44
57
  | Skill | Role | Purpose |
45
58
  |-------|------|---------|
46
- | `wowok-order` | 👤 Customer | Order lifecycle — place orders, track progress, submit disputes, claim compensation |
47
- | `wowok-provider` | 🏪 Provider | Service provider guide — create Service, design Machine, configure Allocators |
48
- | `wowok-machine` | 🏪 Provider | Workflow design — state machines, node definitions, progress tracking |
49
- | `wowok-arbitrator` | ⚖️ Arbitrator | Arbitration service — dispute resolution, voting organization, fee management |
50
- | `wowok-guard` | 🛠️ Shared | Guard design mastery — programmable trust rules, multi-signature authorization |
51
- | `wowok-messenger` | 🛠️ Shared | Encrypted messaging — E2E communication, WTS evidence, conversation management |
52
- | `wowok-output` | 🛠️ Shared | Output processing — post-process responses for human-readable presentation |
53
- | `wowok-tools` | 🛠️ Shared | MCP tool usage mastery — 13+ tools, parameter formats, schema references |
54
- | `wowok-safety` | 🛠️ Shared | Safety protocol — dry-run validation, user confirmation checkpoints |
55
-
56
- #### Role-Based Skill Selection
57
-
58
- - **Use `wowok-order`** when user acts as a customer/buyer placing orders
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.
@@ -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";
@@ -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 fwdName = (fwd.name ?? "").toLowerCase();
59
- const target = (fwd.to ?? "").toLowerCase();
60
- if (fwdName.includes("refund") || target.includes("refund")) {
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 (fwdName.includes("milestone") || target.includes("milestone")) {
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 (!hasArbitrationPath)
83
- riskSignals.push("no_arbitration_path");
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: hasArbitrationPath,
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
- const expectedDimensions = depth === "browse" ? 1
287
- : depth === "evaluate" ? 4
288
- : depth === "preorder" ? 6
289
- : 4;
290
- const actualDimensions = [
291
- serviceBasics,
292
- workflowAnalysis,
293
- fundSafety,
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
- completeness,
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 };