@wowok/agent-mcp 2.6.1 → 2.6.3

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 (125) hide show
  1. package/dist/customer/info-puzzle.d.ts +1 -1
  2. package/dist/customer/info-puzzle.js +4 -2
  3. package/dist/customer/risk-assessment.js +26 -4
  4. package/dist/customer/types.d.ts +2 -0
  5. package/dist/examples/guard-template-balance-check.json +38 -0
  6. package/dist/examples/guard-template-time-lock.json +39 -0
  7. package/dist/examples/machine-template-7node-rental.json +114 -0
  8. package/dist/examples/rental-ziroom-machine-create.json +137 -0
  9. package/dist/examples/rental-ziroom-permission-create.json +35 -0
  10. package/dist/examples/rental-ziroom-service-create.json +80 -0
  11. package/dist/examples/retail-myshop-service-create.json +88 -0
  12. package/dist/extensions/capability-manifest.js +24 -24
  13. package/dist/extensions/constraint-registry.js +18 -18
  14. package/dist/extensions/metric-registry.js +14 -14
  15. package/dist/extensions/mode-evaluator.js +16 -16
  16. package/dist/extensions/modes.js +131 -45
  17. package/dist/extensions/registry.d.ts +2 -0
  18. package/dist/extensions/registry.js +63 -30
  19. package/dist/extensions/types.d.ts +1 -0
  20. package/dist/index.js +50 -0
  21. package/dist/knowledge/deployment-scanner.js +1 -1
  22. package/dist/knowledge/fund-layer.d.ts +72 -0
  23. package/dist/knowledge/fund-layer.js +420 -0
  24. package/dist/knowledge/guard-render.d.ts +57 -0
  25. package/dist/knowledge/guard-render.js +700 -0
  26. package/dist/knowledge/guard-submission-prompt.d.ts +31 -0
  27. package/dist/knowledge/guard-submission-prompt.js +171 -0
  28. package/dist/knowledge/machine-ledger.js +1 -1
  29. package/dist/knowledge/machine-render.d.ts +41 -0
  30. package/dist/knowledge/machine-render.js +565 -0
  31. package/dist/knowledge/machine-templates.js +4 -4
  32. package/dist/knowledge/reward-confirm.js +2 -2
  33. package/dist/knowledge/reward-puzzle.js +1 -1
  34. package/dist/knowledge/reward-risk.js +9 -9
  35. package/dist/knowledge/reward-templates.js +2 -2
  36. package/dist/knowledge/service-confirm.d.ts +1 -1
  37. package/dist/knowledge/service-confirm.js +3 -3
  38. package/dist/knowledge/service-context.js +1 -1
  39. package/dist/knowledge/service-ledger.js +1 -1
  40. package/dist/knowledge/service-risk.d.ts +1 -1
  41. package/dist/knowledge/service-risk.js +3 -3
  42. package/dist/knowledge/service-templates.js +2 -2
  43. package/dist/knowledge/service-translation.d.ts +1 -1
  44. package/dist/knowledge/service-translation.js +7 -7
  45. package/dist/project/deployment-bridge.js +6 -5
  46. package/dist/project/deployment-doc.js +5 -5
  47. package/dist/project/edit-planner.d.ts +123 -0
  48. package/dist/project/edit-planner.js +1371 -0
  49. package/dist/project/evaluation.js +314 -14
  50. package/dist/project/graph-builder.js +6 -1
  51. package/dist/project/handlers.d.ts +99 -2
  52. package/dist/project/handlers.js +358 -16
  53. package/dist/project/stage-gate.js +4 -4
  54. package/dist/schema/call/allocation.js +2 -2
  55. package/dist/schema/call/arbitration.js +19 -6
  56. package/dist/schema/call/contact.js +2 -2
  57. package/dist/schema/call/demand.js +2 -2
  58. package/dist/schema/call/guard.js +1 -1
  59. package/dist/schema/call/machine.d.ts +1975 -777
  60. package/dist/schema/call/machine.js +50 -4
  61. package/dist/schema/call/order.js +3 -6
  62. package/dist/schema/call/permission.js +2 -2
  63. package/dist/schema/call/repository.js +2 -2
  64. package/dist/schema/call/reward.js +3 -3
  65. package/dist/schema/call/semantic.js +7 -1
  66. package/dist/schema/call/service.d.ts +180 -8
  67. package/dist/schema/call/service.js +82 -27
  68. package/dist/schema/call/treasury.js +3 -3
  69. package/dist/schema/common/index.d.ts +2 -0
  70. package/dist/schema/common/index.js +46 -10
  71. package/dist/schema/local/index.js +5 -1
  72. package/dist/schema/operations.d.ts +755 -176
  73. package/dist/schema/operations.js +43 -7
  74. package/dist/schema/project/index.d.ts +969 -6
  75. package/dist/schema/project/index.js +268 -4
  76. package/dist/schema/query/index.d.ts +217 -0
  77. package/dist/schema/query/index.js +63 -33
  78. package/dist/schema/schema-query/index.d.ts +53 -3
  79. package/dist/schema/schema-query/index.js +17 -2
  80. package/dist/schema/utils/node-parser.js +13 -0
  81. package/dist/schema/utils/object-type-utils.d.ts +12 -0
  82. package/dist/schema/utils/object-type-utils.js +35 -0
  83. package/dist/schema/utils/permission-machine-check.d.ts +49 -0
  84. package/dist/schema/utils/permission-machine-check.js +121 -0
  85. package/dist/schema/utils/skills-recommendation.d.ts +2 -0
  86. package/dist/schema/utils/skills-recommendation.js +76 -0
  87. package/dist/schema-query/index.d.ts +14 -1
  88. package/dist/schema-query/index.js +104 -2
  89. package/dist/schemas/account_operation.schema.json +1 -1
  90. package/dist/schemas/index.json +1 -1
  91. package/dist/schemas/onchain_events.output.json +1 -1
  92. package/dist/schemas/onchain_operations.output.json +2820 -0
  93. package/dist/schemas/onchain_operations.schema.json +115 -53
  94. package/dist/schemas/onchain_operations_allocation.schema.json +5 -5
  95. package/dist/schemas/onchain_operations_arbitration.schema.json +9 -7
  96. package/dist/schemas/onchain_operations_contact.schema.json +3 -3
  97. package/dist/schemas/onchain_operations_demand.schema.json +3 -3
  98. package/dist/schemas/onchain_operations_gen_passport.schema.json +2 -2
  99. package/dist/schemas/onchain_operations_guard.schema.json +1 -1
  100. package/dist/schemas/onchain_operations_machine.schema.json +4 -4
  101. package/dist/schemas/onchain_operations_order.schema.json +3 -3
  102. package/dist/schemas/onchain_operations_payment.schema.json +1 -1
  103. package/dist/schemas/onchain_operations_permission.schema.json +2 -2
  104. package/dist/schemas/onchain_operations_progress.schema.json +1 -1
  105. package/dist/schemas/onchain_operations_proof.schema.json +1 -1
  106. package/dist/schemas/onchain_operations_repository.schema.json +3 -3
  107. package/dist/schemas/onchain_operations_reward.schema.json +6 -6
  108. package/dist/schemas/onchain_operations_service.schema.json +77 -17
  109. package/dist/schemas/onchain_operations_treasury.schema.json +4 -4
  110. package/dist/schemas/onchain_table_data.output.json +1 -1
  111. package/dist/schemas/onchain_table_data.schema.json +10 -9
  112. package/dist/schemas/project_operation.output.json +1026 -7
  113. package/dist/schemas/project_operation.schema.json +46 -4
  114. package/dist/schemas/query_toolkit.output.json +15 -15
  115. package/dist/schemas/query_toolkit.schema.json +3 -3
  116. package/dist/schemas/schema_query.output.json +64 -1
  117. package/dist/schemas/schema_query.schema.json +3 -2
  118. package/dist/schemas/wowok_buildin_info.output.json +81 -8
  119. package/dist/schemas/wowok_buildin_info.schema.json +46 -2
  120. package/dist/tools/handlers/onchain.js +571 -6
  121. package/dist/tools/handlers/project.js +25 -2
  122. package/dist/tools/handlers/schema-query.js +24 -1
  123. package/dist/tools/index.d.ts +8 -0
  124. package/dist/tools/index.js +177 -6
  125. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -14,6 +14,47 @@ import { registerAllTools } from "./tools/index.js";
