@wowok/agent-mcp 2.6.9 → 2.7.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 (139) hide show
  1. package/README.md +0 -2
  2. package/dist/customer/risk-assessment.d.ts +5 -1
  3. package/dist/customer/risk-assessment.js +4 -4
  4. package/dist/evaluation/arbitration-game.d.ts +16 -0
  5. package/dist/evaluation/arbitration-game.js +48 -0
  6. package/dist/evaluation/builtins/demand-match.d.ts +15 -0
  7. package/dist/evaluation/builtins/demand-match.js +153 -0
  8. package/dist/evaluation/builtins/index.d.ts +3 -0
  9. package/dist/evaluation/builtins/index.js +10 -0
  10. package/dist/evaluation/builtins/service-risk.d.ts +6 -0
  11. package/dist/evaluation/builtins/service-risk.js +55 -0
  12. package/dist/evaluation/capability.d.ts +50 -0
  13. package/dist/evaluation/capability.js +13 -0
  14. package/dist/evaluation/composer.d.ts +13 -0
  15. package/dist/evaluation/composer.js +40 -0
  16. package/dist/evaluation/engine.d.ts +18 -0
  17. package/dist/evaluation/engine.js +175 -0
  18. package/dist/evaluation/extract.d.ts +30 -0
  19. package/dist/evaluation/extract.js +115 -0
  20. package/dist/evaluation/gap.d.ts +2 -0
  21. package/dist/evaluation/gap.js +100 -0
  22. package/dist/evaluation/index.d.ts +11 -0
  23. package/dist/evaluation/index.js +11 -0
  24. package/dist/evaluation/node-game.d.ts +34 -0
  25. package/dist/evaluation/node-game.js +57 -0
  26. package/dist/evaluation/region.d.ts +10 -0
  27. package/dist/evaluation/region.js +36 -0
  28. package/dist/evaluation/types.d.ts +76 -0
  29. package/dist/evaluation/types.js +1 -0
  30. package/dist/evaluation/user-injection.d.ts +6 -0
  31. package/dist/evaluation/user-injection.js +27 -0
  32. package/dist/examples/education-contact-create.json +40 -0
  33. package/dist/examples/freelance-contact-create.json +40 -0
  34. package/dist/examples/insurance-machine-create.json +1 -1
  35. package/dist/examples/insurance-service-allocators.json +1 -1
  36. package/dist/examples/machine-publish.json +1 -1
  37. package/dist/examples/rental-ziroom-service-create.json +1 -1
  38. package/dist/examples/retail-adv-guard-messenger-proof-create.json +1 -1
  39. package/dist/examples/retail-myshop-service-create.json +1 -1
  40. package/dist/examples/retail-myshop-service-customer-required.json +1 -1
  41. package/dist/examples/subscription-contact-create.json +40 -0
  42. package/dist/examples/threebody-machine-create.json +1 -1
  43. package/dist/examples/travel-machine-create.json +1 -1
  44. package/dist/examples/travel-service-create.json +1 -1
  45. package/dist/harness/plan.js +42 -3
  46. package/dist/intent/enrich.d.ts +1 -0
  47. package/dist/intent/enrich.js +67 -0
  48. package/dist/intent/index.d.ts +23 -0
  49. package/dist/intent/index.js +60 -0
  50. package/dist/intent/matcher.d.ts +2 -0
  51. package/dist/intent/matcher.js +81 -0
  52. package/dist/intent/store.d.ts +19 -0
  53. package/dist/intent/store.js +55 -0
  54. package/dist/intent/types.d.ts +33 -0
  55. package/dist/intent/types.js +10 -0
  56. package/dist/knowledge/arbitration-risk.js +1 -11
  57. package/dist/knowledge/contact-templates.d.ts +27 -0
  58. package/dist/knowledge/contact-templates.js +60 -0
  59. package/dist/knowledge/examples-scanner.js +21 -3
  60. package/dist/knowledge/fund-layer.js +8 -4
  61. package/dist/knowledge/industry-generalizer.js +21 -21
  62. package/dist/knowledge/machine-risk.js +0 -1
  63. package/dist/knowledge/mcp-schema-audit.d.ts +1 -1
  64. package/dist/knowledge/mcp-schema-audit.js +8 -4
  65. package/dist/knowledge/service-risk.js +38 -2
  66. package/dist/knowledge/service-templates.js +1 -1
  67. package/dist/knowledge/service-translation.js +2 -2
  68. package/dist/knowledge/template-registry.d.ts +1 -1
  69. package/dist/knowledge/template-registry.js +13 -1
  70. package/dist/knowledge/tools-reference.js +23 -0
  71. package/dist/persona/analyzer.d.ts +16 -0
  72. package/dist/persona/analyzer.js +66 -0
  73. package/dist/persona/bridge.d.ts +5 -0
  74. package/dist/persona/bridge.js +86 -0
  75. package/dist/persona/defaults.d.ts +4 -0
  76. package/dist/persona/defaults.js +195 -0
  77. package/dist/persona/experience-bridge.d.ts +3 -0
  78. package/dist/persona/experience-bridge.js +23 -0
  79. package/dist/persona/index.d.ts +18 -0
  80. package/dist/persona/index.js +71 -0
  81. package/dist/persona/model.d.ts +7 -0
  82. package/dist/persona/model.js +111 -0
  83. package/dist/persona/store.d.ts +19 -0
  84. package/dist/persona/store.js +62 -0
  85. package/dist/persona/types.d.ts +43 -0
  86. package/dist/persona/types.js +5 -0
  87. package/dist/project/handlers.d.ts +5 -0
  88. package/dist/project/handlers.js +23 -1
  89. package/dist/project/semantic-graph.js +47 -0
  90. package/dist/rules.d.ts +2 -0
  91. package/dist/rules.js +43 -0
  92. package/dist/schema/call/semantic.js +23 -1
  93. package/dist/schema/call/service.js +7 -1
  94. package/dist/schema/evaluation/index.d.ts +4239 -0
  95. package/dist/schema/evaluation/index.js +356 -0
  96. package/dist/schema/index.d.ts +4 -0
  97. package/dist/schema/index.js +4 -0
  98. package/dist/schema/intent-radar/index.d.ts +284 -0
  99. package/dist/schema/intent-radar/index.js +89 -0
  100. package/dist/schema/messenger/index.d.ts +171 -0
  101. package/dist/schema/messenger/index.js +5 -0
  102. package/dist/schema/operations.js +1 -1
  103. package/dist/schema/persona/index.d.ts +1985 -0
  104. package/dist/schema/persona/index.js +148 -0
  105. package/dist/schema/project/index.d.ts +2 -2
  106. package/dist/schema/query/index.js +6 -0
  107. package/dist/schema/sync-layer4.d.ts +1 -1
  108. package/dist/schema/sync-layer4.js +9 -5
  109. package/dist/schema/task-status.d.ts +168 -0
  110. package/dist/schema/task-status.js +36 -0
  111. package/dist/schema/trust/index.js +1 -1
  112. package/dist/schemas/evaluation_operation.output.json +677 -0
  113. package/dist/schemas/evaluation_operation.schema.json +903 -0
  114. package/dist/schemas/index.json +25 -1
  115. package/dist/schemas/intent_radar.output.json +229 -0
  116. package/dist/schemas/intent_radar.schema.json +135 -0
  117. package/dist/schemas/messenger_operation.output.json +14 -0
  118. package/dist/schemas/messenger_operation.schema.json +18 -0
  119. package/dist/schemas/onchain_operations.schema.json +2 -2
  120. package/dist/schemas/onchain_operations_service.schema.json +2 -2
  121. package/dist/schemas/persona_operation.output.json +389 -0
  122. package/dist/schemas/persona_operation.schema.json +247 -0
  123. package/dist/schemas/query_task_status.output.json +115 -0
  124. package/dist/schemas/query_task_status.schema.json +24 -0
  125. package/dist/schemas/query_toolkit.output.json +1 -1
  126. package/dist/schemas/trust_score.schema.json +1 -1
  127. package/dist/tools/handlers/evaluation.d.ts +2 -0
  128. package/dist/tools/handlers/evaluation.js +123 -0
  129. package/dist/tools/handlers/intent-radar.d.ts +2 -0
  130. package/dist/tools/handlers/intent-radar.js +76 -0
  131. package/dist/tools/handlers/onchain.js +98 -0
  132. package/dist/tools/handlers/persona.d.ts +4 -0
  133. package/dist/tools/handlers/persona.js +89 -0
  134. package/dist/tools/handlers/task-status.d.ts +0 -168
  135. package/dist/tools/handlers/task-status.js +0 -29
  136. package/dist/tools/index.js +83 -7
  137. package/dist/tools/wip-deploy-assist.d.ts +1 -1
  138. package/dist/tools/wip-deploy-assist.js +33 -5
  139. package/package.json +2 -2
