@wowok/agent-mcp 2.6.0 → 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 (186) hide show
  1. package/README.md +5 -3
  2. package/dist/customer/info-puzzle.d.ts +1 -1
  3. package/dist/customer/info-puzzle.js +4 -2
  4. package/dist/customer/risk-assessment.js +26 -4
  5. package/dist/customer/types.d.ts +2 -0
  6. package/dist/examples/guard-template-balance-check.json +38 -0
  7. package/dist/examples/guard-template-time-lock.json +39 -0
  8. package/dist/examples/machine-template-7node-rental.json +114 -0
  9. package/dist/examples/rental-ziroom-machine-create.json +137 -0
  10. package/dist/examples/rental-ziroom-permission-create.json +35 -0
  11. package/dist/examples/rental-ziroom-service-create.json +80 -0
  12. package/dist/examples/retail-myshop-service-create.json +88 -0
  13. package/dist/extensions/capability-manifest.d.ts +125 -0
  14. package/dist/extensions/capability-manifest.js +594 -0
  15. package/dist/extensions/constraint-registry.d.ts +24 -0
  16. package/dist/extensions/constraint-registry.js +196 -0
  17. package/dist/extensions/index.d.ts +12 -0
  18. package/dist/extensions/index.js +6 -0
  19. package/dist/extensions/metric-registry.d.ts +26 -0
  20. package/dist/extensions/metric-registry.js +257 -0
  21. package/dist/extensions/mode-evaluator.d.ts +15 -0
  22. package/dist/extensions/mode-evaluator.js +170 -0
  23. package/dist/extensions/modes.d.ts +2 -0
  24. package/dist/extensions/modes.js +493 -0
  25. package/dist/extensions/registry.d.ts +50 -0
  26. package/dist/extensions/registry.js +662 -0
  27. package/dist/extensions/types.d.ts +219 -0
  28. package/dist/extensions/types.js +1 -0
  29. package/dist/index.js +50 -0
  30. package/dist/knowledge/deployment-scanner.d.ts +3 -0
  31. package/dist/knowledge/deployment-scanner.js +64 -3
  32. package/dist/knowledge/fund-layer.d.ts +72 -0
  33. package/dist/knowledge/fund-layer.js +420 -0
  34. package/dist/knowledge/guard-render.d.ts +57 -0
  35. package/dist/knowledge/guard-render.js +700 -0
  36. package/dist/knowledge/guard-risk.d.ts +13 -0
  37. package/dist/knowledge/guard-risk.js +57 -0
  38. package/dist/knowledge/guard-submission-prompt.d.ts +31 -0
  39. package/dist/knowledge/guard-submission-prompt.js +171 -0
  40. package/dist/knowledge/guard-templates.js +278 -0
  41. package/dist/knowledge/machine-ledger.js +1 -1
  42. package/dist/knowledge/machine-render.d.ts +41 -0
  43. package/dist/knowledge/machine-render.js +565 -0
  44. package/dist/knowledge/machine-templates.js +24 -5
  45. package/dist/knowledge/reward-confirm.js +2 -2
  46. package/dist/knowledge/reward-puzzle.js +1 -1
  47. package/dist/knowledge/reward-risk.js +9 -9
  48. package/dist/knowledge/reward-templates.js +2 -2
  49. package/dist/knowledge/service-confirm.d.ts +15 -6
  50. package/dist/knowledge/service-confirm.js +119 -11
  51. package/dist/knowledge/service-context.js +1 -1
  52. package/dist/knowledge/service-ledger.js +1 -1
  53. package/dist/knowledge/service-risk.d.ts +1 -1
  54. package/dist/knowledge/service-risk.js +3 -3
  55. package/dist/knowledge/service-templates.js +2 -2
  56. package/dist/knowledge/service-translation.d.ts +1 -1
  57. package/dist/knowledge/service-translation.js +7 -7
  58. package/dist/knowledge/tool-constraints.js +6 -2
  59. package/dist/project/deployment-bridge.d.ts +1 -1
  60. package/dist/project/deployment-bridge.js +31 -7
  61. package/dist/project/deployment-doc.d.ts +3 -0
  62. package/dist/project/deployment-doc.js +76 -14
  63. package/dist/project/edit-planner.d.ts +123 -0
  64. package/dist/project/edit-planner.js +1371 -0
  65. package/dist/project/evaluation.d.ts +2 -0
  66. package/dist/project/evaluation.js +873 -88
  67. package/dist/project/graph-builder.d.ts +4 -1
  68. package/dist/project/graph-builder.js +132 -62
  69. package/dist/project/graph.d.ts +1 -0
  70. package/dist/project/handlers.d.ts +317 -6
  71. package/dist/project/handlers.js +1171 -19
  72. package/dist/project/stage-gate.d.ts +4 -0
  73. package/dist/project/stage-gate.js +64 -5
  74. package/dist/project/task-tracker.d.ts +26 -0
  75. package/dist/project/task-tracker.js +78 -0
  76. package/dist/safety/preview.js +16 -0
  77. package/dist/schema/call/allocation.d.ts +16 -16
  78. package/dist/schema/call/allocation.js +2 -2
  79. package/dist/schema/call/arbitration.js +19 -6
  80. package/dist/schema/call/base.d.ts +21 -13
  81. package/dist/schema/call/base.js +27 -6
  82. package/dist/schema/call/bridge.d.ts +5 -5
  83. package/dist/schema/call/bridge.js +3 -1
  84. package/dist/schema/call/contact.js +2 -2
  85. package/dist/schema/call/demand.d.ts +23 -31
  86. package/dist/schema/call/demand.js +2 -2
  87. package/dist/schema/call/guard.js +2 -2
  88. package/dist/schema/call/machine.d.ts +1976 -752
  89. package/dist/schema/call/machine.js +50 -4
  90. package/dist/schema/call/order.d.ts +149 -228
  91. package/dist/schema/call/order.js +4 -3
  92. package/dist/schema/call/payment.d.ts +183 -3
  93. package/dist/schema/call/payment.js +21 -3
  94. package/dist/schema/call/permission.js +2 -2
  95. package/dist/schema/call/personal.d.ts +241 -52
  96. package/dist/schema/call/progress.d.ts +53 -61
  97. package/dist/schema/call/progress.js +18 -4
  98. package/dist/schema/call/repository.d.ts +23 -31
  99. package/dist/schema/call/repository.js +2 -2
  100. package/dist/schema/call/reward.js +3 -3
  101. package/dist/schema/call/semantic.d.ts +1 -1
  102. package/dist/schema/call/semantic.js +37 -2
  103. package/dist/schema/call/service.d.ts +275 -127
  104. package/dist/schema/call/service.js +89 -13
  105. package/dist/schema/call/treasury.js +3 -3
  106. package/dist/schema/common/index.d.ts +13 -2
  107. package/dist/schema/common/index.js +80 -15
  108. package/dist/schema/local/index.d.ts +32 -35
  109. package/dist/schema/local/index.js +28 -6
  110. package/dist/schema/messenger/index.d.ts +274 -46
  111. package/dist/schema/operations.d.ts +1264 -544
  112. package/dist/schema/operations.js +65 -7
  113. package/dist/schema/project/index.d.ts +2846 -167
  114. package/dist/schema/project/index.js +570 -16
  115. package/dist/schema/query/index.d.ts +922 -349
  116. package/dist/schema/query/index.js +205 -42
  117. package/dist/schema/schema-query/index.d.ts +65 -3
  118. package/dist/schema/schema-query/index.js +38 -5
  119. package/dist/schema/utils/node-parser.js +20 -4
  120. package/dist/schema/utils/object-type-utils.d.ts +12 -0
  121. package/dist/schema/utils/object-type-utils.js +35 -0
  122. package/dist/schema/utils/permission-machine-check.d.ts +49 -0
  123. package/dist/schema/utils/permission-machine-check.js +121 -0
  124. package/dist/schema/utils/skills-recommendation.d.ts +2 -0
  125. package/dist/schema/utils/skills-recommendation.js +76 -0
  126. package/dist/schema-query/index.d.ts +20 -1
  127. package/dist/schema-query/index.js +306 -4
  128. package/dist/schemas/account_operation.output.json +7 -1
  129. package/dist/schemas/account_operation.schema.json +3 -3
  130. package/dist/schemas/bridge_operation.output.json +6 -0
  131. package/dist/schemas/bridge_operation.schema.json +1 -1
  132. package/dist/schemas/guard-templates.json +379 -0
  133. package/dist/schemas/guard2file.schema.json +1 -1
  134. package/dist/schemas/index.json +1 -1
  135. package/dist/schemas/local_info_operation.output.json +6 -0
  136. package/dist/schemas/local_mark_operation.output.json +7 -1
  137. package/dist/schemas/local_mark_operation.schema.json +1 -1
  138. package/dist/schemas/machineNode2file.schema.json +1 -1
  139. package/dist/schemas/messenger_operation.schema.json +4 -4
  140. package/dist/schemas/onchain_events.output.json +1 -1
  141. package/dist/schemas/onchain_operations.output.json +2820 -0
  142. package/dist/schemas/onchain_operations.schema.json +444 -330
  143. package/dist/schemas/onchain_operations_allocation.schema.json +37 -28
  144. package/dist/schemas/onchain_operations_arbitration.schema.json +16 -14
  145. package/dist/schemas/onchain_operations_contact.schema.json +10 -10
  146. package/dist/schemas/onchain_operations_demand.schema.json +10 -10
  147. package/dist/schemas/onchain_operations_gen_passport.schema.json +16 -16
  148. package/dist/schemas/onchain_operations_gen_proof.schema.json +2 -2
  149. package/dist/schemas/onchain_operations_guard.schema.json +2 -2
  150. package/dist/schemas/onchain_operations_machine.schema.json +43 -35
  151. package/dist/schemas/onchain_operations_order.schema.json +72 -78
  152. package/dist/schemas/onchain_operations_payment.schema.json +141 -111
  153. package/dist/schemas/onchain_operations_permission.schema.json +3 -3
  154. package/dist/schemas/onchain_operations_personal.schema.json +7 -7
  155. package/dist/schemas/onchain_operations_progress.schema.json +9 -9
  156. package/dist/schemas/onchain_operations_proof.schema.json +8 -8
  157. package/dist/schemas/onchain_operations_repository.schema.json +10 -10
  158. package/dist/schemas/onchain_operations_reward.schema.json +14 -14
  159. package/dist/schemas/onchain_operations_service.schema.json +119 -48
  160. package/dist/schemas/onchain_operations_treasury.schema.json +11 -11
  161. package/dist/schemas/onchain_table_data.output.json +33 -25
  162. package/dist/schemas/onchain_table_data.schema.json +22 -21
  163. package/dist/schemas/project_operation.output.json +2194 -23
  164. package/dist/schemas/project_operation.schema.json +84 -6
  165. package/dist/schemas/query_toolkit.output.json +108 -80
  166. package/dist/schemas/query_toolkit.schema.json +12 -12
  167. package/dist/schemas/schema_query.output.json +70 -3
  168. package/dist/schemas/schema_query.schema.json +18 -3
  169. package/dist/schemas/wowok_buildin_info.output.json +81 -8
  170. package/dist/schemas/wowok_buildin_info.schema.json +46 -2
  171. package/dist/tools/handlers/local.js +20 -5
  172. package/dist/tools/handlers/onchain.js +594 -6
  173. package/dist/tools/handlers/project.js +76 -4
  174. package/dist/tools/handlers/query.js +27 -0
  175. package/dist/tools/handlers/schema-query.js +43 -1
  176. package/dist/tools/handlers/task-status.d.ts +170 -0
  177. package/dist/tools/handlers/task-status.js +55 -0
  178. package/dist/tools/handlers/wip.js +47 -1
  179. package/dist/tools/index.d.ts +8 -0
  180. package/dist/tools/index.js +387 -12
  181. package/dist/tools/retry.d.ts +8 -0
  182. package/dist/tools/retry.js +85 -0
  183. package/dist/tools/wip-deploy-assist.d.ts +28 -0
  184. package/dist/tools/wip-deploy-assist.js +278 -0
  185. package/package.json +2 -2
  186. package/dist/schemas/guard-node-examples.md +0 -199
