@wowok/agent-mcp 2.5.5 → 2.6.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.
Files changed (146) hide show
  1. package/README.md +5 -3
  2. package/dist/config/runtime.js +3 -6
  3. package/dist/extensions/capability-manifest.d.ts +125 -0
  4. package/dist/extensions/capability-manifest.js +594 -0
  5. package/dist/extensions/constraint-registry.d.ts +24 -0
  6. package/dist/extensions/constraint-registry.js +196 -0
  7. package/dist/extensions/index.d.ts +12 -0
  8. package/dist/extensions/index.js +6 -0
  9. package/dist/extensions/metric-registry.d.ts +26 -0
  10. package/dist/extensions/metric-registry.js +257 -0
  11. package/dist/extensions/mode-evaluator.d.ts +15 -0
  12. package/dist/extensions/mode-evaluator.js +170 -0
  13. package/dist/extensions/modes.d.ts +2 -0
  14. package/dist/extensions/modes.js +407 -0
  15. package/dist/extensions/registry.d.ts +48 -0
  16. package/dist/extensions/registry.js +629 -0
  17. package/dist/extensions/types.d.ts +218 -0
  18. package/dist/extensions/types.js +1 -0
  19. package/dist/harness/checkpoint.js +2 -2
  20. package/dist/knowledge/deployment-scanner.d.ts +3 -0
  21. package/dist/knowledge/deployment-scanner.js +64 -3
  22. package/dist/knowledge/flywheel-loop.js +2 -5
  23. package/dist/knowledge/guard-risk.d.ts +13 -0
  24. package/dist/knowledge/guard-risk.js +57 -0
  25. package/dist/knowledge/guard-templates.js +278 -0
  26. package/dist/knowledge/machine-templates.js +20 -1
  27. package/dist/knowledge/overrides-loader.js +2 -4
  28. package/dist/knowledge/progress-ledger.js +3 -0
  29. package/dist/knowledge/progress-translation.js +5 -1
  30. package/dist/knowledge/service-confirm.d.ts +14 -5
  31. package/dist/knowledge/service-confirm.js +116 -8
  32. package/dist/knowledge/tool-constraints.js +6 -2
  33. package/dist/loop-engineering/improve.js +2 -4
  34. package/dist/project/deployment-bridge.d.ts +5 -0
  35. package/dist/project/deployment-bridge.js +119 -0
  36. package/dist/project/deployment-doc.d.ts +3 -0
  37. package/dist/project/deployment-doc.js +72 -10
  38. package/dist/project/evaluation.d.ts +2 -0
  39. package/dist/project/evaluation.js +578 -86
  40. package/dist/project/graph-builder.d.ts +4 -1
  41. package/dist/project/graph-builder.js +141 -63
  42. package/dist/project/graph.d.ts +1 -0
  43. package/dist/project/handlers.d.ts +221 -5
  44. package/dist/project/handlers.js +933 -14
  45. package/dist/project/index.js +2 -6
  46. package/dist/project/project-store.js +2 -5
  47. package/dist/project/stage-gate.d.ts +4 -0
  48. package/dist/project/stage-gate.js +64 -5
  49. package/dist/project/task-tracker.d.ts +26 -0
  50. package/dist/project/task-tracker.js +78 -0
  51. package/dist/safety/preview.js +16 -0
  52. package/dist/schema/call/allocation.d.ts +16 -16
  53. package/dist/schema/call/base.d.ts +21 -13
  54. package/dist/schema/call/base.js +27 -6
  55. package/dist/schema/call/bridge.d.ts +5 -5
  56. package/dist/schema/call/bridge.js +3 -1
  57. package/dist/schema/call/demand.d.ts +23 -31
  58. package/dist/schema/call/guard.js +1 -1
  59. package/dist/schema/call/machine.d.ts +402 -376
  60. package/dist/schema/call/order.d.ts +149 -228
  61. package/dist/schema/call/order.js +7 -3
  62. package/dist/schema/call/payment.d.ts +183 -3
  63. package/dist/schema/call/payment.js +21 -3
  64. package/dist/schema/call/personal.d.ts +241 -52
  65. package/dist/schema/call/progress.d.ts +53 -61
  66. package/dist/schema/call/progress.js +18 -4
  67. package/dist/schema/call/repository.d.ts +23 -31
  68. package/dist/schema/call/semantic.d.ts +1 -1
  69. package/dist/schema/call/semantic.js +30 -1
  70. package/dist/schema/call/service.d.ts +95 -119
  71. package/dist/schema/call/service.js +22 -1
  72. package/dist/schema/common/index.d.ts +11 -2
  73. package/dist/schema/common/index.js +43 -14
  74. package/dist/schema/config/index.d.ts +12 -12
  75. package/dist/schema/local/index.d.ts +140 -143
  76. package/dist/schema/local/index.js +44 -23
  77. package/dist/schema/messenger/index.d.ts +290 -62
  78. package/dist/schema/messenger/index.js +2 -2
  79. package/dist/schema/operations.d.ts +680 -545
  80. package/dist/schema/operations.js +22 -0
  81. package/dist/schema/project/index.d.ts +2064 -84
  82. package/dist/schema/project/index.js +354 -12
  83. package/dist/schema/query/index.d.ts +715 -354
  84. package/dist/schema/query/index.js +164 -31
  85. package/dist/schema/schema-query/index.d.ts +15 -3
  86. package/dist/schema/schema-query/index.js +23 -5
  87. package/dist/schema/trust/index.d.ts +8 -8
  88. package/dist/schema/utils/node-parser.js +7 -4
  89. package/dist/schema-query/index.d.ts +7 -1
  90. package/dist/schema-query/index.js +204 -4
  91. package/dist/schemas/account_operation.output.json +14 -22
  92. package/dist/schemas/account_operation.schema.json +11 -25
  93. package/dist/schemas/bridge_operation.output.json +6 -0
  94. package/dist/schemas/bridge_operation.schema.json +1 -1
  95. package/dist/schemas/guard-templates.json +379 -0
  96. package/dist/schemas/guard2file.schema.json +1 -1
  97. package/dist/schemas/index.json +1 -1
  98. package/dist/schemas/local_info_operation.output.json +6 -0
  99. package/dist/schemas/local_mark_operation.output.json +7 -1
  100. package/dist/schemas/local_mark_operation.schema.json +1 -1
  101. package/dist/schemas/machineNode2file.schema.json +1 -1
  102. package/dist/schemas/messenger_operation.schema.json +10 -10
  103. package/dist/schemas/onchain_events.output.json +1 -1
  104. package/dist/schemas/onchain_operations.schema.json +348 -296
  105. package/dist/schemas/onchain_operations_allocation.schema.json +34 -25
  106. package/dist/schemas/onchain_operations_arbitration.schema.json +8 -8
  107. package/dist/schemas/onchain_operations_contact.schema.json +8 -8
  108. package/dist/schemas/onchain_operations_demand.schema.json +8 -8
  109. package/dist/schemas/onchain_operations_gen_passport.schema.json +14 -14
  110. package/dist/schemas/onchain_operations_gen_proof.schema.json +2 -2
  111. package/dist/schemas/onchain_operations_guard.schema.json +1 -1
  112. package/dist/schemas/onchain_operations_machine.schema.json +41 -33
  113. package/dist/schemas/onchain_operations_order.schema.json +71 -77
  114. package/dist/schemas/onchain_operations_payment.schema.json +141 -111
  115. package/dist/schemas/onchain_operations_permission.schema.json +2 -2
  116. package/dist/schemas/onchain_operations_personal.schema.json +7 -7
  117. package/dist/schemas/onchain_operations_progress.schema.json +8 -8
  118. package/dist/schemas/onchain_operations_proof.schema.json +7 -7
  119. package/dist/schemas/onchain_operations_repository.schema.json +8 -8
  120. package/dist/schemas/onchain_operations_reward.schema.json +10 -10
  121. package/dist/schemas/onchain_operations_service.schema.json +46 -35
  122. package/dist/schemas/onchain_operations_treasury.schema.json +8 -8
  123. package/dist/schemas/onchain_table_data.output.json +33 -25
  124. package/dist/schemas/onchain_table_data.schema.json +12 -12
  125. package/dist/schemas/project_operation.output.json +1352 -20
  126. package/dist/schemas/project_operation.schema.json +53 -4
  127. package/dist/schemas/query_toolkit.output.json +111 -82
  128. package/dist/schemas/query_toolkit.schema.json +9 -13
  129. package/dist/schemas/schema_query.output.json +7 -3
  130. package/dist/schemas/schema_query.schema.json +17 -3
  131. package/dist/telemetry/storage.js +2 -2
  132. package/dist/tools/handlers/local.js +20 -5
  133. package/dist/tools/handlers/onchain.js +36 -0
  134. package/dist/tools/handlers/project.js +72 -1
  135. package/dist/tools/handlers/query.js +27 -0
  136. package/dist/tools/handlers/schema-query.js +19 -0
  137. package/dist/tools/handlers/task-status.d.ts +170 -0
  138. package/dist/tools/handlers/task-status.js +55 -0
  139. package/dist/tools/handlers/wip.js +47 -1
  140. package/dist/tools/index.js +212 -8
  141. package/dist/tools/retry.d.ts +8 -0
  142. package/dist/tools/retry.js +85 -0
  143. package/dist/tools/wip-deploy-assist.d.ts +28 -0
  144. package/dist/tools/wip-deploy-assist.js +278 -0
  145. package/package.json +2 -2
  146. package/dist/schemas/guard-node-examples.md +0 -199
