@jinn-network/client 0.1.3-canary.2d6b2676 → 0.1.3-canary.8e61ba74

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 (289) hide show
  1. package/dist/adapters/adapter.d.ts +15 -4
  2. package/dist/adapters/local/adapter.d.ts +9 -8
  3. package/dist/adapters/local/adapter.js +47 -74
  4. package/dist/adapters/local/adapter.js.map +1 -1
  5. package/dist/adapters/mech/adapter.d.ts +29 -13
  6. package/dist/adapters/mech/adapter.js +316 -311
  7. package/dist/adapters/mech/adapter.js.map +1 -1
  8. package/dist/adapters/mech/contracts.d.ts +62 -16
  9. package/dist/adapters/mech/contracts.js +226 -92
  10. package/dist/adapters/mech/contracts.js.map +1 -1
  11. package/dist/adapters/mech/safe-revert.js +0 -6
  12. package/dist/adapters/mech/safe-revert.js.map +1 -1
  13. package/dist/adapters/mech/types.d.ts +237 -84
  14. package/dist/adapters/mech/types.js +125 -50
  15. package/dist/adapters/mech/types.js.map +1 -1
  16. package/dist/agent/agent-ws.d.ts +1 -0
  17. package/dist/agent/agent-ws.js +13 -1
  18. package/dist/agent/agent-ws.js.map +1 -1
  19. package/dist/agent/operator-claude.js +4 -0
  20. package/dist/agent/operator-claude.js.map +1 -1
  21. package/dist/api/gather-status.d.ts +0 -1
  22. package/dist/api/gather-status.js +0 -1
  23. package/dist/api/gather-status.js.map +1 -1
  24. package/dist/api/setup-endpoints.d.ts +20 -0
  25. package/dist/api/setup-endpoints.js +110 -3
  26. package/dist/api/setup-endpoints.js.map +1 -1
  27. package/dist/build-info.json +4 -4
  28. package/dist/build-meta.json +1 -1
  29. package/dist/cli/commands/auth.js +6 -3
  30. package/dist/cli/commands/auth.js.map +1 -1
  31. package/dist/cli/commands/bootstrap.js +0 -1
  32. package/dist/cli/commands/bootstrap.js.map +1 -1
  33. package/dist/cli/commands/create.js +3 -3
  34. package/dist/cli/commands/doctor.js +4 -12
  35. package/dist/cli/commands/doctor.js.map +1 -1
  36. package/dist/cli/commands/fleet-scale.js +0 -1
  37. package/dist/cli/commands/fleet-scale.js.map +1 -1
  38. package/dist/cli/commands/fund-requirements.js +0 -1
  39. package/dist/cli/commands/fund-requirements.js.map +1 -1
  40. package/dist/cli/commands/migrate-agent-id.js +0 -1
  41. package/dist/cli/commands/migrate-agent-id.js.map +1 -1
  42. package/dist/cli/commands/solver-nets.js +1 -1
  43. package/dist/cli/commands/solver-plugins.js +3 -1
  44. package/dist/cli/commands/solver-plugins.js.map +1 -1
  45. package/dist/cli/commands/tasks.js +20 -11
  46. package/dist/cli/commands/tasks.js.map +1 -1
  47. package/dist/cli/commands/version.js +0 -1
  48. package/dist/cli/commands/version.js.map +1 -1
  49. package/dist/cli/deployment-digest.js +0 -5
  50. package/dist/cli/deployment-digest.js.map +1 -1
  51. package/dist/cli/execution-context.js +0 -1
  52. package/dist/cli/execution-context.js.map +1 -1
  53. package/dist/cli/introspection-context.js +0 -1
  54. package/dist/cli/introspection-context.js.map +1 -1
  55. package/dist/config.d.ts +410 -22
  56. package/dist/config.js +41 -30
  57. package/dist/config.js.map +1 -1
  58. package/dist/daemon/creator.d.ts +3 -3
  59. package/dist/daemon/creator.js +3 -3
  60. package/dist/daemon/creator.js.map +1 -1
  61. package/dist/daemon/daemon.d.ts +3 -6
  62. package/dist/daemon/daemon.js +30 -9
  63. package/dist/daemon/daemon.js.map +1 -1
  64. package/dist/daemon/jinn-claim-loop-canonical.d.ts +11 -11
  65. package/dist/daemon/jinn-claim-loop-canonical.js +17 -17
  66. package/dist/daemon/jinn-claim-loop-canonical.js.map +1 -1
  67. package/dist/daemon/jinn-claim-loop-mock.d.ts +4 -4
  68. package/dist/daemon/jinn-claim-loop-mock.js +7 -7
  69. package/dist/daemon/jinn-claim-loop-mock.js.map +1 -1
  70. package/dist/daemon/jinn-claim-loop.d.ts +4 -4
  71. package/dist/daemon/jinn-claim-loop.js +3 -3
  72. package/dist/dashboard/assets/{index-Bxlk5qpa.js → index-CaUOdCs6.js} +6 -6
  73. package/dist/dashboard/index.html +1 -1
  74. package/dist/earning/bootstrap.d.ts +0 -1
  75. package/dist/earning/bootstrap.js +0 -1
  76. package/dist/earning/bootstrap.js.map +1 -1
  77. package/dist/earning/contracts.d.ts +18 -20
  78. package/dist/earning/contracts.js +18 -29
  79. package/dist/earning/contracts.js.map +1 -1
  80. package/dist/earning/funding-plan.d.ts +0 -1
  81. package/dist/earning/funding-plan.js +0 -1
  82. package/dist/earning/funding-plan.js.map +1 -1
  83. package/dist/earning/migrate-agent-id.d.ts +0 -1
  84. package/dist/earning/migrate-agent-id.js +0 -1
  85. package/dist/earning/migrate-agent-id.js.map +1 -1
  86. package/dist/erc8004/reputation.d.ts +2 -2
  87. package/dist/erc8004/reputation.js +2 -2
  88. package/dist/events/types.d.ts +2 -2
  89. package/dist/harnesses/engine/delivery.d.ts +10 -6
  90. package/dist/harnesses/engine/delivery.js +10 -6
  91. package/dist/harnesses/engine/delivery.js.map +1 -1
  92. package/dist/harnesses/engine/engine.d.ts +16 -29
  93. package/dist/harnesses/engine/engine.js +67 -106
  94. package/dist/harnesses/engine/engine.js.map +1 -1
  95. package/dist/harnesses/engine/persistence.d.ts +5 -1
  96. package/dist/harnesses/engine/persistence.js +10 -2
  97. package/dist/harnesses/engine/persistence.js.map +1 -1
  98. package/dist/harnesses/external-impls/loader.d.ts +1 -1
  99. package/dist/harnesses/impls/claude-code-learner/types.d.ts +1 -1
  100. package/dist/harnesses/impls/claude-mcp-prediction/index.d.ts +1 -1
  101. package/dist/harnesses/impls/claude-mcp-prediction/index.js +6 -6
  102. package/dist/harnesses/impls/claude-mcp-prediction/types.d.ts +1 -1
  103. package/dist/harnesses/impls/claude-mcp-prediction/types.js +1 -1
  104. package/dist/harnesses/impls/index.js +5 -5
  105. package/dist/harnesses/impls/portfolio-v0-evaluator/index.js +1 -1
  106. package/dist/harnesses/impls/portfolio-v0-evaluator/index.js.map +1 -1
  107. package/dist/harnesses/impls/prediction-v0-baseline/index.d.ts +4 -4
  108. package/dist/harnesses/impls/prediction-v0-baseline/index.js +10 -10
  109. package/dist/harnesses/impls/prediction-v0-evaluator/canonical-metrics.d.ts +3 -3
  110. package/dist/harnesses/impls/prediction-v0-evaluator/checks/availability.d.ts +1 -1
  111. package/dist/harnesses/impls/prediction-v0-evaluator/index.d.ts +4 -4
  112. package/dist/harnesses/impls/prediction-v0-evaluator/index.js +11 -11
  113. package/dist/harnesses/impls/prediction-v1-baseline/index.d.ts +31 -0
  114. package/dist/harnesses/impls/prediction-v1-baseline/index.js +79 -0
  115. package/dist/harnesses/impls/prediction-v1-baseline/index.js.map +1 -0
  116. package/dist/harnesses/impls/prediction-v1-evaluator/index.d.ts +32 -0
  117. package/dist/harnesses/impls/prediction-v1-evaluator/index.js +198 -0
  118. package/dist/harnesses/impls/prediction-v1-evaluator/index.js.map +1 -0
  119. package/dist/harnesses/manifest/types.d.ts +1 -1
  120. package/dist/harnesses/manifest/types.js +1 -1
  121. package/dist/harnesses/types.d.ts +2 -1
  122. package/dist/harnesses/types.js.map +1 -1
  123. package/dist/main.js +2 -26
  124. package/dist/main.js.map +1 -1
  125. package/dist/mcp/operator-server.js +1 -1
  126. package/dist/mcp/operator-server.js.map +1 -1
  127. package/dist/mcp/server.js +2 -2
  128. package/dist/plugins/manifest.d.ts +1 -1
  129. package/dist/plugins/manifest.js +1 -1
  130. package/dist/plugins/manifest.js.map +1 -1
  131. package/dist/plugins/registry.js +1 -1
  132. package/dist/plugins/registry.js.map +1 -1
  133. package/dist/plugins/resolvers.js +2 -1
  134. package/dist/plugins/resolvers.js.map +1 -1
  135. package/dist/plugins/types.d.ts +4 -8
  136. package/dist/plugins/validator.js +5 -7
  137. package/dist/plugins/validator.js.map +1 -1
  138. package/dist/preflight/claude-auth.d.ts +3 -1
  139. package/dist/preflight/claude-auth.js +5 -5
  140. package/dist/preflight/claude-auth.js.map +1 -1
  141. package/dist/setup/claude-code-install.d.ts +19 -0
  142. package/dist/setup/claude-code-install.js +51 -0
  143. package/dist/setup/claude-code-install.js.map +1 -0
  144. package/dist/solver-nets/contracts.d.ts +2 -0
  145. package/dist/solver-nets/contracts.js +2 -0
  146. package/dist/solver-nets/contracts.js.map +1 -0
  147. package/dist/solver-nets/registry.d.ts +2 -0
  148. package/dist/solver-nets/registry.js +13 -5
  149. package/dist/solver-nets/registry.js.map +1 -1
  150. package/dist/solver-types/constants.d.ts +1 -1
  151. package/dist/solver-types/constants.js +1 -1
  152. package/dist/solver-types/index.d.ts +5 -5
  153. package/dist/solver-types/index.js +5 -5
  154. package/dist/solver-types/prediction-apy-v0-auto.js +12 -0
  155. package/dist/solver-types/prediction-apy-v0-auto.js.map +1 -1
  156. package/dist/solver-types/prediction-v0-auto.d.ts +4 -4
  157. package/dist/solver-types/prediction-v0-auto.js +22 -10
  158. package/dist/solver-types/prediction-v0-auto.js.map +1 -1
  159. package/dist/solver-types/prediction-v0-template.d.ts +10 -10
  160. package/dist/solver-types/prediction-v0-template.js +16 -16
  161. package/dist/solver-types/prediction-v0-template.js.map +1 -1
  162. package/dist/solver-types/prediction-v0.d.ts +2 -2
  163. package/dist/solver-types/prediction-v0.js +11 -11
  164. package/dist/solver-types/prediction-v1-auto.d.ts +22 -0
  165. package/dist/solver-types/prediction-v1-auto.js +211 -0
  166. package/dist/solver-types/prediction-v1-auto.js.map +1 -0
  167. package/dist/solver-types/prediction-v1.d.ts +3 -0
  168. package/dist/solver-types/prediction-v1.js +29 -0
  169. package/dist/solver-types/prediction-v1.js.map +1 -0
  170. package/dist/solver-types/solver-type.d.ts +5 -4
  171. package/dist/store/store.d.ts +4 -0
  172. package/dist/store/store.js +28 -4
  173. package/dist/store/store.js.map +1 -1
  174. package/dist/tasks/posting-service.d.ts +5 -2
  175. package/dist/tasks/posting-service.js +12 -6
  176. package/dist/tasks/posting-service.js.map +1 -1
  177. package/dist/tasks/sources.d.ts +2 -2
  178. package/dist/tasks/sources.js +10 -7
  179. package/dist/tasks/sources.js.map +1 -1
  180. package/dist/templates/harnesses/alternative-harness/package.json.tmpl +1 -1
  181. package/dist/templates/harnesses/alternative-harness/src/coordinator.ts.tmpl +1 -1
  182. package/dist/templates/harnesses/alternative-harness/src/index.ts.tmpl +1 -1
  183. package/dist/templates/harnesses/alternative-harness/src/mock-harness.ts.tmpl +1 -1
  184. package/dist/templates/harnesses/alternative-harness/src/phases/debrief.ts.tmpl +1 -1
  185. package/dist/templates/harnesses/alternative-harness/src/phases/execute.ts.tmpl +1 -1
  186. package/dist/templates/harnesses/alternative-harness/src/phases/improve.ts.tmpl +1 -1
  187. package/dist/templates/harnesses/alternative-harness/src/phases/memory.ts.tmpl +1 -1
  188. package/dist/templates/harnesses/alternative-harness/src/phases/orient.ts.tmpl +1 -1
  189. package/dist/templates/harnesses/alternative-harness/src/phases/plan.ts.tmpl +1 -1
  190. package/dist/templates/harnesses/alternative-harness/src/phases/strategize.ts.tmpl +1 -1
  191. package/dist/templates/harnesses/alternative-harness/test/coordinator.test.ts.tmpl +1 -1
  192. package/dist/templates/harnesses/alternative-harness/test/unit.test.ts.tmpl +1 -1
  193. package/dist/templates/harnesses/evaluator/package.json.tmpl +1 -1
  194. package/dist/templates/harnesses/evaluator/src/index.ts.tmpl +1 -1
  195. package/dist/templates/harnesses/evaluator/test/unit.test.ts.tmpl +1 -1
  196. package/dist/templates/harnesses/forecaster/package.json.tmpl +1 -1
  197. package/dist/templates/harnesses/forecaster/src/index.ts.tmpl +1 -1
  198. package/dist/templates/harnesses/forecaster/test/unit.test.ts.tmpl +1 -1
  199. package/dist/trajectory/schema.d.ts +10 -10
  200. package/dist/types/index.d.ts +2 -2
  201. package/dist/types/index.js +1 -1
  202. package/dist/types/index.js.map +1 -1
  203. package/dist/types/payloads/index.d.ts +1 -0
  204. package/dist/types/payloads/index.js +6 -0
  205. package/dist/types/payloads/index.js.map +1 -1
  206. package/dist/types/payloads/prediction-v1.d.ts +2 -0
  207. package/dist/types/payloads/prediction-v1.js +2 -0
  208. package/dist/types/payloads/prediction-v1.js.map +1 -0
  209. package/dist/types/prediction-v1.d.ts +2 -0
  210. package/dist/types/prediction-v1.js +2 -0
  211. package/dist/types/prediction-v1.js.map +1 -0
  212. package/dist/types/prediction.d.ts +559 -19
  213. package/dist/types/prediction.js +12 -9
  214. package/dist/types/prediction.js.map +1 -1
  215. package/dist/types/task-document.d.ts +389 -0
  216. package/dist/types/task-document.js +11 -0
  217. package/dist/types/task-document.js.map +1 -1
  218. package/dist/types/task.d.ts +285 -1
  219. package/dist/types/task.js +6 -1
  220. package/dist/types/task.js.map +1 -1
  221. package/dist/vendor/@jinn-network/sdk/README.md +61 -0
  222. package/dist/vendor/@jinn-network/sdk/dist/capabilities.d.ts +52 -0
  223. package/dist/vendor/@jinn-network/sdk/dist/capabilities.js +1 -0
  224. package/dist/vendor/@jinn-network/sdk/dist/contracts.d.ts +105 -0
  225. package/dist/vendor/@jinn-network/sdk/dist/contracts.js +147 -0
  226. package/dist/vendor/@jinn-network/sdk/dist/harness.d.ts +69 -0
  227. package/dist/vendor/@jinn-network/sdk/dist/harness.js +1 -0
  228. package/dist/vendor/@jinn-network/sdk/dist/index.d.ts +2 -0
  229. package/dist/vendor/@jinn-network/sdk/dist/index.js +5 -0
  230. package/dist/vendor/@jinn-network/sdk/dist/manifest.d.ts +69 -0
  231. package/dist/vendor/@jinn-network/sdk/dist/manifest.js +3 -0
  232. package/dist/vendor/@jinn-network/sdk/dist/payloads/prediction-v1.d.ts +223 -0
  233. package/dist/vendor/@jinn-network/sdk/dist/payloads/prediction-v1.js +64 -0
  234. package/dist/vendor/@jinn-network/sdk/dist/plugins.d.ts +36 -0
  235. package/dist/vendor/@jinn-network/sdk/dist/plugins.js +54 -0
  236. package/dist/vendor/@jinn-network/sdk/dist/prediction-v1.d.ts +870 -0
  237. package/dist/vendor/@jinn-network/sdk/dist/prediction-v1.js +80 -0
  238. package/dist/vendor/@jinn-network/sdk/dist/solvernets/index.d.ts +2 -0
  239. package/dist/vendor/@jinn-network/sdk/dist/solvernets/index.js +1 -0
  240. package/dist/vendor/@jinn-network/sdk/dist/solvernets/prediction-v1.d.ts +6 -0
  241. package/dist/vendor/@jinn-network/sdk/dist/solvernets/prediction-v1.js +3 -0
  242. package/dist/vendor/@jinn-network/sdk/dist/types.d.ts +116 -0
  243. package/dist/vendor/@jinn-network/sdk/dist/types.js +21 -0
  244. package/dist/vendor/@jinn-network/sdk/package.json +34 -0
  245. package/dist/venues/polymarket/client.d.ts +77 -0
  246. package/dist/venues/polymarket/client.js +309 -0
  247. package/dist/venues/polymarket/client.js.map +1 -0
  248. package/dist/withdraw/run-withdraw-plan.js +0 -2
  249. package/dist/withdraw/run-withdraw-plan.js.map +1 -1
  250. package/package.json +8 -6
  251. package/plugins/jinn-prediction-plugin/.claude-plugin/plugin.json +13 -86
  252. package/plugins/jinn-prediction-plugin/.mcp.json +8 -0
  253. package/plugins/jinn-prediction-plugin/GEMINI.md +3 -0
  254. package/plugins/jinn-prediction-plugin/gemini-extension.json +13 -0
  255. package/plugins/jinn-prediction-plugin/jinn.plugin.json +23 -0
  256. package/plugins/jinn-prediction-plugin/schemas/prediction-v1-solution.schema.json +34 -0
  257. package/plugins/jinn-prediction-plugin/schemas/prediction-v1-task.schema.json +118 -0
  258. package/plugins/jinn-prediction-plugin/schemas/prediction-v1-verdict.schema.json +84 -0
  259. package/templates/harnesses/alternative-harness/package.json.tmpl +1 -1
  260. package/templates/harnesses/alternative-harness/src/coordinator.ts.tmpl +1 -1
  261. package/templates/harnesses/alternative-harness/src/index.ts.tmpl +1 -1
  262. package/templates/harnesses/alternative-harness/src/mock-harness.ts.tmpl +1 -1
  263. package/templates/harnesses/alternative-harness/src/phases/debrief.ts.tmpl +1 -1
  264. package/templates/harnesses/alternative-harness/src/phases/execute.ts.tmpl +1 -1
  265. package/templates/harnesses/alternative-harness/src/phases/improve.ts.tmpl +1 -1
  266. package/templates/harnesses/alternative-harness/src/phases/memory.ts.tmpl +1 -1
  267. package/templates/harnesses/alternative-harness/src/phases/orient.ts.tmpl +1 -1
  268. package/templates/harnesses/alternative-harness/src/phases/plan.ts.tmpl +1 -1
  269. package/templates/harnesses/alternative-harness/src/phases/strategize.ts.tmpl +1 -1
  270. package/templates/harnesses/alternative-harness/test/coordinator.test.ts.tmpl +1 -1
  271. package/templates/harnesses/alternative-harness/test/unit.test.ts.tmpl +1 -1
  272. package/templates/harnesses/evaluator/package.json.tmpl +1 -1
  273. package/templates/harnesses/evaluator/src/index.ts.tmpl +1 -1
  274. package/templates/harnesses/evaluator/test/unit.test.ts.tmpl +1 -1
  275. package/templates/harnesses/forecaster/package.json.tmpl +1 -1
  276. package/templates/harnesses/forecaster/src/index.ts.tmpl +1 -1
  277. package/templates/harnesses/forecaster/test/unit.test.ts.tmpl +1 -1
  278. package/dist/adapters/claim-registry/abi.d.ts +0 -127
  279. package/dist/adapters/claim-registry/abi.js +0 -93
  280. package/dist/adapters/claim-registry/abi.js.map +0 -1
  281. package/dist/adapters/claim-registry/client.d.ts +0 -98
  282. package/dist/adapters/claim-registry/client.js +0 -225
  283. package/dist/adapters/claim-registry/client.js.map +0 -1
  284. package/dist/adapters/mech/claim-policy.d.ts +0 -40
  285. package/dist/adapters/mech/claim-policy.js +0 -104
  286. package/dist/adapters/mech/claim-policy.js.map +0 -1
  287. package/dist/harnesses/engine/claim.d.ts +0 -69
  288. package/dist/harnesses/engine/claim.js +0 -111
  289. package/dist/harnesses/engine/claim.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { getAddress } from 'viem';