@@ -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 (好评) terminal",
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 (差评) terminal",
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 (好评). OrderHolder-only. Reward-eligible." },
1051
- { name: "${no_wonder_node_name}", description: "Negative feedback terminal (差评). OrderHolder-only. Optional Repository write for analytics." },
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
  {
@@ -1316,14 +1316,33 @@ export const MACHINE_TEMPLATES = [
1316
1316
  "Repository records subscription status for cross-Machine verification",
1317
1317
  "Renewal Allocation triggers on Renewed node (not Active — avoids double-charge)",
1318
1318
  "Note: consensus_repositories is declarative-only (NOT auto-passed to Progress); use repository_ref parameter for actual Repository object — see 06 §14.4",
1319
+ "VARIANT — Content subscription (Substack-style): replace time-lock Renew/Expire with content-delivery flow:",
1320
+ " 6-node variant: Paid (entry) → ContentPublished → Completed (terminal-success)",
1321
+ " Paid → Cancelled (terminal-cancel) | Paid → Disputed → Refunded (terminal-dispute)",
1322
+ " Permission: author=index 1000 (publish + cancel), arbitrator=index 1500 (resolve dispute), reader=namedOperator='' (open role, no specific operator)",
1323
+ " Guards: deliver_guard (verify author permission + content hash), cancel_guard (verify subscriber request), refund_guard (verify arbitration outcome)",
1324
+ " Allocators: deliver=100% to author, cancel=100% refund to subscriber, dispute=100% refund to subscriber (Guard-first-wins pattern)",
1325
+ " First-Forward-wins: when Paid has competing forwards (Deliver|Cancel|Dispute), the first triggered forward wins; subscribers cannot double-claim",
1326
+ "CRITICAL — v1→v2 lesson: arbitrations MUST be added BEFORE Service.publish (post-publish, arbitrations become immutable and cannot be added — service.move F-S-21)",
1327
+ " If you accidentally published without arbitrations, you MUST create a new Service v2 with arbitrations bound before publishing (the v1 Service must be paused/abandoned)",
1328
+ " ConfirmGate (C-S-19, QS11) will warn before publish without arbitrations — do not bypass this warning unless explicitly intending no-arbitration operation",
1329
+ "Build order (content variant): Permission → Machine (unpublished, with initial forward) → 3 Guards → 3 Allocators (template on Service) → Service (unpublished, bind machine+buy_guard+order_allocators+arbitrations) → Publish Service → Test order (3 paths: normal/cancel/dispute)",
1319
1330
  ],
1320
1331
  example_use_case: "Monthly SaaS subscription: $10/month. Customer can renew anytime after day 25. " +
1321
- "Auto-expire on day 35 if not renewed. Cancel anytime.",
1332
+ "Auto-expire on day 35 if not renewed. Cancel anytime.\n\n" +
1333
+ "VARIANT — Content subscription (Substack-style): $5/month newsletter. Author publishes content → " +
1334
+ "subscriber reads. Subscriber can cancel anytime for full refund. Disputes resolved by arbitrator " +
1335
+ "(100% refund to subscriber if dispute upheld). 6-node Machine, 3 Guards, 3 Allocators. " +
1336
+ "Tested on testnet with 3 order paths: normal delivery, cancel, dispute.",
1322
1337
  publish_checklist: [
1323
1338
  "Time-lock Guards tested on testnet (both TimeUnlock + AutoExpire)",
1324
1339
  "Competing Pair semantics verified (Renew vs Expire vs Cancel)",
1325
1340
  "Repository write tested (subscription status queryable)",
1326
1341
  "Renewal loop tested: Paid -> Active -> RenewEligible -> Renewed -> Active",
1342
+ "Content variant: initial node (prev_node='') MUST have at least one forward (FIX-002) — empty forwards=[] causes Progress to be permanently stuck",
1343
+ "Content variant: arbitrations added BEFORE publish (cannot add post-publish per F-S-21)",
1344
+ "Content variant: 3 order paths tested on testnet — normal delivery, cancel-within-grace, dispute-resolution",
1345
+ "Content variant: machineNode2file export as backup (covers all 6 nodes)",
1327
1346
  ],
1328
1347
  },
1329
1348
  ];
@@ -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 = ["好评", "praise", "wonder", "thumbs_up", "positive review", "satisfaction reward", "好评奖励"];
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 '好评/praise', Guard must check 'wonder' (NOT 'completed'). " +
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 (好评/wonder) must check 'wonder' node, not 'completed'. " +
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
  }