@wowok/agent-mcp 2.3.16 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/customer/index.d.ts +9 -0
- package/dist/customer/index.js +9 -0
- package/dist/customer/industry-risks.d.ts +4 -0
- package/dist/customer/industry-risks.js +45 -31
- package/dist/customer/order-monitor.js +57 -57
- package/dist/customer/order-strategy.js +89 -89
- package/dist/customer/post-purchase.js +97 -97
- package/dist/customer/reminder-system.js +26 -26
- package/dist/customer/risk-assessment.js +46 -46
- package/dist/customer/types.d.ts +4 -4
- package/dist/customer/types.js +4 -4
- package/dist/customer/user-preferences.d.ts +3 -0
- package/dist/customer/user-preferences.js +59 -50
- package/dist/experience/intent-distill.js +6 -6
- package/dist/experience/realtime-feedback.js +17 -0
- package/dist/index.js +360 -273
- package/dist/knowledge/acquisition-flywheel.d.ts +3 -0
- package/dist/knowledge/acquisition-flywheel.js +73 -64
- package/dist/knowledge/arbitration-trust.js +10 -10
- package/dist/knowledge/demand-matching.js +8 -8
- package/dist/knowledge/dynamic-pricing.js +18 -18
- package/dist/knowledge/flywheel-loop.js +11 -11
- package/dist/knowledge/glossary.js +22 -22
- package/dist/knowledge/guard-ledger.d.ts +19 -0
- package/dist/knowledge/guard-ledger.js +246 -0
- package/dist/knowledge/guard-lint.d.ts +76 -0
- package/dist/knowledge/guard-lint.js +590 -0
- package/dist/knowledge/guard-puzzle.d.ts +188 -0
- package/dist/knowledge/guard-puzzle.js +804 -0
- package/dist/knowledge/guard-risk.d.ts +67 -0
- package/dist/knowledge/guard-risk.js +1046 -0
- package/dist/knowledge/guard-templates.d.ts +37 -0
- package/dist/knowledge/guard-templates.js +329 -0
- package/dist/knowledge/guard-translation.d.ts +58 -0
- package/dist/knowledge/guard-translation.js +1118 -0
- package/dist/knowledge/index.d.ts +17 -0
- package/dist/knowledge/index.js +17 -0
- package/dist/knowledge/industry-evolution.js +29 -29
- package/dist/knowledge/industry-generalizer.js +52 -52
- package/dist/knowledge/industry-registry.d.ts +1 -2
- package/dist/knowledge/industry-registry.js +43 -36
- package/dist/knowledge/intent-metrics.js +67 -67
- package/dist/knowledge/process-model.js +80 -80
- package/dist/knowledge/reputation-rules.js +9 -9
- package/dist/knowledge/reward-templates.js +51 -51
- package/dist/knowledge/safety-rules.js +63 -2
- package/dist/knowledge/tool-constraints.js +17 -11
- package/dist/knowledge/trust-metrics.js +26 -26
- package/dist/safety/confirm-gate.d.ts +4 -2
- package/dist/safety/confirm-gate.js +48 -4
- package/dist/schema/call/allocation.d.ts +24 -14
- package/dist/schema/call/arbitration.d.ts +142 -132
- package/dist/schema/call/base.d.ts +901 -43
- package/dist/schema/call/base.js +77 -13
- package/dist/schema/call/bridge-handler.js +15 -2
- package/dist/schema/call/bridge.d.ts +795 -27
- package/dist/schema/call/bridge.js +37 -0
- package/dist/schema/call/contact.d.ts +10 -0
- package/dist/schema/call/demand.d.ts +94 -84
- package/dist/schema/call/guard.d.ts +229 -46
- package/dist/schema/call/guard.js +50 -0
- package/dist/schema/call/handler.js +46 -14
- package/dist/schema/call/machine.d.ts +158 -138
- package/dist/schema/call/order.d.ts +10 -0
- package/dist/schema/call/payment.d.ts +10 -0
- package/dist/schema/call/permission.d.ts +314 -304
- package/dist/schema/call/personal.d.ts +10 -0
- package/dist/schema/call/progress.d.ts +10 -0
- package/dist/schema/call/proof.d.ts +20 -0
- package/dist/schema/call/repository.d.ts +32 -22
- package/dist/schema/call/reward.d.ts +10 -0
- package/dist/schema/call/semantic.d.ts +9 -2
- package/dist/schema/call/semantic.js +617 -25
- package/dist/schema/call/service.d.ts +20 -10
- package/dist/schema/call/treasury.d.ts +10 -0
- package/dist/schema/config/index.d.ts +18 -0
- package/dist/schema/config/index.js +17 -0
- package/dist/schema/index.d.ts +6 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/local/index.d.ts +9 -9
- package/dist/schema/local/index.js +9 -0
- package/dist/schema/local/wip.d.ts +39 -19
- package/dist/schema/local/wip.js +5 -5
- package/dist/schema/messenger/index.d.ts +376 -55
- package/dist/schema/messenger/index.js +19 -19
- package/dist/schema/operations.d.ts +1881 -681
- package/dist/schema/operations.js +91 -96
- package/dist/schema/project/index.d.ts +21 -0
- package/dist/schema/project/index.js +32 -0
- package/dist/schema/query/index.d.ts +1074 -168
- package/dist/schema/query/index.js +8 -8
- package/dist/schema/schema-query/index.d.ts +33 -0
- package/dist/schema/schema-query/index.js +36 -0
- package/dist/schema/schema-version.d.ts +25 -0
- package/dist/schema/schema-version.js +76 -0
- package/dist/schema/trust/index.d.ts +407 -0
- package/dist/schema/trust/index.js +113 -0
- package/dist/schema/utils/guard-parser.js +4 -4
- package/dist/schema/utils/node-parser.js +14 -14
- package/dist/schemas/bridge_operation.output.json +233 -9
- package/dist/schemas/bridge_operation.schema.json +67 -8
- package/dist/schemas/config_operation.output.json +18 -0
- package/dist/schemas/config_operation.schema.json +34 -0
- package/dist/schemas/guard-node-schema.json +82 -0
- package/dist/schemas/guard2file.schema.json +8 -0
- package/dist/schemas/index.json +19 -1
- package/dist/schemas/machineNode2file.schema.json +8 -0
- package/dist/schemas/messenger_operation.output.json +236 -10
- package/dist/schemas/onchain_events.output.json +718 -0
- package/dist/schemas/onchain_operations.output.json +286 -11
- package/dist/schemas/onchain_operations.schema.json +19 -16
- package/dist/schemas/onchain_operations_allocation.schema.json +8 -0
- package/dist/schemas/onchain_operations_arbitration.schema.json +8 -0
- package/dist/schemas/onchain_operations_contact.schema.json +8 -0
- package/dist/schemas/onchain_operations_demand.schema.json +8 -0
- package/dist/schemas/onchain_operations_gen_passport.schema.json +16 -0
- package/dist/schemas/onchain_operations_guard.schema.json +11 -16
- package/dist/schemas/onchain_operations_machine.schema.json +8 -0
- package/dist/schemas/onchain_operations_order.schema.json +8 -0
- package/dist/schemas/onchain_operations_payment.schema.json +8 -0
- package/dist/schemas/onchain_operations_permission.schema.json +8 -0
- package/dist/schemas/onchain_operations_personal.schema.json +8 -0
- package/dist/schemas/onchain_operations_progress.schema.json +8 -0
- package/dist/schemas/onchain_operations_repository.schema.json +8 -0
- package/dist/schemas/onchain_operations_reward.schema.json +8 -0
- package/dist/schemas/onchain_operations_service.schema.json +8 -0
- package/dist/schemas/onchain_operations_treasury.schema.json +8 -0
- package/dist/schemas/onchain_table_data.output.json +718 -0
- package/dist/schemas/project_operation.output.json +18 -0
- package/dist/schemas/project_operation.schema.json +42 -0
- package/dist/schemas/query_toolkit.schema.json +360 -368
- package/dist/schemas/schema_query.schema.json +6 -3
- package/dist/schemas/trust_score.output.json +327 -0
- package/dist/schemas/trust_score.schema.json +46 -0
- package/dist/schemas/wip_file.output.json +24 -1
- package/package.json +2 -2
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
import { validateGuardData, MAX_DESCRIPTION_LENGTH, MAX_TABLE_SIZE, } from "@wowok/wowok";
|
|
2
|
+
import { assessGuardRisks } from "./guard-risk.js";
|
|
3
|
+
import { findScene, inferSceneFromAction } from "./guard-ledger.js";
|
|
4
|
+
import { suggestTemplateForScene } from "./guard-templates.js";
|
|
5
|
+
function deepClone(obj) {
|
|
6
|
+
return JSON.parse(JSON.stringify(obj));
|
|
7
|
+
}
|
|
8
|
+
function rootToString(root) {
|
|
9
|
+
return JSON.stringify(root);
|
|
10
|
+
}
|
|
11
|
+
function hasSignerContext(root) {
|
|
12
|
+
const str = rootToString(root);
|
|
13
|
+
return str.includes('"Signer"') || str.includes('"context":"Signer"') || str.includes('"context": "Signer"');
|
|
14
|
+
}
|
|
15
|
+
function getSubmissionEntries(table) {
|
|
16
|
+
return table.filter((e) => e.b_submission === true);
|
|
17
|
+
}
|
|
18
|
+
function inferValueType(value) {
|
|
19
|
+
if (typeof value === "string") {
|
|
20
|
+
if (value.startsWith("0x") && value.length >= 3)
|
|
21
|
+
return "Address";
|
|
22
|
+
return "String";
|
|
23
|
+
}
|
|
24
|
+
if (typeof value === "number") {
|
|
25
|
+
if (Number.isInteger(value)) {
|
|
26
|
+
if (value >= 0 && value <= 255)
|
|
27
|
+
return "U8";
|
|
28
|
+
if (value >= 0 && value <= 65535)
|
|
29
|
+
return "U16";
|
|
30
|
+
return "U64";
|
|
31
|
+
}
|
|
32
|
+
return "U256";
|
|
33
|
+
}
|
|
34
|
+
if (typeof value === "boolean")
|
|
35
|
+
return "Bool";
|
|
36
|
+
if (Array.isArray(value)) {
|
|
37
|
+
if (value.length > 0) {
|
|
38
|
+
const elemType = inferValueType(value[0]);
|
|
39
|
+
if (elemType === "Address")
|
|
40
|
+
return "Vec<Address>";
|
|
41
|
+
if (elemType === "String")
|
|
42
|
+
return "Vec<String>";
|
|
43
|
+
if (elemType === "Bool")
|
|
44
|
+
return "Vec<Bool>";
|
|
45
|
+
}
|
|
46
|
+
return "Vec<Address>";
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
function sdkIssueToDiagnostic(issue) {
|
|
51
|
+
return {
|
|
52
|
+
code: issue.code,
|
|
53
|
+
message: issue.message,
|
|
54
|
+
severity: issue.severity,
|
|
55
|
+
source: "sdk",
|
|
56
|
+
location: {
|
|
57
|
+
path: issue.path,
|
|
58
|
+
},
|
|
59
|
+
fix: {
|
|
60
|
+
description: `SDK validation failed (${issue.sdk_error ?? "InvalidParam"}); manual fix required`,
|
|
61
|
+
auto_fixable: false,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async function checkSdkSyntax(json) {
|
|
66
|
+
const tableForSdk = json.table.map((e) => ({
|
|
67
|
+
identifier: e.identifier,
|
|
68
|
+
b_submission: e.b_submission ?? false,
|
|
69
|
+
value_type: (e.value_type ?? "Address"),
|
|
70
|
+
value: e.value,
|
|
71
|
+
name: e.name ?? "",
|
|
72
|
+
}));
|
|
73
|
+
const issues = await validateGuardData({
|
|
74
|
+
description: json.description,
|
|
75
|
+
table: tableForSdk,
|
|
76
|
+
root: json.root,
|
|
77
|
+
rely: json.rely,
|
|
78
|
+
});
|
|
79
|
+
return issues.map(sdkIssueToDiagnostic);
|
|
80
|
+
}
|
|
81
|
+
function checkSemanticErrors(json) {
|
|
82
|
+
const diagnostics = [];
|
|
83
|
+
const { table, root, binding_hint } = json;
|
|
84
|
+
const submissionEntries = getSubmissionEntries(table);
|
|
85
|
+
if (submissionEntries.length > 0 && root && !hasSignerContext(root)) {
|
|
86
|
+
const hasAddressSubmission = submissionEntries.some((e) => e.value_type === "Address");
|
|
87
|
+
if (hasAddressSubmission) {
|
|
88
|
+
const indices = submissionEntries
|
|
89
|
+
.filter((e) => e.value_type === "Address")
|
|
90
|
+
.map((e) => table.indexOf(e));
|
|
91
|
+
diagnostics.push({
|
|
92
|
+
code: "LE-04",
|
|
93
|
+
message: "Submitted data has no Signer binding (submission forgery risk)",
|
|
94
|
+
severity: "error",
|
|
95
|
+
source: "lint",
|
|
96
|
+
location: { path: indices.map((i) => `/table/${i}`).join(" → ") },
|
|
97
|
+
fix: {
|
|
98
|
+
description: "Add logic_equal(context(Signer), identifier[N]) in root to bind the submitter identity",
|
|
99
|
+
auto_fixable: false,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const scene = binding_hint ? findScene(binding_hint.host_object ?? "", binding_hint.binding_field ?? "") : undefined;
|
|
105
|
+
if (scene?.id === "arbitration_voting_guard") {
|
|
106
|
+
const hasWeightSubmission = submissionEntries.some((e) => ["U8", "U16", "U32", "U64", "U128", "U256"].includes(e.value_type ?? ""));
|
|
107
|
+
if (hasWeightSubmission) {
|
|
108
|
+
const indices = submissionEntries
|
|
109
|
+
.filter((e) => ["U8", "U16", "U32", "U64", "U128", "U256"].includes(e.value_type ?? ""))
|
|
110
|
+
.map((e) => table.indexOf(e));
|
|
111
|
+
diagnostics.push({
|
|
112
|
+
code: "LE-05",
|
|
113
|
+
message: "voting_guard voting weight comes from submitted data (weight forgery risk)",
|
|
114
|
+
severity: "error",
|
|
115
|
+
source: "lint",
|
|
116
|
+
location: { path: indices.map((i) => `/table/${i}`).join(" → ") },
|
|
117
|
+
fix: {
|
|
118
|
+
description: "Derive arbitrator weights from the Arb object via witness (e.g. 105 TypeArbArbitration) rather than submitting them directly",
|
|
119
|
+
auto_fixable: false,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (scene?.id === "gen_passport_guard" || binding_hint?.action === "gen_passport") {
|
|
125
|
+
const rootStr = rootToString(root);
|
|
126
|
+
if (rootStr.includes("1167") || rootStr.includes("repository.data")) {
|
|
127
|
+
const hasQuoteGuard = rootStr.includes("quote_guard");
|
|
128
|
+
if (hasQuoteGuard) {
|
|
129
|
+
diagnostics.push({
|
|
130
|
+
code: "LE-06",
|
|
131
|
+
message: "gen_passport scene uses quote_guard (will always fail)",
|
|
132
|
+
severity: "error",
|
|
133
|
+
source: "lint",
|
|
134
|
+
location: { path: "/root" },
|
|
135
|
+
fix: {
|
|
136
|
+
description: "Remove quote_guard authentication, or switch to a query method that does not depend on impack_list",
|
|
137
|
+
auto_fixable: false,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (binding_hint?.host_object && scene) {
|
|
144
|
+
const hostType = binding_hint.host_object;
|
|
145
|
+
const circularEntries = table.filter((e) => !e.b_submission && e.value_type === "Address" &&
|
|
146
|
+
typeof e.value === "string" && e.value.startsWith("0x") &&
|
|
147
|
+
e.object_type === hostType);
|
|
148
|
+
if (circularEntries.length > 0) {
|
|
149
|
+
const indices = circularEntries.map((e) => table.indexOf(e));
|
|
150
|
+
diagnostics.push({
|
|
151
|
+
code: "LE-07",
|
|
152
|
+
message: `Circular reference uses an address rather than a name (Host Object may not be published yet when the Guard is created)`,
|
|
153
|
+
severity: "error",
|
|
154
|
+
source: "lint",
|
|
155
|
+
location: { path: indices.map((i) => `/table/${i}/value`).join(" → ") },
|
|
156
|
+
fix: {
|
|
157
|
+
description: `Change value from an address to an object name (e.g. "service1"); the SDK will resolve it to an address after the Host Object is published`,
|
|
158
|
+
auto_fixable: false,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return diagnostics;
|
|
164
|
+
}
|
|
165
|
+
function checkSyntaxHints(json) {
|
|
166
|
+
const diagnostics = [];
|
|
167
|
+
const { table } = json;
|
|
168
|
+
if (!json.description || json.description.trim() === "") {
|
|
169
|
+
diagnostics.push({
|
|
170
|
+
code: "SH-01",
|
|
171
|
+
message: "description is missing or empty",
|
|
172
|
+
severity: "info",
|
|
173
|
+
source: "lint",
|
|
174
|
+
location: { path: "/description" },
|
|
175
|
+
fix: {
|
|
176
|
+
description: "Add a description, e.g. 'Service buy_guard: whitelist validation'",
|
|
177
|
+
auto_fixable: true,
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
for (let i = 0; i < table.length; i++) {
|
|
182
|
+
const entry = table[i];
|
|
183
|
+
if (!entry.name || entry.name.trim() === "") {
|
|
184
|
+
diagnostics.push({
|
|
185
|
+
code: "SH-03",
|
|
186
|
+
message: `table[${i}] is missing name`,
|
|
187
|
+
severity: "warning",
|
|
188
|
+
source: "lint",
|
|
189
|
+
location: { path: `/table/${i}/name` },
|
|
190
|
+
fix: {
|
|
191
|
+
description: `Add a name, e.g. "data entry ${entry.identifier}"`,
|
|
192
|
+
auto_fixable: true,
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
for (let i = 0; i < table.length; i++) {
|
|
198
|
+
const entry = table[i];
|
|
199
|
+
if (!entry.value_type) {
|
|
200
|
+
diagnostics.push({
|
|
201
|
+
code: "SH-05",
|
|
202
|
+
message: `table[${i}] is missing value_type`,
|
|
203
|
+
severity: "warning",
|
|
204
|
+
source: "lint",
|
|
205
|
+
location: { path: `/table/${i}/value_type` },
|
|
206
|
+
fix: {
|
|
207
|
+
description: "Infer value_type from value (e.g. Address/String/U64, etc.)",
|
|
208
|
+
auto_fixable: true,
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (table.length > MAX_TABLE_SIZE) {
|
|
214
|
+
diagnostics.push({
|
|
215
|
+
code: "SH-07",
|
|
216
|
+
message: `table entry count ${table.length} exceeds the maximum limit ${MAX_TABLE_SIZE}`,
|
|
217
|
+
severity: "error",
|
|
218
|
+
source: "lint",
|
|
219
|
+
location: { path: "/table" },
|
|
220
|
+
fix: {
|
|
221
|
+
description: "Slim down the table by merging similar entries, or split into multiple Guards + rely",
|
|
222
|
+
auto_fixable: false,
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
for (let i = 0; i < table.length; i++) {
|
|
227
|
+
if (table[i].b_submission === undefined) {
|
|
228
|
+
diagnostics.push({
|
|
229
|
+
code: "SH-10",
|
|
230
|
+
message: `table[${i}] b_submission is missing`,
|
|
231
|
+
severity: "info",
|
|
232
|
+
source: "lint",
|
|
233
|
+
location: { path: `/table/${i}/b_submission` },
|
|
234
|
+
fix: {
|
|
235
|
+
description: "Set to false (constant) or true (submitted data)",
|
|
236
|
+
auto_fixable: true,
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return diagnostics;
|
|
242
|
+
}
|
|
243
|
+
function checkSuggestions(json) {
|
|
244
|
+
const diagnostics = [];
|
|
245
|
+
const { root, binding_hint, table } = json;
|
|
246
|
+
if (root) {
|
|
247
|
+
const rootStr = rootToString(root);
|
|
248
|
+
if (rootStr.includes("vec_contains_address") && rootStr.includes("Signer")) {
|
|
249
|
+
diagnostics.push({
|
|
250
|
+
code: "SG-01",
|
|
251
|
+
message: "Matches Pattern P04 (whitelist validation)",
|
|
252
|
+
severity: "hint",
|
|
253
|
+
source: "lint",
|
|
254
|
+
location: { path: "/root" },
|
|
255
|
+
fix: { description: "Refer to the P04 Pattern to ensure best practices", auto_fixable: false },
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
else if (rootStr.includes("logic_equal") && rootStr.includes("Signer")) {
|
|
259
|
+
const hasAddressConst = table.some((e) => !e.b_submission && e.value_type === "Address" && typeof e.value === "string" && e.value.length > 0);
|
|
260
|
+
if (hasAddressConst) {
|
|
261
|
+
diagnostics.push({
|
|
262
|
+
code: "SG-01",
|
|
263
|
+
message: "Matches Pattern P04 (whitelist validation — single-address variant)",
|
|
264
|
+
severity: "hint",
|
|
265
|
+
source: "lint",
|
|
266
|
+
location: { path: "/root" },
|
|
267
|
+
fix: { description: "P04 single-address form; for multi-address scenarios, consider vec_contains_address", auto_fixable: false },
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (rootStr.includes("logic_as_u256_greater") ||
|
|
272
|
+
rootStr.includes("logic_as_u256_lesser")) {
|
|
273
|
+
diagnostics.push({
|
|
274
|
+
code: "SG-01",
|
|
275
|
+
message: "Matches Pattern P02 (numeric threshold validation)",
|
|
276
|
+
severity: "hint",
|
|
277
|
+
source: "lint",
|
|
278
|
+
location: { path: "/root" },
|
|
279
|
+
fix: { description: "Suitable for amount/quantity threshold validation scenarios", auto_fixable: false },
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
if (rootStr.includes("convert_witness")) {
|
|
283
|
+
diagnostics.push({
|
|
284
|
+
code: "SG-01",
|
|
285
|
+
message: "Matches Pattern P09 (witness-derived validation)",
|
|
286
|
+
severity: "hint",
|
|
287
|
+
source: "lint",
|
|
288
|
+
location: { path: "/root" },
|
|
289
|
+
fix: { description: "Suitable for cross-object relationship validation (e.g. Order→Progress)", auto_fixable: false },
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (binding_hint?.action) {
|
|
294
|
+
const validActions = ["buy", "forward", "allocate", "claim_reward", "vote", "dispute", "write_repository", "submit_progress", "gen_passport"];
|
|
295
|
+
if (validActions.includes(binding_hint.action)) {
|
|
296
|
+
const scene = inferSceneFromAction(binding_hint.action);
|
|
297
|
+
if (scene) {
|
|
298
|
+
const template = suggestTemplateForScene(scene.id);
|
|
299
|
+
if (template) {
|
|
300
|
+
diagnostics.push({
|
|
301
|
+
code: "SG-02",
|
|
302
|
+
message: `Recommended scene template: ${template.name}`,
|
|
303
|
+
severity: "hint",
|
|
304
|
+
source: "lint",
|
|
305
|
+
location: { path: "/" },
|
|
306
|
+
fix: { description: `Refer to template ${template.id} (${template.example_use_case})`, auto_fixable: false },
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const submissionEntries = getSubmissionEntries(table);
|
|
313
|
+
for (const entry of submissionEntries) {
|
|
314
|
+
const name = entry.name ?? "";
|
|
315
|
+
if (name.length < 10 || /^[a-z_]+$/i.test(name)) {
|
|
316
|
+
const idx = table.indexOf(entry);
|
|
317
|
+
diagnostics.push({
|
|
318
|
+
code: "SG-03",
|
|
319
|
+
message: `table[${idx}] submitted data has a vague name description`,
|
|
320
|
+
severity: "info",
|
|
321
|
+
source: "lint",
|
|
322
|
+
location: { path: `/table/${idx}/name` },
|
|
323
|
+
fix: { description: "Use a full natural-language description (≥10 characters), e.g. 'user-submitted operation type (return/damage)'", auto_fixable: false },
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return diagnostics;
|
|
328
|
+
}
|
|
329
|
+
export function autoFixGuard(json) {
|
|
330
|
+
const fixed = deepClone(json);
|
|
331
|
+
const actions = [];
|
|
332
|
+
if (!fixed.description || fixed.description.trim() === "") {
|
|
333
|
+
fixed.description = `Guard (table: ${fixed.table.length} items, root: ${fixed.root?.type ?? "unknown"})`;
|
|
334
|
+
actions.push({
|
|
335
|
+
diagnostic_code: "SH-01",
|
|
336
|
+
description: "Fill in default description",
|
|
337
|
+
after: fixed.description,
|
|
338
|
+
path: "/description",
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
if (fixed.description && fixed.description.length > MAX_DESCRIPTION_LENGTH) {
|
|
342
|
+
const before = fixed.description;
|
|
343
|
+
fixed.description = fixed.description.substring(0, MAX_DESCRIPTION_LENGTH);
|
|
344
|
+
actions.push({
|
|
345
|
+
diagnostic_code: "G-001",
|
|
346
|
+
description: `Truncate description to ${MAX_DESCRIPTION_LENGTH} characters`,
|
|
347
|
+
before: `${before.length} characters`,
|
|
348
|
+
after: `${fixed.description.length} characters`,
|
|
349
|
+
path: "/description",
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
for (let i = 0; i < fixed.table.length; i++) {
|
|
353
|
+
const entry = fixed.table[i];
|
|
354
|
+
if (!entry.name || entry.name.trim() === "") {
|
|
355
|
+
entry.name = `data entry ${entry.identifier}`;
|
|
356
|
+
actions.push({
|
|
357
|
+
diagnostic_code: "SH-03",
|
|
358
|
+
description: `table[${i}] fill in name`,
|
|
359
|
+
after: entry.name,
|
|
360
|
+
path: `/table/${i}/name`,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
for (let i = 0; i < fixed.table.length; i++) {
|
|
365
|
+
const entry = fixed.table[i];
|
|
366
|
+
if (!entry.value_type && entry.value !== undefined && entry.value !== null) {
|
|
367
|
+
const inferred = inferValueType(entry.value);
|
|
368
|
+
if (inferred) {
|
|
369
|
+
entry.value_type = inferred;
|
|
370
|
+
actions.push({
|
|
371
|
+
diagnostic_code: "SH-05",
|
|
372
|
+
description: `table[${i}] infer value_type`,
|
|
373
|
+
after: inferred,
|
|
374
|
+
path: `/table/${i}/value_type`,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const idMap = new Map();
|
|
380
|
+
let nextId = 0;
|
|
381
|
+
for (let i = 0; i < fixed.table.length; i++) {
|
|
382
|
+
const entry = fixed.table[i];
|
|
383
|
+
if (!idMap.has(entry.identifier)) {
|
|
384
|
+
idMap.set(entry.identifier, nextId);
|
|
385
|
+
if (entry.identifier !== nextId) {
|
|
386
|
+
actions.push({
|
|
387
|
+
diagnostic_code: "G-007",
|
|
388
|
+
description: `table[${i}] identifier ${entry.identifier} → ${nextId}`,
|
|
389
|
+
before: String(entry.identifier),
|
|
390
|
+
after: String(nextId),
|
|
391
|
+
path: `/table/${i}/identifier`,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
entry.identifier = nextId;
|
|
395
|
+
nextId++;
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
const oldId = entry.identifier;
|
|
399
|
+
entry.identifier = nextId;
|
|
400
|
+
idMap.set(oldId, nextId);
|
|
401
|
+
actions.push({
|
|
402
|
+
diagnostic_code: "G-007",
|
|
403
|
+
description: `table[${i}] identifier ${oldId} duplicate → reallocated to ${nextId}`,
|
|
404
|
+
before: String(oldId),
|
|
405
|
+
after: String(nextId),
|
|
406
|
+
path: `/table/${i}/identifier`,
|
|
407
|
+
});
|
|
408
|
+
nextId++;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
for (let i = 0; i < fixed.table.length; i++) {
|
|
412
|
+
const entry = fixed.table[i];
|
|
413
|
+
if (entry.b_submission === undefined) {
|
|
414
|
+
entry.b_submission = false;
|
|
415
|
+
actions.push({
|
|
416
|
+
diagnostic_code: "SH-10",
|
|
417
|
+
description: `table[${i}] b_submission default set to false`,
|
|
418
|
+
after: "false",
|
|
419
|
+
path: `/table/${i}/b_submission`,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (fixed.rely?.guards && fixed.rely.guards.length > 0) {
|
|
424
|
+
const unique = [...new Set(fixed.rely.guards)];
|
|
425
|
+
if (unique.length !== fixed.rely.guards.length) {
|
|
426
|
+
actions.push({
|
|
427
|
+
diagnostic_code: "G-009",
|
|
428
|
+
description: `rely deduplicate: ${fixed.rely.guards.length} → ${unique.length}`,
|
|
429
|
+
before: JSON.stringify(fixed.rely.guards),
|
|
430
|
+
after: JSON.stringify(unique),
|
|
431
|
+
path: "/rely/guards",
|
|
432
|
+
});
|
|
433
|
+
fixed.rely.guards = unique;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return { fixed, actions };
|
|
437
|
+
}
|
|
438
|
+
export async function lintGuard(json, options) {
|
|
439
|
+
const doAutoFix = options?.auto_fix ?? true;
|
|
440
|
+
const { table, root, rely, binding_hint } = json;
|
|
441
|
+
const diagnostics = [];
|
|
442
|
+
let risk_assessment;
|
|
443
|
+
const sdkDiagnostics = await checkSdkSyntax(json);
|
|
444
|
+
diagnostics.push(...sdkDiagnostics);
|
|
445
|
+
const semanticDiagnostics = checkSemanticErrors(json);
|
|
446
|
+
diagnostics.push(...semanticDiagnostics);
|
|
447
|
+
const hintDiagnostics = checkSyntaxHints(json);
|
|
448
|
+
diagnostics.push(...hintDiagnostics);
|
|
449
|
+
let fixed_json;
|
|
450
|
+
let fix_actions = [];
|
|
451
|
+
if (doAutoFix) {
|
|
452
|
+
const { fixed, actions } = autoFixGuard(json);
|
|
453
|
+
if (actions.length > 0) {
|
|
454
|
+
fixed_json = fixed;
|
|
455
|
+
fix_actions = actions;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (table.length > 0 && root) {
|
|
459
|
+
const scene = binding_hint
|
|
460
|
+
? findScene(binding_hint.host_object ?? "", binding_hint.binding_field ?? "")
|
|
461
|
+
: undefined;
|
|
462
|
+
const mappedTable = table.map((e) => ({
|
|
463
|
+
identifier: e.identifier,
|
|
464
|
+
b_submission: e.b_submission ?? false,
|
|
465
|
+
value_type: e.value_type ?? "Address",
|
|
466
|
+
value: e.value,
|
|
467
|
+
name: e.name,
|
|
468
|
+
object_type: e.object_type,
|
|
469
|
+
}));
|
|
470
|
+
risk_assessment = assessGuardRisks({ table: mappedTable, root, rely, scene });
|
|
471
|
+
for (const r of risk_assessment.risks) {
|
|
472
|
+
const severity = r.level === "critical" || r.level === "high" ? "error" :
|
|
473
|
+
r.level === "medium" ? "warning" :
|
|
474
|
+
r.level === "low" ? "info" : "hint";
|
|
475
|
+
const rawEvidence = r.evidence ?? "";
|
|
476
|
+
const path = rawEvidence.startsWith("/") ? rawEvidence : "/";
|
|
477
|
+
diagnostics.push({
|
|
478
|
+
code: r.id,
|
|
479
|
+
message: `[${r.level}] ${r.title}`,
|
|
480
|
+
severity,
|
|
481
|
+
source: "risk",
|
|
482
|
+
location: { path },
|
|
483
|
+
fix: {
|
|
484
|
+
description: r.mitigation,
|
|
485
|
+
auto_fixable: false,
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
const suggestionDiagnostics = checkSuggestions(json);
|
|
491
|
+
diagnostics.push(...suggestionDiagnostics);
|
|
492
|
+
const errors = diagnostics.filter((d) => d.severity === "error").length;
|
|
493
|
+
const warnings = diagnostics.filter((d) => d.severity === "warning").length;
|
|
494
|
+
const infos = diagnostics.filter((d) => d.severity === "info" || d.severity === "hint").length;
|
|
495
|
+
const ready = errors === 0;
|
|
496
|
+
let next_step;
|
|
497
|
+
if (errors > 0) {
|
|
498
|
+
next_step = `Fix ${errors} error(s) and re-run lint`;
|
|
499
|
+
}
|
|
500
|
+
else if (warnings > 0) {
|
|
501
|
+
next_step = `Consider addressing ${warnings} warning(s), then submit to SDK newGuard() for final syntax validation`;
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
next_step = "lint passed (no errors); submit to SDK newGuard() for final syntax validation before going on-chain";
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
json_summary: {
|
|
508
|
+
table_count: table.length,
|
|
509
|
+
root_type: root?.type ?? "unknown",
|
|
510
|
+
has_rely: !!rely?.guards?.length,
|
|
511
|
+
rely_count: rely?.guards?.length ?? 0,
|
|
512
|
+
has_description: !!json.description,
|
|
513
|
+
},
|
|
514
|
+
diagnostics,
|
|
515
|
+
fixed_json,
|
|
516
|
+
fix_actions,
|
|
517
|
+
risk_assessment,
|
|
518
|
+
summary: {
|
|
519
|
+
errors,
|
|
520
|
+
warnings,
|
|
521
|
+
infos,
|
|
522
|
+
auto_fixes_applied: fix_actions.length,
|
|
523
|
+
ready,
|
|
524
|
+
next_step,
|
|
525
|
+
},
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
export async function isGuardReady(json) {
|
|
529
|
+
const result = await lintGuard(json, { auto_fix: false });
|
|
530
|
+
return result.summary.ready;
|
|
531
|
+
}
|
|
532
|
+
export async function getGuardErrors(json) {
|
|
533
|
+
const result = await lintGuard(json, { auto_fix: false });
|
|
534
|
+
return result.diagnostics.filter((d) => d.severity === "error");
|
|
535
|
+
}
|
|
536
|
+
export function formatLintResult(result) {
|
|
537
|
+
const lines = [];
|
|
538
|
+
lines.push("=== Guard JSON Lint Report ===");
|
|
539
|
+
lines.push("");
|
|
540
|
+
lines.push("Summary:");
|
|
541
|
+
lines.push(` Errors: ${result.summary.errors}`);
|
|
542
|
+
lines.push(` Warnings: ${result.summary.warnings}`);
|
|
543
|
+
lines.push(` Infos: ${result.summary.infos}`);
|
|
544
|
+
lines.push(` Auto-fixes: ${result.summary.auto_fixes_applied}`);
|
|
545
|
+
lines.push(` Ready: ${result.summary.ready ? "YES" : "NO"}`);
|
|
546
|
+
lines.push(` Next step: ${result.summary.next_step}`);
|
|
547
|
+
lines.push("");
|
|
548
|
+
if (result.fix_actions.length > 0) {
|
|
549
|
+
lines.push("--- Auto-fix Actions ---");
|
|
550
|
+
for (const action of result.fix_actions) {
|
|
551
|
+
lines.push(` [${action.diagnostic_code}] ${action.description}`);
|
|
552
|
+
lines.push(` Path: ${action.path}`);
|
|
553
|
+
if (action.before && action.after) {
|
|
554
|
+
lines.push(` ${action.before} -> ${action.after}`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
lines.push("");
|
|
558
|
+
}
|
|
559
|
+
const errors = result.diagnostics.filter((d) => d.severity === "error");
|
|
560
|
+
const warnings = result.diagnostics.filter((d) => d.severity === "warning");
|
|
561
|
+
const infos = result.diagnostics.filter((d) => d.severity === "info" || d.severity === "hint");
|
|
562
|
+
if (errors.length > 0) {
|
|
563
|
+
lines.push("--- Errors (must fix) ---");
|
|
564
|
+
for (const d of errors) {
|
|
565
|
+
lines.push(` [${d.code}] (${d.source}) ${d.message}`);
|
|
566
|
+
lines.push(` Path: ${d.location.path}`);
|
|
567
|
+
if (d.fix)
|
|
568
|
+
lines.push(` Fix: ${d.fix.description}`);
|
|
569
|
+
}
|
|
570
|
+
lines.push("");
|
|
571
|
+
}
|
|
572
|
+
if (warnings.length > 0) {
|
|
573
|
+
lines.push("--- Warnings (consider fixing) ---");
|
|
574
|
+
for (const d of warnings) {
|
|
575
|
+
lines.push(` [${d.code}] (${d.source}) ${d.message}`);
|
|
576
|
+
lines.push(` Path: ${d.location.path}`);
|
|
577
|
+
if (d.fix)
|
|
578
|
+
lines.push(` Fix: ${d.fix.description}`);
|
|
579
|
+
}
|
|
580
|
+
lines.push("");
|
|
581
|
+
}
|
|
582
|
+
if (infos.length > 0) {
|
|
583
|
+
lines.push("--- Info & Hints ---");
|
|
584
|
+
for (const d of infos) {
|
|
585
|
+
lines.push(` [${d.code}] (${d.source}) ${d.message}`);
|
|
586
|
+
lines.push(` Path: ${d.location.path}`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return lines.join("\n");
|
|
590
|
+
}
|