2
- import { keccak256 } from 'viem';
1
+ import { getAddress, zeroAddress } from 'viem';
2
+ import { keccak256, toBytes } from 'viem';
3
3
  import { privateKeyToAccount } from 'viem/accounts';
4
4
  import { base, baseSepolia } from 'viem/chains';
5
5
  import { PermanentError, parseTask } from '../../types/index.js';
@@ -7,13 +7,19 @@ import { createClients } from './safe.js';
7
7
  import { buildResultPayload, uploadToIpfs, cidToDigestHex, fetchFromIpfs, fetchSignedTaskFromIpfs, fetchSignedEnvelopeFromIpfs, } from './ipfs.js';
8
8
  import { canonicalJson } from '../../harnesses/engine/canonical-json.js';
9
9
  import { SignedEnvelopeSchema } from '../../types/envelope.js';
10
- import { submitTask, submitEvaluationJob, claimDelivery, getMechDeliveryRate, getTimeoutBounds, decodeMarketplaceRequestLogs, decodeDeliverLogs, callDeliverToMarketplace, scanTasks, scanEvaluationJobs, scanLatestRequestDataByRid, scanLatestDeliveryDataByRid, findLatestDeliveryDataHexForRequest, } from './contracts.js';
11
- import { MECH_MARKETPLACE_ABI } from './types.js';
12
- import { AcceptAllPolicy } from './claim-policy.js';
10
+ import { submitTask, claimTask as claimTaskOnchain, claimEvaluation as claimEvaluationOnchain, claimDelivery, getMechDeliveryRate, getTimeoutBounds, decodeTaskCreatedLogs, decodeSolutionDeliveryClaimedLogs, decodeDeliverLogs, findLatestDeliveryDataHexForRequest, getMarketplaceRequestDeliveryMech, getTaskCidDigest, callDeliverToMarketplace, } from './contracts.js';
13
11
  import { withRecoverableRetry } from '../../tx-retry.js';