14
14
  if (process.env.WOWOK_TELEMETRY === "false" || process.env.WOWOK_TELEMETRY === "0") {
15
15
  setTelemetryEnabled(false);
16
16
  }
17
+ process.on("unhandledRejection", (reason, promise) => {
18
+ const reasonStr = reason instanceof Error
19
+ ? `${reason.name}: ${reason.message}\n${reason.stack ?? ""}`
20
+ : String(reason);
21
+ console.error(`[unhandledRejection] ${new Date().toISOString()}`);
22
+ console.error(` reason: ${reasonStr}`);
23
+ });
24
+ process.on("uncaughtException", (error) => {
25
+ console.error(`[uncaughtException] ${new Date().toISOString()}`);
26
+ console.error(` name: ${error.name}`);
27
+ console.error(` message: ${error.message}`);
28
+ console.error(` stack: ${error.stack ?? ""}`);
29
+ });
30
+ process.on("beforeExit", (code) => {
31
+ console.error(`[beforeExit] ${new Date().toISOString()} code=${code} — event loop empty, process would exit`);
32
+ console.error(` active handles: ${process._getActiveHandles?.()?.length ?? "?"}`);
33
+ console.error(` active requests: ${process._getActiveRequests?.()?.length ?? "?"}`);
34
+ });
35
+ process.on("exit", (code) => {
36
+ console.error(`[exit] ${new Date().toISOString()} code=${code} — process exiting`);
37
+ });
38
+ process.stdin.on("end", () => {
39
+ console.error(`[stdin end] ${new Date().toISOString()} — MCP client closed stdin`);
40
+ });
41
+ process.stdin.on("close", () => {
42
+ console.error(`[stdin close] ${new Date().toISOString()} — stdin stream closed`);
43
+ });
44
+ let keepaliveTimer = null;
45
+ function startKeepalive() {
46
+ if (keepaliveTimer)
47
+ return;
48
+ keepaliveTimer = setInterval(() => {
49
+ const mem = process.memoryUsage();
50
+ const heapUsedMB = Math.round(mem.heapUsed / 1024 / 1024);
51
+ const heapTotalMB = Math.round(mem.heapTotal / 1024 / 1024);
52
+ const rssMB = Math.round(mem.rss / 1024 / 1024);
53
+ const level = heapUsedMB > 1200 ? "WARNING" : heapUsedMB > 800 ? "INFO" : "DEBUG";
54
+ console.error(`[memory ${level}] heap=${heapUsedMB}MB/${heapTotalMB}MB rss=${rssMB}MB`);
55
+ }, 60_000);
56
+ }
57
+ startKeepalive();
17
58
  const SERVER_DESCRIPTION = `WoWok MCP Server - Making It Easy for AI Agents to Communicate, Collaborate, Trade, and Trust.
18
59
 
19
60
  ## CRITICAL: Unified Single-Tool Architecture
@@ -210,5 +251,14 @@ async function main() {
210
251
  await server.close();
211
252
  process.exit(0);
212
253
  });
254
+ process.on("SIGTERM", async () => {
255
+ console.error(`[SIGTERM] ${new Date().toISOString()} — graceful shutdown`);
256
+ try {
257
+ await server.close();
258
+ }
259
+ catch {
260
+ }
261
+ process.exit(0);
262
+ });
213
263
  }
214
264
  main().catch(console.error);
@@ -672,7 +672,7 @@ export function analyzeMachineAndServiceReadiness(input) {
672
672
  add(findings, "D-20", "warning", `No Service block in the deployment doc includes a compensation_fund_add step. ` +
673
673
  `Without deposit, arbitration rulings that award refunds to buyers cannot be paid out from the Service. ` +
674
674
  `Industries like freelance may not require this (info), but rental/travel/education typically do.`, "§3.2 / §4.2 Service blocks", `Add a compensation_fund_add step for the Service (e.g., data.compensation_fund_add = {balance: <amount>}). ` +
675
- `Recommended: 5× the max single-order amount (P1-1 evaluation rule).`);
675
+ `Recommended: 3-10× the max single-order amount, depending on industry (subscription: 3×, retail: 5×, rental: 10×). See P1-1 evaluation rule.`);
676
676
  }
677
677
  }
678
678
  }
@@ -0,0 +1,72 @@
1
+ import type { ENTRYPOINT } from "@wowok/wowok";
2
+ export interface OfficialTokenEntry {
3
+ type: string;
4
+ symbol: string;
5
+ decimals: number;
6
+ name: string;
7
+ description: string;
8
+ }
9
+ export type TokenNetwork = "gas" | "testnet" | "mainnet";
10
+ export declare const OFFICIAL_TOKEN_REGISTRY: Record<TokenNetwork, OfficialTokenEntry[]>;
11
+ export declare function getRegistryForNetwork(network?: string): OfficialTokenEntry[];
12
+ export type TokenSource = "registry" | "cache" | "onchain";
13
+ export interface TokenResolution {
14
+ type: string;
15
+ symbol: string;
16
+ decimals: number;
17
+ name: string;
18
+ source: TokenSource;
19
+ verified: boolean;
20
+ }
21
+ export declare function findInRegistryByType(tokenType: string, network?: string): OfficialTokenEntry | undefined;
22
+ export declare function findInRegistryBySymbol(symbol: string, network?: string): OfficialTokenEntry[];
23
+ export declare function resolveTokenByType(tokenType: string, network?: ENTRYPOINT): Promise<TokenResolution | undefined>;
24
+ export declare function resolveTokensBySymbol(symbol: string, network?: ENTRYPOINT, preferredType?: string): Promise<TokenResolution[]>;
25
+ export interface ParsedAmount {
26
+ smallestUnit: bigint;
27
+ displayAmount: string;
28
+ originalInput: string | number;
29
+ tokenType: string;
30
+ tokenSymbol: string;
31
+ decimals: number;
32
+ converted: boolean;
33
+ source: TokenSource;
34
+ verified: boolean;
35
+ }
36
+ export declare function isAmountWithSymbol(value: unknown): boolean;
37
+ export declare function parseAmount(amount: string | number, tokenType: string, network?: ENTRYPOINT): Promise<ParsedAmount>;
38
+ export declare function smallestToDisplay(value: bigint, decimals: number): string;
39
+ export interface FundFieldConfirmation {
40
+ path: string;
41
+ displayAmount: string;
42
+ tokenType: string;
43
+ tokenSymbol: string;
44
+ verified: boolean;
45
+ source: TokenSource;
46
+ converted: boolean;
47
+ context: string;
48
+ }
49
+ export interface TokenAmbiguity {
50
+ symbol: string;
51
+ candidates: {
52
+ type: string;
53
+ decimals: number;
54
+ source: TokenSource;
55
+ verified: boolean;
56
+ }[];
57
+ }
58
+ export interface FundConfirmationRequest {
59
+ requiresConfirmation: boolean;
60
+ reason: string;
61
+ fields: FundFieldConfirmation[];
62
+ ambiguities: TokenAmbiguity[];
63
+ message: string;
64
+ }
65
+ export declare function generateFundConfirmation(fields: FundFieldConfirmation[], ambiguities?: TokenAmbiguity[]): FundConfirmationRequest;
66
+ export interface FundProcessResult {
67
+ convertedData: any;
68
+ warnings: string[];
69
+ confirmation: FundConfirmationRequest | null;
70
+ }
71
+ export declare function processFundOperation(data: any, operationType: string, network?: ENTRYPOINT): Promise<FundProcessResult>;
72
+ export declare function generateSkillsHint(confirmation: FundConfirmationRequest | null): string | null;
@@ -0,0 +1,420 @@
1
+ import { GasTokenType } from "@wowok/wowok";
2
+ const GAS_TOKENS = [
3
+ {
4
+ type: "0x2::wow::WOW",
5
+ symbol: "WOW",
6
+ decimals: 9,
7
+ name: "Wowok Token",
8
+ description: "Default platform gas token for all WoWok operations. 1 WOW = 10^9 MIST. Available on testnet and mainnet.",
9
+ },
10
+ ];
11
+ const TESTNET_TOKENS = [];
12
+ function buildMainnetTokensFromSDK() {
13
+ try {
14
+ const { MAINNET_BRIDGE_CONFIG } = require("@wowok/wowok");
15
+ if (!MAINNET_BRIDGE_CONFIG?.tokens)
16
+ return [];
17
+ const seen = new Set();
18
+ return MAINNET_BRIDGE_CONFIG.tokens
19
+ .filter((t) => {
20
+ if (seen.has(t.wowTypeTag))
21
+ return false;
22
+ seen.add(t.wowTypeTag);
23
+ return true;
24
+ })
25
+ .map((t) => ({
26
+ type: t.wowTypeTag,
27
+ symbol: t.symbol,
28
+ decimals: t.wowDecimals,
29
+ name: `${t.symbol} (Bridge)`,
30
+ description: t.description ?? `Bridge token on WOW mainnet. ${t.wowDecimals} decimals.`,
31
+ }));
32
+ }
33
+ catch {
34
+ return [];
35
+ }
36
+ }
37
+ export const OFFICIAL_TOKEN_REGISTRY = {
38
+ gas: GAS_TOKENS,
39
+ testnet: TESTNET_TOKENS,
40
+ mainnet: buildMainnetTokensFromSDK(),
41
+ };
42
+ export function getRegistryForNetwork(network) {
43
+ return [
44
+ ...OFFICIAL_TOKEN_REGISTRY.gas,
45
+ ...(network === "testnet" ? OFFICIAL_TOKEN_REGISTRY.testnet : []),
46
+ ...(network === "mainnet" ? OFFICIAL_TOKEN_REGISTRY.mainnet : []),
47
+ ];
48
+ }
49
+ export function findInRegistryByType(tokenType, network) {
50
+ const gasMatch = OFFICIAL_TOKEN_REGISTRY.gas.find((t) => t.type === tokenType);
51
+ if (gasMatch)
52
+ return gasMatch;
53
+ const netTokens = getRegistryForNetwork(network);
54
+ return netTokens.find((t) => t.type === tokenType);
55
+ }
56
+ export function findInRegistryBySymbol(symbol, network) {
57
+ const lower = symbol.toLowerCase();
58
+ const gasMatches = OFFICIAL_TOKEN_REGISTRY.gas.filter((t) => t.symbol.toLowerCase() === lower);
59
+ const netTokens = getRegistryForNetwork(network);
60
+ const netMatches = netTokens.filter((t) => t.symbol.toLowerCase() === lower && !gasMatches.some((g) => g.type === t.type));
61
+ return [...gasMatches, ...netMatches];
62
+ }
63
+ export async function resolveTokenByType(tokenType, network) {
64
+ const registryEntry = findInRegistryByType(tokenType, network);
65
+ if (registryEntry) {
66
+ return {
67
+ type: registryEntry.type,
68
+ symbol: registryEntry.symbol,
69
+ decimals: registryEntry.decimals,
70
+ name: registryEntry.name,
71
+ source: "registry",
72
+ verified: true,
73
+ };
74
+ }
75
+ try {
76
+ const { TokenInfo } = await import("@wowok/wowok");
77
+ const tokenInfo = TokenInfo.Instance();
78
+ const fetched = await tokenInfo.fetch(tokenType, undefined, network);
79
+ if (fetched) {
80
+ return {
81
+ type: fetched.type,
82
+ symbol: fetched.symbol ?? "",
83
+ decimals: fetched.decimals ?? 0,
84
+ name: fetched.name ?? "",
85
+ source: "cache",
86
+ verified: false,
87
+ };
88
+ }
89
+ }
90
+ catch {
91
+ }
92
+ return undefined;
93
+ }
94
+ export async function resolveTokensBySymbol(symbol, network, preferredType) {
95
+ const results = [];
96
+ const registryMatches = findInRegistryBySymbol(symbol, network);
97
+ for (const entry of registryMatches) {
98
+ results.push({
99
+ type: entry.type,
100
+ symbol: entry.symbol,
101
+ decimals: entry.decimals,
102
+ name: entry.name,
103
+ source: "registry",
104
+ verified: true,
105
+ });
106
+ }
107
+ try {
108
+ const { query_local_token_list } = await import("@wowok/wowok");
109
+ const cached = await query_local_token_list({ symbol });
110
+ for (const token of cached) {
111
+ if (results.some((r) => r.type === token.type))
112
+ continue;
113
+ results.push({
114
+ type: token.type,
115
+ symbol: token.symbol ?? "",
116
+ decimals: token.decimals ?? 0,
117
+ name: token.name ?? "",
118
+ source: "cache",
119
+ verified: false,
120
+ });
121
+ }
122
+ }
123
+ catch {
124
+ }
125
+ if (preferredType) {
126
+ results.sort((a, b) => {
127
+ if (a.type === preferredType)
128
+ return -1;
129
+ if (b.type === preferredType)
130
+ return 1;
131
+ if (a.verified && !b.verified)
132
+ return -1;
133
+ if (!a.verified && b.verified)
134
+ return 1;
135
+ return 0;
136
+ });
137
+ }
138
+ return results;
139
+ }
140
+ const AMOUNT_WITH_SYMBOL = /^(\d+(?:\.\d+)?)\s*([A-Za-z][A-Za-z0-9]*)$/;
141
+ export function isAmountWithSymbol(value) {
142
+ if (typeof value !== "string")
143
+ return false;
144
+ const match = value.match(AMOUNT_WITH_SYMBOL);
145
+ if (!match)
146
+ return false;
147
+ const symbol = match[2];
148
+ return symbol.length >= 2 && symbol.length <= 10;
149
+ }
150
+ export async function parseAmount(amount, tokenType, network) {
151
+ const originalInput = amount;
152
+ const resolution = await resolveTokenByType(tokenType, network);
153
+ const decimals = resolution?.decimals ?? 9;
154
+ const symbol = resolution?.symbol ?? "";
155
+ const source = resolution?.source ?? "registry";
156
+ const verified = resolution?.verified ?? false;
157
+ if (typeof amount === "string") {
158
+ const match = amount.match(AMOUNT_WITH_SYMBOL);
159
+ if (match) {
160
+ const numericPart = parseFloat(match[1]);
161
+ const symbolPart = match[2];
162
+ if (symbol && symbolPart.toLowerCase() !== symbol.toLowerCase()) {
163
+ throw new Error(`Amount symbol "${symbolPart}" does not match the expected token symbol "${symbol}" ` +
164
+ `(type: ${tokenType}). Either use the correct symbol or pass a pure numeric value ` +
165
+ `in smallest units (e.g., ${(numericPart * Math.pow(10, decimals)).toFixed(0)}).`);
166
+ }
167
+ const smallestUnit = displayToSmallestUnit(numericPart, decimals);
168
+ return {
169
+ smallestUnit,
170
+ displayAmount: `${numericPart} ${symbolPart}`,
171
+ originalInput,
172
+ tokenType,
173
+ tokenSymbol: symbol || symbolPart,
174
+ decimals,
175
+ converted: true,
176
+ source,
177
+ verified,
178
+ };
179
+ }
180
+ }
181
+ let smallestUnit;
182
+ try {
183
+ smallestUnit = BigInt(amount);
184
+ }
185
+ catch {
186
+ throw new Error(`Invalid amount: "${amount}". Expected a number, a numeric string (smallest unit), ` +
187
+ `or a display-format string like "2.5WOW" (matching the token's symbol).`);
188
+ }
189
+ const displayValue = smallestToDisplay(smallestUnit, decimals);
190
+ return {
191
+ smallestUnit,
192
+ displayAmount: symbol ? `${displayValue} ${symbol}` : displayValue,
193
+ originalInput,
194
+ tokenType,
195
+ tokenSymbol: symbol,
196
+ decimals,
197
+ converted: false,
198
+ source,
199
+ verified,
200
+ };
201
+ }
202
+ function displayToSmallestUnit(display, decimals) {
203
+ const str = display.toString();
204
+ const [wholePart, fracPart = ""] = str.split(".");
205
+ const paddedFrac = fracPart.padEnd(decimals, "0").slice(0, decimals);
206
+ const combined = wholePart + paddedFrac;
207
+ return BigInt(combined || "0");
208
+ }
209
+ export function smallestToDisplay(value, decimals) {
210
+ const divisor = BigInt(10) ** BigInt(decimals);
211
+ const whole = value / divisor;
212
+ const frac = value % divisor;
213
+ if (frac === 0n)
214
+ return whole.toString();
215
+ const fracStr = frac.toString().padStart(decimals, "0").replace(/0+$/, "");
216
+ return `${whole.toString()}.${fracStr}`;
217
+ }
218
+ export function generateFundConfirmation(fields, ambiguities = []) {
219
+ const hasAmbiguity = ambiguities.length > 0;
220
+ const parts = [
221
+ "MONEY TRANSACTION CONFIRMATION REQUIRED",
222
+ "",
223
+ ];
224
+ for (const field of fields) {
225
+ const verifiedTag = field.verified ? " [VERIFIED]" : " [UNVERIFIED]";
226
+ const sourceTag = ` (source: ${field.source})`;
227
+ parts.push(` • ${field.context}: ${field.displayAmount}${verifiedTag}${sourceTag}`);
228
+ if (field.converted) {
229
+ parts.push(` (converted from display format to smallest unit)`);
230
+ }
231
+ }
232
+ if (hasAmbiguity) {
233
+ parts.push("", "AMBIGUITY WARNING — multiple tokens share the same symbol:");
234
+ for (const amb of ambiguities) {
235
+ parts.push(` Symbol "${amb.symbol}" matches:`);
236
+ for (const c of amb.candidates) {
237
+ const vTag = c.verified ? " [VERIFIED]" : " [UNVERIFIED]";
238
+ parts.push(` - type: ${c.type}, decimals: ${c.decimals}${vTag} (source: ${c.source})`);
239
+ }
240
+ parts.push(` → Specify the full type string in type_parameter to disambiguate.`);
241
+ }
242
+ }
243
+ parts.push("", "This operation involves money. Please confirm before proceeding.");
244
+ return {
245
+ requiresConfirmation: true,
246
+ reason: hasAmbiguity
247
+ ? "Money transaction with ambiguous token symbol — user must specify full type"
248
+ : "Money transaction requires user confirmation",
249
+ fields,
250
+ ambiguities,
251
+ message: parts.join("\n"),
252
+ };
253
+ }
254
+ function extractTokenType(data, operationType) {
255
+ if (data?.object && typeof data.object === "object" && data.object.type_parameter) {
256
+ return data.object.type_parameter;
257
+ }
258
+ if (operationType === "payment" && data?.type_parameter) {
259
+ return data.type_parameter;
260
+ }
261
+ return GasTokenType;
262
+ }
263
+ const MONETARY_FIELDS = {
264
+ service: [
265
+ { path: "sales.sales", context: "Service sale price", isArray: true, amountField: "price" },
266
+ { path: "compensation_fund_add.balance", context: "Service compensation fund deposit" },
267
+ { path: "order_new.buy.total_pay.balance", context: "Order payment amount" },
268
+ { path: "discount.discount_value", context: "Discount value" },
269
+ ],
270
+ arbitration: [
271
+ { path: "dispute.fee.balance", context: "Arbitration dispute fee" },
272
+ { path: "fee", context: "Arbitration fee" },
273
+ ],
274
+ reward: [
275
+ { path: "coin_add.balance", context: "Reward coin add" },
276
+ ],
277
+ treasury: [
278
+ { path: "deposit.coin.balance", context: "Treasury deposit amount" },
279
+ { path: "withdraw.amount.fixed", context: "Treasury withdrawal amount" },
280
+ ],
281
+ payment: [
282
+ { path: "revenue", context: "Payment amount", isArray: true, amountField: "amount.balance" },
283
+ ],
284
+ };
285
+ function getNestedValue(obj, path) {
286
+ const parts = path.split(".");
287
+ let current = obj;
288
+ for (const part of parts) {
289
+ if (current == null)
290
+ return undefined;
291
+ current = current[part];
292
+ }
293
+ return current;
294
+ }
295
+ export async function processFundOperation(data, operationType, network) {
296
+ const result = {
297
+ convertedData: data,
298
+ warnings: [],
299
+ confirmation: null,
300
+ };
301
+ const fields = MONETARY_FIELDS[operationType];
302
+ if (!fields) {
303
+ return result;
304
+ }
305
+ const tokenType = extractTokenType(data, operationType);
306
+ const confirmationFields = [];
307
+ const ambiguities = [];
308
+ const seenSymbols = new Map();
309
+ for (const fieldDesc of fields) {
310
+ const isArr = fieldDesc.isArray === true;
311
+ const amountField = fieldDesc.amountField;
312
+ if (isArr && amountField) {
313
+ const arr = getNestedValue(data, fieldDesc.path);
314
+ if (Array.isArray(arr)) {
315
+ for (let i = 0; i < arr.length; i++) {
316
+ const item = arr[i];
317
+ const parts = amountField.split(".");
318
+ let parentObj = item;
319
+ for (let j = 0; j < parts.length - 1; j++) {
320
+ parentObj = parentObj?.[parts[j]];
321
+ if (parentObj == null)
322
+ break;
323
+ }
324
+ const finalKey = parts[parts.length - 1];
325
+ if (parentObj && parentObj[finalKey] != null) {
326
+ const path = `${fieldDesc.path}[${i}].${amountField}`;
327
+ await processSingleField(parentObj, finalKey, path, fieldDesc.context, tokenType, network, result, confirmationFields, ambiguities, seenSymbols);
328
+ }
329
+ }
330
+ }
331
+ }
332
+ else {
333
+ const lastDot = fieldDesc.path.lastIndexOf(".");
334
+ const parentPath = lastDot >= 0 ? fieldDesc.path.slice(0, lastDot) : "";
335
+ const finalKey = lastDot >= 0 ? fieldDesc.path.slice(lastDot + 1) : fieldDesc.path;
336
+ const parentObj = parentPath ? getNestedValue(data, parentPath) : data;
337
+ if (parentObj && parentObj[finalKey] != null) {
338
+ await processSingleField(parentObj, finalKey, fieldDesc.path, fieldDesc.context, tokenType, network, result, confirmationFields, ambiguities, seenSymbols);
339
+ }
340
+ }
341
+ }
342
+ if (confirmationFields.length > 0) {
343
+ result.confirmation = generateFundConfirmation(confirmationFields, ambiguities);
344
+ const totalAmounts = confirmationFields.length;
345
+ const hasUnverified = confirmationFields.some((f) => !f.verified);
346
+ const hasAmbiguity = ambiguities.length > 0;
347
+ if (hasAmbiguity) {
348
+ result.warnings.push(`AMBIGUOUS TOKEN: ${ambiguities.length} token symbol(s) have multiple candidates. ` +
349
+ `User must specify full type string in type_parameter to disambiguate. ` +
350
+ `See confirmation message for details.`);
351
+ }
352
+ if (hasUnverified) {
353
+ result.warnings.push(`UNVERIFIED TOKEN: some monetary fields use tokens not in the official registry. ` +
354
+ `Precision was resolved from cache/on-chain. Verify the token type before proceeding.`);
355
+ }
356
+ result.warnings.push(`MONEY CONFIRMATION: ${totalAmounts} monetary field(s) require user confirmation. ` +
357
+ `Token type: ${tokenType}. Use env.confirmed=true after user consent.`);
358
+ }
359
+ return result;
360
+ }
361
+ async function processSingleField(parentObj, fieldKey, fieldPath, context, tokenType, network, result, confirmationFields, ambiguities, seenSymbols) {
362
+ const value = parentObj[fieldKey];
363
+ if (value == null)
364
+ return;
365
+ try {
366
+ const parsed = await parseAmount(value, tokenType, network);
367
+ if (parsed.converted) {
368
+ parentObj[fieldKey] = parsed.smallestUnit.toString();
369
+ }
370
+ confirmationFields.push({
371
+ path: fieldPath,
372
+ displayAmount: parsed.displayAmount,
373
+ tokenType: parsed.tokenType,
374
+ tokenSymbol: parsed.tokenSymbol,
375
+ verified: parsed.verified,
376
+ source: parsed.source,
377
+ converted: parsed.converted,
378
+ context,
379
+ });
380
+ if (parsed.tokenSymbol && parsed.source !== "registry") {
381
+ const symbolKey = parsed.tokenSymbol.toLowerCase();
382
+ let candidates = seenSymbols.get(symbolKey);
383
+ if (!candidates) {
384
+ const allMatches = await resolveTokensBySymbol(parsed.tokenSymbol, network, tokenType);
385
+ if (allMatches.length > 1) {
386
+ candidates = allMatches;
387
+ seenSymbols.set(symbolKey, candidates);
388
+ ambiguities.push({
389
+ symbol: parsed.tokenSymbol,
390
+ candidates: candidates.map((c) => ({
391
+ type: c.type,
392
+ decimals: c.decimals,
393
+ source: c.source,
394
+ verified: c.verified,
395
+ })),
396
+ });
397
+ }
398
+ }
399
+ }
400
+ }
401
+ catch (err) {
402
+ result.warnings.push(`FUND LAYER ERROR at ${fieldPath}: ${err.message}`);
403
+ }
404
+ }
405
+ export function generateSkillsHint(confirmation) {
406
+ if (!confirmation || !confirmation.requiresConfirmation)
407
+ return null;
408
+ const hasAmbiguity = confirmation.ambiguities.length > 0;
409
+ if (hasAmbiguity) {
410
+ return ("SKILLS HINT: This money transaction has ambiguous token symbols. " +
411
+ "If the wowok-safety skill is installed, it can guide the user through " +
412
+ "interactive disambiguation. Otherwise, the MCP fund layer has already " +
413
+ "listed all candidates in the confirmation message — ask the user to " +
414
+ "specify the full token type string in type_parameter.");
415
+ }
416
+ return ("SKILLS HINT: This money transaction requires user confirmation. " +
417
+ "If the wowok-safety skill is installed, it provides R1-R10 confirmation " +
418
+ "dialogue scripts. Otherwise, present the confirmation message to the user " +
419
+ "and proceed only after explicit consent (env.confirmed=true).");
420
+ }
@@ -0,0 +1,57 @@
1
+ export interface GuardNodeForRender {
2
+ type: string;
3
+ nodes?: GuardNodeForRender[];
4
+ node?: GuardNodeForRender;
5
+ nodeLeft?: GuardNodeForRender;
6
+ nodeRight?: GuardNodeForRender;
7
+ query?: number | string;
8
+ object?: {
9
+ identifier?: number;
10
+ convert_witness?: number;
11
+ };
12
+ parameters?: GuardNodeForRender[];
13
+ identifier?: number;
14
+ context?: string;
15
+ historyIdx?: GuardNodeForRender;
16
+ sessionIdx?: GuardNodeForRender;
17
+ forwardIdx?: GuardNodeForRender;
18
+ where?: GuardNodeForRender;
19
+ find?: GuardNodeForRender;
20
+ objectLeft?: GuardNodeForRender;
21
+ objectRight?: GuardNodeForRender;
22
+ recipient?: GuardNodeForRender;
23
+ }
24
+ export interface GuardRenderInput {
25
+ root: GuardNodeForRender;
26
+ table?: Array<{
27
+ identifier: number;
28
+ name?: string;
29
+ value_type: string | number;
30
+ b_submission: boolean;
31
+ value?: unknown;
32
+ }>;
33
+ guardName?: string | null;
34
+ guardAddress?: string | null;
35
+ rely?: {
36
+ guards: string[];
37
+ logic_or?: boolean;
38
+ } | null;
39
+ }
40
+ export declare function renderGuardMermaid(root: GuardNodeForRender, options?: {
41
+ colored?: boolean;
42
+ }): string;
43
+ export declare function renderGuardTreeIndented(input: GuardRenderInput): string;
44
+ export declare function parseGuardTreeIndented(text: string): {
45
+ success: boolean;
46
+ data?: GuardNodeForRender;
47
+ errors: string[];
48
+ };
49
+ export declare function renderGuardAscii(input: GuardRenderInput): string;
50
+ export declare function renderGuardMarkmap(input: GuardRenderInput): string;
51
+ export declare function parseGuardTableJSON(text: string): Array<{
52
+ identifier: number;
53
+ name?: string;
54
+ value_type: string | number;
55
+ b_submission: boolean;
56
+ value?: unknown;
57
+ }> | null;