@@ -0,0 +1,115 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://wowok.net/schemas/query_task_status.json",
4
+ "title": "Task Status - Async Operation Tracking",
5
+ "description": "Query the status of a long-running async task (created by build_graph or evaluate_project with async_mode=true). Returns progress percentage, current status, and the final result.",
6
+ "$ref": "#/definitions/query_task_status",
7
+ "definitions": {
8
+ "query_task_status": {
9
+ "type": "object",
10
+ "properties": {
11
+ "task_id": {
12
+ "type": "string",
13
+ "description": "The queried task ID (present when a single task was queried)."
14
+ },
15
+ "task": {
16
+ "anyOf": [
17
+ {
18
+ "type": "object",
19
+ "properties": {
20
+ "task_id": {
21
+ "type": "string",
22
+ "description": "Unique task identifier."
23
+ },
24
+ "operation": {
25
+ "type": "string",
26
+ "description": "Operation type that created this task (e.g. 'build_graph')."
27
+ },
28
+ "status": {
29
+ "type": "string",
30
+ "enum": [
31
+ "pending",
32
+ "running",
33
+ "completed",
34
+ "failed",
35
+ "cancelled"
36
+ ],
37
+ "description": "Current task status."
38
+ },
39
+ "progress": {
40
+ "type": "number",
41
+ "description": "Progress percentage 0-100."
42
+ },
43
+ "message": {
44
+ "type": "string",
45
+ "description": "Human-readable progress message."
46
+ },
47
+ "result": {
48
+ "anyOf": [
49
+ {},
50
+ {
51
+ "type": "null"
52
+ }
53
+ ],
54
+ "description": "Result payload (set when status='completed'). null otherwise."
55
+ },
56
+ "error": {
57
+ "type": [
58
+ "string",
59
+ "null"
60
+ ],
61
+ "description": "Error message (set when status='failed'). null otherwise."
62
+ },
63
+ "started_at": {
64
+ "type": "number",
65
+ "description": "Timestamp (ms) when the task was created."
66
+ },
67
+ "completed_at": {
68
+ "type": [
69
+ "number",
70
+ "null"
71
+ ],
72
+ "description": "Timestamp (ms) when the task reached a terminal state. null if still running."
73
+ },
74
+ "project_id": {
75
+ "type": [
76
+ "string",
77
+ "null"
78
+ ],
79
+ "description": "Project ID associated with this task, if any."
80
+ }
81
+ },
82
+ "required": [
83
+ "task_id",
84
+ "operation",
85
+ "status",
86
+ "progress",
87
+ "message",
88
+ "error",
89
+ "started_at",
90
+ "completed_at",
91
+ "project_id"
92
+ ],
93
+ "additionalProperties": false
94
+ },
95
+ {
96
+ "type": "null"
97
+ }
98
+ ],
99
+ "description": "The task state. null if task_id not found or when listing."
100
+ },
101
+ "tasks": {
102
+ "type": "array",
103
+ "items": {
104
+ "$ref": "#/definitions/query_task_status/properties/task/anyOf/0"
105
+ },
106
+ "description": "List of tasks (present when task_id was omitted)."
107
+ }
108
+ },
109
+ "required": [
110
+ "task"
111
+ ],
112
+ "additionalProperties": false
113
+ }
114
+ }
115
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://wowok.net/schemas/query_task_status.json",
4
+ "title": "Task Status - Async Operation Tracking",
5
+ "description": "Query the status of a long-running async task (created by build_graph or evaluate_project with async_mode=true). Returns progress percentage, current status, and the final result.",
6
+ "$ref": "#/definitions/query_task_status",
7
+ "definitions": {
8
+ "query_task_status": {
9
+ "type": "object",
10
+ "properties": {
11
+ "task_id": {
12
+ "type": "string",
13
+ "description": "The task ID to query (returned by a prior async operation). If omitted, lists all recent tasks (most recent first, max 50)."
14
+ },
15
+ "project_id": {
16
+ "type": "string",
17
+ "description": "Optional filter — when listing tasks (task_id omitted), only return tasks for this project."
18
+ }
19
+ },
20
+ "additionalProperties": false,
21
+ "description": "Query the status of an async task, or list all recent tasks when task_id is omitted."
22
+ }
23
+ }
24
+ }
@@ -2648,7 +2648,7 @@
2648
2648
  },