14
12
  import { formatRpcError } from '../../rpc-error-context.js';
15
- import { RESTORATION_TASK_CID_CONTEXT_KEY, RESTORATION_ENVELOPE_CID_CONTEXT_KEY } from '../../harnesses/impls/evaluation-context.js';
13
+ import { RESTORATION_ENVELOPE_CID_CONTEXT_KEY, RESTORATION_TASK_CID_CONTEXT_KEY, } from '../../harnesses/impls/evaluation-context.js';
16
14
  import { signTaskV1 } from '../../tasks/signing.js';
15
+ const ROUTER_REQUEST_CURSOR_CONFIG_KEY = 'mech_router_request_block_cursor_v1';
16
+ const PENDING_EVALUATION_SOLUTIONS_CONFIG_KEY = 'mech_pending_evaluation_solutions_v1';
17
+ const DEFAULT_MECH_CLAIM_POLICY = {
18
+ mode: 'exclusive',
19
+ maxClaims: 1,
20
+ maxClaimsPerOperator: 1,
21
+ claimLeaseTtlSeconds: 30 * 60,
22
+ };
17
23
  export class MechAdapter {
18
24
  name = 'mech';
19
25
  publicClient;
@@ -23,27 +29,17 @@ export class MechAdapter {
23
29
  requestBlockCursor = 0n;
24
30
  deliveryBlockCursor = 0n;
25
31
  pendingEvaluations = new Map();
26
- pendingEvaluationClaims = new Set();
27
- // Restoration requests where claimDelivery succeeded but evaluation creation failed.
28
- // Swept on each poll cycle so they don't require a new Deliver event.
29
- claimedButNotEvaluated = new Set();
30
- // Restoration result content cached across evaluation-job retries so a transient
31
- // Safe/router failure does not silently strip evaluator context on the next poll.
32
- pendingEvaluationResults = new Map();
33
- // IPFS CID of the restoration envelope — threaded into evaluation context so
34
- // evaluators can populate restorationEnvelope.cid without a synchronous IPFS fetch.
35
- pendingEvaluationResultCids = new Map();
36
- // RIDs with no delivery found in backfill window; avoids repeated 500k-block rescans.
37
- backfillMissRids = new Set();
32
+ observedTasks = new Map();
33
+ requestKinds = new Map();
34
+ evaluationOpportunities = new Map();
35
+ pendingEvaluationSolutions = new Map();
38
36
  // Original Tasks keyed by request ID (restoration and evaluation)
39
37
  // so we can yield accurate Task in DeliveredResult
40
38
  originalStates = new Map();
41
39
  store;
42
- claimPolicy;
43
40
  constructor(config, store) {
44
41
  this.config = config;
45
42
  this.store = store;
46
- this.claimPolicy = config.claimPolicy ?? new AcceptAllPolicy();
47
43
  }
48
44
  async initialize() {
49
45
  const chain = this.config.chainId === 84532 ? baseSepolia : base;
@@ -64,102 +60,74 @@ export class MechAdapter {
64
60
  this.deliveryBlockCursor = blockNumber;
65
61
  // Recover pending state from on-chain events
66
62
  if (this.store) {
63
+ this.loadPendingEvaluationSolutions();
67
64
  await this.recoverPendingState(blockNumber);
68
65
  }
69
66
  }
70
67
  async recoverPendingState(currentBlock) {
71
68
  const fromBlock = this.store?.getLastProcessedBlock() ?? currentBlock;
72
- if (fromBlock >= currentBlock)
73
- return;
74
- console.error(`[mech] Recovering pending state from block ${fromBlock} to ${currentBlock}`);
75
- // Scan for restoration jobs this creator posted
76
- const restorations = await scanTasks(this.publicClient, this.config.routerAddress, this.config.safeAddress, fromBlock, currentBlock);
77
- // Scan for evaluation jobs this creator posted
78
- const evaluations = await scanEvaluationJobs(this.publicClient, this.config.routerAddress, this.config.safeAddress, fromBlock, currentBlock);
79
- // Build set of restoration IDs that already have evaluation jobs
80
- const hasEvaluation = new Set(evaluations.map(e => e.restorationRequestId));
81
- // Check each restoration's delivery status
82
- for (const restoration of restorations) {
83
- if (hasEvaluation.has(restoration.requestId)) {
84
- // Evaluation already created — check if eval delivery needs claiming
85
- const evalJob = evaluations.find(e => e.restorationRequestId === restoration.requestId);
86
- if (evalJob) {
87
- const evalInfo = await this.publicClient.readContract({
88
- address: this.config.mechMarketplaceAddress,
89
- abi: MECH_MARKETPLACE_ABI,
90
- functionName: 'mapRequestIdInfos',
91
- args: [evalJob.requestId],
92
- });
93
- if (evalInfo[1] === '0x0000000000000000000000000000000000000000') {
94
- // Evaluation not yet delivered — track it
95
- this.pendingEvaluationClaims.add(evalJob.requestId);
96
- }
97
- // If delivered, fully complete — nothing to track
98
- }
99
- continue;
100
- }
101
- // No evaluation job yet — check delivery status
102
- const info = await this.publicClient.readContract({
103
- address: this.config.mechMarketplaceAddress,
104
- abi: MECH_MARKETPLACE_ABI,
105
- functionName: 'mapRequestIdInfos',
106
- args: [restoration.requestId],
107
- });
108
- const deliveryMech = info[1];
109
- if (deliveryMech === '0x0000000000000000000000000000000000000000') {
110
- // Not delivered yet — track for evaluation after delivery
111
- this.pendingEvaluations.set(restoration.requestId, {
112
- id: restoration.requestId,
113
- description: '', // Original description not available from events, but not needed for evaluation creation
114
- });
115
- }
116
- else {
117
- // Delivered but no evaluation — needs claim + evaluation creation
118
- this.pendingEvaluations.set(restoration.requestId, {
119
- id: restoration.requestId,
120
- description: '',
121
- });
122
- this.claimedButNotEvaluated.add(restoration.requestId);
123
- }
69
+ if (fromBlock < currentBlock) {
70
+ console.error(`[mech] TaskCoordinator clean-break recovery starts at block ${fromBlock}; ` +
71
+ 'old request-first recovery is intentionally disabled');
72
+ this.deliveryBlockCursor = fromBlock;
124
73
  }
125
- // Set delivery block cursor to scan from recovery point
126
- this.deliveryBlockCursor = fromBlock;
127
- const latestRequestDataByRid = await scanLatestRequestDataByRid(this.publicClient, this.config.mechMarketplaceAddress, fromBlock, currentBlock);
128
- const latestDeliveryDataByRid = await scanLatestDeliveryDataByRid(this.publicClient, this.config.mechContractAddress, fromBlock, currentBlock);
129
- for (const { requestId } of restorations) {
130
- const pe = this.pendingEvaluations.get(requestId);
131
- if (!pe)
132
- continue;
133
- const requestDataHex = latestRequestDataByRid.get(requestId.toLowerCase());
134
- if (requestDataHex) {
135
- const d = String(requestDataHex);
136
- const digest = d.startsWith('0x') ? d.slice(2) : d;
137
- this.pendingEvaluations.set(requestId, {
138
- ...pe,
139
- context: { ...pe.context, [RESTORATION_TASK_CID_CONTEXT_KEY]: `f01551220${digest}` },
140
- });
141
- }
142
- const deliveryDataHex = latestDeliveryDataByRid.get(requestId.toLowerCase());
143
- if (deliveryDataHex) {
144
- try {
145
- const d = String(deliveryDataHex);
146
- const dig = d.startsWith('0x') ? d.slice(2) : d;
147
- const envelopeCid = `f01551220${dig}`;
148
- const payload = (await fetchFromIpfs(this.config.ipfsGatewayUrl, envelopeCid));
149
- this.pendingEvaluationResults.set(requestId, payload.data ?? JSON.stringify(payload));
150
- this.pendingEvaluationResultCids.set(requestId, envelopeCid);
151
- this.backfillMissRids.delete(requestId);
152
- }
153
- catch (err) {
154
- console.error(`[mech] recovery: could not load restoration result IPFS for ${requestId}:`, err);
74
+ const routerCursorRaw = this.store?.getConfigValue(ROUTER_REQUEST_CURSOR_CONFIG_KEY);
75
+ const routerFromBlock = routerCursorRaw != null
76
+ ? BigInt(routerCursorRaw)
77
+ : fromBlock;
78
+ if (routerFromBlock < currentBlock) {
79
+ this.requestBlockCursor = routerFromBlock;
80
+ }
81
+ }
82
+ loadPendingEvaluationSolutions() {
83
+ const raw = this.store?.getConfigValue(PENDING_EVALUATION_SOLUTIONS_CONFIG_KEY);
84
+ if (!raw)
85
+ return;
86
+ try {
87
+ const parsed = JSON.parse(raw);
88
+ if (!Array.isArray(parsed))
89
+ return;
90
+ for (const value of parsed) {
91
+ if (value == null || typeof value !== 'object')
92
+ continue;
93
+ const item = value;
94
+ if (typeof item.taskId !== 'string' ||
95
+ typeof item.requestId !== 'string' ||
96
+ typeof item.operator !== 'string' ||
97
+ typeof item.attemptIndex !== 'number') {
98
+ continue;
155
99
  }
100
+ const solution = {
101
+ taskId: item.taskId,
102
+ attemptIndex: item.attemptIndex,
103
+ requestId: item.requestId,
104
+ operator: item.operator,
105
+ transactionHash: typeof item.transactionHash === 'string'
106
+ ? item.transactionHash
107
+ : undefined,
108
+ blockNumber: typeof item.blockNumber === 'number' ? item.blockNumber : undefined,
109
+ };
110
+ this.pendingEvaluationSolutions.set(solution.requestId, solution);
156
111
  }
157
112
  }
158
- const recovered = this.pendingEvaluations.size + this.pendingEvaluationClaims.size + this.claimedButNotEvaluated.size;
159
- if (recovered > 0) {
160
- console.error(`[mech] Recovered: ${this.pendingEvaluations.size} pending evaluations, ${this.pendingEvaluationClaims.size} pending eval claims, ${this.claimedButNotEvaluated.size} claimed but not evaluated`);
113
+ catch (err) {
114
+ console.error('[mech] Failed to load pending evaluation solutions:', err);
161
115
  }
162
116
  }
117
+ persistPendingEvaluationSolutions() {
118
+ if (!this.store)
119
+ return;
120
+ this.store.setConfigValue(PENDING_EVALUATION_SOLUTIONS_CONFIG_KEY, JSON.stringify(Array.from(this.pendingEvaluationSolutions.values())));
121
+ }
122
+ rememberPendingEvaluationSolution(solution) {
123
+ this.pendingEvaluationSolutions.set(solution.requestId, solution);
124
+ this.persistPendingEvaluationSolutions();
125
+ }
126
+ forgetPendingEvaluationSolution(requestId) {
127
+ if (!this.pendingEvaluationSolutions.delete(requestId))
128
+ return;
129
+ this.persistPendingEvaluationSolutions();
130
+ }
163
131
  async postTask(state) {
164
132
  const restorationState = {
165
133
  ...state,
@@ -177,25 +145,27 @@ export class MechAdapter {
177
145
  const restorationTaskCid = `f01551220${digestNo0x}`;
178
146
  const deliveryRate = await getMechDeliveryRate(this.publicClient, this.config.mechContractAddress);
179
147
  const { max: maxTimeout } = await getTimeoutBounds(this.publicClient, this.config.mechMarketplaceAddress);
180
- const taskSubmission = await submitTask(this.publicClient, this.walletClient, this.config.safeAddress, this.config.routerAddress, this.config.mechContractAddress, restorationDataHex, deliveryRate, maxTimeout, this.config.evictionRecovery);
181
- const restorationRequestIds = taskSubmission.requestIds;
182
- if (restorationRequestIds.length === 0) {
183
- throw new PermanentError('No request IDs returned from router ' +
184
- `(tx=${taskSubmission.txHash}, router=${this.config.routerAddress}, safe=${this.config.safeAddress}, ` +
185
- `mech=${this.config.mechContractAddress}, receiptLogs=${taskSubmission.receiptLogCount}, chainId=${this.config.chainId})`);
186
- }
187
- const restorationRequestId = restorationRequestIds[0];
188
- // Store for evaluation creation after delivery is claimed. The evaluation
189
- // job’s IPFS CID is different from the restoration intended-state CID; evaluators
190
- // need the latter to verify submission.signedTask.cid (see context.restorationTaskCid).
191
- const stateForEval = {
192
- ...state,
193
- signedTask,
194
- context: { ...(state.context ?? {}), [RESTORATION_TASK_CID_CONTEXT_KEY]: restorationTaskCid },
148
+ const solverTypeDigest = keccak256(toBytes(signedTask.solverType));
149
+ const policy = this.contractPolicyForTask(restorationState);
150
+ const taskSubmission = await submitTask(this.publicClient, this.walletClient, this.config.safeAddress, this.config.routerAddress, restorationDataHex, solverTypeDigest, policy, deliveryRate, deliveryRate, maxTimeout, this.config.evictionRecovery);
151
+ const announcement = {
152
+ taskId: taskSubmission.taskId,
153
+ task: {
154
+ ...restorationState,
155
+ signedTask,
156
+ context: { ...(restorationState.context ?? {}), [RESTORATION_TASK_CID_CONTEXT_KEY]: restorationTaskCid },
157
+ },
158
+ taskCid: restorationTaskCid,
159
+ onchainCreationTx: taskSubmission.txHash,
160
+ onchainCreationBlock: taskSubmission.blockNumber,
161
+ };
162
+ this.observedTasks.set(taskSubmission.taskId, announcement);
163
+ return {
164
+ taskId: taskSubmission.taskId,
165
+ taskCid: restorationTaskCid,
166
+ txHash: taskSubmission.txHash,
167
+ blockNumber: taskSubmission.blockNumber,
195
168
  };
196
- this.pendingEvaluations.set(restorationRequestId, stateForEval);
197
- this.originalStates.set(restorationRequestId, { ...stateForEval, role: 'restoration' });
198
- return restorationRequestId;
199
169
  }
200
170
  async signTaskDocument(state) {
201
171
  const now = Date.now();
@@ -218,6 +188,7 @@ export class MechAdapter {
218
188
  window: state.window ?? { startTs: now, endTs: now + 86_400_000 },
219
189
  spec: state.spec ?? {},
220
190
  eligibility: state.eligibility ?? {},
191
+ claimPolicy: state.claimPolicy ?? DEFAULT_MECH_CLAIM_POLICY,
221
192
  creator: {
222
193
  safeAddress: getAddress(this.config.safeAddress),
223
194
  agentEoa: account.address,
@@ -227,24 +198,159 @@ export class MechAdapter {
227
198
  };
228
199
  return signTaskV1(taskDoc, this.config.agentEoaPrivateKey);
229
200
  }
230
- async *watchForRequests() {
201
+ contractPolicyForTask(state) {
202
+ const nowSeconds = Math.floor(Date.now() / 1000);
203
+ const claimPolicy = state.claimPolicy ?? DEFAULT_MECH_CLAIM_POLICY;
204
+ const normalizeTs = (value, fallback) => {
205
+ const raw = value ?? fallback;
206
+ return BigInt(raw > 10_000_000_000 ? Math.floor(raw / 1000) : raw);
207
+ };
208
+ const claimWindowStart = normalizeTs(claimPolicy.claimWindowStartTs ?? state.window?.startTs, nowSeconds);
209
+ const claimWindowEnd = normalizeTs(claimPolicy.claimWindowEndTs ?? state.window?.endTs, nowSeconds + 30 * 60);
210
+ const submissionDeadline = normalizeTs(claimPolicy.submissionDeadlineTs, Number(claimWindowEnd) + claimPolicy.claimLeaseTtlSeconds);
211
+ return {
212
+ claimWindowStart,
213
+ claimWindowEnd,
214
+ submissionDeadline,
215
+ claimLeaseTtlSeconds: claimPolicy.claimLeaseTtlSeconds,
216
+ maxClaims: claimPolicy.maxClaims,
217
+ maxClaimsPerOperator: claimPolicy.maxClaimsPerOperator,
218
+ policyHook: (claimPolicy.policyHook ?? zeroAddress),
219
+ evaluationPolicy: {
220
+ requiredVerdicts: 1,
221
+ passThreshold: 1,
222
+ evaluationDeadline: submissionDeadline + BigInt(claimPolicy.claimLeaseTtlSeconds),
223
+ maxVerdictsPerEvaluator: 1,
224
+ disallowSolverSelfEvaluation: true,
225
+ },
226
+ };
227
+ }
228
+ buildEvaluationTask(params) {
229
+ return {
230
+ ...params.task,
231
+ id: `${params.task.id}:evaluation:${params.attemptIndex}`,
232
+ role: 'evaluation',
233
+ restorationRequestId: params.solutionRequestId,
234
+ attemptId: params.solutionRequestId,
235
+ attemptNumber: params.attemptIndex,
236
+ context: {
237
+ ...(params.task.context ?? {}),
238
+ restorationResult: params.resultData,
239
+ [RESTORATION_TASK_CID_CONTEXT_KEY]: params.task.context?.[RESTORATION_TASK_CID_CONTEXT_KEY] ?? params.taskCid,
240
+ [RESTORATION_ENVELOPE_CID_CONTEXT_KEY]: params.restorationEnvelopeCid,
241
+ },
242
+ };
243
+ }
244
+ async restorationAnnouncementForTaskId(taskId) {
245
+ const cached = this.observedTasks.get(taskId);
246
+ if (cached)
247
+ return cached;
248
+ const taskCidDigest = await getTaskCidDigest(this.publicClient, this.config.routerAddress, taskId);
249
+ const digest = taskCidDigest.startsWith('0x') ? taskCidDigest.slice(2) : taskCidDigest;
250
+ const taskCid = `f01551220${digest}`;
251
+ const signed = await fetchSignedTaskFromIpfs(this.config.ipfsGatewayUrl, taskCid);
252
+ const task = parseTask({ signedTask: signed });
253
+ const announcement = {
254
+ taskId,
255
+ task,
256
+ taskCid,
257
+ };
258
+ this.observedTasks.set(taskId, announcement);
259
+ return announcement;
260
+ }
261
+ async deliveryEnvelopeCidForSolution(solution) {
262
+ const deliveryMech = await getMarketplaceRequestDeliveryMech(this.publicClient, this.config.mechMarketplaceAddress, solution.requestId);
263
+ const toBlock = solution.blockNumber != null
264
+ ? BigInt(solution.blockNumber)
265
+ : await this.publicClient.getBlockNumber();
266
+ const configuredLookback = this.config.mechDeliverBackfillLookbackBlocks;
267
+ const fromBlock = configuredLookback == null
268
+ ? 0n
269
+ : toBlock > configuredLookback
270
+ ? toBlock - configuredLookback
271
+ : 0n;
272
+ const deliveryDataHex = await findLatestDeliveryDataHexForRequest(this.publicClient, deliveryMech, solution.requestId, fromBlock, toBlock);
273
+ if (!deliveryDataHex) {
274
+ throw new Error(`No Deliver event data found for solution ${solution.requestId} on mech ${deliveryMech} ` +
275
+ `between blocks ${fromBlock} and ${toBlock}`);
276
+ }
277
+ const digest = deliveryDataHex.startsWith('0x') ? deliveryDataHex.slice(2) : deliveryDataHex;
278
+ return `f01551220${digest}`;
279
+ }
280
+ async evaluationAnnouncementForSolution(solution) {
281
+ if (solution.operator.toLowerCase() === this.config.safeAddress.toLowerCase()) {
282
+ return undefined;
283
+ }
284
+ const restoration = await this.restorationAnnouncementForTaskId(solution.taskId);
285
+ const restorationEnvelopeCid = await this.deliveryEnvelopeCidForSolution(solution);
286
+ const resultPayload = await fetchFromIpfs(this.config.ipfsGatewayUrl, restorationEnvelopeCid);
287
+ const resultData = resultPayload.data ?? JSON.stringify(resultPayload);
288
+ const evaluationTask = this.buildEvaluationTask({
289
+ task: restoration.task,
290
+ solutionRequestId: solution.requestId,
291
+ attemptIndex: solution.attemptIndex,
292
+ resultData,
293
+ restorationEnvelopeCid,
294
+ taskCid: restoration.taskCid,
295
+ });
296
+ const opportunityId = `evaluation:${solution.taskId}:${solution.attemptIndex}:${solution.requestId}`;
297
+ const announcement = {
298
+ taskId: opportunityId,
299
+ task: evaluationTask,
300
+ taskCid: restoration.taskCid,
301
+ onchainCreationTx: solution.transactionHash,
302
+ onchainCreationBlock: solution.blockNumber,
303
+ };
304
+ this.evaluationOpportunities.set(opportunityId, {
305
+ taskId: solution.taskId,
306
+ attemptIndex: solution.attemptIndex,
307
+ task: evaluationTask,
308
+ });
309
+ this.observedTasks.set(opportunityId, announcement);
310
+ return announcement;
311
+ }
312
+ async *retryPendingEvaluationSolutions() {
313
+ for (const [requestId, solution] of Array.from(this.pendingEvaluationSolutions)) {
314
+ try {
315
+ const announcement = await this.evaluationAnnouncementForSolution(solution);
316
+ if (announcement) {
317
+ yield announcement;
318
+ }
319
+ else {
320
+ this.forgetPendingEvaluationSolution(requestId);
321
+ }
322
+ }
323
+ catch (err) {
324
+ console.error(`[mech] evaluation opportunity retry failed for ${requestId}:`, err);
325
+ }
326
+ }
327
+ }
328
+ async *watchForTasks() {
231
329
  while (!this.stopped) {
232
330
  try {
331
+ for await (const announcement of this.retryPendingEvaluationSolutions()) {
332
+ yield announcement;
333
+ }
233
334
  const currentBlock = await this.publicClient.getBlockNumber();
234
335
  if (currentBlock > this.requestBlockCursor) {
336
+ const fromBlock = this.requestBlockCursor + 1n;
235
337
  const logs = await this.publicClient.getLogs({
236
- address: this.config.mechMarketplaceAddress,
237
- fromBlock: this.requestBlockCursor + 1n,
338
+ address: this.config.routerAddress,
339
+ fromBlock,
238
340
  toBlock: currentBlock,
239
341
  });
342
+ const submittedSolutions = decodeSolutionDeliveryClaimedLogs(logs);
343
+ for (const solution of submittedSolutions) {
344
+ this.rememberPendingEvaluationSolution(solution);
345
+ }
240
346
  this.requestBlockCursor = currentBlock;
241
- const decoded = decodeMarketplaceRequestLogs(logs);
242
- for (const { requestId, requestDataHex, priorityMech, transactionHash, blockNumber } of decoded) {
243
- if (!this.claimPolicy.shouldAccept({ requestId, requestDataHex, priorityMech })) {
244
- continue;
245
- }
347
+ if (this.store) {
348
+ this.store.setConfigValue(ROUTER_REQUEST_CURSOR_CONFIG_KEY, currentBlock.toString());
349
+ }
350
+ const createdTasks = decodeTaskCreatedLogs(logs);
351
+ for (const { taskId, taskCidDigest, transactionHash, blockNumber } of createdTasks) {
246
352
  try {
247
- const digest = requestDataHex.startsWith('0x') ? requestDataHex.slice(2) : requestDataHex;
353
+ const digest = taskCidDigest.startsWith('0x') ? taskCidDigest.slice(2) : taskCidDigest;
248
354
  // CIDv1 hex with raw codec (0x55) + sha2-256 (0x12) + 32-byte length (0x20).
249
355
  // The Autonolas registry returns raw-codec CIDs when uploading files with
250
356
  // cid-version=1 (Kubo default for files). This is confirmed by the existing
@@ -253,37 +359,80 @@ export class MechAdapter {
253
359
  const taskCid = `f01551220${digest}`;
254
360
  const signed = await fetchSignedTaskFromIpfs(this.config.ipfsGatewayUrl, taskCid);
255
361
  const task = parseTask({ signedTask: signed });
256
- yield {
257
- requestId,
362
+ const announcement = {
363
+ taskId,
258
364
  task,
259
365
  taskCid,
260
366
  onchainCreationTx: transactionHash,
261
367
  onchainCreationBlock: blockNumber,
262
368
  };
369
+ this.observedTasks.set(taskId, announcement);
370
+ yield announcement;
263
371
  }
264
372
  catch (err) {
265
- console.error(`[mech] Failed to parse request ${requestId}:`, err);
373
+ console.error(`[mech] Failed to parse task ${taskId}:`, err);
266
374
  }
267
375
  }
376
+ for await (const announcement of this.retryPendingEvaluationSolutions()) {
377
+ yield announcement;
378
+ }
268
379
  }
269
380
  }
270
381
  catch (err) {
271
- console.error('[mech] Error polling for requests:', formatRpcError(err, {
272
- operation: 'pollMarketplaceRequests',
382
+ console.error('[mech] Error polling for tasks:', formatRpcError(err, {
383
+ operation: 'pollTaskCreated',
273
384
  chain: this.config.chainId === 84532 ? 'base-sepolia' : 'base',
274
385
  rpcUrl: this.config.rpcUrl,
275
- contract: this.config.mechMarketplaceAddress,
386
+ contract: this.config.routerAddress,
276
387
  fromBlock: this.requestBlockCursor + 1n,
277
388
  }));
278
389
  }
279
390
  await new Promise(r => setTimeout(r, this.config.pollIntervalMs));
280
391
  }
281
392
  }
282
- async claimRequest(requestId) {
283
- const allowed = await this.claimPolicy.confirmClaim(requestId);
284
- if (!allowed) {
285
- throw new PermanentError(`Claim policy rejected request ${requestId}`);
393
+ async claimTask(taskId) {
394
+ const evaluationOpportunity = this.evaluationOpportunities.get(taskId);
395
+ if (evaluationOpportunity) {
396
+ const signedEvaluationTask = await this.signTaskDocument(evaluationOpportunity.task);
397
+ const evaluationCid = await uploadToIpfs(this.config.ipfsRegistryUrl, signedEvaluationTask);
398
+ const evaluationTaskCidDigest = cidToDigestHex(evaluationCid);
399
+ const claimed = await this.claimEvaluation(evaluationOpportunity.taskId, evaluationOpportunity.attemptIndex, evaluationTaskCidDigest);
400
+ this.pendingEvaluations.set(claimed.requestId, evaluationOpportunity.task);
401
+ this.originalStates.set(claimed.requestId, evaluationOpportunity.task);
402
+ this.requestKinds.set(claimed.requestId, 'verdict');
403
+ this.evaluationOpportunities.delete(taskId);
404
+ const solutionRequestId = evaluationOpportunity.task.restorationRequestId;
405
+ if (solutionRequestId) {
406
+ this.forgetPendingEvaluationSolution(solutionRequestId);
407
+ }
408
+ return {
409
+ requestId: claimed.requestId,
410
+ taskId: claimed.taskId,
411
+ attemptIndex: claimed.attemptIndex,
412
+ task: evaluationOpportunity.task,
413
+ taskCid: evaluationCid,
414
+ onchainCreationTx: claimed.txHash,
415
+ onchainCreationBlock: claimed.blockNumber,
416
+ };
286
417
  }
418
+ const announcement = this.observedTasks.get(taskId);
419
+ if (!announcement) {
420
+ throw new PermanentError(`Cannot claim unknown task ${taskId}`);
421
+ }
422
+ const claimed = await claimTaskOnchain(this.publicClient, this.walletClient, this.config.safeAddress, this.config.routerAddress, taskId, this.config.mechContractAddress, this.config.evictionRecovery);
423
+ const task = announcement.task;
424
+ this.pendingEvaluations.set(claimed.requestId, task);
425
+ this.originalStates.set(claimed.requestId, { ...task, role: task.role ?? 'restoration' });
426
+ this.requestKinds.set(claimed.requestId, 'solution');
427
+ return {
428
+ requestId: claimed.requestId,
429
+ taskId: claimed.taskId,
430
+ attemptIndex: claimed.attemptIndex,
431
+ task,
432
+ taskCid: announcement.taskCid,
433
+ onchainCreationTx: claimed.txHash,
434
+ onchainCreationBlock: claimed.blockNumber,
435
+ };
287
436
  }
288
437
  async submitResult(requestId, result) {
289
438
  const payload = buildResultPayload(requestId, result);
@@ -292,9 +441,21 @@ export class MechAdapter {
292
441
  // Safe → AgentMech.deliverToMarketplace() → Marketplace.deliverMarketplace()
293
442
  await callDeliverToMarketplace(this.publicClient, this.walletClient, this.config.safeAddress, this.config.mechContractAddress, [requestId], [deliveryDigest], this.config.evictionRecovery);
294
443
  }
444
+ async claimEvaluation(taskId, attemptIndex, evaluationTaskCidDigest) {
445
+ const claimed = await claimEvaluationOnchain(this.publicClient, this.walletClient, this.config.safeAddress, this.config.routerAddress, taskId, attemptIndex, this.config.mechContractAddress, evaluationTaskCidDigest, this.config.evictionRecovery);
446
+ this.requestKinds.set(claimed.requestId, 'verdict');
447
+ return claimed;
448
+ }
449
+ async submitSolutionDelivery(requestId, solutionDigest) {
450
+ await claimDelivery(this.publicClient, this.walletClient, this.config.safeAddress, this.config.routerAddress, requestId, { variant: 'v3', kind: 'solution', evidenceHash: solutionDigest }, this.config.evictionRecovery);
451
+ }
452
+ async submitVerdictDelivery(requestId, verdictDigest, verdictCode = 1) {
453
+ await claimDelivery(this.publicClient, this.walletClient, this.config.safeAddress, this.config.routerAddress, requestId, { variant: 'v3', kind: 'verdict', evidenceHash: verdictDigest, verdictCode }, this.config.evictionRecovery);
454
+ }
295
455
  async evidenceHashForDelivery(requestId, deliveryDataHex) {
296
- if (this.config.routerClaimDeliveryVariant !== 'v2')
456
+ if (this.config.routerClaimDeliveryVariant !== 'v2' && this.config.routerClaimDeliveryVariant !== 'v3') {
297
457
  return undefined;
458
+ }
298
459
  const deliveryDigest = deliveryDataHex.startsWith('0x')
299
460
  ? deliveryDataHex.slice(2)
300
461
  : deliveryDataHex;
@@ -320,7 +481,11 @@ export class MechAdapter {
320
481
  return 'retry';
321
482
  }
322
483
  try {
323
- await claimDelivery(this.publicClient, this.walletClient, this.config.safeAddress, this.config.routerAddress, requestId, { variant: this.config.routerClaimDeliveryVariant, evidenceHash }, this.config.evictionRecovery);
484
+ await claimDelivery(this.publicClient, this.walletClient, this.config.safeAddress, this.config.routerAddress, requestId, {
485
+ variant: this.config.routerClaimDeliveryVariant,
486
+ kind: this.requestKinds.get(requestId) ?? 'solution',
487
+ evidenceHash,
488
+ }, this.config.evictionRecovery);
324
489
  return 'claimed';
325
490
  }
326
491
  catch (err) {
@@ -353,12 +518,10 @@ export class MechAdapter {
353
518
  // (a) Did this Safe DELIVER this? → claim it (counter credit goes to msg.sender)
354
519
  // The Deliver event's mechAddress is mechServiceMultisig (the Safe that owns
355
520
  // the mech), so we compare against this.config.safeAddress.
356
- // (b) Did this Safe CREATE the underlying request? → act on the delivery
357
- // (trigger eval creation for restoration deliveries; clean up for eval deliveries)
521
+ // (b) Did this Safe claim the underlying Task? → act on the delivery.
358
522
  const iDelivered = mechAddress.toLowerCase() === this.config.safeAddress.toLowerCase();
359
523
  const iCreatedRestoration = this.pendingEvaluations.has(requestId);
360
- const iCreatedEvaluation = this.pendingEvaluationClaims.has(requestId);
361
- if (!iDelivered && !iCreatedRestoration && !iCreatedEvaluation)
524
+ if (!iDelivered && !iCreatedRestoration)
362
525
  continue;
363
526
  // (a) Deliverer-side claim path: if this Safe delivered the request,
364
527
  // claim it first so router counters credit the deliverer.
@@ -368,40 +531,14 @@ export class MechAdapter {
368
531
  if (deliveryClaimStatus === 'retry')
369
532
  continue;
370
533
  }
371
- // (b) Creator-side claim path: we created the restoration, so the
372
- // router needs `restorationDeliveryClaimed` flipped before
373
- // `createEvaluationJob` can fire. Production engine deliveries
374
- // usually claim atomically; this call is idempotent and also
375
- // covers legacy/test adapter deliveries that only wrote to the
376
- // marketplace.
534
+ // (b) Task-side claim path: if this request came from our Task
535
+ // claim, make sure JinnRouterV3 records the Solution submission.
377
536
  if (iCreatedRestoration && deliveryClaimStatus !== 'claimed' && deliveryClaimStatus !== 'already-claimed') {
378
537
  const creatorClaimStatus = await this.ensureDeliveryClaimed(requestId, deliveryDataHex);
379
538
  if (creatorClaimStatus === 'retry')
380
539
  continue;
381
540
  }
382
- // (c) If I created the restoration, post the eval job once the claim is on-chain.
383
- // The deliverer (someone else, or us if we also delivered) must have claimed first.
384
- // tryCreateEvaluationJob calls verifyRestorationClaimed() which polls for the claim.
385
- if (iCreatedRestoration) {
386
- let restorationResultData;
387
- let restorationEnvelopeCid;
388
- try {
389
- const digest = deliveryDataHex.startsWith('0x') ? deliveryDataHex.slice(2) : deliveryDataHex;
390
- restorationEnvelopeCid = `f01551220${digest}`;
391
- const payload = await fetchFromIpfs(this.config.ipfsGatewayUrl, restorationEnvelopeCid);
392
- restorationResultData = payload.data ?? JSON.stringify(payload);
393
- this.backfillMissRids.delete(requestId);
394
- }
395
- catch (err) {
396
- console.error(`[mech] Failed to fetch restoration result for evaluation: ${requestId}`, err);
397
- }
398
- await this.tryCreateEvaluationJob(requestId, restorationResultData, restorationEnvelopeCid);
399
- }
400
- // (d) If I created the evaluation, clean up tracking once delivered.
401
- if (iCreatedEvaluation) {
402
- this.pendingEvaluationClaims.delete(requestId);
403
- }
404
- // (e) Yield the delivery result.
541
+ // (c) Yield the delivery result.
405
542
  try {
406
543
  const deliveryDigest = deliveryDataHex.startsWith('0x') ? deliveryDataHex.slice(2) : deliveryDataHex;
407
544
  const resultPayload = await fetchFromIpfs(this.config.ipfsGatewayUrl, `f01551220${deliveryDigest}`);
@@ -422,18 +559,14 @@ export class MechAdapter {
422
559
  };
423
560
  // Clean up after yielding
424
561
  this.originalStates.delete(requestId);
562
+ this.pendingEvaluations.delete(requestId);
563
+ this.requestKinds.delete(requestId);
425
564
  }
426
565
  catch (err) {
427
566
  console.error(`[mech] Failed to parse delivery ${requestId}:`, err);
428
567
  }
429
568
  }
430
569
  }
431
- // After new Deliver events are processed (and `pendingEvaluationResults` may be warm),
432
- // retry evaluation creation. Doing this *before* deliver processing can upload an
433
- // evaluation Task without `restorationResult` in context.
434
- for (const rid of [...this.claimedButNotEvaluated]) {
435
- await this.tryCreateEvaluationJob(rid);
436
- }
437
570
  }
438
571
  catch (err) {
439
572
  console.error('[mech] Error polling for deliveries:', formatRpcError(err, {
@@ -451,134 +584,6 @@ export class MechAdapter {
451
584
  await new Promise(r => setTimeout(r, this.config.pollIntervalMs));
452
585
  }
453
586
  }
454
- async backfillRestorationResultFromChain(requestId) {
455
- if (this.pendingEvaluationResults.has(requestId)) {
456
- return this.pendingEvaluationResults.get(requestId);
457
- }
458
- if (this.backfillMissRids.has(requestId)) {
459
- return undefined;
460
- }
461
- const currentBlock = await this.publicClient.getBlockNumber();
462
- const lookbackBlocks = this.config.mechDeliverBackfillLookbackBlocks ?? 500000n;
463
- const from = currentBlock > lookbackBlocks
464
- ? currentBlock - lookbackBlocks
465
- : 1n;
466
- const dhex = await findLatestDeliveryDataHexForRequest(this.publicClient, this.config.mechContractAddress, requestId, from, currentBlock);
467
- if (!dhex) {
468
- this.backfillMissRids.add(requestId);
469
- return undefined;
470
- }
471
- try {
472
- const d = String(dhex);
473
- const dig = d.startsWith('0x') ? d.slice(2) : d;
474
- const envelopeCid = `f01551220${dig}`;
475
- const payload = (await fetchFromIpfs(this.config.ipfsGatewayUrl, envelopeCid));
476
- const data = payload.data ?? JSON.stringify(payload);
477
- this.pendingEvaluationResults.set(requestId, data);
478
- this.pendingEvaluationResultCids.set(requestId, envelopeCid);
479
- this.backfillMissRids.delete(requestId);
480
- return data;
481
- }
482
- catch (err) {
483
- console.error(`[mech] backfill: failed to load restoration result for ${requestId}:`, err);
484
- return undefined;
485
- }
486
- }
487
- async tryCreateEvaluationJob(requestId, restorationResultData, restorationEnvelopeCid) {
488
- if (!this.pendingEvaluations.has(requestId))
489
- return;
490
- const originalState = this.pendingEvaluations.get(requestId);
491
- if (restorationResultData) {
492
- this.pendingEvaluationResults.set(requestId, restorationResultData);
493
- this.backfillMissRids.delete(requestId);
494
- }
495
- if (restorationEnvelopeCid) {
496
- this.pendingEvaluationResultCids.set(requestId, restorationEnvelopeCid);
497
- }
498
- let cachedRestorationResultData = restorationResultData ?? this.pendingEvaluationResults.get(requestId);
499
- if (cachedRestorationResultData == null) {
500
- cachedRestorationResultData = await this.backfillRestorationResultFromChain(requestId);
501
- }
502
- if (cachedRestorationResultData == null) {
503
- this.claimedButNotEvaluated.add(requestId);
504
- console.error(`[mech] no restoration result yet for ${requestId} (evaluation job and IPFS upload deferred until Deliver or backfill)`);
505
- return;
506
- }
507
- const cachedEnvelopeCid = restorationEnvelopeCid ?? this.pendingEvaluationResultCids.get(requestId);
508
- try {
509
- const evaluationState = {
510
- ...originalState,
511
- role: 'evaluation',
512
- restorationRequestId: requestId,
513
- context: {
514
- ...originalState.context,
515
- restorationResult: cachedRestorationResultData,
516
- ...(cachedEnvelopeCid ? { [RESTORATION_ENVELOPE_CID_CONTEXT_KEY]: cachedEnvelopeCid } : {}),
517
- },
518
- };
519
- const evaluationPayload = await this.signTaskDocument(evaluationState);
520
- const evaluationCid = await uploadToIpfs(this.config.ipfsRegistryUrl, evaluationPayload);
521
- const evaluationDataHex = cidToDigestHex(evaluationCid);
522
- // Verify the restoration claim landed on-chain before submitting.
523
- // RPC load balancers can serve stale state right after a write,
524
- // causing the router's restorationDeliveryClaimed check to fail
525
- // with RestorationNotClaimed (surfaces as Safe GS013).
526
- const isClaimed = await this.verifyRestorationClaimed(requestId);
527
- if (!isClaimed) {
528
- console.error(`[mech] restorationDeliveryClaimed not yet visible for ${requestId} — will retry`);
529
- this.claimedButNotEvaluated.add(requestId);
530
- return;
531
- }
532
- const deliveryRate = await getMechDeliveryRate(this.publicClient, this.config.mechContractAddress);
533
- const { max: maxTimeout } = await getTimeoutBounds(this.publicClient, this.config.mechMarketplaceAddress);
534
- const evalRequestIds = await submitEvaluationJob(this.publicClient, this.walletClient, this.config.safeAddress, this.config.routerAddress, requestId, this.config.mechContractAddress, evaluationDataHex, deliveryRate, maxTimeout, this.config.evictionRecovery);
535
- if (evalRequestIds.length > 0) {
536
- this.pendingEvaluationClaims.add(evalRequestIds[0]);
537
- // Copy original state to evaluation request ID so delivery can use it
538
- const origState = this.originalStates.get(requestId);
539
- if (origState) {
540
- this.originalStates.set(evalRequestIds[0], { ...origState, role: 'evaluation' });
541
- }
542
- }
543
- // Success — clean up both tracking sets
544
- this.pendingEvaluations.delete(requestId);
545
- this.claimedButNotEvaluated.delete(requestId);
546
- this.pendingEvaluationResults.delete(requestId);
547
- this.pendingEvaluationResultCids.delete(requestId);
548
- }
549
- catch (err) {
550
- console.error(`[mech] Failed to create evaluation job for ${requestId}:`, err);
551
- // Track for retry on next poll cycle (doesn't require a new Deliver event)
552
- this.claimedButNotEvaluated.add(requestId);
553
- }
554
- }
555
- async verifyRestorationClaimed(requestId) {
556
- // RPC / fork can lag right after claimDelivery; give more room than 5×2s so
557
- // tryCreateEvaluationJob succeeds on the first watchForDeliveries pass when possible.
558
- const MAX_POLLS = 12;
559
- const POLL_DELAY_MS = 1_500;
560
- const abi = [{
561
- type: 'function',
562
- name: 'restorationDeliveryClaimed',
563
- inputs: [{ name: 'requestId', type: 'bytes32' }],
564
- outputs: [{ name: '', type: 'bool' }],
565
- stateMutability: 'view',
566
- }];
567
- for (let i = 0; i < MAX_POLLS; i++) {
568
- const claimed = await this.publicClient.readContract({
569
- address: this.config.routerAddress,
570
- abi,
571
- functionName: 'restorationDeliveryClaimed',
572
- args: [requestId],
573
- });
574
- if (claimed)
575
- return true;
576
- if (i < MAX_POLLS - 1) {
577
- await new Promise(r => setTimeout(r, POLL_DELAY_MS));
578
- }
579
- }
580
- return false;
581
- }
582
587
  async stop() {
583
588
  this.stopped = true;
584
589
  }