@wowok/agent-mcp 2.5.2 → 2.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/customer/index.d.ts +2 -2
  2. package/dist/customer/index.js +1 -1
  3. package/dist/customer/info-puzzle.d.ts +39 -2
  4. package/dist/customer/info-puzzle.js +370 -29
  5. package/dist/customer/risk-assessment.js +51 -14
  6. package/dist/customer/types.d.ts +85 -0
  7. package/dist/index.js +46 -6
  8. package/dist/knowledge/guard-design-patterns.js +104 -1
  9. package/dist/knowledge/guard-ledger.js +3 -3
  10. package/dist/knowledge/guard-lint.js +3 -3
  11. package/dist/knowledge/guard-risk.js +23 -15
  12. package/dist/knowledge/guard-templates.js +174 -2
  13. package/dist/knowledge/guard-translation.js +3 -3
  14. package/dist/knowledge/index.d.ts +3 -0
  15. package/dist/knowledge/index.js +9 -0
  16. package/dist/knowledge/machine-context.d.ts +1 -1
  17. package/dist/knowledge/machine-context.js +21 -0
  18. package/dist/knowledge/machine-ledger.d.ts +1 -0
  19. package/dist/knowledge/machine-ledger.js +60 -37
  20. package/dist/knowledge/machine-risk.js +23 -0
  21. package/dist/knowledge/machine-templates.js +150 -164
  22. package/dist/knowledge/overrides-loader.d.ts +13 -0
  23. package/dist/knowledge/overrides-loader.js +67 -0
  24. package/dist/knowledge/payment-risk.d.ts +4 -0
  25. package/dist/knowledge/payment-risk.js +67 -0
  26. package/dist/knowledge/repository-confirm.d.ts +4 -0
  27. package/dist/knowledge/repository-confirm.js +44 -0
  28. package/dist/knowledge/repository-risk.d.ts +8 -2
  29. package/dist/knowledge/repository-risk.js +61 -0
  30. package/dist/knowledge/repository-translation.js +23 -0
  31. package/dist/knowledge/reward-templates.d.ts +1 -1
  32. package/dist/knowledge/reward-templates.js +77 -5
  33. package/dist/knowledge/scenario-modes.js +22 -5
  34. package/dist/knowledge/service-templates.js +38 -0
  35. package/dist/knowledge/treasury-templates.js +42 -0
  36. package/dist/knowledge/trust-metrics.d.ts +4 -0
  37. package/dist/knowledge/trust-metrics.js +67 -7
  38. package/dist/loop-engineering/context-collect.d.ts +7 -0
  39. package/dist/loop-engineering/context-collect.js +83 -0
  40. package/dist/loop-engineering/improve.d.ts +29 -0
  41. package/dist/loop-engineering/improve.js +178 -0
  42. package/dist/project/context-assembly.d.ts +16 -0
  43. package/dist/project/context-assembly.js +130 -0
  44. package/dist/project/deployment-doc.d.ts +115 -0
  45. package/dist/project/deployment-doc.js +595 -0
  46. package/dist/project/index.js +37 -1
  47. package/dist/project/migration.d.ts +22 -0
  48. package/dist/project/migration.js +125 -0
  49. package/dist/project/project-store.d.ts +110 -0
  50. package/dist/project/project-store.js +425 -0
  51. package/dist/project/rollback-policy.d.ts +45 -0
  52. package/dist/project/rollback-policy.js +290 -0
  53. package/dist/project/stage-gate.d.ts +49 -0
  54. package/dist/project/stage-gate.js +310 -0
  55. package/dist/schema/call/allocation.d.ts +5 -0
  56. package/dist/schema/call/arbitration.d.ts +59 -54
  57. package/dist/schema/call/base.d.ts +3 -0
  58. package/dist/schema/call/base.js +1 -0
  59. package/dist/schema/call/bridge.d.ts +24 -0
  60. package/dist/schema/call/contact.d.ts +5 -0
  61. package/dist/schema/call/demand.d.ts +53 -48
  62. package/dist/schema/call/guard.d.ts +15 -0
  63. package/dist/schema/call/machine.d.ts +10 -0
  64. package/dist/schema/call/order.d.ts +5 -0
  65. package/dist/schema/call/payment.d.ts +5 -0
  66. package/dist/schema/call/permission.d.ts +5 -0
  67. package/dist/schema/call/personal.d.ts +5 -0
  68. package/dist/schema/call/progress.d.ts +5 -0
  69. package/dist/schema/call/proof.d.ts +10 -0
  70. package/dist/schema/call/repository.d.ts +5 -0
  71. package/dist/schema/call/reward.d.ts +5 -0
  72. package/dist/schema/call/service.d.ts +5 -0
  73. package/dist/schema/call/treasury.d.ts +5 -0
  74. package/dist/schema/operations.d.ts +286 -76
  75. package/dist/schema/operations.js +24 -0
  76. package/dist/schema/project/index.d.ts +2633 -120
  77. package/dist/schema/project/index.js +622 -2
  78. package/dist/schema/query/index.d.ts +601 -0
  79. package/dist/schema/query/index.js +61 -0
  80. package/dist/schema/trust/index.d.ts +10 -10
  81. package/dist/schemas/bridge_operation.schema.json +4 -0
  82. package/dist/schemas/guard2file.schema.json +4 -0
  83. package/dist/schemas/index.json +1 -1
  84. package/dist/schemas/machineNode2file.schema.json +4 -0
  85. package/dist/schemas/onchain_operations.schema.json +20 -0
  86. package/dist/schemas/onchain_operations_allocation.schema.json +4 -0
  87. package/dist/schemas/onchain_operations_arbitration.schema.json +4 -0
  88. package/dist/schemas/onchain_operations_contact.schema.json +4 -0
  89. package/dist/schemas/onchain_operations_demand.schema.json +4 -0
  90. package/dist/schemas/onchain_operations_gen_passport.schema.json +8 -0
  91. package/dist/schemas/onchain_operations_gen_proof.schema.json +8 -0
  92. package/dist/schemas/onchain_operations_guard.schema.json +4 -0
  93. package/dist/schemas/onchain_operations_machine.schema.json +4 -0
  94. package/dist/schemas/onchain_operations_order.schema.json +4 -0
  95. package/dist/schemas/onchain_operations_payment.schema.json +4 -0
  96. package/dist/schemas/onchain_operations_permission.schema.json +4 -0
  97. package/dist/schemas/onchain_operations_personal.schema.json +4 -0
  98. package/dist/schemas/onchain_operations_progress.schema.json +4 -0
  99. package/dist/schemas/onchain_operations_proof.schema.json +4 -0
  100. package/dist/schemas/onchain_operations_repository.schema.json +4 -0
  101. package/dist/schemas/onchain_operations_reward.schema.json +4 -0
  102. package/dist/schemas/onchain_operations_service.schema.json +4 -0
  103. package/dist/schemas/onchain_operations_treasury.schema.json +4 -0
  104. package/dist/schemas/project_operation.output.json +1798 -0
  105. package/dist/schemas/project_operation.schema.json +132 -2
  106. package/dist/schemas/query_toolkit.output.json +407 -1
  107. package/dist/schemas/query_toolkit.schema.json +132 -0
  108. package/dist/tools/handlers/project.js +742 -1
  109. package/dist/tools/handlers/query.js +38 -2
  110. package/package.json +2 -2