@@ -0,0 +1,55 @@
1
+ import { z } from "zod";
2
+ import { TaskTracker } from "../../project/task-tracker.js";
3
+ export const QueryTaskStatusInputSchema = z.object({
4
+ task_id: z
5
+ .string()
6
+ .optional()
7
+ .describe("The task ID to query (returned by a prior async operation). " +
8
+ "If omitted, lists all recent tasks (most recent first, max 50)."),
9
+ project_id: z
10
+ .string()
11
+ .optional()
12
+ .describe("Optional filter — when listing tasks (task_id omitted), only return tasks for this project."),
13
+ }).strict().describe("Query the status of an async task, or list all recent tasks when task_id is omitted.");
14
+ export const TaskStateSchema = z.object({
15
+ task_id: z.string().describe("Unique task identifier."),
16
+ operation: z.string().describe("Operation type that created this task (e.g. 'build_graph')."),
17
+ status: z.enum(["pending", "running", "completed", "failed", "cancelled"]).describe("Current task status."),
18
+ progress: z.number().describe("Progress percentage 0-100."),
19
+ message: z.string().describe("Human-readable progress message."),
20
+ result: z.unknown().nullable().describe("Result payload (set when status='completed'). null otherwise."),
21
+ error: z.string().nullable().describe("Error message (set when status='failed'). null otherwise."),
22
+ started_at: z.number().describe("Timestamp (ms) when the task was created."),
23
+ completed_at: z.number().nullable().describe("Timestamp (ms) when the task reached a terminal state. null if still running."),
24
+ project_id: z.string().nullable().describe("Project ID associated with this task, if any."),
25
+ }).strict();
26
+ export const QueryTaskStatusOutputSchema = z.object({
27
+ task_id: z.string().optional().describe("The queried task ID (present when a single task was queried)."),
28
+ task: TaskStateSchema.nullable().describe("The task state. null if task_id not found or when listing."),
29
+ tasks: z.array(TaskStateSchema).optional().describe("List of tasks (present when task_id was omitted)."),
30
+ }).strict();
31
+ export async function handleQueryTaskStatus(args) {
32
+ const validated = (args && typeof args === "object" ? args : {});
33
+ if (validated.task_id) {
34
+ const task = TaskTracker.get(validated.task_id);
35
+ const result = {
36
+ task_id: validated.task_id,
37
+ task: task ?? null,
38
+ tasks: undefined,
39
+ };
40
+ return {
41
+ content: [{ type: "text", text: JSON.stringify(result) }],
42
+ structuredContent: result,
43
+ };
44
+ }
45
+ const tasks = TaskTracker.list(validated.project_id).slice(0, 50);
46
+ const result = {
47
+ task_id: undefined,
48
+ task: null,
49
+ tasks,
50
+ };
51
+ return {
52
+ content: [{ type: "text", text: JSON.stringify(result) }],
53
+ structuredContent: result,
54
+ };
55
+ }
@@ -1,6 +1,36 @@
1
1
  import { strictParse, WipOperationsSchema, } from "../../schema/index.js";