2649
2649
  "wip": {
2650
2650
  "type": "string",
2651
- "description": "WIP file URL. EMPTY string \"\" skips verification (TESTING ONLY). Production MUST use a real HTTP URL pointing to a .wip file generated by the wip_file tool. Example: \"https://cdn.example.com/products/phone_v1.wip\""
2651
+ "description": "WIP file URL. EMPTY string \"\" skips verification (TESTING ONLY). Production MUST use a real HTTP URL pointing to a .wip file generated by the wip_file tool. ⚠️ NETWORK DEPLOYMENT REQUIRED: the URL is stored ON-CHAIN and every customer fetches it when placing an order. A local file path (C:\\..., /path/file.wip), a localhost/loopback URL or a LAN private-IP URL is unreachable by customers on other machines — it passes the merchant-side set-time verification but makes order_new abort 100% for customers. Either deploy the .wip file to a public endpoint (GitHub Pages / IPFS / your own website) and set this to the public URL, or leave it empty (\"\") to skip WIP verification (TESTING ONLY). Example: \"https://cdn.example.com/products/phone_v1.wip\""
2652
2652
  },
2653
2653
  "wip_hash": {
2654
2654
  "type": "string",
@@ -291,7 +291,7 @@
291
291
  "metrics"
292
292
  ],