@@ -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 };
@@ -14,23 +14,23 @@ export function riskLevelEmoji(level) {
14
14
  function assessWorkflowRisk(puzzle) {
15
15
  const wf = puzzle.workflow_analysis;
16
16
  const checks = [];
17
- const hasRefund = wf?.has_refund_path ?? false;
17
+ const hasArb = wf?.has_arbitration_binding ?? wf?.has_arbitration_path ?? false;
18
18
  checks.push({
19
- id: "wf_refund_path",
19
+ id: "wf_arbitration_binding",
20
20
  dimension: "workflow",
21
- description: "Refund path exists (100%→Order Allocator)",
21
+ description: "Service has Arbitration object bound (dispute independence)",
22
22
  weight: 10,
23
- passed: hasRefund,
24
- advice: hasRefund ? undefined : "⚠️ No refund path: if delivery fails, funds may not be recoverable. Recommend negotiating refund terms with the merchant via Messenger and writing them into the WTS before ordering.",
23
+ passed: hasArb,
24
+ advice: hasArb ? undefined : "⚠️ No Arbitration binding: disputes cannot be escalated to third-party arbitration. Recommend requiring the merchant to bind an Arbitration service (with voting_guard config) before ordering. Dispute is filed via arbitration::dispute(order, arbitration), independent of the machine workflow.",
25
25
  });
26
- const hasArb = wf?.has_arbitration_path ?? false;
26
+ const hasRefund = wf?.has_refund_path ?? false;
27
27
  checks.push({
28
- id: "wf_arbitration_path",
28
+ id: "wf_refund_path",
29
29
  dimension: "workflow",
30
- description: "Arbitration path exists",
30
+ description: "Refund path exists (Arbitration+compensation_fund OR refund Allocator)",
31
31
  weight: 10,
32
- passed: hasArb,
33
- advice: hasArb ? undefined : "⚠️ No arbitration path: disputes cannot be escalated to third-party arbitration. Recommend requiring the merchant to bind an Arbitration service before ordering.",
32
+ passed: hasRefund,
33
+ advice: hasRefund ? undefined : "⚠️ No refund path: if delivery fails, funds may not be recoverable. Either (a) bind Arbitration AND deposit compensation_fund, or (b) configure a refund-to-order Allocator. Refund via arbitration flows: dispute → arbitration() ruling with indemnity → arb_withdraw by order holder.",
34
34
  });
35
35
  const userOps = wf?.user_operable_forwards ?? 0;
36
36
  checks.push({
@@ -51,14 +51,19 @@ function assessWorkflowRisk(puzzle) {
51
51
  passed: noDeadEnds,
52
52
  advice: noDeadEnds ? undefined : `⚠️ Detected ${deadEnds.length} potential dead-end node(s): ${deadEnds.join(", ")}. All exits of these nodes depend on merchant action.`,
53
53
  });
54
- const machineUsable = wf ? (wf.machine_published && !wf.machine_paused) : false;
54
+ const machineUsable = wf
55
+ ? (wf.machine_published && !wf.machine_paused && !wf.has_dispute_anti_pattern)
56
+ : false;
55
57
  checks.push({
56
58
  id: "wf_machine_usable",
57
59
  dimension: "workflow",
58
- description: "Machine is published and not paused",
60
+ description: "Machine published, not paused, no dispute anti-pattern",
59
61
  weight: 3,
60
62
  passed: machineUsable,
61
- advice: machineUsable ? undefined : "⚠️ Machine is not published or is paused: the workflow may not be in effect or has been actively paused by the merchant.",
63
+ advice: machineUsable ? undefined
64
+ : wf?.has_dispute_anti_pattern
65
+ ? "🔴 Machine contains dispute anti-pattern nodes (disputed/refunded/arb/arbitration). Dispute is INDEPENDENT of machine — these nodes create dead-end paths because arbitrators are in Arbitration.voting_guard, not Permission. Recommend removing these nodes and binding an Arbitration object to the Service instead."
66
+ : "⚠️ Machine is not published or is paused: the workflow may not be in effect or has been actively paused by the merchant.",
62
67
  });
63
68
  const score = checks.reduce((sum, c) => sum + (c.passed ? c.weight : 0), 0);
64
69
  return { name: "workflow", score, max_score: RISK_DIMENSION_MAX.workflow, checks };
@@ -264,7 +269,12 @@ export function assessRisk(puzzle) {
264
269
  assessTrustRisk(puzzle),
265
270
  assessBehaviorRisk(puzzle),
266
271
  ];
267
- const total = dimensions.reduce((sum, d) => sum + d.score, 0);
272
+ const baseTotal = dimensions.reduce((sum, d) => sum + d.score, 0);
273
+ const userMetricsAdjustment = puzzle.user_metrics?.adjustment ?? 0;
274
+ const serviceNestingRisk = puzzle.service_nesting?.overall_risk_score;
275
+ const serviceNestingLevel = puzzle.service_nesting?.overall_risk_level;
276
+ const wonderBonus = puzzle.workflow_analysis?.has_wonder_node ? 2 : 0;
277
+ const total = Math.max(0, Math.min(100, baseTotal + userMetricsAdjustment + wonderBonus));
268
278
  const level = riskLevelFromScore(total);
269
279
  const redFlags = [];
270
280
  for (const dim of dimensions) {
@@ -284,11 +294,18 @@ export function assessRisk(puzzle) {
284
294
  redFlags.push(`[behavior_flag] ${f}`);
285
295
  }
286
296
  }
297
+ if (serviceNestingLevel === "high" || serviceNestingLevel === "critical") {
298
+ redFlags.push(`[service_nesting] overall nesting risk: ${serviceNestingLevel} (score ${serviceNestingRisk})`);
299
+ }
287
300
  const advice = generateAdvice(puzzle, total, level, dimensions);
288
301
  return {
289
302
  total,
303
+ base_total: baseTotal,
290
304
  level,
291
305
  dimensions,
306
+ user_metrics_adjustment: userMetricsAdjustment,
307
+ service_nesting_risk: serviceNestingRisk,
308
+ service_nesting_level: serviceNestingLevel,
292
309
  red_flags: redFlags,
293
310
  advice,
294
311
  };
@@ -326,6 +343,26 @@ function generateAdvice(puzzle, total, level, dimensions) {
326
343
  if (puzzle.completeness < 0.8) {
327
344
  advice.push(`ℹ️ Information completeness is only ${Math.round(puzzle.completeness * 100)}%; missing: ${puzzle.gaps.join(", ") || "none"}. Recommend supplementing data before making a decision.`);
328
345
  }
346
+ if (puzzle.workflow_analysis?.has_wonder_node) {
347
+ advice.push("🌟 Machine implements wonder/no-wonder praise design: customers can advance Progress to 'wonder' (positive review) or 'no_wonder' (negative review) after completion. Reward objects can claim against Progress.current == 'wonder' to incentivize positive outcomes.");
348
+ }
349
+ if (puzzle.workflow_analysis?.has_dispute_anti_pattern) {
350
+ advice.push("🔴 Machine contains dispute anti-pattern nodes (disputed/refunded/arb/arbitration). Dispute is INDEPENDENT of machine — it flows through Order → Arbitration.dispute() → Arb → arbitration() → arb_withdraw. Arbitrators are configured in Arbitration.voting_guard, NOT in Permission. Recommend the merchant remove these nodes and bind an Arbitration object instead.");
351
+ }
352
+ if (puzzle.user_metrics) {
353
+ const um = puzzle.user_metrics;
354
+ const passedCount = um.metrics.filter((m) => m.passed).length;
355
+ advice.push(`📊 User-customized metrics: ${passedCount}/${um.metrics.length} passed (score ${um.score}/100, adjustment ${um.adjustment >= 0 ? "+" : ""}${um.adjustment}).${um.source_file ? ` Source: ${um.source_file}` : ""}`);
356
+ for (const m of um.metrics) {
357
+ if (!m.passed && m.advice) {
358
+ advice.push(` • ${m.label}: ${m.advice}`);
359
+ }
360
+ }
361
+ }
362
+ if (puzzle.service_nesting) {
363
+ const sn = puzzle.service_nesting;
364
+ advice.push(`🔗 Service nesting evaluation: ${sn.total_bindings_evaluated} bound objects, overall risk ${sn.overall_risk_level} (score ${sn.overall_risk_score}/100).${sn.truncated ? " Recursion truncated at depth limit." : ""}`);
365
+ }
329
366
  return advice;
330
367
  }
331
368
  export function passesRiskThreshold(score, minLevel = "medium_high") {