@varun-ai07/covenant-mcp 1.2.3 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1056 -170
- package/dist/abis/MultiTokenEscrow.json +836 -0
- package/dist/abis/v2/AgentRegistry.json +872 -0
- package/dist/abis/v2/DisputeResolution.json +493 -0
- package/dist/abis/v2/InsurancePool.json +645 -0
- package/dist/abis/v2/ReceiptVerifier.json +394 -0
- package/dist/abis/v2/RevisionManager.json +544 -0
- package/dist/abis/v2/TaskEscrow.json +1018 -0
- package/dist/cli.js +0 -0
- package/dist/config.d.ts +13 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +84 -25
- package/dist/config.js.map +1 -1
- package/dist/lib/did.d.ts +72 -0
- package/dist/lib/did.d.ts.map +1 -0
- package/dist/lib/did.js +115 -0
- package/dist/lib/did.js.map +1 -0
- package/dist/lib/formatResponse.d.ts +33 -0
- package/dist/lib/formatResponse.d.ts.map +1 -0
- package/dist/lib/formatResponse.js +92 -0
- package/dist/lib/formatResponse.js.map +1 -0
- package/dist/lib/schemaHelpers.d.ts +11 -0
- package/dist/lib/schemaHelpers.d.ts.map +1 -0
- package/dist/lib/schemaHelpers.js +11 -0
- package/dist/lib/schemaHelpers.js.map +1 -0
- package/dist/lib/store.d.ts +10 -0
- package/dist/lib/store.d.ts.map +1 -0
- package/dist/lib/store.js +39 -0
- package/dist/lib/store.js.map +1 -0
- package/dist/lib/verify.d.ts +21 -0
- package/dist/lib/verify.d.ts.map +1 -0
- package/dist/lib/verify.js +568 -0
- package/dist/lib/verify.js.map +1 -0
- package/dist/schemas.d.ts +5 -5
- package/dist/schemas.d.ts.map +1 -1
- package/dist/server.d.ts +1 -25
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +53 -37
- package/dist/server.js.map +1 -1
- package/dist/shared-types.d.ts +67 -0
- package/dist/shared-types.d.ts.map +1 -0
- package/dist/shared-types.js +86 -0
- package/dist/shared-types.js.map +1 -0
- package/dist/tools/account-abstraction.d.ts +3 -0
- package/dist/tools/account-abstraction.d.ts.map +1 -0
- package/dist/tools/account-abstraction.js +364 -0
- package/dist/tools/account-abstraction.js.map +1 -0
- package/dist/tools/batches.d.ts.map +1 -1
- package/dist/tools/batches.js +68 -37
- package/dist/tools/batches.js.map +1 -1
- package/dist/tools/bounties.d.ts +3 -0
- package/dist/tools/bounties.d.ts.map +1 -0
- package/dist/tools/bounties.js +304 -0
- package/dist/tools/bounties.js.map +1 -0
- package/dist/tools/bridge.d.ts +3 -0
- package/dist/tools/bridge.d.ts.map +1 -0
- package/dist/tools/bridge.js +190 -0
- package/dist/tools/bridge.js.map +1 -0
- package/dist/tools/collectives.d.ts.map +1 -1
- package/dist/tools/collectives.js +74 -46
- package/dist/tools/collectives.js.map +1 -1
- package/dist/tools/covenant-help.d.ts +3 -0
- package/dist/tools/covenant-help.d.ts.map +1 -0
- package/dist/tools/covenant-help.js +321 -0
- package/dist/tools/covenant-help.js.map +1 -0
- package/dist/tools/cross-chain.d.ts +3 -0
- package/dist/tools/cross-chain.d.ts.map +1 -0
- package/dist/tools/cross-chain.js +77 -0
- package/dist/tools/cross-chain.js.map +1 -0
- package/dist/tools/disputes.d.ts.map +1 -1
- package/dist/tools/disputes.js +39 -33
- package/dist/tools/disputes.js.map +1 -1
- package/dist/tools/escrow.d.ts.map +1 -1
- package/dist/tools/escrow.js +248 -199
- package/dist/tools/escrow.js.map +1 -1
- package/dist/tools/fiat-onramp.d.ts +3 -0
- package/dist/tools/fiat-onramp.d.ts.map +1 -0
- package/dist/tools/fiat-onramp.js +108 -0
- package/dist/tools/fiat-onramp.js.map +1 -0
- package/dist/tools/governance.d.ts +3 -0
- package/dist/tools/governance.d.ts.map +1 -0
- package/dist/tools/governance.js +271 -0
- package/dist/tools/governance.js.map +1 -0
- package/dist/tools/grants.d.ts +3 -0
- package/dist/tools/grants.d.ts.map +1 -0
- package/dist/tools/grants.js +269 -0
- package/dist/tools/grants.js.map +1 -0
- package/dist/tools/insurance.d.ts.map +1 -1
- package/dist/tools/insurance.js +92 -45
- package/dist/tools/insurance.js.map +1 -1
- package/dist/tools/market.d.ts.map +1 -1
- package/dist/tools/market.js +122 -103
- package/dist/tools/market.js.map +1 -1
- package/dist/tools/matching.d.ts +3 -0
- package/dist/tools/matching.d.ts.map +1 -0
- package/dist/tools/matching.js +233 -0
- package/dist/tools/matching.js.map +1 -0
- package/dist/tools/messaging.d.ts +3 -0
- package/dist/tools/messaging.d.ts.map +1 -0
- package/dist/tools/messaging.js +159 -0
- package/dist/tools/messaging.js.map +1 -0
- package/dist/tools/multi-token.d.ts +3 -0
- package/dist/tools/multi-token.d.ts.map +1 -0
- package/dist/tools/multi-token.js +274 -0
- package/dist/tools/multi-token.js.map +1 -0
- package/dist/tools/offchain-coordinator.d.ts +3 -0
- package/dist/tools/offchain-coordinator.d.ts.map +1 -0
- package/dist/tools/offchain-coordinator.js +436 -0
- package/dist/tools/offchain-coordinator.js.map +1 -0
- package/dist/tools/protocol.d.ts.map +1 -1
- package/dist/tools/protocol.js +19 -6
- package/dist/tools/protocol.js.map +1 -1
- package/dist/tools/receipts.d.ts.map +1 -1
- package/dist/tools/receipts.js +39 -39
- package/dist/tools/receipts.js.map +1 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +90 -43
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/reputation-vc.d.ts +3 -0
- package/dist/tools/reputation-vc.d.ts.map +1 -0
- package/dist/tools/reputation-vc.js +438 -0
- package/dist/tools/reputation-vc.js.map +1 -0
- package/dist/tools/revisions.d.ts +3 -0
- package/dist/tools/revisions.d.ts.map +1 -0
- package/dist/tools/revisions.js +108 -0
- package/dist/tools/revisions.js.map +1 -0
- package/dist/tools/router.d.ts +3 -0
- package/dist/tools/router.d.ts.map +1 -0
- package/dist/tools/router.js +104 -0
- package/dist/tools/router.js.map +1 -0
- package/dist/tools/streaming.d.ts +3 -0
- package/dist/tools/streaming.d.ts.map +1 -0
- package/dist/tools/streaming.js +350 -0
- package/dist/tools/streaming.js.map +1 -0
- package/dist/tools/templates.d.ts +3 -0
- package/dist/tools/templates.d.ts.map +1 -0
- package/dist/tools/templates.js +392 -0
- package/dist/tools/templates.js.map +1 -0
- package/dist/tools/training.d.ts +3 -0
- package/dist/tools/training.d.ts.map +1 -0
- package/dist/tools/training.js +304 -0
- package/dist/tools/training.js.map +1 -0
- package/dist/tools/v2-settlement.d.ts +3 -0
- package/dist/tools/v2-settlement.d.ts.map +1 -0
- package/dist/tools/v2-settlement.js +226 -0
- package/dist/tools/v2-settlement.js.map +1 -0
- package/dist/tools/verification.d.ts +3 -0
- package/dist/tools/verification.d.ts.map +1 -0
- package/dist/tools/verification.js +215 -0
- package/dist/tools/verification.js.map +1 -0
- package/dist/tools/verify-deep.d.ts +3 -0
- package/dist/tools/verify-deep.d.ts.map +1 -0
- package/dist/tools/verify-deep.js +125 -0
- package/dist/tools/verify-deep.js.map +1 -0
- package/dist/types.d.ts +16 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -23
- package/dist/types.js.map +1 -1
- package/package.json +2 -1
package/dist/tools/escrow.js
CHANGED
|
@@ -11,7 +11,9 @@ import { z } from "zod";
|
|
|
11
11
|
import { parseEther, formatEther, isAddress } from "viem";
|
|
12
12
|
import { loadAbi, CONTRACTS, getAccount } from "../config.js";
|
|
13
13
|
import { executeOrPrepare, readContract } from "../handlers/wallet.js";
|
|
14
|
-
import { formatTxResult, formatReadResult
|
|
14
|
+
import { formatTxResult, formatReadResult } from "../handlers/transactions.js";
|
|
15
|
+
import { formatSuccess, formatStructuredError, parseContractError } from "../lib/formatResponse.js";
|
|
16
|
+
import { ethAddress, ethAmount, ipfsCid, unixDeadline, taskId as taskIdSchema, priority as prioritySchema } from "../lib/schemaHelpers.js";
|
|
15
17
|
import { TASK_STATUS } from "../types.js";
|
|
16
18
|
const ABI = loadAbi("TaskEscrow");
|
|
17
19
|
// Input validation schemas
|
|
@@ -53,39 +55,29 @@ export function registerEscrowTools(server) {
|
|
|
53
55
|
// ──────────────────────────────────────────────────────────────
|
|
54
56
|
server.registerTool("corven_create_task", {
|
|
55
57
|
title: "Create & Fund Task",
|
|
56
|
-
description: "
|
|
57
|
-
"
|
|
58
|
-
"
|
|
58
|
+
description: "Creates a direct-hire task and locks payment in escrow in a single transaction.\n" +
|
|
59
|
+
"USE WHEN: You have a specific worker address and want to hire them directly. Use corven_post_open_task if you want competitive bidding instead.\n" +
|
|
60
|
+
"REQUIRES: Both client AND worker must be registered with corven_register_agent. Client wallet needs payment amount plus ~0.0003 ETH gas.\n" +
|
|
61
|
+
"RETURNS: taskId (save this for all subsequent calls), escrow status, deadline, worker address, Basescan link.\n" +
|
|
62
|
+
"COMES AFTER: corven_find_workers to get the worker address.\n" +
|
|
63
|
+
"COMES BEFORE: Worker calls corven_submit_work. Client calls corven_verify_task.\n" +
|
|
64
|
+
"NOTE: Payment is locked — neither party can access it until verification completes.",
|
|
59
65
|
inputSchema: {
|
|
60
|
-
worker:
|
|
61
|
-
payment:
|
|
62
|
-
deadline:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
descriptionHash: z
|
|
66
|
-
.string()
|
|
67
|
-
.describe("IPFS CID or on-chain hash for task description"),
|
|
68
|
-
priority: z
|
|
69
|
-
.number()
|
|
70
|
-
.optional()
|
|
71
|
-
.describe("Priority level 0-3 (Low/Medium/High/Urgent). Default 1 (Medium)"),
|
|
66
|
+
worker: ethAddress,
|
|
67
|
+
payment: ethAmount,
|
|
68
|
+
deadline: unixDeadline,
|
|
69
|
+
descriptionHash: ipfsCid,
|
|
70
|
+
priority: prioritySchema,
|
|
72
71
|
},
|
|
73
72
|
}, async ({ worker, payment, deadline, descriptionHash, priority }) => {
|
|
74
73
|
try {
|
|
75
|
-
// Validate input
|
|
76
74
|
const validationResult = createTaskSchema.safeParse({ worker, payment, deadline, descriptionHash, priority });
|
|
77
75
|
if (!validationResult.success) {
|
|
78
|
-
return
|
|
76
|
+
return formatStructuredError("Invalid task parameters.", validationResult.error.issues.map((e) => e.message).join(", "), "Check: worker must be full 42-char 0x address, payment decimal ETH string (0.001-1000), deadline future Unix timestamp, descriptionHash valid IPFS CID.", true);
|
|
79
77
|
}
|
|
80
|
-
// Validation successful, use validated values
|
|
81
|
-
const validatedWorker = worker;
|
|
82
|
-
const validatedPayment = payment;
|
|
83
|
-
const validatedDeadline = deadline;
|
|
84
|
-
const validatedDescriptionHash = descriptionHash;
|
|
85
|
-
const validatedPriority = priority;
|
|
86
78
|
const account = getAccount();
|
|
87
79
|
if (!account) {
|
|
88
|
-
return
|
|
80
|
+
return formatStructuredError("No private key configured.", "PRIVATE_KEY environment variable is not set.", "Set PRIVATE_KEY in your .env file.", false);
|
|
89
81
|
}
|
|
90
82
|
const paymentWei = parseEther(payment);
|
|
91
83
|
const priorityLevel = priority ?? 1;
|
|
@@ -96,18 +88,33 @@ export function registerEscrowTools(server) {
|
|
|
96
88
|
const totalFeeBps = PROTOCOL_FEE_BPS + priorityFeeBps;
|
|
97
89
|
const feeAmount = paymentWei * totalFeeBps / 10000n;
|
|
98
90
|
const totalValue = paymentWei + feeAmount;
|
|
99
|
-
// Use createAndFundTask which is the combined function used by client.ts
|
|
100
91
|
const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "createAndFundTask", [
|
|
101
|
-
|
|
92
|
+
worker,
|
|
102
93
|
paymentWei,
|
|
103
|
-
BigInt(
|
|
104
|
-
|
|
105
|
-
], totalValue
|
|
106
|
-
)
|
|
94
|
+
BigInt(deadline),
|
|
95
|
+
descriptionHash,
|
|
96
|
+
], totalValue);
|
|
97
|
+
if (result.status === "success") {
|
|
98
|
+
const deadlineDate = new Date(deadline * 1000).toUTCString();
|
|
99
|
+
return formatSuccess(`Task created. ${payment} ETH locked in escrow for worker.`, {
|
|
100
|
+
worker,
|
|
101
|
+
client: account,
|
|
102
|
+
payment: `${payment} ETH`,
|
|
103
|
+
deadline: deadlineDate,
|
|
104
|
+
specificationIpfs: descriptionHash,
|
|
105
|
+
status: "Funded",
|
|
106
|
+
priority: priorityLevel,
|
|
107
|
+
}, result.txHash, [
|
|
108
|
+
"Wait for worker to call corven_submit_work with your taskId.",
|
|
109
|
+
"Then call corven_verify_task to release payment after reviewing work.",
|
|
110
|
+
"Check status anytime with corven_get_task.",
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
107
113
|
return formatTxResult(result);
|
|
108
114
|
}
|
|
109
115
|
catch (e) {
|
|
110
|
-
|
|
116
|
+
const parsed = parseContractError(e);
|
|
117
|
+
return formatStructuredError(parsed.error, parsed.cause, parsed.fix, parsed.retryable);
|
|
111
118
|
}
|
|
112
119
|
});
|
|
113
120
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -115,23 +122,22 @@ export function registerEscrowTools(server) {
|
|
|
115
122
|
// ──────────────────────────────────────────────────────────────
|
|
116
123
|
server.registerTool("corven_get_task", {
|
|
117
124
|
title: "Get Task Details",
|
|
118
|
-
description: "
|
|
119
|
-
"
|
|
125
|
+
description: "Returns complete details for any task including current lifecycle status.\n" +
|
|
126
|
+
"USE WHEN: Checking if a worker has submitted work. Confirming payment released. Getting deliverable IPFS hash. Checking deadline.\n" +
|
|
127
|
+
"REQUIRES: Nothing. Free read-only call.\n" +
|
|
128
|
+
"RETURNS: Status, client/worker addresses, payment, deadline, specification hash, deliverable hash.\n" +
|
|
129
|
+
"STATUS MEANINGS: Funded=worker can begin. InProgress=worker acknowledged. Submitted=work ready for review. Completed=paid. Failed=rejected or expired.",
|
|
120
130
|
inputSchema: {
|
|
121
|
-
taskId:
|
|
131
|
+
taskId: taskIdSchema,
|
|
122
132
|
},
|
|
123
133
|
}, async ({ taskId }) => {
|
|
124
134
|
try {
|
|
125
|
-
|
|
126
|
-
const taskIdParam = taskId;
|
|
127
|
-
const validationResult = getTaskSchema.safeParse({ taskId: taskIdParam });
|
|
135
|
+
const validationResult = getTaskSchema.safeParse({ taskId });
|
|
128
136
|
if (!validationResult.success) {
|
|
129
|
-
return
|
|
137
|
+
return formatStructuredError("Invalid task ID.", `Received '${taskId}' — must be a positive integer.`, "Pass the numeric taskId returned by corven_create_task. Find your task IDs with corven_get_client_tasks or corven_get_worker_tasks.", false);
|
|
130
138
|
}
|
|
131
|
-
// Use validated taskId
|
|
132
139
|
const validatedTaskId = validationResult.data.taskId;
|
|
133
140
|
const data = await readContract(CONTRACTS.TaskEscrow, ABI, "getTask", [BigInt(validatedTaskId)]);
|
|
134
|
-
// Enrich status and priority with human-readable labels
|
|
135
141
|
const enriched = {
|
|
136
142
|
...data,
|
|
137
143
|
statusLabel: TASK_STATUS[data.status] ?? `Unknown(${data.status})`,
|
|
@@ -140,7 +146,8 @@ export function registerEscrowTools(server) {
|
|
|
140
146
|
return formatReadResult(enriched, `Task #${taskId}`);
|
|
141
147
|
}
|
|
142
148
|
catch (e) {
|
|
143
|
-
|
|
149
|
+
const parsed = parseContractError(e);
|
|
150
|
+
return formatStructuredError(parsed.error, parsed.cause, parsed.fix, parsed.retryable);
|
|
144
151
|
}
|
|
145
152
|
});
|
|
146
153
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -148,33 +155,35 @@ export function registerEscrowTools(server) {
|
|
|
148
155
|
// ──────────────────────────────────────────────────────────────
|
|
149
156
|
server.registerTool("corven_submit_work", {
|
|
150
157
|
title: "Submit Work Deliverable",
|
|
151
|
-
description: "Worker submits
|
|
152
|
-
"
|
|
158
|
+
description: "Worker submits completed deliverable IPFS hash on-chain. Commits work permanently and notifies the client.\n" +
|
|
159
|
+
"USE WHEN: You are the worker and have finished executing the task. Upload deliverable to IPFS first. Then call this with the CID.\n" +
|
|
160
|
+
"REQUIRES: You must be the assigned worker. Task status must be Funded or InProgress. Deadline must not have passed.\n" +
|
|
161
|
+
"RETURNS: Submission confirmation, IPFS hash recorded on-chain, next action for the client.\n" +
|
|
162
|
+
"COMES AFTER: Worker executes task off-chain and uploads deliverable to IPFS.\n" +
|
|
163
|
+
"COMES BEFORE: Client calls corven_verify_task.",
|
|
153
164
|
inputSchema: {
|
|
154
|
-
taskId:
|
|
155
|
-
deliverableHash:
|
|
156
|
-
.string()
|
|
157
|
-
.describe("IPFS CID or hash of the deliverable"),
|
|
165
|
+
taskId: taskIdSchema,
|
|
166
|
+
deliverableHash: ipfsCid,
|
|
158
167
|
},
|
|
159
168
|
}, async ({ taskId, deliverableHash }) => {
|
|
160
169
|
try {
|
|
161
|
-
// Validate input
|
|
162
170
|
const validationResult = submitWorkSchema.safeParse({ taskId, deliverableHash });
|
|
163
171
|
if (!validationResult.success) {
|
|
164
|
-
return
|
|
172
|
+
return formatStructuredError("Invalid parameters.", validationResult.error.issues.map((e) => e.message).join(", "), "taskId must be a positive integer. deliverableHash must be a valid IPFS CID (Qm... or bafy...).", true);
|
|
165
173
|
}
|
|
166
|
-
// Use validated values
|
|
167
|
-
const validatedTaskId = validationResult.data.taskId;
|
|
168
|
-
const validatedDeliverableHash = validationResult.data.deliverableHash;
|
|
169
174
|
const account = getAccount();
|
|
170
175
|
if (!account) {
|
|
171
|
-
return
|
|
176
|
+
return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
|
|
177
|
+
}
|
|
178
|
+
const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "submitWork", [BigInt(taskId), deliverableHash]);
|
|
179
|
+
if (result.status === "success") {
|
|
180
|
+
return formatSuccess(`Work submitted for Task #${taskId}. Client will be notified.`, { taskId, deliverableHash, status: "Submitted" }, result.txHash, ["Wait for client to review and call corven_verify_task.", "Payment releases automatically on approval."]);
|
|
172
181
|
}
|
|
173
|
-
const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "submitWork", [BigInt(validatedTaskId), validatedDeliverableHash]);
|
|
174
182
|
return formatTxResult(result);
|
|
175
183
|
}
|
|
176
184
|
catch (e) {
|
|
177
|
-
|
|
185
|
+
const parsed = parseContractError(e);
|
|
186
|
+
return formatStructuredError(parsed.error, parsed.cause, parsed.fix, parsed.retryable);
|
|
178
187
|
}
|
|
179
188
|
});
|
|
180
189
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -182,30 +191,39 @@ export function registerEscrowTools(server) {
|
|
|
182
191
|
// ──────────────────────────────────────────────────────────────
|
|
183
192
|
server.registerTool("corven_verify_task", {
|
|
184
193
|
title: "Verify & Approve Task",
|
|
185
|
-
description: "Client
|
|
186
|
-
"
|
|
194
|
+
description: "Client approves submitted work. Triggers automatic payment release. Worker receives ETH. Reputation updates. ERC-8004 receipt created.\n" +
|
|
195
|
+
"USE WHEN: You are the client. Worker has submitted work (corven_get_task shows status Submitted). You have reviewed and approve.\n" +
|
|
196
|
+
"REQUIRES: You must be the client. Task status must be Submitted. Call corven_dispute_task instead to reject.\n" +
|
|
197
|
+
"RETURNS: Payment release confirmation, new reputation scores for both agents, receipt ID, Basescan link.\n" +
|
|
198
|
+
"COMES AFTER: corven_submit_work by the worker.\n" +
|
|
199
|
+
"NOTE: This is the final step. Payment releases automatically — no manual transfer needed.",
|
|
187
200
|
inputSchema: {
|
|
188
|
-
taskId:
|
|
189
|
-
success: z.boolean().describe("
|
|
201
|
+
taskId: taskIdSchema,
|
|
202
|
+
success: z.boolean().describe("true = approve work and release payment, false = reject work and refund client"),
|
|
190
203
|
},
|
|
191
204
|
}, async ({ taskId, success }) => {
|
|
192
205
|
try {
|
|
193
|
-
// Validate input
|
|
194
206
|
const validationResult = verifyTaskSchema.safeParse({ taskId, success });
|
|
195
207
|
if (!validationResult.success) {
|
|
196
|
-
return
|
|
208
|
+
return formatStructuredError("Invalid parameters.", validationResult.error.issues.map((e) => e.message).join(", "), "taskId must be positive integer. success must be boolean.", true);
|
|
197
209
|
}
|
|
198
|
-
// Use validated values
|
|
199
|
-
const { taskId: validatedTaskId, success: validatedSuccess } = validationResult.data;
|
|
200
210
|
const account = getAccount();
|
|
201
211
|
if (!account) {
|
|
202
|
-
return
|
|
212
|
+
return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
|
|
213
|
+
}
|
|
214
|
+
const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "verifyTask", [BigInt(taskId), success], undefined, "TaskEscrow");
|
|
215
|
+
if (result.status === "success") {
|
|
216
|
+
return formatSuccess(success
|
|
217
|
+
? `Task #${taskId} approved. Payment released to worker automatically.`
|
|
218
|
+
: `Task #${taskId} rejected. Payment refunded to client.`, { taskId, verdict: success ? "APPROVED" : "REJECTED" }, result.txHash, success
|
|
219
|
+
? ["Payment has been transferred to the worker's wallet.", "Both agents' reputation scores have been updated.", "An ERC-8004 receipt has been created as permanent proof."]
|
|
220
|
+
: ["Payment has been refunded to your wallet.", "Worker's reputation has been penalized."]);
|
|
203
221
|
}
|
|
204
|
-
const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "verifyTask", [BigInt(validatedTaskId), validatedSuccess], undefined, "TaskEscrow");
|
|
205
222
|
return formatTxResult(result);
|
|
206
223
|
}
|
|
207
224
|
catch (e) {
|
|
208
|
-
|
|
225
|
+
const parsed = parseContractError(e);
|
|
226
|
+
return formatStructuredError(parsed.error, parsed.cause, parsed.fix, parsed.retryable);
|
|
209
227
|
}
|
|
210
228
|
});
|
|
211
229
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -213,34 +231,30 @@ export function registerEscrowTools(server) {
|
|
|
213
231
|
// ──────────────────────────────────────────────────────────────
|
|
214
232
|
server.registerTool("corven_dispute_task", {
|
|
215
233
|
title: "Dispute a Task",
|
|
216
|
-
description: "
|
|
217
|
-
"
|
|
234
|
+
description: "Freezes a task and initiates jury-based dispute resolution. Three randomly-selected agents vote. Majority decides.\n" +
|
|
235
|
+
"USE WHEN: You are the client and submitted work clearly fails the specification. Or you are the worker and were unfairly rejected.\n" +
|
|
236
|
+
"REQUIRES: Task status must be Submitted. Either client or worker can call.\n" +
|
|
237
|
+
"RETURNS: Dispute ID, jury selection confirmation, voting deadline.\n" +
|
|
238
|
+
"NOTE: Call corven_get_task first to confirm the task is in Submitted status.",
|
|
218
239
|
inputSchema: {
|
|
219
|
-
taskId:
|
|
220
|
-
reason: z
|
|
221
|
-
.string()
|
|
222
|
-
.optional()
|
|
223
|
-
.describe("Optional reason for the dispute (stored off-chain / emitted in event)"),
|
|
240
|
+
taskId: taskIdSchema,
|
|
241
|
+
reason: z.string().max(500).optional().describe("Optional reason for the dispute"),
|
|
224
242
|
},
|
|
225
243
|
}, async ({ taskId, reason }) => {
|
|
226
244
|
try {
|
|
227
|
-
// Validate input
|
|
228
|
-
const validationResult = disputeTaskSchema.safeParse({ taskId, reason });
|
|
229
|
-
if (!validationResult.success) {
|
|
230
|
-
return formatError(new Error(`Invalid input: ${validationResult.error.issues.map((e) => e.message).join(", ")}`));
|
|
231
|
-
}
|
|
232
|
-
// Use validated values
|
|
233
|
-
const validatedTaskId = validationResult.data.taskId;
|
|
234
|
-
const validatedReason = validationResult.data.reason;
|
|
235
245
|
const account = getAccount();
|
|
236
246
|
if (!account) {
|
|
237
|
-
return
|
|
247
|
+
return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
|
|
248
|
+
}
|
|
249
|
+
const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "disputeTask", [BigInt(taskId)], undefined, "TaskEscrow");
|
|
250
|
+
if (result.status === "success") {
|
|
251
|
+
return formatSuccess(`Task #${taskId} disputed. Payment frozen pending jury resolution.`, { taskId, reason: reason || "No reason provided", status: "Disputed" }, result.txHash, ["Three jurors will be randomly selected to vote.", "Resolution typically takes 24-48 hours."]);
|
|
238
252
|
}
|
|
239
|
-
const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "disputeTask", [BigInt(validatedTaskId)], undefined, "TaskEscrow");
|
|
240
253
|
return formatTxResult(result);
|
|
241
254
|
}
|
|
242
255
|
catch (e) {
|
|
243
|
-
|
|
256
|
+
const parsed = parseContractError(e);
|
|
257
|
+
return formatStructuredError(parsed.error, parsed.cause, parsed.fix, parsed.retryable);
|
|
244
258
|
}
|
|
245
259
|
});
|
|
246
260
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -248,20 +262,25 @@ export function registerEscrowTools(server) {
|
|
|
248
262
|
// ──────────────────────────────────────────────────────────────
|
|
249
263
|
server.registerTool("corven_create_task_with_priority", {
|
|
250
264
|
title: "Create Task with Priority",
|
|
251
|
-
description: "
|
|
252
|
-
"
|
|
265
|
+
description: "Creates a direct-hire task with a specific priority level and locks payment in escrow.\n" +
|
|
266
|
+
"USE WHEN: You have a specific worker and need urgent or high-priority execution with guaranteed faster attention.\n" +
|
|
267
|
+
"REQUIRES: Both client AND worker registered. Client wallet needs payment + priority fee + ~0.0003 ETH gas.\n" +
|
|
268
|
+
"RETURNS: taskId, escrow status, priority level, total cost breakdown, Basescan link.\n" +
|
|
269
|
+
"COMES AFTER: corven_find_workers to get the worker address.\n" +
|
|
270
|
+
"COMES BEFORE: Worker calls corven_submit_work. Client calls corven_verify_task.\n" +
|
|
271
|
+
"NOTE: Priority fees — Low: 0.5%, Medium: 1%, High: 2%, Urgent: 5%. Use corven_create_task for default Medium priority.",
|
|
253
272
|
inputSchema: {
|
|
254
|
-
worker:
|
|
255
|
-
payment:
|
|
256
|
-
deadline:
|
|
257
|
-
descriptionHash:
|
|
258
|
-
priority:
|
|
273
|
+
worker: ethAddress,
|
|
274
|
+
payment: ethAmount,
|
|
275
|
+
deadline: unixDeadline,
|
|
276
|
+
descriptionHash: ipfsCid,
|
|
277
|
+
priority: prioritySchema,
|
|
259
278
|
},
|
|
260
279
|
}, async ({ worker, payment, deadline, descriptionHash, priority }) => {
|
|
261
280
|
try {
|
|
262
281
|
const account = getAccount();
|
|
263
282
|
if (!account)
|
|
264
|
-
return
|
|
283
|
+
return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
|
|
265
284
|
const paymentWei = parseEther(payment);
|
|
266
285
|
// Calculate total value: payment + protocol fee (1%) + priority fee
|
|
267
286
|
const PROTOCOL_FEE_BPS = 100n;
|
|
@@ -274,7 +293,8 @@ export function registerEscrowTools(server) {
|
|
|
274
293
|
return formatTxResult(result);
|
|
275
294
|
}
|
|
276
295
|
catch (e) {
|
|
277
|
-
|
|
296
|
+
const p = parseContractError(e);
|
|
297
|
+
return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
|
|
278
298
|
}
|
|
279
299
|
});
|
|
280
300
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -282,13 +302,18 @@ export function registerEscrowTools(server) {
|
|
|
282
302
|
// ──────────────────────────────────────────────────────────────
|
|
283
303
|
server.registerTool("corven_create_milestone_task", {
|
|
284
304
|
title: "Create Milestone Task",
|
|
285
|
-
description: "
|
|
286
|
-
"
|
|
305
|
+
description: "Creates a task with incremental milestone-based payments. Each milestone is verified and paid independently.\n" +
|
|
306
|
+
"USE WHEN: Task has distinct phases (e.g., research, draft, final). You want partial payment tied to checkpoints.\n" +
|
|
307
|
+
"REQUIRES: Both client AND worker registered. Client wallet needs totalPayment + ~2% fees + ~0.0003 ETH gas.\n" +
|
|
308
|
+
"RETURNS: taskId, milestone count, individual milestone amounts, total escrowed, Basescan link.\n" +
|
|
309
|
+
"COMES AFTER: corven_find_workers to get the worker address.\n" +
|
|
310
|
+
"COMES BEFORE: Worker calls corven_submit_milestone per milestone. Client calls corven_verify_milestone to release each payment.\n" +
|
|
311
|
+
"NOTE: totalPayment must equal the sum of milestonePayments. Milestones are verified in order (0, 1, 2, ...).",
|
|
287
312
|
inputSchema: {
|
|
288
|
-
worker:
|
|
289
|
-
totalPayment:
|
|
290
|
-
deadline:
|
|
291
|
-
descriptionHash:
|
|
313
|
+
worker: ethAddress,
|
|
314
|
+
totalPayment: ethAmount,
|
|
315
|
+
deadline: unixDeadline,
|
|
316
|
+
descriptionHash: ipfsCid,
|
|
292
317
|
milestoneDescriptions: z.array(z.string()).describe("Array of milestone descriptions"),
|
|
293
318
|
milestonePayments: z.array(z.string()).describe("Array of milestone payments in ETH"),
|
|
294
319
|
},
|
|
@@ -296,7 +321,7 @@ export function registerEscrowTools(server) {
|
|
|
296
321
|
try {
|
|
297
322
|
const account = getAccount();
|
|
298
323
|
if (!account)
|
|
299
|
-
return
|
|
324
|
+
return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
|
|
300
325
|
const paymentWei = parseEther(totalPayment);
|
|
301
326
|
const payments = milestonePayments.map(p => parseEther(p));
|
|
302
327
|
// Calculate total value: payment + protocol fee (1%) + priority fee (default Medium=1%)
|
|
@@ -309,7 +334,8 @@ export function registerEscrowTools(server) {
|
|
|
309
334
|
return formatTxResult(result);
|
|
310
335
|
}
|
|
311
336
|
catch (e) {
|
|
312
|
-
|
|
337
|
+
const p = parseContractError(e);
|
|
338
|
+
return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
|
|
313
339
|
}
|
|
314
340
|
});
|
|
315
341
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -317,22 +343,29 @@ export function registerEscrowTools(server) {
|
|
|
317
343
|
// ──────────────────────────────────────────────────────────────
|
|
318
344
|
server.registerTool("corven_submit_milestone", {
|
|
319
345
|
title: "Submit Milestone",
|
|
320
|
-
description: "
|
|
346
|
+
description: "Worker submits a deliverable for a specific milestone in a milestone-based task.\n" +
|
|
347
|
+
"USE WHEN: You are the worker and have completed one milestone phase. Upload deliverable to IPFS first.\n" +
|
|
348
|
+
"REQUIRES: You must be the assigned worker. Milestone must not already be submitted. Task status must be Funded or InProgress.\n" +
|
|
349
|
+
"RETURNS: Submission confirmation, milestone index, IPFS hash recorded on-chain.\n" +
|
|
350
|
+
"COMES AFTER: corven_create_milestone_task created the task with milestones.\n" +
|
|
351
|
+
"COMES BEFORE: Client calls corven_verify_milestone to approve and release payment for this milestone.\n" +
|
|
352
|
+
"NOTE: Milestones must be submitted in order. You cannot skip ahead.",
|
|
321
353
|
inputSchema: {
|
|
322
|
-
taskId:
|
|
354
|
+
taskId: taskIdSchema,
|
|
323
355
|
milestoneIndex: z.number().describe("Milestone index (0-based)"),
|
|
324
|
-
deliverableHash:
|
|
356
|
+
deliverableHash: ipfsCid,
|
|
325
357
|
},
|
|
326
358
|
}, async ({ taskId, milestoneIndex, deliverableHash }) => {
|
|
327
359
|
try {
|
|
328
360
|
const account = getAccount();
|
|
329
361
|
if (!account)
|
|
330
|
-
return
|
|
362
|
+
return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
|
|
331
363
|
const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "submitMilestone", [BigInt(taskId), BigInt(milestoneIndex), deliverableHash]);
|
|
332
364
|
return formatTxResult(result);
|
|
333
365
|
}
|
|
334
366
|
catch (e) {
|
|
335
|
-
|
|
367
|
+
const p = parseContractError(e);
|
|
368
|
+
return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
|
|
336
369
|
}
|
|
337
370
|
});
|
|
338
371
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -340,9 +373,15 @@ export function registerEscrowTools(server) {
|
|
|
340
373
|
// ──────────────────────────────────────────────────────────────
|
|
341
374
|
server.registerTool("corven_verify_milestone", {
|
|
342
375
|
title: "Verify Milestone",
|
|
343
|
-
description: "
|
|
376
|
+
description: "Client verifies a submitted milestone and releases its payment to the worker.\n" +
|
|
377
|
+
"USE WHEN: You are the client. Worker has submitted a milestone (corven_get_milestone shows submitted). You have reviewed and approve.\n" +
|
|
378
|
+
"REQUIRES: You must be the client. Milestone must have been submitted by the worker.\n" +
|
|
379
|
+
"RETURNS: Verification result, payment released amount, milestone status update.\n" +
|
|
380
|
+
"COMES AFTER: corven_submit_milestone by the worker.\n" +
|
|
381
|
+
"COMES BEFORE: Next milestone submission, or task completion if this was the final milestone.\n" +
|
|
382
|
+
"NOTE: Rejecting a milestone does not refund the entire task — only that milestone's payment is withheld.",
|
|
344
383
|
inputSchema: {
|
|
345
|
-
taskId:
|
|
384
|
+
taskId: taskIdSchema,
|
|
346
385
|
milestoneIndex: z.number().describe("Milestone index (0-based)"),
|
|
347
386
|
success: z.boolean().describe("Whether the milestone passes verification"),
|
|
348
387
|
},
|
|
@@ -350,47 +389,42 @@ export function registerEscrowTools(server) {
|
|
|
350
389
|
try {
|
|
351
390
|
const account = getAccount();
|
|
352
391
|
if (!account)
|
|
353
|
-
return
|
|
392
|
+
return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
|
|
354
393
|
const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "verifyMilestone", [BigInt(taskId), BigInt(milestoneIndex), success], undefined, "TaskEscrow");
|
|
355
394
|
return formatTxResult(result);
|
|
356
395
|
}
|
|
357
396
|
catch (e) {
|
|
358
|
-
|
|
397
|
+
const p = parseContractError(e);
|
|
398
|
+
return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
|
|
359
399
|
}
|
|
360
400
|
});
|
|
361
401
|
// ──────────────────────────────────────────────────────────────
|
|
362
|
-
// corven_get_milestone
|
|
402
|
+
// corven_get_milestone (includes count when no index provided)
|
|
363
403
|
// ──────────────────────────────────────────────────────────────
|
|
364
404
|
server.registerTool("corven_get_milestone", {
|
|
365
|
-
title: "Get Milestone
|
|
366
|
-
description: "
|
|
405
|
+
title: "Get Milestone",
|
|
406
|
+
description: "Reads milestone details by index, or returns the total milestone count if no index is provided.\n" +
|
|
407
|
+
"USE WHEN: Checking which milestones have been submitted or verified. Counting milestones before submission.\n" +
|
|
408
|
+
"REQUIRES: Nothing. Free read-only call.\n" +
|
|
409
|
+
"RETURNS: Milestone description, payment amount, submission hash, verification status — or milestone count if index omitted.\n" +
|
|
410
|
+
"COMES AFTER: corven_create_milestone_task created the task.\n" +
|
|
411
|
+
"COMES BEFORE: corven_submit_milestone or corven_verify_milestone.",
|
|
367
412
|
inputSchema: {
|
|
368
|
-
taskId:
|
|
369
|
-
milestoneIndex: z.number().describe("Milestone index (0-based)"),
|
|
413
|
+
taskId: taskIdSchema,
|
|
414
|
+
milestoneIndex: z.number().optional().describe("Milestone index (0-based). Omit to get count."),
|
|
370
415
|
},
|
|
371
416
|
}, async ({ taskId, milestoneIndex }) => {
|
|
372
417
|
try {
|
|
418
|
+
if (milestoneIndex === undefined) {
|
|
419
|
+
const count = await readContract(CONTRACTS.TaskEscrow, ABI, "getMilestoneCount", [BigInt(taskId)]);
|
|
420
|
+
return formatReadResult({ taskId, milestoneCount: Number(count) }, `Milestone count for Task #${taskId}`);
|
|
421
|
+
}
|
|
373
422
|
const data = await readContract(CONTRACTS.TaskEscrow, ABI, "getMilestone", [BigInt(taskId), BigInt(milestoneIndex)]);
|
|
374
423
|
return formatReadResult(data, `Milestone ${milestoneIndex} of Task #${taskId}`);
|
|
375
424
|
}
|
|
376
425
|
catch (e) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
});
|
|
380
|
-
// ──────────────────────────────────────────────────────────────
|
|
381
|
-
// corven_get_milestone_count
|
|
382
|
-
// ──────────────────────────────────────────────────────────────
|
|
383
|
-
server.registerTool("corven_get_milestone_count", {
|
|
384
|
-
title: "Get Milestone Count",
|
|
385
|
-
description: "Get the number of milestones in a task.",
|
|
386
|
-
inputSchema: { taskId: z.number().describe("Task ID") },
|
|
387
|
-
}, async ({ taskId }) => {
|
|
388
|
-
try {
|
|
389
|
-
const count = await readContract(CONTRACTS.TaskEscrow, ABI, "getMilestoneCount", [BigInt(taskId)]);
|
|
390
|
-
return formatReadResult({ taskId, milestoneCount: Number(count) }, `Milestones for Task #${taskId}`);
|
|
391
|
-
}
|
|
392
|
-
catch (e) {
|
|
393
|
-
return formatError(e);
|
|
426
|
+
const p = parseContractError(e);
|
|
427
|
+
return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
|
|
394
428
|
}
|
|
395
429
|
});
|
|
396
430
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -398,19 +432,25 @@ export function registerEscrowTools(server) {
|
|
|
398
432
|
// ──────────────────────────────────────────────────────────────
|
|
399
433
|
server.registerTool("corven_create_subtask", {
|
|
400
434
|
title: "Create Subtask",
|
|
401
|
-
description: "
|
|
435
|
+
description: "Creates a child task under a parent task with its own worker, payment, and deadline.\n" +
|
|
436
|
+
"USE WHEN: Decomposing a large task into parallel subtasks for different specialists.\n" +
|
|
437
|
+
"REQUIRES: Parent task must exist. Both client and subtask worker must be registered. Client wallet needs payment + ~2% fees.\n" +
|
|
438
|
+
"RETURNS: Subtask taskId, parent reference, worker, payment, deadline.\n" +
|
|
439
|
+
"COMES AFTER: corven_create_task created the parent task.\n" +
|
|
440
|
+
"COMES BEFORE: Subtask worker calls corven_submit_work. Client calls corven_verify_task on the subtask.\n" +
|
|
441
|
+
"NOTE: Subtask payment is funded independently from the parent. Use corven_get_child_tasks to list all subtasks.",
|
|
402
442
|
inputSchema: {
|
|
403
|
-
parentTaskId:
|
|
404
|
-
worker:
|
|
405
|
-
payment:
|
|
406
|
-
deadline:
|
|
407
|
-
descriptionHash:
|
|
443
|
+
parentTaskId: taskIdSchema,
|
|
444
|
+
worker: ethAddress,
|
|
445
|
+
payment: ethAmount,
|
|
446
|
+
deadline: unixDeadline,
|
|
447
|
+
descriptionHash: ipfsCid,
|
|
408
448
|
},
|
|
409
449
|
}, async ({ parentTaskId, worker, payment, deadline, descriptionHash }) => {
|
|
410
450
|
try {
|
|
411
451
|
const account = getAccount();
|
|
412
452
|
if (!account)
|
|
413
|
-
return
|
|
453
|
+
return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
|
|
414
454
|
const paymentWei = parseEther(payment);
|
|
415
455
|
// Calculate total value: payment + protocol fee (1%) + priority fee (default Medium=1%)
|
|
416
456
|
const PROTOCOL_FEE_BPS = 100n;
|
|
@@ -422,7 +462,8 @@ export function registerEscrowTools(server) {
|
|
|
422
462
|
return formatTxResult(result);
|
|
423
463
|
}
|
|
424
464
|
catch (e) {
|
|
425
|
-
|
|
465
|
+
const p = parseContractError(e);
|
|
466
|
+
return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
|
|
426
467
|
}
|
|
427
468
|
});
|
|
428
469
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -430,15 +471,21 @@ export function registerEscrowTools(server) {
|
|
|
430
471
|
// ──────────────────────────────────────────────────────────────
|
|
431
472
|
server.registerTool("corven_get_child_tasks", {
|
|
432
473
|
title: "Get Child Tasks",
|
|
433
|
-
description: "
|
|
434
|
-
|
|
474
|
+
description: "Returns the IDs of all child tasks (subtasks) under a parent task.\n" +
|
|
475
|
+
"USE WHEN: Tracking progress of decomposed tasks. Checking if all subtasks are complete before parent verification.\n" +
|
|
476
|
+
"REQUIRES: Nothing. Free read-only call.\n" +
|
|
477
|
+
"RETURNS: Parent task ID, child count, array of child task IDs.\n" +
|
|
478
|
+
"COMES AFTER: corven_create_subtask created child tasks.\n" +
|
|
479
|
+
"COMES BEFORE: Use corven_get_task on each child ID to check individual status.",
|
|
480
|
+
inputSchema: { parentTaskId: taskIdSchema },
|
|
435
481
|
}, async ({ parentTaskId }) => {
|
|
436
482
|
try {
|
|
437
483
|
const childIds = await readContract(CONTRACTS.TaskEscrow, ABI, "getChildTasks", [BigInt(parentTaskId)]);
|
|
438
484
|
return formatReadResult({ parentTaskId, childCount: childIds.length, childTaskIds: childIds }, `Child tasks of Task #${parentTaskId}`);
|
|
439
485
|
}
|
|
440
486
|
catch (e) {
|
|
441
|
-
|
|
487
|
+
const p = parseContractError(e);
|
|
488
|
+
return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
|
|
442
489
|
}
|
|
443
490
|
});
|
|
444
491
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -446,9 +493,15 @@ export function registerEscrowTools(server) {
|
|
|
446
493
|
// ──────────────────────────────────────────────────────────────
|
|
447
494
|
server.registerTool("corven_submit_query", {
|
|
448
495
|
title: "Submit Task Query",
|
|
449
|
-
description: "
|
|
496
|
+
description: "Worker submits a clarifying question about a task during execution. The query is recorded on-chain.\n" +
|
|
497
|
+
"USE WHEN: Task specification is ambiguous. You need additional resources. You want to confirm feasibility before proceeding.\n" +
|
|
498
|
+
"REQUIRES: You must be the assigned worker. Task must be in Funded or InProgress status.\n" +
|
|
499
|
+
"RETURNS: Query ID, task ID, query type, submission confirmation.\n" +
|
|
500
|
+
"COMES AFTER: corven_create_task assigned you the task.\n" +
|
|
501
|
+
"COMES BEFORE: Client calls corven_respond_to_query. Then you continue work.\n" +
|
|
502
|
+
"NOTE: Query types: 0=Specification (unclear requirements), 1=Resource (need more time/data), 2=Feasibility (concern about viability).",
|
|
450
503
|
inputSchema: {
|
|
451
|
-
taskId:
|
|
504
|
+
taskId: taskIdSchema,
|
|
452
505
|
queryText: z.string().describe("The query text"),
|
|
453
506
|
queryType: z.number().describe("Query type: 0=Specification, 1=Resource, 2=Feasibility"),
|
|
454
507
|
},
|
|
@@ -456,12 +509,13 @@ export function registerEscrowTools(server) {
|
|
|
456
509
|
try {
|
|
457
510
|
const account = getAccount();
|
|
458
511
|
if (!account)
|
|
459
|
-
return
|
|
512
|
+
return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
|
|
460
513
|
const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "submitQuery", [BigInt(taskId), queryText, queryType]);
|
|
461
514
|
return formatTxResult(result);
|
|
462
515
|
}
|
|
463
516
|
catch (e) {
|
|
464
|
-
|
|
517
|
+
const p = parseContractError(e);
|
|
518
|
+
return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
|
|
465
519
|
}
|
|
466
520
|
});
|
|
467
521
|
// ──────────────────────────────────────────────────────────────
|
|
@@ -469,88 +523,83 @@ export function registerEscrowTools(server) {
|
|
|
469
523
|
// ──────────────────────────────────────────────────────────────
|
|
470
524
|
server.registerTool("corven_respond_to_query", {
|
|
471
525
|
title: "Respond to Query",
|
|
472
|
-
description: "
|
|
526
|
+
description: "Client responds to a worker's clarifying query about a task. Response is recorded on-chain.\n" +
|
|
527
|
+
"USE WHEN: A worker has submitted a query (corven_get_query shows pending queries). You want to provide clarification.\n" +
|
|
528
|
+
"REQUIRES: You must be the task client. A query must exist on the task.\n" +
|
|
529
|
+
"RETURNS: Response confirmation, task ID, response text recorded.\n" +
|
|
530
|
+
"COMES AFTER: corven_submit_query by the worker.\n" +
|
|
531
|
+
"COMES BEFORE: Worker continues task execution with your clarification.\n" +
|
|
532
|
+
"NOTE: Check query details first with corven_get_query to understand what the worker is asking.",
|
|
473
533
|
inputSchema: {
|
|
474
|
-
taskId:
|
|
534
|
+
taskId: taskIdSchema,
|
|
475
535
|
responseText: z.string().describe("The response text"),
|
|
476
536
|
},
|
|
477
537
|
}, async ({ taskId, responseText }) => {
|
|
478
538
|
try {
|
|
479
539
|
const account = getAccount();
|
|
480
540
|
if (!account)
|
|
481
|
-
return
|
|
541
|
+
return formatStructuredError("No private key configured.", "PRIVATE_KEY not set.", "Set PRIVATE_KEY in .env.", false);
|
|
482
542
|
const result = await executeOrPrepare(CONTRACTS.TaskEscrow, ABI, "respondToQuery", [BigInt(taskId), responseText]);
|
|
483
543
|
return formatTxResult(result);
|
|
484
544
|
}
|
|
485
545
|
catch (e) {
|
|
486
|
-
|
|
546
|
+
const p = parseContractError(e);
|
|
547
|
+
return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
|
|
487
548
|
}
|
|
488
549
|
});
|
|
489
550
|
// ──────────────────────────────────────────────────────────────
|
|
490
|
-
// corven_get_query
|
|
551
|
+
// corven_get_query (includes count when no queryId provided)
|
|
491
552
|
// ──────────────────────────────────────────────────────────────
|
|
492
553
|
server.registerTool("corven_get_query", {
|
|
493
|
-
title: "Get Query
|
|
494
|
-
description: "
|
|
554
|
+
title: "Get Query",
|
|
555
|
+
description: "Reads query details by index, or returns the total query count if no index is provided.\n" +
|
|
556
|
+
"USE WHEN: Checking if a worker has submitted questions. Reading query text before responding. Counting outstanding queries.\n" +
|
|
557
|
+
"REQUIRES: Nothing. Free read-only call.\n" +
|
|
558
|
+
"RETURNS: Query text, type (Specification/Resource/Feasibility), response status — or query count if index omitted.\n" +
|
|
559
|
+
"COMES AFTER: corven_submit_query created a query.\n" +
|
|
560
|
+
"COMES BEFORE: corven_respond_to_query to provide an answer.",
|
|
495
561
|
inputSchema: {
|
|
496
|
-
taskId:
|
|
497
|
-
queryId: z.number().describe("Query index (0-based)"),
|
|
562
|
+
taskId: taskIdSchema,
|
|
563
|
+
queryId: z.number().optional().describe("Query index (0-based). Omit to get count."),
|
|
498
564
|
},
|
|
499
565
|
}, async ({ taskId, queryId }) => {
|
|
500
566
|
try {
|
|
567
|
+
if (queryId === undefined) {
|
|
568
|
+
const count = await readContract(CONTRACTS.TaskEscrow, ABI, "getQueryCount", [BigInt(taskId)]);
|
|
569
|
+
return formatReadResult({ taskId, queryCount: Number(count) }, `Query count for Task #${taskId}`);
|
|
570
|
+
}
|
|
501
571
|
const data = await readContract(CONTRACTS.TaskEscrow, ABI, "getQuery", [BigInt(taskId), BigInt(queryId)]);
|
|
502
572
|
return formatReadResult(data, `Query ${queryId} on Task #${taskId}`);
|
|
503
573
|
}
|
|
504
574
|
catch (e) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
});
|
|
508
|
-
// ──────────────────────────────────────────────────────────────
|
|
509
|
-
// corven_get_query_count
|
|
510
|
-
// ──────────────────────────────────────────────────────────────
|
|
511
|
-
server.registerTool("corven_get_query_count", {
|
|
512
|
-
title: "Get Query Count",
|
|
513
|
-
description: "Get the number of queries submitted on a task.",
|
|
514
|
-
inputSchema: { taskId: z.number().describe("Task ID") },
|
|
515
|
-
}, async ({ taskId }) => {
|
|
516
|
-
try {
|
|
517
|
-
const count = await readContract(CONTRACTS.TaskEscrow, ABI, "getQueryCount", [BigInt(taskId)]);
|
|
518
|
-
return formatReadResult({ taskId, queryCount: Number(count) }, `Queries on Task #${taskId}`);
|
|
519
|
-
}
|
|
520
|
-
catch (e) {
|
|
521
|
-
return formatError(e);
|
|
575
|
+
const p = parseContractError(e);
|
|
576
|
+
return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
|
|
522
577
|
}
|
|
523
578
|
});
|
|
524
579
|
// ──────────────────────────────────────────────────────────────
|
|
525
|
-
//
|
|
580
|
+
// corven_get_tasks (consolidated: client, worker, or child tasks)
|
|
526
581
|
// ──────────────────────────────────────────────────────────────
|
|
527
|
-
server.registerTool("
|
|
528
|
-
title: "Get
|
|
529
|
-
description: "
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
}
|
|
539
|
-
})
|
|
540
|
-
// ──────────────────────────────────────────────────────────────
|
|
541
|
-
// corven_get_worker_tasks
|
|
542
|
-
// ──────────────────────────────────────────────────────────────
|
|
543
|
-
server.registerTool("corven_get_worker_tasks", {
|
|
544
|
-
title: "Get Worker Tasks",
|
|
545
|
-
description: "Get all task IDs where the given address is the worker.",
|
|
546
|
-
inputSchema: { worker: z.string().describe("Worker's Ethereum address") },
|
|
547
|
-
}, async ({ worker }) => {
|
|
582
|
+
server.registerTool("corven_get_tasks", {
|
|
583
|
+
title: "Get Tasks",
|
|
584
|
+
description: "Returns all task IDs where the given address is the client or the worker.\n" +
|
|
585
|
+
"USE WHEN: Finding your active tasks. Checking which tasks you need to work on. Building a task dashboard.\n" +
|
|
586
|
+
"REQUIRES: Nothing. Free read-only call.\n" +
|
|
587
|
+
"RETURNS: Address, role, task count, array of task IDs.\n" +
|
|
588
|
+
"COMES BEFORE: Use corven_get_task on each returned ID to get full details.\n" +
|
|
589
|
+
"NOTE: Use role='worker' to find tasks assigned to you. Use role='client' to find tasks you posted.",
|
|
590
|
+
inputSchema: {
|
|
591
|
+
address: ethAddress,
|
|
592
|
+
role: z.enum(["client", "worker"]).describe("Filter: 'client' or 'worker'"),
|
|
593
|
+
},
|
|
594
|
+
}, async ({ address, role }) => {
|
|
548
595
|
try {
|
|
549
|
-
const
|
|
550
|
-
|
|
596
|
+
const fn = role === "client" ? "getClientTasks" : "getWorkerTasks";
|
|
597
|
+
const taskIds = await readContract(CONTRACTS.TaskEscrow, ABI, fn, [address]);
|
|
598
|
+
return formatReadResult({ address, role, taskCount: taskIds.length, taskIds }, `Tasks where ${address} is ${role}`);
|
|
551
599
|
}
|
|
552
600
|
catch (e) {
|
|
553
|
-
|
|
601
|
+
const p = parseContractError(e);
|
|
602
|
+
return formatStructuredError(p.error, p.cause, p.fix, p.retryable);
|
|
554
603
|
}
|
|
555
604
|
});
|
|
556
605
|
}
|