293
293
  "additionalProperties": false,
294
- "description": "Optional user-controllable scoring factors (评分体系用户可控). Evaluated at depth=preorder; the composite adjustment (±cap) nudges the order confidence score."
294
+ "description": "Optional user-controllable scoring factors. Evaluated at depth=preorder; the composite adjustment (±cap) nudges the order confidence score."
295
295
  },
296
296
  "network": {
297
297
  "type": "string",
@@ -0,0 +1,2 @@
1
+ import type { ToolDispatchResult } from "../types.js";
2
+ export declare function handleEvaluationOperation(args: unknown): Promise<ToolDispatchResult>;
@@ -0,0 +1,123 @@
1
+ import { strictParse } from "../../schema/index.js";
2
+ import { EvaluationOperationInputSchema } from "../../schema/evaluation/index.js";
3
+ import { evaluateArbGame } from "../../evaluation/arbitration-game.js";
4
+ import { evaluateNodeGame } from "../../evaluation/node-game.js";
5
+ import { summarizeCapabilityGaps } from "../../evaluation/gap.js";
6
+ import { composeServicesForDemand } from "../../evaluation/composer.js";
7
+ import { matchDemandToServices, matchServiceToDemands } from "../../evaluation/builtins/demand-match.js";
8
+ import { assessServiceRisk } from "../../evaluation/builtins/service-risk.js";
9
+ import { registerBuiltinEvaluations } from "../../evaluation/builtins/index.js";
10
+ import { getEffectivePersona, mergeInjections, personaToInjection } from "../../persona/index.js";
11
+ registerBuiltinEvaluations();
12
+ function capabilityFromInput(input) {
13
+ return {
14
+ id: input.id,
15
+ kind: input.kind,
16
+ owner: input.owner ?? "",
17
+ industry: input.industry,
18
+ industry_confidence: input.industry_confidence ?? 0,
19
+ region: input.region,
20
+ price_min: input.price_min !== undefined ? BigInt(input.price_min) : undefined,
21
+ price_max: input.price_max !== undefined ? BigInt(input.price_max) : undefined,
22
+ token_type: input.token_type ?? "",
23
+ ticket_size: input.ticket_size ?? "medium",
24
+ trust_score: input.trust_score ?? 50,
25
+ reputation_like_ratio: input.reputation_like_ratio ?? 1,
26
+ arbitration_independent: input.arbitration_independent ?? false,
27
+ merchant_history_count: input.merchant_history_count ?? 0,
28
+ capabilities: input.capabilities,
29
+ tags: input.tags ?? [],
30
+ keywords: input.keywords ?? [],
31
+ };
32
+ }
33
+ function puzzleRawFromInput(input) {
34
+ return {
35
+ service: {
36
+ ...input.service,
37
+ priceRaw: input.service.priceRaw !== undefined ? BigInt(input.service.priceRaw) : undefined,
38
+ compensation_fund: input.service.compensation_fund
39
+ ? { balance: input.service.compensation_fund.balance !== undefined ? BigInt(input.service.compensation_fund.balance) : undefined }
40
+ : undefined,
41
+ },
42
+ machine_nodes: input.machine_nodes,
43
+ machine_published: input.machine_published,
44
+ machine_paused: input.machine_paused,
45
+ reviews: input.reviews,
46
+ events: input.events,
47
+ similar_services: input.similar_services?.map((s) => ({ price: BigInt(s.price) })),
48
+ order_amount: input.order_amount !== undefined ? BigInt(input.order_amount) : undefined,
49
+ trust_score: input.trust_score,
50
+ arbitration_owner: input.arbitration_owner,
51
+ service_owner: input.service_owner,
52
+ wip_verified: input.wip_verified,
53
+ order_history: input.order_history,
54
+ merchant_reputation: input.merchant_reputation,
55
+ };
56
+ }
57
+ async function resolveInjection(account, explicit) {
58
+ const base = account ? personaToInjection(await getEffectivePersona(account)) : undefined;
59
+ return mergeInjections(base, explicit);
60
+ }
61
+ export async function handleEvaluationOperation(args) {
62
+ const validated = strictParse(EvaluationOperationInputSchema, args, "evaluation_operation input");
63
+ let result;
64
+ switch (validated.action) {
65
+ case "arb_game": {
66
+ result = { action: "arb_game", ...evaluateArbGame(validated.status, validated.perspective) };
67
+ break;
68
+ }
69
+ case "node_game": {
70
+ result = {
71
+ action: "node_game",
72
+ ...evaluateNodeGame({
73
+ current_node: validated.current_node,
74
+ perspective: validated.perspective,
75
+ operable: validated.operable,
76
+ waiting_on: validated.waiting_on ?? [],
77
+ }),
78
+ };
79
+ break;
80
+ }
81
+ case "capability_gap": {
82
+ result = {
83
+ action: "capability_gap",
84
+ report: summarizeCapabilityGaps(capabilityFromInput(validated.demand), capabilityFromInput(validated.service)),
85
+ };
86
+ break;
87
+ }
88
+ case "compose_service": {
89
+ result = {
90
+ action: "compose_service",
91
+ plan: composeServicesForDemand(capabilityFromInput(validated.demand), validated.services.map(capabilityFromInput)),
92
+ };
93
+ break;
94
+ }
95
+ case "demand_match": {
96
+ result = {
97
+ action: "demand_match",
98
+ matches: await matchDemandToServices(capabilityFromInput(validated.demand_cap), validated.services, await resolveInjection(validated.account, validated.injection)),
99
+ };
100
+ break;
101
+ }
102
+ case "service_match": {
103
+ result = {
104
+ action: "service_match",
105
+ matches: await matchServiceToDemands(capabilityFromInput(validated.service_cap), validated.demands, await resolveInjection(validated.account, validated.injection)),
106
+ };
107
+ break;
108
+ }
109
+ case "service_risk": {
110
+ result = {
111
+ action: "service_risk",
112
+ result: await assessServiceRisk(validated.service_id, puzzleRawFromInput(validated.puzzle_raw), await resolveInjection(validated.account, validated.injection), validated.depth ?? "preorder"),
113
+ };
114
+ break;
115
+ }
116
+ default:
117
+ throw new Error("Unknown evaluation_operation action");
118
+ }
119
+ return {
120
+ content: [{ type: "text", text: JSON.stringify({ result }) }],
121
+ structuredContent: { result },
122
+ };
123
+ }
@@ -0,0 +1,2 @@
1
+ import type { ToolDispatchResult } from "../types.js";
2
+ export declare function handleIntentRadar(args: unknown): Promise<ToolDispatchResult>;
@@ -0,0 +1,76 @@
1
+ import { strictParse } from "../../schema/index.js";
2
+ import { IntentRadarInputSchema } from "../../schema/intent-radar/index.js";
3
+ import { createIntent, listIntents, matchAllIntents, matchIntent, removeIntent, } from "../../intent/index.js";
4
+ import { enrichEventsWithObjectText } from "../../intent/enrich.js";
5
+ import { query_events } from "@wowok/wowok";
6
+ const AUTO_PULL_TYPES = [
7
+ "NewOrderEvent",
8
+ "DemandPresentEvent",
9
+ "DemandFeedbackEvent",
10
+ "ProgressEvent",
11
+ "NewEntityEvent",
12
+ "ArbEvent",
13
+ ];
14
+ async function pullRecentEvents(network, limit) {
15
+ const all = [];
16
+ for (const type of AUTO_PULL_TYPES) {
17
+ try {
18
+ const res = await query_events({
19
+ type,
20
+ cursor: null,
21
+ limit,
22
+ order: "descending",
23
+ no_cache: false,
24
+ network: network,
25
+ });
26
+ const data = res?.data;
27
+ if (Array.isArray(data))
28
+ all.push(...data);
29
+ }
30
+ catch {
31
+ }
32
+ }
33
+ return all;
34
+ }
35
+ export async function handleIntentRadar(args) {
36
+ const validated = strictParse(IntentRadarInputSchema, args, "intent_radar input");
37
+ let result;
38
+ switch (validated.action) {
39
+ case "save": {
40
+ const intent = await createIntent({
41
+ intent: validated.intent,
42
+ account: validated.account,
43
+ role: validated.role,
44
+ target_types: validated.target_types,
45
+ });
46
+ result = { action: "save", intent };
47
+ break;
48
+ }
49
+ case "list": {
50
+ result = { action: "list", intents: await listIntents(validated.account) };
51
+ break;
52
+ }
53
+ case "remove": {
54
+ result = { action: "remove", removed: await removeIntent(validated.id) };
55
+ break;
56
+ }
57
+ case "match": {
58
+ let events = validated.events ?? [];
59
+ if (events.length === 0) {
60
+ events = await pullRecentEvents(validated.network, validated.limit ?? 5);
61
+ }
62
+ events = await enrichEventsWithObjectText(events, validated.network);
63
+ const results = validated.intent_id
64
+ ? [await matchIntent(validated.intent_id, events)].filter((r) => r !== null)
65
+ : await matchAllIntents(events, validated.account);
66
+ result = { action: "match", results };
67
+ break;
68
+ }
69
+ default:
70
+ throw new Error("Unknown intent_radar action");
71
+ }
72
+ return {
73
+ content: [{ type: "text", text: JSON.stringify({ result }) }],
74
+ structuredContent: { result },
75
+ };
76
+ }
@@ -9,6 +9,7 @@ import { isProjectServiceEnabled, normalize as projectNormalize, } from "../../p
9
9
  import { getSkillsHintForOnchain } from "../../schema/utils/skills-recommendation.js";
10
10
  import { prepareFundOperation, generateSkillsHint as generateFundSkillsHint } from "../../knowledge/fund-layer.js";
11
11
  import { generateGuardSubmissionPrompt, formatSubmissionPrompt } from "../../knowledge/guard-submission-prompt.js";
12
+ import { classifyWipPath } from "../wip-deploy-assist.js";
12
13
  import { withHarness } from "../shared.js";
13
14
  const REFERRER_FILE = join(getWowMcpDir(), "referrer");
14
15
  function readStoredReferrer() {
@@ -362,6 +363,99 @@ function buildFundBlockResult(fund, operationType) {
362
363
  },
363
364
  };
364
365
  }