2
2
  import { generate_wip, verify_wip, sign_wip, wip2html, } from "@wowok/wowok";
3
+ import { analyzeWipDeployment, formatDeployHints, } from "../wip-deploy-assist.js";
3
4
  export async function handleWipOperations(args) {
5
+ if (typeof args === "object" && args !== null
6
+ && args.type === "deploy_assist") {
7
+ const wipPath = args.wipFilePath;
8
+ if (typeof wipPath !== "string") {
9
+ return {
10
+ content: [{
11
+ type: "text",
12
+ text: "Error: 'wipFilePath' is required for deploy_assist action",
13
+ }],
14
+ structuredContent: {
15
+ result: {
16
+ type: "error",
17
+ error: "'wipFilePath' is required for deploy_assist action",
18
+ },
19
+ },
20
+ };
21
+ }
22
+ const suggestion = analyzeWipDeployment(wipPath);
23
+ const hints = formatDeployHints(suggestion);
24
+ return {
25
+ content: [{ type: "text", text: hints || "No deployment needed." }],
26
+ structuredContent: {
27
+ result: {
28
+ type: "deploy_assist",
29
+ ...suggestion,
30
+ },
31
+ },
32
+ };
33
+ }
4
34
  const validated = strictParse(WipOperationsSchema, args, "wip_file input");
5
35
  let structuredContent;
6
36
  switch (validated.type) {
@@ -10,8 +40,24 @@ export async function handleWipOperations(args) {
10
40
  break;
11
41
  }
12
42
  case "verify": {
43
+ const deploySuggestion = analyzeWipDeployment(validated.wipFilePath);
13
44
  const result = await verify_wip(validated.wipFilePath, validated.hash_equal, validated.requireSignature);
14
- structuredContent = { result: { type: "verify", ...result } };
45
+ const attachHints = deploySuggestion.needs_deployment
46
+ || (!result.valid && deploySuggestion.classification.type !== "data_url");
47
+ if (attachHints) {
48
+ const hints = formatDeployHints(deploySuggestion);
49
+ structuredContent = {
50
+ result: {
51
+ type: "verify",
52
+ ...result,
53
+ deployment_assistance: deploySuggestion,
54
+ },
55
+ deployment_hints: hints,
56
+ };
57
+ }
58
+ else {
59
+ structuredContent = { result: { type: "verify", ...result } };
60
+ }
15
61
  break;
16
62
  }
17
63
  case "sign": {
@@ -24,6 +24,7 @@ import { handleBridgeOperations } from "./handlers/bridge.js";
24
24
  import { handleProjectOperation } from "./handlers/project.js";
25
25
  import { handleConfigOperation } from "./handlers/config.js";
26
26
  import { handleTrustScore } from "./handlers/trust.js";
27
+ import { handleQueryTaskStatus, QueryTaskStatusInputSchema, QueryTaskStatusOutputSchema } from "./handlers/task-status.js";
27
28
  import { applyOnchainRules } from "./rules-hook.js";
28
29
  import { createToolMeta, OnchainOperationsSchema, AccountOperationSchema, LocalMarkOperationSchema, LocalInfoOperationSchema, WipOperationsSchema, MessengerOperationInputSchema, Guard2File_InputSchema, MachineNode2File_InputSchema, WatchQueryOperationsSchema, WatchQueryOperationsResultSchema, OnchainTableDataSchema, OnchainEventsInputSchema, ProtocolInfoQuerySchema, SchemaQueryInputSchema, BridgeOperationsSchema, ProjectOperationInputSchema, ConfigOperationInputSchema, TrustScoreInputSchema, CallOutputSchema, AccountOperationOutputWrappedSchema, LocalMarkOperationOutputWrappedSchema, LocalInfoOperationOutputWrappedSchema, WipOperationOutputSchema, MessengerOperationOutputSchema, Guard2File_OutputWrappedSchema, MachineNode2File_OutputWrappedSchema, OnchainTableDataResultSchema, OnchainEventsResultSchema, ProtocolInfoResultSchema, SchemaQueryOutputSchema, BridgeCallOutputSchema, ProjectOperationOutputSchema, ConfigOperationOutputSchema, TrustScoreOutputSchema, } from "../schema/index.js";
29
30
  export const TOOL_REGISTRY = [
@@ -84,7 +85,7 @@ export const TOOL_REGISTRY = [
84
85
  handler: handleWipOperations,
85
86
  config: {
86
87
  title: "🤝 Witness Immutable Promise File Operations",
87
- description: "generate (create WIP files from markdown and images), verify (integrity check), sign (add signatures), or wip2html (convert to HTML).\n\n💡 Schema hint: Use schema_query with action='get' and name='wip_file' to retrieve the exact parameter schema before calling.",
88
+ description: "generate (create WIP files from markdown and images), verify (integrity check), sign (add signatures), or wip2html (convert to HTML).\n\n⚠️ IMPORTANT — On-chain WIP URL requirement: When using a WIP file URL in `service.sales[].wip` (stored on-chain), the URL MUST be publicly reachable. Local file paths (e.g., 'C:\\\\path\\\\file.wip' or '/home/user/file.wip') are NOT acceptable on-chain — they only work locally and will fail verification for buyers.\n\nDeployment options for the .wip file:\n • GitHub Pages: https://raw.githubusercontent.com/<user>/<repo>/<branch>/<file>.wip\n • IPFS: https://ipfs.io/ipfs/<CID> or https://cloudflare-ipfs.com/ipfs/<CID> (upload via pinata.cloud)\n • User's own static website URL\n\nUse type='generate' to create the WIP file first, upload it to any of the above, then use the resulting URL in service.sales[].wip.\n\n💡 Schema hint: Use schema_query with action='get' and name='wip_file' to retrieve the exact parameter schema before calling.",
88
89
  inputSchema: WipOperationsSchema,
89
90
  outputSchema: z.object({
90
91
  result: WipOperationOutputSchema,
@@ -324,7 +325,7 @@ export const TOOL_REGISTRY = [
324
325
  " • order_monitor — In-progress Order Monitoring (Phase 3 C-3.2, default OFF)\n" +
325
326
  " • industry_evolution — Industry Specialization Evolution Flywheel (Phase 4, default ON)\n\n" +
326
327
  "NOTE: Toggles persist for the MCP process lifetime. Set WOWOK_RUNTIME_CONFIG_PERSIST=1 " +
327
- "to persist across sessions to ~/.wowok/runtime-config.json.\n\n" +
328
+ "to persist across sessions to ~/.wow/mcp/runtime-config.json.\n\n" +
328
329
  "💡 Schema hint: Use schema_query with action='get' and name='config_operation' to retrieve the exact parameter schema with all actions and field definitions.",
329
330
  inputSchema: ConfigOperationInputSchema,
330
331
  outputSchema: ConfigOperationOutputSchema,
@@ -360,6 +361,28 @@ export const TOOL_REGISTRY = [
360
361
  _meta: createToolMeta("trust", ["trust", "score", "risk", "service", "merchant", "reputation", "safety", "evaluate"]),
361
362
  },
362
363
  },
364
+ {
365
+ name: "query_task_status",
366
+ handler: handleQueryTaskStatus,
367
+ config: {
368
+ title: "📊 Task Status - Async Operation Tracking",
369
+ description: "Query the status of a long-running async task (created by build_graph or evaluate_project " +
370
+ "with async_mode=true). Returns progress percentage, current status, and the final result " +
371
+ "when completed.\n\n" +
372
+ "Tasks are ephemeral (retained 5 minutes after completion, in-memory only).\n\n" +
373
+ "Usage:\n" +
374
+ " • Pass task_id to query a single task's status.\n" +
375
+ " • Omit task_id to list all recent tasks (max 50, most recent first).\n" +
376
+ " • Pass project_id (with task_id omitted) to filter by project.\n\n" +
377
+ "This tool is read-only and always safe to call. Use it to check if a long-running " +
378
+ "operation (e.g. build_graph with enablePotential=true) has completed.\n\n" +
379
+ "💡 Schema hint: Use schema_query with action='get' and name='query_task_status' to retrieve the exact parameter schema.",
380
+ inputSchema: QueryTaskStatusInputSchema,
381
+ outputSchema: QueryTaskStatusOutputSchema,
382
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
383
+ _meta: createToolMeta("task", ["task", "status", "async", "progress", "background", "tracker"]),
384
+ },
385
+ },
363
386
  ];
364
387
  export const TOOL_REGISTRY_MAP = new Map(TOOL_REGISTRY.map(t => [t.name, t]));
365
388
  const SUB_TOOL_NAMES = TOOL_REGISTRY.map(t => t.name);
@@ -478,12 +501,71 @@ const WOWOK_TOOL_DESCRIPTION = [
478
501
  " general → fallback for unclear industry",
479
502
  "═══════════════════════════════════════════════════════════════",
480
503
  ].join("\n");
481
- function formatZodErrors(error) {
504
+ function formatZodErrors(error, input) {
482
505
  return error.issues.map(issue => {
483
- const path = issue.path.length > 0 ? issue.path.join(".") : "(root)";
484
- return `${path}: ${issue.message}`;
506
+ const path = issue.path.length > 0
507
+ ? issue.path.map(p => typeof p === "number" ? `[${p}]` : p).join(".").replace(".[", "[")
508
+ : "(root)";
509
+ const code = issue.code;
510
+ const message = issue.message;
511
+ let extras = "";
512
+ const issueAny = issue;
513
+ if (code === "invalid_type") {
514
+ const expected = issueAny.expected;
515
+ const received = issueAny.received;
516
+ if (typeof expected === "string" && typeof received === "string") {
517
+ extras = ` (expected: ${expected}; received: ${received})`;
518
+ }
519
+ }
520
+ else if (code === "invalid_literal" || code === "invalid_enum_value") {
521
+ const expected = issueAny.expected;
522
+ if (expected !== undefined) {
523
+ const expStr = typeof expected === "string" ? expected : JSON.stringify(expected);
524
+ extras = ` (expected: ${expStr.slice(0, 60)})`;
525
+ }
526
+ }
527
+ else if (code === "too_big" || code === "too_small") {
528
+ const maximum = issueAny.maximum ?? issueAny.minimum;
529
+ const origin = issueAny.origin;
530
+ if (maximum !== undefined && origin !== undefined) {
531
+ extras = ` (${origin}: ${maximum})`;
532
+ }
533
+ }
534
+ const receivedVal = extractValueAtPath(input, issue.path);
535
+ if (receivedVal !== undefined) {
536
+ const valStr = typeof receivedVal === "string"
537
+ ? `"${receivedVal.slice(0, 60)}"`
538
+ : typeof receivedVal === "object"
539
+ ? JSON.stringify(receivedVal).slice(0, 60)
540
+ : String(receivedVal);
541
+ if (!extras.includes("received:")) {
542
+ extras += ` (received value: ${valStr})`;
543
+ }
544
+ }
545
+ return `${path} [${code}]: ${message}${extras}`;
485
546
  });
486
547
  }
548
+ function extractValueAtPath(input, path) {
549
+ if (input === undefined || input === null || path.length === 0) {
550
+ return undefined;
551
+ }
552
+ let current = input;
553
+ for (const key of path) {
554
+ if (current === undefined || current === null)
555
+ return undefined;
556
+ if (typeof key === "number") {
557
+ if (!Array.isArray(current))
558
+ return undefined;
559
+ current = current[key];
560
+ }
561
+ else {
562
+ if (typeof current !== "object")
563
+ return undefined;
564
+ current = current[key];
565
+ }
566
+ }
567
+ return current;
568
+ }
487
569
  function checkUnknownFields(schema, input) {
488
570
  let innerSchema = schema;
489
571
  while (true) {
@@ -518,6 +600,109 @@ function isSchemaValidationError(message) {
518
600
  message.includes("Invalid input") ||
519
601
  message.includes("Required"));
520
602
  }
603
+ function buildSchemaPreview(schema, toolName, data) {
604
+ try {
605
+ const lines = [`Schema preview for ${toolName}:`];
606
+ let topProps;
607
+ let topRequired = [];
608
+ let topDescription = "";
609
+ if (schema.properties && typeof schema.properties === "object") {
610
+ topProps = schema.properties;
611
+ topRequired = Array.isArray(schema.required) ? schema.required : [];
612
+ topDescription = typeof schema.description === "string" ? schema.description : "";
613
+ }
614
+ else if (Array.isArray(schema.oneOf) || Array.isArray(schema.anyOf)) {
615
+ const variants = (schema.oneOf ?? schema.anyOf);
616
+ for (const v of variants) {
617
+ if (v && typeof v === "object") {
618
+ const vo = v;
619
+ if (vo.properties && typeof vo.properties === "object") {
620
+ topProps = vo.properties;
621
+ topRequired = Array.isArray(vo.required) ? vo.required : [];
622
+ topDescription = typeof vo.description === "string" ? vo.description : "";
623
+ break;
624
+ }
625
+ }
626
+ }
627
+ if (!topProps) {
628
+ lines.push(` (discriminated union with ${variants.length} variants — see structuredContent for full schema)`);
629
+ return lines.join("\n");
630
+ }
631
+ }
632
+ if (topDescription && topDescription.length > 0 && topDescription.length < 200) {
633
+ lines.push(` ${topDescription}`);
634
+ }
635
+ if (!topProps) {
636
+ lines.push(" (no properties available — see structuredContent for full schema)");
637
+ return lines.join("\n");
638
+ }
639
+ const requiredSet = new Set(topRequired);
640
+ const requiredList = topRequired.length > 0 ? topRequired.join(", ") : "(none)";
641
+ const optionalList = Object.keys(topProps).filter(k => !requiredSet.has(k)).join(", ");
642
+ lines.push(`Required: ${requiredList}`);
643
+ if (optionalList) {
644
+ lines.push(`Optional: ${optionalList}`);
645
+ }
646
+ lines.push("Fields:");
647
+ for (const [fieldName, fieldSchema] of Object.entries(topProps)) {
648
+ if (fieldSchema && typeof fieldSchema === "object") {
649
+ const f = fieldSchema;
650
+ const type = describeSchemaType(f);
651
+ const required = requiredSet.has(fieldName) ? "required" : "optional";
652
+ let desc = typeof f.description === "string" ? f.description : "";
653
+ if (desc.length > 120)
654
+ desc = desc.slice(0, 117) + "...";
655
+ let subFields = "";
656
+ if (f.properties && typeof f.properties === "object") {
657
+ const subProps = Object.keys(f.properties);
658
+ if (subProps.length > 0) {
659
+ subFields = ` { ${subProps.slice(0, 8).join(", ")}${subProps.length > 8 ? ", ..." : ""} }`;
660
+ }
661
+ }
662
+ if (f.items && typeof f.items === "object") {
663
+ const items = f.items;
664
+ const itemType = describeSchemaType(items);
665
+ subFields = ` (array of ${itemType})`;
666
+ }
667
+ if (Array.isArray(f.enum)) {
668
+ subFields = ` (enum: ${f.enum.slice(0, 6).map(v => JSON.stringify(v)).join(", ")}${f.enum.length > 6 ? ", ..." : ""})`;
669
+ }
670
+ lines.push(` - ${fieldName} (${type}, ${required})${subFields}${desc ? ` — ${desc}` : ""}`);
671
+ }
672
+ }
673
+ if (toolName === "onchain_operations" && typeof data === "object" && data !== null) {
674
+ const opType = data.operation_type;
675
+ if (typeof opType === "string") {
676
+ lines.push(``);
677
+ lines.push(`operation_type-specific schema: onchain_operations_${opType}`);
678
+ lines.push(`(Use schema_query with action='get' and name='onchain_operations_${opType}' for the full sub-schema.)`);
679
+ }
680
+ }
681
+ return lines.join("\n");
682
+ }
683
+ catch {
684
+ return `Schema preview for ${toolName}: (unable to build preview — see structuredContent for full schema)`;
685
+ }
686
+ }
687
+ function describeSchemaType(schema) {
688
+ if (typeof schema.type === "string")
689
+ return schema.type;
690
+ if (Array.isArray(schema.type))
691
+ return schema.type.join("|");
692
+ if (schema.$ref)
693
+ return "$ref";
694
+ if (Array.isArray(schema.oneOf))
695
+ return "oneOf";
696
+ if (Array.isArray(schema.anyOf))
697
+ return "anyOf";
698
+ if (Array.isArray(schema.allOf))
699
+ return "allOf";
700
+ if (schema.enum)
701
+ return "enum";
702
+ if (schema.const)
703
+ return `const(${JSON.stringify(schema.const)})`;
704
+ return "object";
705
+ }
521
706
  export async function dispatchToolCall(name, args, options) {
522
707
  if (name === "wowok") {
523
708
  return handleWowok(args);
@@ -647,17 +832,28 @@ async function handleWowok(args) {
647
832
  if (!validationResult.success || unknownFields.length > 0) {
648
833
  const allErrors = [];
649
834
  if (!validationResult.success) {
650
- allErrors.push(...formatZodErrors(validationResult.error));
835
+ allErrors.push(...formatZodErrors(validationResult.error, normalizedData));
651
836
  }
652
837
  if (unknownFields.length > 0) {
653
838
  allErrors.push(`Unknown field(s): ${unknownFields.join(", ")}`);
654
839
  }
655
840
  const fullSchema = loadPreGeneratedSchema(normalizedToolName) ?? zodToJsonSchemaSafe(entry.config.inputSchema, normalizedToolName);
656
841
  const relevantSchema = extractRelevantSchema(fullSchema, normalizedToolName, normalizedData);
842
+ const errorPreview = allErrors.slice(0, 5).map((e, i) => ` ${i + 1}. ${e}`).join("\n");
843
+ const schemaPreview = buildSchemaPreview(relevantSchema, normalizedToolName, normalizedData);
844
+ const textContent = [
845
+ `Schema mismatch for ${normalizedToolName}. ${allErrors.length} error(s):`,
846
+ errorPreview,
847
+ "",
848
+ schemaPreview,
849
+ "",
850
+ "Fix the parameters based on the errors above and the schema summary, then retry.",
851
+ `Full JSON Schema is in the structuredContent.schema.input field (cache it for future ${normalizedToolName} calls).`,
852
+ ].filter(Boolean).join("\n");
657
853
  return {
658
854
  content: [{
659
855
  type: "text",
660
- text: `Schema mismatch for ${normalizedToolName}. ${allErrors.length} error(s). Read the schema below, fix your params, and retry.`,
856
+ text: textContent,
661
857
  }],
662
858
  structuredContent: {
663
859
  result: {
@@ -706,10 +902,18 @@ async function handleWowok(args) {
706
902
  if (isSchemaValidationError(errorMessage)) {
707
903
  const fullSchema = loadPreGeneratedSchema(normalizedToolName) ?? zodToJsonSchemaSafe(entry.config.inputSchema, normalizedToolName);
708
904
  const relevantSchema = extractRelevantSchema(fullSchema, normalizedToolName, normalizedData);
905
+ const schemaPreview = buildSchemaPreview(relevantSchema, normalizedToolName, normalizedData);
906
+ const textContent = [
907
+ `Schema mismatch for ${normalizedToolName}: ${errorMessage}`,
908
+ "",
909
+ schemaPreview,
910
+ "",
911
+ "Fix the parameters and retry. Full JSON Schema is in structuredContent.schema.input.",
912
+ ].join("\n");
709
913
  return {
710
914
  content: [{
711
915
  type: "text",
712
- text: `Schema mismatch for ${normalizedToolName}: ${errorMessage}`,
916
+ text: textContent,
713
917
  }],
714
918
  structuredContent: {
715
919
  result: {
@@ -0,0 +1,8 @@
1
+ export interface RetryConfig {
2
+ maxRetries: number;
3
+ intervalMs: number;
4
+ }
5
+ export declare const DEFAULT_FAUCET_RETRY_CONFIG: RetryConfig;
6
+ export declare function loadFaucetRetryConfig(): RetryConfig;
7
+ export declare function isFaucetRateLimitError(error: unknown): boolean;
8
+ export declare function withFaucetRetry<T, R>(fn: (input: T) => Promise<R>, config?: RetryConfig): (input: T) => Promise<R>;
@@ -0,0 +1,85 @@
1
+ export const DEFAULT_FAUCET_RETRY_CONFIG = {
2
+ maxRetries: 5,
3
+ intervalMs: 3000,
4
+ };
5
+ export function loadFaucetRetryConfig() {
6
+ const maxRetries = Number(process.env.WOWOK_FAUCET_RETRY_COUNT);
7
+ const intervalMs = Number(process.env.WOWOK_FAUCET_RETRY_INTERVAL_MS);
8
+ return {
9
+ maxRetries: Number.isFinite(maxRetries) && maxRetries > 0
10
+ ? Math.floor(maxRetries)
11
+ : DEFAULT_FAUCET_RETRY_CONFIG.maxRetries,
12
+ intervalMs: Number.isFinite(intervalMs) && intervalMs > 0
13
+ ? Math.floor(intervalMs)
14
+ : DEFAULT_FAUCET_RETRY_CONFIG.intervalMs,
15
+ };
16
+ }
17
+ export function isFaucetRateLimitError(error) {
18
+ if (!error)
19
+ return false;
20
+ const msg = error instanceof Error ? error.message : String(error);
21
+ const lower = msg.toLowerCase();
22
+ if (lower.includes("429"))
23
+ return true;
24
+ if (lower.includes("rate limit"))
25
+ return true;
26
+ if (lower.includes("rate-limit"))
27
+ return true;
28
+ if (lower.includes("too many requests"))
29
+ return true;
30
+ if (lower.includes("try again in"))
31
+ return true;
32
+ if (lower.includes("throttle"))
33
+ return true;
34
+ return false;
35
+ }
36
+ function isFaucetOperation(input) {
37
+ if (typeof input !== "object" || input === null)
38
+ return false;
39
+ const obj = input;
40
+ return obj.faucet !== undefined && obj.faucet !== null;
41
+ }
42
+ export function withFaucetRetry(fn, config = loadFaucetRetryConfig()) {
43
+ return async (input) => {
44
+ if (!isFaucetOperation(input)) {
45
+ return fn(input);
46
+ }
47
+ let lastError;
48
+ for (let attempt = 1; attempt <= config.maxRetries; attempt++) {
49
+ try {
50
+ const result = await fn(input);
51
+ if (result && typeof result === "object" && !Array.isArray(result)) {
52
+ const obj = result;
53
+ if (obj.faucet_retry_count === undefined && attempt > 1) {
54
+ obj.faucet_retry_count = attempt;
55
+ }
56
+ }
57
+ return result;
58
+ }
59
+ catch (error) {
60
+ lastError = error;
61
+ if (!isFaucetRateLimitError(error)) {
62
+ throw error;
63
+ }
64
+ if (attempt === config.maxRetries) {
65
+ break;
66
+ }
67
+ await new Promise((resolve) => setTimeout(resolve, config.intervalMs));
68
+ }
69
+ }
70
+ const lastMsg = lastError instanceof Error ? lastError.message : String(lastError);
71
+ const err = new Error(`Faucet request failed after ${config.maxRetries} attempts ` +
72
+ `(interval ${config.intervalMs}ms). Last error: ${lastMsg}. ` +
73
+ `The faucet endpoint is currently rate-limiting this account. ` +
74
+ `Suggestions:\n` +
75
+ ` 1. Wait a few minutes and try again.\n` +
76
+ ` 2. Use a different faucet network if available.\n` +
77
+ ` 3. Override retry config via env vars: ` +
78
+ `WOWOK_FAUCET_RETRY_COUNT (default 5), ` +
79
+ `WOWOK_FAUCET_RETRY_INTERVAL_MS (default 3000).`);
80
+ if (lastError instanceof Error && lastError.code) {
81
+ err.code = lastError.code;
82
+ }
83
+ throw err;
84
+ };
85
+ }
@@ -0,0 +1,28 @@
1
+ export type WipPathType = "local_unix" | "local_windows" | "local_relative" | "http_url" | "ipfs" | "data_url" | "unknown";
2
+ export interface WipPathClassification {
3
+ type: WipPathType;
4
+ is_local: boolean;
5
+ is_network: boolean;
6
+ description: string;
7
+ }
8
+ export declare function classifyWipPath(path: string): WipPathClassification;
9
+ export interface WipDeployOption {
10
+ name: string;
11
+ description: string;
12
+ difficulty: "easy" | "medium" | "hard";
13
+ free: boolean;
14
+ result_url_pattern: string;
15
+ steps: string[];
16
+ notes?: string[];
17
+ }
18
+ export declare function getDeployOptions(localFilePath: string): WipDeployOption[];
19
+ export interface WipDeploySuggestion {
20
+ path: string;
21
+ classification: WipPathClassification;
22
+ needs_deployment: boolean;
23
+ reason: string;
24
+ options: WipDeployOption[];
25
+ remind_update_hash: boolean;
26
+ }
27
+ export declare function analyzeWipDeployment(path: string): WipDeploySuggestion;
28
+ export declare function formatDeployHints(suggestion: WipDeploySuggestion): string;