@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.
- package/dist/customer/info-puzzle.d.ts +1 -1
- package/dist/customer/info-puzzle.js +4 -2
- package/dist/customer/risk-assessment.js +26 -4
- package/dist/customer/types.d.ts +2 -0
- package/dist/examples/guard-template-balance-check.json +38 -0
- package/dist/examples/guard-template-time-lock.json +39 -0
- package/dist/examples/machine-template-7node-rental.json +114 -0
- package/dist/examples/rental-ziroom-machine-create.json +137 -0
- package/dist/examples/rental-ziroom-permission-create.json +35 -0
- package/dist/examples/rental-ziroom-service-create.json +80 -0
- package/dist/examples/retail-myshop-service-create.json +88 -0
- package/dist/extensions/capability-manifest.js +24 -24
- package/dist/extensions/constraint-registry.js +18 -18
- package/dist/extensions/metric-registry.js +14 -14
- package/dist/extensions/mode-evaluator.js +16 -16
- package/dist/extensions/modes.js +131 -45
- package/dist/extensions/registry.d.ts +2 -0
- package/dist/extensions/registry.js +63 -30
- package/dist/extensions/types.d.ts +1 -0
- package/dist/index.js +50 -0
- package/dist/knowledge/deployment-scanner.js +1 -1
- package/dist/knowledge/fund-layer.d.ts +72 -0
- package/dist/knowledge/fund-layer.js +420 -0
- package/dist/knowledge/guard-render.d.ts +57 -0
- package/dist/knowledge/guard-render.js +700 -0
- package/dist/knowledge/guard-submission-prompt.d.ts +31 -0
- package/dist/knowledge/guard-submission-prompt.js +171 -0
- package/dist/knowledge/machine-ledger.js +1 -1
- package/dist/knowledge/machine-render.d.ts +41 -0
- package/dist/knowledge/machine-render.js +565 -0
- package/dist/knowledge/machine-templates.js +4 -4
- package/dist/knowledge/reward-confirm.js +2 -2
- package/dist/knowledge/reward-puzzle.js +1 -1
- package/dist/knowledge/reward-risk.js +9 -9
- package/dist/knowledge/reward-templates.js +2 -2
- package/dist/knowledge/service-confirm.d.ts +1 -1
- package/dist/knowledge/service-confirm.js +3 -3
- package/dist/knowledge/service-context.js +1 -1
- package/dist/knowledge/service-ledger.js +1 -1
- package/dist/knowledge/service-risk.d.ts +1 -1
- package/dist/knowledge/service-risk.js +3 -3
- package/dist/knowledge/service-templates.js +2 -2
- package/dist/knowledge/service-translation.d.ts +1 -1
- package/dist/knowledge/service-translation.js +7 -7
- package/dist/project/deployment-bridge.js +6 -5
- package/dist/project/deployment-doc.js +5 -5
- package/dist/project/edit-planner.d.ts +123 -0
- package/dist/project/edit-planner.js +1371 -0
- package/dist/project/evaluation.js +314 -14
- package/dist/project/graph-builder.js +6 -1
- package/dist/project/handlers.d.ts +99 -2
- package/dist/project/handlers.js +358 -16
- package/dist/project/stage-gate.js +4 -4
- package/dist/schema/call/allocation.js +2 -2
- package/dist/schema/call/arbitration.js +19 -6
- package/dist/schema/call/contact.js +2 -2
- package/dist/schema/call/demand.js +2 -2
- package/dist/schema/call/guard.js +1 -1
- package/dist/schema/call/machine.d.ts +1975 -777
- package/dist/schema/call/machine.js +50 -4
- package/dist/schema/call/order.js +3 -6
- package/dist/schema/call/permission.js +2 -2
- package/dist/schema/call/repository.js +2 -2
- package/dist/schema/call/reward.js +3 -3
- package/dist/schema/call/semantic.js +7 -1
- package/dist/schema/call/service.d.ts +180 -8
- package/dist/schema/call/service.js +82 -27
- package/dist/schema/call/treasury.js +3 -3
- package/dist/schema/common/index.d.ts +2 -0
- package/dist/schema/common/index.js +46 -10
- package/dist/schema/local/index.js +5 -1
- package/dist/schema/operations.d.ts +755 -176
- package/dist/schema/operations.js +43 -7
- package/dist/schema/project/index.d.ts +969 -6
- package/dist/schema/project/index.js +268 -4
- package/dist/schema/query/index.d.ts +217 -0
- package/dist/schema/query/index.js +63 -33
- package/dist/schema/schema-query/index.d.ts +53 -3
- package/dist/schema/schema-query/index.js +17 -2
- package/dist/schema/utils/node-parser.js +13 -0
- package/dist/schema/utils/object-type-utils.d.ts +12 -0
- package/dist/schema/utils/object-type-utils.js +35 -0
- package/dist/schema/utils/permission-machine-check.d.ts +49 -0
- package/dist/schema/utils/permission-machine-check.js +121 -0
- package/dist/schema/utils/skills-recommendation.d.ts +2 -0
- package/dist/schema/utils/skills-recommendation.js +76 -0
- package/dist/schema-query/index.d.ts +14 -1
- package/dist/schema-query/index.js +104 -2
- package/dist/schemas/account_operation.schema.json +1 -1
- package/dist/schemas/index.json +1 -1
- package/dist/schemas/onchain_events.output.json +1 -1
- package/dist/schemas/onchain_operations.output.json +2820 -0
- package/dist/schemas/onchain_operations.schema.json +115 -53
- package/dist/schemas/onchain_operations_allocation.schema.json +5 -5
- package/dist/schemas/onchain_operations_arbitration.schema.json +9 -7
- package/dist/schemas/onchain_operations_contact.schema.json +3 -3
- package/dist/schemas/onchain_operations_demand.schema.json +3 -3
- package/dist/schemas/onchain_operations_gen_passport.schema.json +2 -2
- package/dist/schemas/onchain_operations_guard.schema.json +1 -1
- package/dist/schemas/onchain_operations_machine.schema.json +4 -4
- package/dist/schemas/onchain_operations_order.schema.json +3 -3
- package/dist/schemas/onchain_operations_payment.schema.json +1 -1
- package/dist/schemas/onchain_operations_permission.schema.json +2 -2
- package/dist/schemas/onchain_operations_progress.schema.json +1 -1
- package/dist/schemas/onchain_operations_proof.schema.json +1 -1
- package/dist/schemas/onchain_operations_repository.schema.json +3 -3
- package/dist/schemas/onchain_operations_reward.schema.json +6 -6
- package/dist/schemas/onchain_operations_service.schema.json +77 -17
- package/dist/schemas/onchain_operations_treasury.schema.json +4 -4
- package/dist/schemas/onchain_table_data.output.json +1 -1
- package/dist/schemas/onchain_table_data.schema.json +10 -9
- package/dist/schemas/project_operation.output.json +1026 -7
- package/dist/schemas/project_operation.schema.json +46 -4
- package/dist/schemas/query_toolkit.output.json +15 -15
- package/dist/schemas/query_toolkit.schema.json +3 -3
- package/dist/schemas/schema_query.output.json +64 -1
- package/dist/schemas/schema_query.schema.json +3 -2
- package/dist/schemas/wowok_buildin_info.output.json +81 -8
- package/dist/schemas/wowok_buildin_info.schema.json +46 -2
- package/dist/tools/handlers/onchain.js +571 -6
- package/dist/tools/handlers/project.js +25 -2
- package/dist/tools/handlers/schema-query.js +24 -1
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.js +177 -6
- package/package.json +2 -2
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
function sanitizeMermaidLabel(text) {
|
|
2
|
+
if (/[<>"\[\]{}()|&]/.test(text)) {
|
|
3
|
+
return `"${text.replace(/"/g, "'")}"`;
|
|
4
|
+
}
|
|
5
|
+
return text;
|
|
6
|
+
}
|
|
7
|
+
const MACHINE_NODE_COLORS = {
|
|
8
|
+
entry: { fill: "#10b981", stroke: "#065f46" },
|
|
9
|
+
terminal: { fill: "#ef4444", stroke: "#991b1b" },
|
|
10
|
+
revision: { fill: "#f59e0b", stroke: "#92400e" },
|
|
11
|
+
normal: { fill: "#3b82f6", stroke: "#1e40af" },
|
|
12
|
+
};
|
|
13
|
+
function classifyMachineNode(nodeName, edges, entryNodes) {
|
|
14
|
+
const outEdges = edges.filter(e => e.from === nodeName);
|
|
15
|
+
if (outEdges.length === 0)
|
|
16
|
+
return "terminal";
|
|
17
|
+
const hasOutgoingToNode = outEdges.some(e => e.to !== "");
|
|
18
|
+
if (!hasOutgoingToNode)
|
|
19
|
+
return "terminal";
|
|
20
|
+
const isRevisionTarget = edges.some(e => e.to === nodeName && e.from === nodeName);
|
|
21
|
+
if (isRevisionTarget)
|
|
22
|
+
return "revision";
|
|
23
|
+
if (entryNodes.has(nodeName))
|
|
24
|
+
return "entry";
|
|
25
|
+
return "normal";
|
|
26
|
+
}
|
|
27
|
+
export function sanitizeMermaidNodeId(name) {
|
|
28
|
+
if (/[^a-zA-Z0-9_]/.test(name)) {
|
|
29
|
+
return `"${name}"`;
|
|
30
|
+
}
|
|
31
|
+
return name;
|
|
32
|
+
}
|
|
33
|
+
export function renderMachineMermaid(nodes, edges, options) {
|
|
34
|
+
const colored = options?.colored !== false;
|
|
35
|
+
const lines = ["stateDiagram-v2"];
|
|
36
|
+
if (colored) {
|
|
37
|
+
lines.push(" %% Color Legend (BehaviorTree.CPP-inspired workflow roles):");
|
|
38
|
+
lines.push(" %% green #10b981 - entry (start state, targeted by [*])");
|
|
39
|
+
lines.push(" %% red #ef4444 - terminal (end state, no outgoing edges)");
|
|
40
|
+
lines.push(" %% amber #f59e0b - revision (self-loop / revision cycle)");
|
|
41
|
+
lines.push(" %% blue #3b82f6 - normal (regular intermediate state)");
|
|
42
|
+
lines.push(" %% Priority: terminal > revision > entry > normal");
|
|
43
|
+
}
|
|
44
|
+
const nodesWithIncoming = new Set(edges.filter(e => e.from !== "").map(e => e.to));
|
|
45
|
+
const entryNodes = new Set();
|
|
46
|
+
for (const node of nodes) {
|
|
47
|
+
if (!nodesWithIncoming.has(node.name)) {
|
|
48
|
+
entryNodes.add(node.name);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const entryNodesList = nodes.filter(n => entryNodes.has(n.name));
|
|
52
|
+
for (const entry of entryNodesList) {
|
|
53
|
+
const entryEdges = edges.filter(e => e.from === "" && e.to === entry.name);
|
|
54
|
+
if (entryEdges.length > 0) {
|
|
55
|
+
for (const edge of entryEdges) {
|
|
56
|
+
for (const fwd of edge.forwards) {
|
|
57
|
+
const label = buildForwardLabel(fwd, edge.threshold);
|
|
58
|
+
lines.push(` [*] --> ${sanitizeMermaidNodeId(entry.name)}: ${sanitizeMermaidLabel(label)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
lines.push(` [*] --> ${sanitizeMermaidNodeId(entry.name)}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const edge of edges) {
|
|
67
|
+
if (edge.from === "")
|
|
68
|
+
continue;
|
|
69
|
+
const fromId = sanitizeMermaidNodeId(edge.from);
|
|
70
|
+
const toId = edge.to === "" ? "[*]" : sanitizeMermaidNodeId(edge.to);
|
|
71
|
+
for (const fwd of edge.forwards) {
|
|
72
|
+
const label = buildForwardLabel(fwd, edge.threshold);
|
|
73
|
+
lines.push(` ${fromId} --> ${toId}: ${sanitizeMermaidLabel(label)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const node of nodes) {
|
|
77
|
+
if (node.description) {
|
|
78
|
+
lines.push(` ${sanitizeMermaidNodeId(node.name)}: ${sanitizeMermaidLabel(node.description)}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (colored) {
|
|
82
|
+
lines.push("");
|
|
83
|
+
lines.push(" %% Node colors (BehaviorTree.CPP-inspired workflow roles)");
|
|
84
|
+
for (const node of nodes) {
|
|
85
|
+
const category = classifyMachineNode(node.name, edges, entryNodes);
|
|
86
|
+
const color = MACHINE_NODE_COLORS[category];
|
|
87
|
+
lines.push(` style ${sanitizeMermaidNodeId(node.name)} fill:${color.fill},stroke:${color.stroke},color:#fff`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return lines.join("\n");
|
|
91
|
+
}
|
|
92
|
+
function buildForwardLabel(fwd, threshold) {
|
|
93
|
+
let label = fwd.name;
|
|
94
|
+
const annotations = [];
|
|
95
|
+
if (threshold > 1) {
|
|
96
|
+
annotations.push(`threshold=${threshold}`);
|
|
97
|
+
}
|
|
98
|
+
if (fwd.guard) {
|
|
99
|
+
const shortGuard = fwd.guard.length > 10
|
|
100
|
+
? `${fwd.guard.slice(0, 6)}…${fwd.guard.slice(-4)}`
|
|
101
|
+
: fwd.guard;
|
|
102
|
+
annotations.push(`guard=${shortGuard}`);
|
|
103
|
+
}
|
|
104
|
+
if (annotations.length > 0) {
|
|
105
|
+
label += ` (${annotations.join(", ")})`;
|
|
106
|
+
}
|
|
107
|
+
return label;
|
|
108
|
+
}
|
|
109
|
+
export function renderMachineTreeIndented(input) {
|
|
110
|
+
const { nodes, edges, machineName, machineAddress, published, paused } = input;
|
|
111
|
+
const lines = [];
|
|
112
|
+
lines.push("# Machine Workflow Definition");
|
|
113
|
+
lines.push("");
|
|
114
|
+
if (machineName) {
|
|
115
|
+
lines.push(`**Machine:** ${machineName}`);
|
|
116
|
+
}
|
|
117
|
+
if (machineAddress) {
|
|
118
|
+
lines.push(`**Address:** ${machineAddress}`);
|
|
119
|
+
}
|
|
120
|
+
lines.push(`**Total Nodes:** ${nodes.length}`);
|
|
121
|
+
lines.push(`**Total Edges:** ${edges.length}`);
|
|
122
|
+
if (published !== null && published !== undefined) {
|
|
123
|
+
lines.push(`**Published:** ${published}`);
|
|
124
|
+
}
|
|
125
|
+
if (paused !== null && paused !== undefined) {
|
|
126
|
+
lines.push(`**Paused:** ${paused}`);
|
|
127
|
+
}
|
|
128
|
+
lines.push("");
|
|
129
|
+
lines.push("## Topology (directed graph outline)");
|
|
130
|
+
lines.push("");
|
|
131
|
+
const entryEdges = edges.filter(e => e.from === "");
|
|
132
|
+
const regularEdges = edges.filter(e => e.from !== "");
|
|
133
|
+
for (const edge of entryEdges) {
|
|
134
|
+
const header = `- [*] --> ${edge.to} (threshold=${edge.threshold})`;
|
|
135
|
+
lines.push(header);
|
|
136
|
+
for (const fwd of edge.forwards) {
|
|
137
|
+
lines.push(` - forward: ${formatForwardInline(fwd)}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
for (const edge of regularEdges) {
|
|
141
|
+
const isSelfLoop = edge.from === edge.to;
|
|
142
|
+
const annotation = isSelfLoop ? " # self-loop (revision cycle)" : "";
|
|
143
|
+
const toLabel = edge.to === "" ? "[*]" : edge.to;
|
|
144
|
+
const header = `- ${edge.from} --> ${toLabel} (threshold=${edge.threshold})${annotation}`;
|
|
145
|
+
lines.push(header);
|
|
146
|
+
for (const fwd of edge.forwards) {
|
|
147
|
+
lines.push(` - forward: ${formatForwardInline(fwd)}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const nodesWithDesc = nodes.filter(n => n.description);
|
|
151
|
+
if (nodesWithDesc.length > 0) {
|
|
152
|
+
lines.push("");
|
|
153
|
+
lines.push("## Node Descriptions");
|
|
154
|
+
lines.push("");
|
|
155
|
+
for (const node of nodes) {
|
|
156
|
+
if (node.description) {
|
|
157
|
+
lines.push(`- ${node.name}: ${node.description}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const incomingMap = new Map();
|
|
162
|
+
for (const edge of edges) {
|
|
163
|
+
if (!incomingMap.has(edge.to))
|
|
164
|
+
incomingMap.set(edge.to, []);
|
|
165
|
+
incomingMap.get(edge.to).push({
|
|
166
|
+
from: edge.from === "" ? "[*]" : edge.from,
|
|
167
|
+
forwards: edge.forwards.map(f => f.name),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (incomingMap.size > 0) {
|
|
171
|
+
lines.push("");
|
|
172
|
+
lines.push("## Backrefs (incoming edges per node)");
|
|
173
|
+
lines.push("");
|
|
174
|
+
for (const node of nodes) {
|
|
175
|
+
const incoming = incomingMap.get(node.name);
|
|
176
|
+
if (!incoming || incoming.length === 0)
|
|
177
|
+
continue;
|
|
178
|
+
const parts = incoming.map(inc => {
|
|
179
|
+
const fwdStr = inc.forwards.length > 0 ? ` (${inc.forwards.join(", ")})` : "";
|
|
180
|
+
const selfLoop = inc.from === node.name ? " # self-loop" : "";
|
|
181
|
+
return `<- ${inc.from}${fwdStr}${selfLoop}`;
|
|
182
|
+
});
|
|
183
|
+
lines.push(`- ${node.name}: ${parts.join(", ")}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
lines.push("");
|
|
187
|
+
lines.push("## JSON Definition");
|
|
188
|
+
lines.push("");
|
|
189
|
+
lines.push("```json");
|
|
190
|
+
lines.push(JSON.stringify({ nodes, edges }, null, 2));
|
|
191
|
+
lines.push("```");
|
|
192
|
+
return lines.join("\n");
|
|
193
|
+
}
|
|
194
|
+
function formatForwardInline(fwd) {
|
|
195
|
+
const parts = [`weight=${fwd.weight}`];
|
|
196
|
+
if (fwd.named_operator !== null && fwd.named_operator !== undefined) {
|
|
197
|
+
parts.push(`namedOperator='${fwd.named_operator}'`);
|
|
198
|
+
}
|
|
199
|
+
if (fwd.permission_index !== null && fwd.permission_index !== undefined) {
|
|
200
|
+
parts.push(`permissionIndex=${fwd.permission_index}`);
|
|
201
|
+
}
|
|
202
|
+
if (fwd.guard) {
|
|
203
|
+
parts.push(`guard=${fwd.guard}`);
|
|
204
|
+
}
|
|
205
|
+
if (fwd.retained_submission) {
|
|
206
|
+
const rsStr = Array.isArray(fwd.retained_submission)
|
|
207
|
+
? `[${fwd.retained_submission.join(",")}]`
|
|
208
|
+
: String(fwd.retained_submission);
|
|
209
|
+
parts.push(`retained_submission=${rsStr}`);
|
|
210
|
+
}
|
|
211
|
+
return `${fwd.name} (${parts.join(", ")})`;
|
|
212
|
+
}
|
|
213
|
+
export function parseMachineTreeIndented(text) {
|
|
214
|
+
const errors = [];
|
|
215
|
+
const jsonMatch = text.match(/```json\s*\n([\s\S]*?)```/);
|
|
216
|
+
if (jsonMatch) {
|
|
217
|
+
try {
|
|
218
|
+
const data = JSON.parse(jsonMatch[1].trim());
|
|
219
|
+
if (data && typeof data === "object" && Array.isArray(data.nodes) && Array.isArray(data.edges)) {
|
|
220
|
+
return { success: true, data, errors: [] };
|
|
221
|
+
}
|
|
222
|
+
if (Array.isArray(data)) {
|
|
223
|
+
const { nodes, edges } = convertNodeArrayToGraph(data);
|
|
224
|
+
return { success: true, data: { nodes, edges }, errors: [] };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (e) {
|
|
228
|
+
errors.push(`JSON code block parse error: ${e.message}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const topologyMatch = text.match(/##\s*Topology[^\n]*\n([\s\S]*?)(?=\n##\s|$)/i);
|
|
232
|
+
if (!topologyMatch) {
|
|
233
|
+
return {
|
|
234
|
+
success: false,
|
|
235
|
+
errors: [
|
|
236
|
+
"No '## Topology' section found. " +
|
|
237
|
+
"Use the format produced by renderMachineTreeIndented() or a JSON code block.",
|
|
238
|
+
],
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const topologySection = topologyMatch[1];
|
|
242
|
+
const nodeNames = new Set();
|
|
243
|
+
const edges = [];
|
|
244
|
+
const edgeLines = topologySection.split("\n").filter(l => l.trim().startsWith("- "));
|
|
245
|
+
const pendingForwards = [];
|
|
246
|
+
let currentEdge = null;
|
|
247
|
+
for (const line of edgeLines) {
|
|
248
|
+
const trimmed = line.trim();
|
|
249
|
+
const isIndented = line.startsWith(" -");
|
|
250
|
+
if (isIndented) {
|
|
251
|
+
const fwdMatch = trimmed.match(/^-\s*forward:\s*(.+)$/);
|
|
252
|
+
if (fwdMatch && currentEdge) {
|
|
253
|
+
const fwd = parseForwardInline(fwdMatch[1]);
|
|
254
|
+
if (fwd) {
|
|
255
|
+
currentEdge.forwards.push(fwd);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const edgeMatch = trimmed.match(/^-\s+(.+?)\s+-->\s+(\S+)(?:\s+\(threshold=(\d+)\))?(.*)$/);
|
|
261
|
+
if (edgeMatch) {
|
|
262
|
+
if (currentEdge) {
|
|
263
|
+
edges.push(currentEdge);
|
|
264
|
+
}
|
|
265
|
+
const fromRaw = edgeMatch[1].trim();
|
|
266
|
+
const to = edgeMatch[2].trim();
|
|
267
|
+
const threshold = edgeMatch[3] ? parseInt(edgeMatch[3], 10) : 1;
|
|
268
|
+
const from = fromRaw === "[*]" ? "" : fromRaw;
|
|
269
|
+
currentEdge = {
|
|
270
|
+
from,
|
|
271
|
+
to,
|
|
272
|
+
threshold,
|
|
273
|
+
forwards: [],
|
|
274
|
+
};
|
|
275
|
+
nodeNames.add(to);
|
|
276
|
+
if (from)
|
|
277
|
+
nodeNames.add(from);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (currentEdge) {
|
|
281
|
+
edges.push(currentEdge);
|
|
282
|
+
}
|
|
283
|
+
if (edges.length === 0) {
|
|
284
|
+
return {
|
|
285
|
+
success: false,
|
|
286
|
+
errors: ["No edges found in '## Topology' section. Expected lines like '- A --> B (threshold=1)'."],
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const descMatch = text.match(/##\s*Node Descriptions[^\n]*\n([\s\S]*?)(?=\n##\s|$)/i);
|
|
290
|
+
const nodeDescMap = new Map();
|
|
291
|
+
if (descMatch) {
|
|
292
|
+
const descLines = descMatch[1].split("\n");
|
|
293
|
+
for (const line of descLines) {
|
|
294
|
+
const m = line.match(/^-\s+(\S+):\s*(.+)$/);
|
|
295
|
+
if (m) {
|
|
296
|
+
nodeDescMap.set(m[1], m[2]);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
const nodes = [];
|
|
301
|
+
for (const name of nodeNames) {
|
|
302
|
+
nodes.push({
|
|
303
|
+
name,
|
|
304
|
+
description: nodeDescMap.get(name) ?? null,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
return { success: true, data: { nodes, edges }, errors };
|
|
308
|
+
}
|
|
309
|
+
function parseForwardInline(text) {
|
|
310
|
+
const match = text.match(/^(\S+)\s*\(([^)]*)\)$/);
|
|
311
|
+
if (!match) {
|
|
312
|
+
return {
|
|
313
|
+
name: text.trim(),
|
|
314
|
+
weight: 1,
|
|
315
|
+
named_operator: null,
|
|
316
|
+
permission_index: null,
|
|
317
|
+
guard: null,
|
|
318
|
+
retained_submission: null,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
const name = match[1];
|
|
322
|
+
const annotationStr = match[2];
|
|
323
|
+
const fwd = {
|
|
324
|
+
name,
|
|
325
|
+
weight: 1,
|
|
326
|
+
named_operator: null,
|
|
327
|
+
permission_index: null,
|
|
328
|
+
guard: null,
|
|
329
|
+
retained_submission: null,
|
|
330
|
+
};
|
|
331
|
+
for (const part of annotationStr.split(",").map(s => s.trim())) {
|
|
332
|
+
if (!part)
|
|
333
|
+
continue;
|
|
334
|
+
const [key, ...valParts] = part.split("=");
|
|
335
|
+
const val = valParts.join("=").trim();
|
|
336
|
+
switch (key.trim()) {
|
|
337
|
+
case "weight":
|
|
338
|
+
fwd.weight = parseInt(val, 10) || 1;
|
|
339
|
+
break;
|
|
340
|
+
case "namedOperator":
|
|
341
|
+
fwd.named_operator = val.replace(/^'|'$/g, "");
|
|
342
|
+
break;
|
|
343
|
+
case "permissionIndex":
|
|
344
|
+
fwd.permission_index = parseInt(val, 10) || null;
|
|
345
|
+
break;
|
|
346
|
+
case "guard":
|
|
347
|
+
fwd.guard = val || null;
|
|
348
|
+
break;
|
|
349
|
+
case "retained_submission":
|
|
350
|
+
if (val.startsWith("[") && val.endsWith("]")) {
|
|
351
|
+
fwd.retained_submission = val
|
|
352
|
+
.slice(1, -1)
|
|
353
|
+
.split(",")
|
|
354
|
+
.map(s => parseInt(s.trim(), 10))
|
|
355
|
+
.filter(n => !isNaN(n));
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
fwd.retained_submission = val;
|
|
359
|
+
}
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return fwd;
|
|
364
|
+
}
|
|
365
|
+
function convertNodeArrayToGraph(data) {
|
|
366
|
+
const nodes = [];
|
|
367
|
+
const edges = [];
|
|
368
|
+
for (const item of data) {
|
|
369
|
+
if (!item || typeof item !== "object")
|
|
370
|
+
continue;
|
|
371
|
+
const node = item;
|
|
372
|
+
const name = typeof node.name === "string" ? node.name : "";
|
|
373
|
+
if (!name)
|
|
374
|
+
continue;
|
|
375
|
+
nodes.push({
|
|
376
|
+
name,
|
|
377
|
+
description: typeof node.description === "string" ? node.description : null,
|
|
378
|
+
});
|
|
379
|
+
const pairs = Array.isArray(node.pairs) ? node.pairs : [];
|
|
380
|
+
for (const pairRaw of pairs) {
|
|
381
|
+
if (!pairRaw || typeof pairRaw !== "object")
|
|
382
|
+
continue;
|
|
383
|
+
const pair = pairRaw;
|
|
384
|
+
const prevNode = typeof pair.prev_node === "string" ? pair.prev_node : "";
|
|
385
|
+
const threshold = typeof pair.threshold === "number" ? pair.threshold : 1;
|
|
386
|
+
const forwardsRaw = Array.isArray(pair.forwards) ? pair.forwards : [];
|
|
387
|
+
const forwards = forwardsRaw.map((f) => {
|
|
388
|
+
const fwd = f;
|
|
389
|
+
return {
|
|
390
|
+
name: typeof fwd.name === "string" ? fwd.name : "",
|
|
391
|
+
weight: typeof fwd.weight === "number" ? fwd.weight : 1,
|
|
392
|
+
named_operator: typeof fwd.namedOperator === "string" ? fwd.namedOperator : null,
|
|
393
|
+
permission_index: typeof fwd.permissionIndex === "number" ? fwd.permissionIndex : null,
|
|
394
|
+
guard: typeof fwd.guard === "string"
|
|
395
|
+
? fwd.guard
|
|
396
|
+
: (fwd.guard && typeof fwd.guard.guard === "string")
|
|
397
|
+
? fwd.guard.guard
|
|
398
|
+
: null,
|
|
399
|
+
retained_submission: null,
|
|
400
|
+
};
|
|
401
|
+
});
|
|
402
|
+
edges.push({
|
|
403
|
+
from: prevNode,
|
|
404
|
+
to: name,
|
|
405
|
+
threshold,
|
|
406
|
+
forwards,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return { nodes, edges };
|
|
411
|
+
}
|
|
412
|
+
export function renderMachineAscii(input) {
|
|
413
|
+
const { nodes, edges, machineName, machineAddress, published, paused } = input;
|
|
414
|
+
const lines = [];
|
|
415
|
+
if (machineName) {
|
|
416
|
+
lines.push(`Machine: ${machineName}`);
|
|
417
|
+
}
|
|
418
|
+
if (machineAddress) {
|
|
419
|
+
lines.push(`Address: ${machineAddress}`);
|
|
420
|
+
}
|
|
421
|
+
if (published !== null && published !== undefined) {
|
|
422
|
+
lines.push(`Published: ${published}`);
|
|
423
|
+
}
|
|
424
|
+
if (paused !== null && paused !== undefined) {
|
|
425
|
+
lines.push(`Paused: ${paused}`);
|
|
426
|
+
}
|
|
427
|
+
if (machineName || machineAddress) {
|
|
428
|
+
lines.push("");
|
|
429
|
+
}
|
|
430
|
+
const outgoingMap = new Map();
|
|
431
|
+
for (const edge of edges) {
|
|
432
|
+
if (edge.from === "")
|
|
433
|
+
continue;
|
|
434
|
+
if (!outgoingMap.has(edge.from))
|
|
435
|
+
outgoingMap.set(edge.from, []);
|
|
436
|
+
outgoingMap.get(edge.from).push(edge);
|
|
437
|
+
}
|
|
438
|
+
const terminalNodes = new Set();
|
|
439
|
+
for (const node of nodes) {
|
|
440
|
+
const outEdges = outgoingMap.get(node.name) ?? [];
|
|
441
|
+
const hasOutgoingToNode = outEdges.some(e => e.to !== "");
|
|
442
|
+
if (!hasOutgoingToNode) {
|
|
443
|
+
const isTargeted = edges.some(e => e.to === node.name);
|
|
444
|
+
if (isTargeted) {
|
|
445
|
+
terminalNodes.add(node.name);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
const entryNodes = new Set();
|
|
450
|
+
for (const edge of edges) {
|
|
451
|
+
if (edge.from === "") {
|
|
452
|
+
entryNodes.add(edge.to);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const revisionNodes = new Set();
|
|
456
|
+
for (const edge of edges) {
|
|
457
|
+
if (edge.from === edge.to && edge.from !== "") {
|
|
458
|
+
revisionNodes.add(edge.from);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
for (const edge of edges) {
|
|
462
|
+
const fromLabel = edge.from === "" ? "[*]" : edge.from;
|
|
463
|
+
const toLabel = edge.to === "" ? "[*]" : edge.to;
|
|
464
|
+
const isCycle = edge.from === edge.to && edge.from !== "";
|
|
465
|
+
for (const fwd of edge.forwards) {
|
|
466
|
+
const annotations = [];
|
|
467
|
+
if (isCycle)
|
|
468
|
+
annotations.push("cycle");
|
|
469
|
+
if (fwd.guard)
|
|
470
|
+
annotations.push(`guard=${fwd.guard}`);
|
|
471
|
+
if (fwd.named_operator !== null && fwd.named_operator !== undefined && fwd.named_operator !== "") {
|
|
472
|
+
annotations.push(`op=${fwd.named_operator}`);
|
|
473
|
+
}
|
|
474
|
+
if (fwd.permission_index !== null && fwd.permission_index !== undefined) {
|
|
475
|
+
annotations.push(`perm=${fwd.permission_index}`);
|
|
476
|
+
}
|
|
477
|
+
if (edge.threshold > 1)
|
|
478
|
+
annotations.push(`threshold=${edge.threshold}`);
|
|
479
|
+
const annotationStr = annotations.length > 0 ? ` [${annotations.join(", ")}]` : "";
|
|
480
|
+
const cycleStr = isCycle ? " ⟲" : "";
|
|
481
|
+
lines.push(`${fromLabel} ─${fwd.name}→ ${toLabel}${cycleStr}${annotationStr}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
const annotatedNodes = [];
|
|
485
|
+
for (const node of nodes) {
|
|
486
|
+
const annotations = [];
|
|
487
|
+
if (entryNodes.has(node.name))
|
|
488
|
+
annotations.push("entry");
|
|
489
|
+
if (terminalNodes.has(node.name))
|
|
490
|
+
annotations.push("terminal");
|
|
491
|
+
if (revisionNodes.has(node.name))
|
|
492
|
+
annotations.push("revision");
|
|
493
|
+
if (annotations.length > 0) {
|
|
494
|
+
annotatedNodes.push(` ${node.name}: ${annotations.join(", ")}`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
if (annotatedNodes.length > 0) {
|
|
498
|
+
lines.push("");
|
|
499
|
+
lines.push("Node roles:");
|
|
500
|
+
lines.push(...annotatedNodes);
|
|
501
|
+
}
|
|
502
|
+
return lines.join("\n");
|
|
503
|
+
}
|
|
504
|
+
export function renderMachineMarkmap(input) {
|
|
505
|
+
const { nodes, edges, machineName, machineAddress, published, paused } = input;
|
|
506
|
+
const lines = [];
|
|
507
|
+
const titleParts = ["Machine"];
|
|
508
|
+
if (machineName)
|
|
509
|
+
titleParts.push(machineName);
|
|
510
|
+
lines.push(`# ${titleParts.join(": ")}`);
|
|
511
|
+
if (machineAddress || published !== null || paused !== null) {
|
|
512
|
+
lines.push("");
|
|
513
|
+
if (machineAddress)
|
|
514
|
+
lines.push(`**Address:** ${machineAddress}`);
|
|
515
|
+
if (published !== null && published !== undefined)
|
|
516
|
+
lines.push(`**Published:** ${published}`);
|
|
517
|
+
if (paused !== null && paused !== undefined)
|
|
518
|
+
lines.push(`**Paused:** ${paused}`);
|
|
519
|
+
}
|
|
520
|
+
lines.push("");
|
|
521
|
+
const outgoingMap = new Map();
|
|
522
|
+
for (const edge of edges) {
|
|
523
|
+
if (edge.from === "")
|
|
524
|
+
continue;
|
|
525
|
+
if (!outgoingMap.has(edge.from))
|
|
526
|
+
outgoingMap.set(edge.from, []);
|
|
527
|
+
outgoingMap.get(edge.from).push(edge);
|
|
528
|
+
}
|
|
529
|
+
const terminalNodes = new Set();
|
|
530
|
+
for (const node of nodes) {
|
|
531
|
+
const outEdges = outgoingMap.get(node.name) ?? [];
|
|
532
|
+
const hasOutgoingToNode = outEdges.some(e => e.to !== "");
|
|
533
|
+
if (!hasOutgoingToNode) {
|
|
534
|
+
const isTargeted = edges.some(e => e.to === node.name);
|
|
535
|
+
if (isTargeted)
|
|
536
|
+
terminalNodes.add(node.name);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
for (const edge of edges) {
|
|
540
|
+
const fromLabel = edge.from === "" ? "[*]" : edge.from;
|
|
541
|
+
const toLabel = edge.to === "" ? "[*]" : edge.to;
|
|
542
|
+
const isCycle = edge.from === edge.to && edge.from !== "";
|
|
543
|
+
const cycleStr = isCycle ? " (cycle)" : "";
|
|
544
|
+
const terminalStr = terminalNodes.has(edge.to) ? " (terminal)" : "";
|
|
545
|
+
lines.push(`## ${fromLabel} → ${toLabel}${cycleStr}${terminalStr}`);
|
|
546
|
+
for (const fwd of edge.forwards) {
|
|
547
|
+
const parts = [`weight=${fwd.weight}`];
|
|
548
|
+
if (fwd.named_operator !== null && fwd.named_operator !== undefined) {
|
|
549
|
+
parts.push(`namedOperator='${fwd.named_operator}'`);
|
|
550
|
+
}
|
|
551
|
+
if (fwd.permission_index !== null && fwd.permission_index !== undefined) {
|
|
552
|
+
parts.push(`permissionIndex=${fwd.permission_index}`);
|
|
553
|
+
}
|
|
554
|
+
if (fwd.guard) {
|
|
555
|
+
parts.push(`guard=${fwd.guard}`);
|
|
556
|
+
}
|
|
557
|
+
if (edge.threshold > 1) {
|
|
558
|
+
parts.push(`threshold=${edge.threshold}`);
|
|
559
|
+
}
|
|
560
|
+
lines.push(`### forward: ${fwd.name} (${parts.join(", ")})`);
|
|
561
|
+
}
|
|
562
|
+
lines.push("");
|
|
563
|
+
}
|
|
564
|
+
return lines.join("\n");
|
|
565
|
+
}
|
|
@@ -1016,7 +1016,7 @@ export const MACHINE_TEMPLATES = [
|
|
|
1016
1016
|
type: "string",
|
|
1017
1017
|
required: true,
|
|
1018
1018
|
default: "wonder",
|
|
1019
|
-
description: "Node name for positive feedback (
|
|
1019
|
+
description: "Node name for positive feedback (praise) terminal",
|
|
1020
1020
|
constraint: "Must match /^(wonder|praise|thumbs_up)$/i OR be a custom name; must NOT match dispute anti-pattern regex",
|
|
1021
1021
|
},
|
|
1022
1022
|
{
|
|
@@ -1024,7 +1024,7 @@ export const MACHINE_TEMPLATES = [
|
|
|
1024
1024
|
type: "string",
|
|
1025
1025
|
required: true,
|
|
1026
1026
|
default: "no_wonder",
|
|
1027
|
-
description: "Node name for negative feedback (
|
|
1027
|
+
description: "Node name for negative feedback (complaint) terminal",
|
|
1028
1028
|
constraint: "Must match /^(no_wonder|complain|thumbs_down)$/i OR be a custom name; must NOT match dispute anti-pattern regex",
|
|
1029
1029
|
},
|
|
1030
1030
|
{
|
|
@@ -1047,8 +1047,8 @@ export const MACHINE_TEMPLATES = [
|
|
|
1047
1047
|
{ name: "Shipped", description: "Merchant dispatched goods/service" },
|
|
1048
1048
|
{ name: "Received", description: "Customer confirmed receipt" },
|
|
1049
1049
|
{ name: "Completed", description: "Terminal business state — triggers merchant Allocation. Funds settled." },
|
|
1050
|
-
{ name: "${wonder_node_name}", description: "Positive feedback terminal (
|
|
1051
|
-
{ name: "${no_wonder_node_name}", description: "Negative feedback terminal (
|
|
1050
|
+
{ name: "${wonder_node_name}", description: "Positive feedback terminal (praise). OrderHolder-only. Reward-eligible." },
|
|
1051
|
+
{ name: "${no_wonder_node_name}", description: "Negative feedback terminal (complaint). OrderHolder-only. Optional Repository write for analytics." },
|
|
1052
1052
|
],
|
|
1053
1053
|
example_pairs: [
|
|
1054
1054
|
{
|
|
@@ -137,7 +137,7 @@ function runStaticChecklist(input) {
|
|
|
137
137
|
const cr11 = items.find(i => i.id === "C-R-11");
|
|
138
138
|
if (data.guard_expected_progress_state && input.user_intent_phrases && input.user_intent_phrases.length > 0) {
|
|
139
139
|
const intentLower = input.user_intent_phrases.join(" ").toLowerCase();
|
|
140
|
-
const praiseKeywords = ["
|
|
140
|
+
const praiseKeywords = ["praise", "wonder", "thumbs_up", "positive review", "satisfaction reward", "praise reward"];
|
|
141
141
|
const isPraiseIntent = praiseKeywords.some(kw => intentLower.includes(kw.toLowerCase()));
|
|
142
142
|
if (isPraiseIntent && input.machine_has_wonder_node !== false && data.guard_expected_progress_state !== "wonder") {
|
|
143
143
|
cr11.passed = false;
|
|
@@ -224,7 +224,7 @@ export const REWARD_CONFIRMATION_QUESTIONS = [
|
|
|
224
224
|
{
|
|
225
225
|
id: "QRW7",
|
|
226
226
|
question: "⚠️ For guard_add with progress.current check: does the Guard's expected progress node match the business intent? " +
|
|
227
|
-
"If intent is '
|
|
227
|
+
"If intent is 'praise/positive review', Guard must check 'wonder' (NOT 'completed'). " +
|
|
228
228
|
"Premature claim at wrong node drains reward pool — Guard is immutable after creation.",
|
|
229
229
|
requires_explicit_yes: true,
|
|
230
230
|
related_risk_ids: ["R-R-6-01", "R-R-6-02"],
|
|
@@ -184,7 +184,7 @@ export function validateConstraints(puzzle) {
|
|
|
184
184
|
constraint_id: "F-R-09",
|
|
185
185
|
severity: "HIGH",
|
|
186
186
|
message: "Guard's expected progress.current value does not match business intent — " +
|
|
187
|
-
"praise reward (
|
|
187
|
+
"praise reward (praise/wonder) must check 'wonder' node, not 'completed'. " +
|
|
188
188
|
"Guard is immutable after creation; mismatch requires full rebuild.",
|
|
189
189
|
});
|
|
190
190
|
}
|
|
@@ -472,7 +472,7 @@ const R_R_5_03 = {
|
|
|
472
472
|
const R_R_6_01 = {
|
|
473
473
|
id: "R-R-6-01",
|
|
474
474
|
category: "business_semantic",
|
|
475
|
-
trigger: "intent mentions praise
|
|
475
|
+
trigger: "intent mentions praise/positive-review but Guard checks non-wonder progress node",
|
|
476
476
|
check: (ctx) => {
|
|
477
477
|
if (!ctx.user_intent_phrases || ctx.user_intent_phrases.length === 0)
|
|
478
478
|
return null;
|
|
@@ -480,8 +480,8 @@ const R_R_6_01 = {
|
|
|
480
480
|
return null;
|
|
481
481
|
const intentLower = ctx.user_intent_phrases.join(" ").toLowerCase();
|
|
482
482
|
const praiseKeywords = [
|
|
483
|
-
"
|
|
484
|
-
"positive review", "satisfaction reward", "
|
|
483
|
+
"praise", "wonder", "thumbs_up",
|
|
484
|
+
"positive review", "satisfaction reward", "praise reward",
|
|
485
485
|
];
|
|
486
486
|
const isPraiseIntent = praiseKeywords.some(kw => intentLower.includes(kw.toLowerCase()));
|
|
487
487
|
if (!isPraiseIntent)
|
|
@@ -495,10 +495,10 @@ const R_R_6_01 = {
|
|
|
495
495
|
level: "HIGH",
|
|
496
496
|
category: "business_semantic",
|
|
497
497
|
title: `Guard checks progress=="${expected}" but intent indicates praise reward (wonder node)`,
|
|
498
|
-
description: `User intent mentions praise
|
|
498
|
+
description: `User intent mentions praise/positive-review/wonder, which maps to the 'wonder' progress node ` +
|
|
499
499
|
`(customer gives positive review AFTER service completion). But the Guard's table bakes ` +
|
|
500
500
|
`expected state="${expected}", allowing reward claim at the wrong lifecycle stage. ` +
|
|
501
|
-
`This violates the business semantics: "
|
|
501
|
+
`This violates the business semantics: "praise reward" should only be claimable AFTER the ` +
|
|
502
502
|
`customer advances to the 'wonder' node, not before. ` +
|
|
503
503
|
`Guard is immutable — fixing this requires creating a new Guard + Reward pair.`,
|
|
504
504
|
affected_stakeholders: ["owner", "service_provider", "claimer"],
|
|
@@ -526,8 +526,8 @@ const R_R_6_02 = {
|
|
|
526
526
|
return null;
|
|
527
527
|
const intentLower = ctx.user_intent_phrases.join(" ").toLowerCase();
|
|
528
528
|
const praiseKeywords = [
|
|
529
|
-
"
|
|
530
|
-
"positive review", "satisfaction reward", "
|
|
529
|
+
"praise", "wonder", "thumbs_up",
|
|
530
|
+
"positive review", "satisfaction reward", "praise reward",
|
|
531
531
|
];
|
|
532
532
|
const isPraiseIntent = praiseKeywords.some(kw => intentLower.includes(kw.toLowerCase()));
|
|
533
533
|
if (isPraiseIntent)
|
|
@@ -538,12 +538,12 @@ const R_R_6_02 = {
|
|
|
538
538
|
category: "business_semantic",
|
|
539
539
|
title: "Guard checks wonder node but intent does not mention praise",
|
|
540
540
|
description: `Guard's expected progress state is "wonder" (positive-review node), but the user intent ` +
|
|
541
|
-
`does not mention any praise
|
|
541
|
+
`does not mention any praise/positive-review keyword. This may be a copy-paste from a wonder_praise ` +
|
|
542
542
|
`template without adjusting the Guard for the actual business semantics. If this is a ` +
|
|
543
543
|
`general feedback reward (wonder_feedback), this is fine. Otherwise, verify the node choice.`,
|
|
544
544
|
affected_stakeholders: ["owner"],
|
|
545
545
|
scenario: "Designer copies wonder_praise template but actually wants a completion-based reward",
|
|
546
|
-
mitigation: "Verify: if intent is praise reward, add '
|
|
546
|
+
mitigation: "Verify: if intent is praise reward, add 'praise/positive review' to description. " +
|
|
547
547
|
"If intent is completion-based reward, change Guard to check 'completed' node.",
|
|
548
548
|
evidence: `expected_state="wonder", intent="${intentLower}" (no praise keyword)`,
|
|
549
549
|
auto_fixable: false,
|