366
+ function checkSalesWipNetworkDeployment(data, env, warnings) {
367
+ const sales = data?.sales;
368
+ if (!sales || typeof sales !== "object")
369
+ return null;
370
+ if (sales.op !== "add" && sales.op !== "set")
371
+ return null;
372
+ const items = Array.isArray(sales.sales) ? sales.sales : [];
373
+ if (items.length === 0)
374
+ return null;
375
+ const isLocalnet = env?.network === "localnet";
376
+ for (const sale of items) {
377
+ const wip = sale?.wip;
378
+ if (typeof wip !== "string" || wip === "")
379
+ continue;
380
+ const classification = classifyWipPath(wip);
381
+ if (!classification.is_local)
382
+ continue;
383
+ if (classification.type === "local_http") {
384
+ if (isLocalnet) {
385
+ warnings.push(`WIP INTERNAL-TEST-ONLY REMINDER (sale "${sale.name}"): wip = "${wip}" is a ` +
386
+ `local-network URL (${classification.description}). It is reachable ONLY on the ` +
387
+ `local machine / LAN where a local WIP server runs. This is acceptable for LOCALNET ` +
388
+ `internal testing, but customers and on-chain verifiers on OTHER machines CANNOT ` +
389
+ `reach it — on testnet/mainnet, every customer order_new would fail 100% because the ` +
390
+ `WIP file cannot be verified. For any non-localnet deployment, replace this URL with ` +
391
+ `a public endpoint (GitHub Pages / IPFS / your own website) or leave sale.wip empty.`);
392
+ continue;
393
+ }
394
+ const typeLabel = `a local-network URL (${classification.description})`;
395
+ const errorText = `⛔ WIP IS NOT NETWORK-DEPLOYABLE — sales NOT submitted\n\n` +
396
+ `Sale "${sale.name}" has wip = "${wip}", which is ${typeLabel}.\n\n` +
397
+ `INTERNAL TEST USE ONLY: this URL is reachable only from the merchant's own machine/LAN. ` +
398
+ `Customers on other machines cannot reach it, so verifying the WIP file at order time ` +
399
+ `fails and order_new aborts 100%. Local-network WIP URLs are allowed ONLY for ` +
400
+ `localnet internal testing (env.network = "localnet"); on ${env?.network ?? "this network"} ` +
401
+ `they must never be stored on-chain.\n\n` +
402
+ `Fix: deploy the .wip file to a publicly reachable endpoint, then re-run with ` +
403
+ `sale.wip = "<public URL>" (e.g. GitHub Pages, IPFS gateway, or your own website).\n` +
404
+ ` • GitHub Pages: https://<user>.github.io/<repo>/<file>.wip\n` +
405
+ ` • IPFS: https://ipfs.io/ipfs/<CID> (or ipfs://<CID>)\n` +
406
+ ` • Your site: https://<your-domain>/<path>/<file>.wip\n` +
407
+ `Alternatively, set sale.wip = "" (empty) to SKIP WIP verification — ` +
408
+ `TESTING ONLY, not recommended for production.`;
409
+ return {
410
+ content: [{ type: "text", text: errorText }],
411
+ structuredContent: {
412
+ result: {
413
+ type: "error",
414
+ error: `Sale "${sale.name}" wip is ${typeLabel}: ${wip}. Local-network URLs are INTERNAL TEST USE ONLY — customers cannot verify them, aborting order_new 100%.`,
415
+ error_code: "wip_not_network_deployed",
416
+ retryable: true,
417
+ recovery_hint: "Deploy the .wip file to a public endpoint (GitHub Pages / IPFS / your own website) " +
418
+ "and set sale.wip to the public URL; or set sale.wip = \"\" (empty) to skip WIP " +
419
+ "verification (TESTING ONLY).",
420
+ related_object: sale.name,
421
+ },
422
+ message: "WIP is a local-network URL (INTERNAL TEST USE ONLY) — sales not submitted. See recovery_hint.",
423
+ },
424
+ };
425
+ }
426
+ const typeLabel = `a LOCAL ${classification.type} path`;
427
+ const errorText = `⛔ WIP IS NOT NETWORK-DEPLOYABLE — sales NOT submitted\n\n` +
428
+ `Sale "${sale.name}" has wip = "${wip}", which is ${typeLabel}.\n\n` +
429
+ `Why this is fatal: the WIP path is stored ON-CHAIN and every customer ` +
430
+ `(on a different machine) fetches it when placing an order. A local path / ` +
431
+ `localhost / LAN URL is reachable only from YOUR machine — it passes the ` +
432
+ `merchant-side set-time verification but makes order_new abort 100% for customers.\n\n` +
433
+ `Fix: deploy the .wip file to a publicly reachable endpoint, then re-run with ` +
434
+ `sale.wip = "<public URL>" (e.g. GitHub Pages, IPFS gateway, or your own website).\n` +
435
+ ` • GitHub Pages: https://<user>.github.io/<repo>/<file>.wip\n` +
436
+ ` • IPFS: https://ipfs.io/ipfs/<CID> (or ipfs://<CID>)\n` +
437
+ ` • Your site: https://<your-domain>/<path>/<file>.wip\n` +
438
+ `Alternatively, set sale.wip = "" (empty) to SKIP WIP verification — ` +
439
+ `TESTING ONLY, not recommended for production.`;
440
+ return {
441
+ content: [{ type: "text", text: errorText }],
442
+ structuredContent: {
443
+ result: {
444
+ type: "error",
445
+ error: `Sale "${sale.name}" wip is ${typeLabel}: ${wip}. Local WIP paths are unreachable by customers and abort order_new 100%.`,
446
+ error_code: "wip_not_network_deployed",
447
+ retryable: true,
448
+ recovery_hint: "Deploy the .wip file to a public endpoint (GitHub Pages / IPFS / your own website) " +
449
+ "and set sale.wip to the public URL; or set sale.wip = \"\" (empty) to skip WIP " +
450
+ "verification (TESTING ONLY).",
451
+ related_object: sale.name,
452
+ },
453
+ message: "WIP is not network-deployable — sales not submitted. See recovery_hint.",
454
+ },
455
+ };
456
+ }
457
+ return null;
458
+ }
365
459
  export async function handleOnchainOperations(args) {
366
460
  let validated;
367
461
  let constraintWarnings = [];
@@ -479,6 +573,10 @@ export async function handleOnchainOperations(args) {
479
573
  if (circularDepGuide) {
480
574
  return circularDepGuide;
481
575
  }
576
+ const wipDeployBlock = checkSalesWipNetworkDeployment(data, env, constraintWarnings);
577
+ if (wipDeployBlock) {
578
+ return wipDeployBlock;
579
+ }
482
580
  const fundResult = await prepareFundOperation(data, "service", env?.network);
483
581
  if (fundResult.blockingErrors.length > 0) {
484
582
  return buildFundBlockResult(fundResult, "service");
@@ -0,0 +1,4 @@
1
+ import { GENERIC_PERSONA } from "../../persona/index.js";
2
+ import type { ToolDispatchResult } from "../types.js";
3
+ export declare function handlePersonaOperation(args: unknown): Promise<ToolDispatchResult>;
4
+ export { GENERIC_PERSONA };
@@ -0,0 +1,89 @@
1
+ import { strictParse } from "../../schema/index.js";
2
+ import { PersonaOperationInputSchema } from "../../schema/persona/index.js";
3
+ import { GENERIC_PERSONA, analyzePersonaContext, applyAnalysisToCurrent, countPersonaLines, getEffectivePersona, getPersonaStore, listIndustryDefaults, personaToContextText, personaToInjection, syncExperienceToCurrent, validatePersona, } from "../../persona/index.js";
4
+ export async function handlePersonaOperation(args) {
5
+ const validated = strictParse(PersonaOperationInputSchema, args, "persona_operation input");
6
+ const store = getPersonaStore();
7
+ let result;
8
+ switch (validated.action) {
9
+ case "get": {
10
+ const effective = await getEffectivePersona(validated.account, validated.industry, validated.role);
11
+ result = {
12
+ action: "get",
13
+ effective,
14
+ context_text: personaToContextText(effective),
15
+ injection: personaToInjection(effective),
16
+ };
17
+ break;
18
+ }
19
+ case "list": {
20
+ result = {
21
+ action: "list",
22
+ personas: await store.list(),
23
+ defaults: listIndustryDefaults(),
24
+ };
25
+ break;
26
+ }
27
+ case "set": {
28
+ const existing = validated.scope === "account" && validated.account
29
+ ? await store.get("account", validated.account)
30
+ : await store.get("system");
31
+ const persona = {
32
+ version: 1,
33
+ scope: validated.scope,
34
+ account: validated.scope === "account" ? validated.account : undefined,
35
+ role: validated.role,
36
+ industries: validated.industries,
37
+ long_term: validated.long_term ?? existing?.long_term ?? {},
38
+ current: validated.current ?? existing?.current ?? {},
39
+ updated_at: Date.now(),
40
+ };
41
+ const issues = validatePersona(persona);
42
+ if (issues.length > 0) {
43
+ throw new Error(`persona invalid: ${issues.join("; ")}`);
44
+ }
45
+ await store.put(persona);
46
+ result = { action: "set", persona, line_count: countPersonaLines(persona) };
47
+ break;
48
+ }
49
+ case "analyze": {
50
+ result = { action: "analyze", analysis: analyzePersonaContext(validated.context) };
51
+ break;
52
+ }
53
+ case "apply": {
54
+ const analysis = analyzePersonaContext(validated.context);
55
+ const persona = await applyAnalysisToCurrent(validated.scope, validated.account, analysis, validated.mode ?? "merge", validated.role, validated.industries);
56
+ result = {
57
+ action: "apply",
58
+ persona,
59
+ effective: await getEffectivePersona(validated.account, undefined, persona.role),
60
+ applied: analysis.suggestions,
61
+ };
62
+ break;
63
+ }
64
+ case "sync": {
65
+ const synced = await syncExperienceToCurrent(validated.account);
66
+ result = {
67
+ action: "sync",
68
+ persona: synced.persona,
69
+ applied: synced.applied,
70
+ effective: synced.effective,
71
+ };
72
+ break;
73
+ }
74
+ case "remove": {
75
+ const removed = validated.scope === "account" && validated.account
76
+ ? await store.delete("account", validated.account)
77
+ : await store.delete("system");
78
+ result = { action: "remove", removed };
79
+ break;
80
+ }
81
+ default:
82
+ throw new Error("Unknown persona_operation action");
83
+ }
84
+ return {
85
+ content: [{ type: "text", text: JSON.stringify({ result }) }],
86
+ structuredContent: { result },
87
+ };
88
+ }
89
+ export { GENERIC_PERSONA };