@jinn-network/client 0.1.5 → 0.1.6-canary.107ea271

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 (431) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/dist/adapters/mech/adapter.d.ts +23 -1
  3. package/dist/adapters/mech/adapter.js +169 -33
  4. package/dist/adapters/mech/adapter.js.map +1 -1
  5. package/dist/adapters/mech/contracts.d.ts +1 -0
  6. package/dist/adapters/mech/contracts.js +1 -0
  7. package/dist/adapters/mech/contracts.js.map +1 -1
  8. package/dist/adapters/mech/ipfs-pinfile.d.ts +22 -0
  9. package/dist/adapters/mech/ipfs-pinfile.js +54 -0
  10. package/dist/adapters/mech/ipfs-pinfile.js.map +1 -0
  11. package/dist/adapters/mech/ipfs.d.ts +1 -0
  12. package/dist/adapters/mech/ipfs.js +24 -1
  13. package/dist/adapters/mech/ipfs.js.map +1 -1
  14. package/dist/adapters/mech/verdict-code.d.ts +1 -0
  15. package/dist/adapters/mech/verdict-code.js +18 -0
  16. package/dist/adapters/mech/verdict-code.js.map +1 -1
  17. package/dist/api/bootstrap-endpoint.js +63 -1
  18. package/dist/api/bootstrap-endpoint.js.map +1 -1
  19. package/dist/api/codex-doctor-endpoint.d.ts +73 -0
  20. package/dist/api/codex-doctor-endpoint.js +158 -0
  21. package/dist/api/codex-doctor-endpoint.js.map +1 -0
  22. package/dist/api/discovery-endpoint.d.ts +31 -0
  23. package/dist/api/discovery-endpoint.js +78 -0
  24. package/dist/api/discovery-endpoint.js.map +1 -0
  25. package/dist/api/fleet-build.d.ts +8 -0
  26. package/dist/api/fleet-build.js +8 -2
  27. package/dist/api/fleet-build.js.map +1 -1
  28. package/dist/api/gather-status.js +90 -4
  29. package/dist/api/gather-status.js.map +1 -1
  30. package/dist/api/harness-readiness-endpoint.d.ts +25 -0
  31. package/dist/api/harness-readiness-endpoint.js +24 -0
  32. package/dist/api/harness-readiness-endpoint.js.map +1 -0
  33. package/dist/api/hermes-doctor-endpoint.d.ts +66 -0
  34. package/dist/api/hermes-doctor-endpoint.js +75 -0
  35. package/dist/api/hermes-doctor-endpoint.js.map +1 -0
  36. package/dist/api/portfolio-v0-build.d.ts +10 -0
  37. package/dist/api/portfolio-v0-build.js +24 -5
  38. package/dist/api/portfolio-v0-build.js.map +1 -1
  39. package/dist/api/prediction-v1-build.d.ts +9 -0
  40. package/dist/api/prediction-v1-build.js +6 -1
  41. package/dist/api/prediction-v1-build.js.map +1 -1
  42. package/dist/api/server.d.ts +70 -0
  43. package/dist/api/server.js +107 -1
  44. package/dist/api/server.js.map +1 -1
  45. package/dist/api/setup-endpoints.d.ts +21 -0
  46. package/dist/api/setup-endpoints.js +101 -8
  47. package/dist/api/setup-endpoints.js.map +1 -1
  48. package/dist/api/setup-retry-endpoint.d.ts +19 -0
  49. package/dist/api/setup-retry-endpoint.js +32 -0
  50. package/dist/api/setup-retry-endpoint.js.map +1 -0
  51. package/dist/api/solvernets-endpoints.js +8 -1
  52. package/dist/api/solvernets-endpoints.js.map +1 -1
  53. package/dist/api/status-build.d.ts +14 -0
  54. package/dist/api/status-build.js +23 -18
  55. package/dist/api/status-build.js.map +1 -1
  56. package/dist/api/task-run-routing.d.ts +7 -0
  57. package/dist/api/task-run-routing.js +12 -0
  58. package/dist/api/task-run-routing.js.map +1 -0
  59. package/dist/api/task-runs-build.d.ts +20 -0
  60. package/dist/api/task-runs-build.js +4 -0
  61. package/dist/api/task-runs-build.js.map +1 -1
  62. package/dist/build-info.json +4 -4
  63. package/dist/build-meta.json +1 -1
  64. package/dist/chain-read-errors.d.ts +10 -0
  65. package/dist/chain-read-errors.js +15 -0
  66. package/dist/chain-read-errors.js.map +1 -1
  67. package/dist/cli/commands/auth.js +3 -1
  68. package/dist/cli/commands/auth.js.map +1 -1
  69. package/dist/cli/commands/create.d.ts +5 -3
  70. package/dist/cli/commands/create.js +102 -36
  71. package/dist/cli/commands/create.js.map +1 -1
  72. package/dist/cli/commands/solver-nets.d.ts +19 -0
  73. package/dist/cli/commands/solver-nets.js +164 -11
  74. package/dist/cli/commands/solver-nets.js.map +1 -1
  75. package/dist/cli/commands/solver-plugins-publish.d.ts +31 -0
  76. package/dist/cli/commands/solver-plugins-publish.js +169 -0
  77. package/dist/cli/commands/solver-plugins-publish.js.map +1 -0
  78. package/dist/cli/commands/solver-plugins-revoke.d.ts +15 -0
  79. package/dist/cli/commands/solver-plugins-revoke.js +91 -0
  80. package/dist/cli/commands/solver-plugins-revoke.js.map +1 -0
  81. package/dist/cli/commands/solver-plugins.d.ts +50 -6
  82. package/dist/cli/commands/solver-plugins.js +205 -68
  83. package/dist/cli/commands/solver-plugins.js.map +1 -1
  84. package/dist/cli/commands/update.d.ts +10 -0
  85. package/dist/cli/commands/update.js +36 -0
  86. package/dist/cli/commands/update.js.map +1 -1
  87. package/dist/config.d.ts +51 -0
  88. package/dist/config.js +49 -2
  89. package/dist/config.js.map +1 -1
  90. package/dist/conformance/checks/hash-signature.js +6 -2
  91. package/dist/conformance/checks/hash-signature.js.map +1 -1
  92. package/dist/conformance/checks/payload.js +4 -2
  93. package/dist/conformance/checks/payload.js.map +1 -1
  94. package/dist/conformance/checks/verdict.d.ts +10 -10
  95. package/dist/conformance/checks/verdict.js +16 -15
  96. package/dist/conformance/checks/verdict.js.map +1 -1
  97. package/dist/conformance/harness.d.ts +1 -1
  98. package/dist/conformance/harness.js +16 -9
  99. package/dist/conformance/harness.js.map +1 -1
  100. package/dist/conformance/types.d.ts +10 -3
  101. package/dist/conformance/types.js.map +1 -1
  102. package/dist/corpus/acquire.d.ts +1 -3
  103. package/dist/corpus/acquire.js.map +1 -1
  104. package/dist/corpus/envelope-projection.d.ts +1 -1
  105. package/dist/corpus/envelope-projection.js +14 -7
  106. package/dist/corpus/envelope-projection.js.map +1 -1
  107. package/dist/corpus/index.d.ts +2 -1
  108. package/dist/corpus/index.js.map +1 -1
  109. package/dist/corpus/prediction-brier-scoreboard-report.js +1 -1
  110. package/dist/corpus/prediction-brier-scoreboard-report.js.map +1 -1
  111. package/dist/corpus/prediction-brier-scoreboard.js +3 -1
  112. package/dist/corpus/prediction-brier-scoreboard.js.map +1 -1
  113. package/dist/corpus/types.d.ts +2 -2
  114. package/dist/daemon/daemon.d.ts +26 -1
  115. package/dist/daemon/daemon.js +63 -1
  116. package/dist/daemon/daemon.js.map +1 -1
  117. package/dist/daemon/eviction-loop.d.ts +40 -0
  118. package/dist/daemon/eviction-loop.js +67 -0
  119. package/dist/daemon/eviction-loop.js.map +1 -0
  120. package/dist/daemon/freeze-fence.js +6 -3
  121. package/dist/daemon/freeze-fence.js.map +1 -1
  122. package/dist/daemon/readiness-gate.d.ts +30 -0
  123. package/dist/daemon/readiness-gate.js +31 -0
  124. package/dist/daemon/readiness-gate.js.map +1 -0
  125. package/dist/daemon/skip-log-dedup.d.ts +69 -0
  126. package/dist/daemon/skip-log-dedup.js +106 -0
  127. package/dist/daemon/skip-log-dedup.js.map +1 -0
  128. package/dist/dashboard/assets/{index-D_NMfDfV.css → index-DE4qUnzV.css} +1 -1
  129. package/dist/dashboard/assets/index-Di7xA4eB.js +170 -0
  130. package/dist/dashboard/index.html +2 -2
  131. package/dist/discovery/http.js +121 -0
  132. package/dist/discovery/http.js.map +1 -1
  133. package/dist/discovery/onchain.d.ts +5 -0
  134. package/dist/discovery/onchain.js +292 -7
  135. package/dist/discovery/onchain.js.map +1 -1
  136. package/dist/discovery/types.d.ts +127 -1
  137. package/dist/discovery/types.js +8 -10
  138. package/dist/discovery/types.js.map +1 -1
  139. package/dist/discovery/with-fallback.js +9 -0
  140. package/dist/discovery/with-fallback.js.map +1 -1
  141. package/dist/earning/agent-wallet-binding.d.ts +20 -1
  142. package/dist/earning/agent-wallet-binding.js +54 -16
  143. package/dist/earning/agent-wallet-binding.js.map +1 -1
  144. package/dist/earning/bootstrap.d.ts +178 -0
  145. package/dist/earning/bootstrap.js +628 -57
  146. package/dist/earning/bootstrap.js.map +1 -1
  147. package/dist/earning/contracts.d.ts +12 -0
  148. package/dist/earning/contracts.js +16 -1
  149. package/dist/earning/contracts.js.map +1 -1
  150. package/dist/earning/funding-plan.js +15 -2
  151. package/dist/earning/funding-plan.js.map +1 -1
  152. package/dist/earning/jinn-rewards.d.ts +46 -0
  153. package/dist/earning/jinn-rewards.js +32 -0
  154. package/dist/earning/jinn-rewards.js.map +1 -1
  155. package/dist/earning/store.d.ts +8 -0
  156. package/dist/earning/store.js +48 -1
  157. package/dist/earning/store.js.map +1 -1
  158. package/dist/earning/testnet-setup-migration.d.ts +12 -0
  159. package/dist/earning/testnet-setup-migration.js +27 -1
  160. package/dist/earning/testnet-setup-migration.js.map +1 -1
  161. package/dist/earning/types.d.ts +45 -0
  162. package/dist/earning/types.js +37 -0
  163. package/dist/earning/types.js.map +1 -1
  164. package/dist/erc8004/abis.d.ts +64 -0
  165. package/dist/erc8004/abis.js +48 -0
  166. package/dist/erc8004/abis.js.map +1 -1
  167. package/dist/erc8004/plugin-registry.d.ts +102 -0
  168. package/dist/erc8004/plugin-registry.js +165 -0
  169. package/dist/erc8004/plugin-registry.js.map +1 -0
  170. package/dist/erc8004/reputation.d.ts +8 -0
  171. package/dist/erc8004/reputation.js +22 -3
  172. package/dist/erc8004/reputation.js.map +1 -1
  173. package/dist/events/types.d.ts +2 -2
  174. package/dist/harnesses/cost-estimates.d.ts +145 -0
  175. package/dist/harnesses/cost-estimates.js +297 -0
  176. package/dist/harnesses/cost-estimates.js.map +1 -0
  177. package/dist/harnesses/engine/engine.d.ts +40 -0
  178. package/dist/harnesses/engine/engine.js +69 -8
  179. package/dist/harnesses/engine/engine.js.map +1 -1
  180. package/dist/harnesses/engine/envelope-assembly.d.ts +2 -2
  181. package/dist/harnesses/engine/envelope-assembly.js +4 -2
  182. package/dist/harnesses/engine/envelope-assembly.js.map +1 -1
  183. package/dist/harnesses/engine/persistence.d.ts +21 -0
  184. package/dist/harnesses/engine/persistence.js +39 -0
  185. package/dist/harnesses/engine/persistence.js.map +1 -1
  186. package/dist/harnesses/engine/work-dir-reaper.d.ts +65 -0
  187. package/dist/harnesses/engine/work-dir-reaper.js +100 -0
  188. package/dist/harnesses/engine/work-dir-reaper.js.map +1 -0
  189. package/dist/harnesses/freeze.d.ts +4 -1
  190. package/dist/harnesses/freeze.js +12 -2
  191. package/dist/harnesses/freeze.js.map +1 -1
  192. package/dist/harnesses/impls/claude-mcp-prediction/index.d.ts +4 -1
  193. package/dist/harnesses/impls/claude-mcp-prediction/index.js +7 -2
  194. package/dist/harnesses/impls/claude-mcp-prediction/index.js.map +1 -1
  195. package/dist/harnesses/impls/claude-mcp-prediction-apy/index.d.ts +4 -1
  196. package/dist/harnesses/impls/claude-mcp-prediction-apy/index.js +7 -2
  197. package/dist/harnesses/impls/claude-mcp-prediction-apy/index.js.map +1 -1
  198. package/dist/harnesses/impls/evaluation-context.d.ts +15 -4
  199. package/dist/harnesses/impls/evaluation-context.js +24 -8
  200. package/dist/harnesses/impls/evaluation-context.js.map +1 -1
  201. package/dist/harnesses/impls/hermes-agent/adapter.d.ts +34 -0
  202. package/dist/harnesses/impls/hermes-agent/adapter.js +205 -0
  203. package/dist/harnesses/impls/hermes-agent/adapter.js.map +1 -0
  204. package/dist/harnesses/impls/hermes-agent/bootstrap.d.ts +18 -0
  205. package/dist/harnesses/impls/hermes-agent/bootstrap.js +231 -0
  206. package/dist/harnesses/impls/hermes-agent/bootstrap.js.map +1 -0
  207. package/dist/harnesses/impls/hermes-agent/config-builder.d.ts +49 -0
  208. package/dist/harnesses/impls/hermes-agent/config-builder.js +132 -0
  209. package/dist/harnesses/impls/hermes-agent/config-builder.js.map +1 -0
  210. package/dist/harnesses/impls/hermes-agent/harness.d.ts +58 -0
  211. package/dist/harnesses/impls/hermes-agent/harness.js +118 -0
  212. package/dist/harnesses/impls/hermes-agent/harness.js.map +1 -0
  213. package/dist/harnesses/impls/hermes-agent/index.d.ts +5 -0
  214. package/dist/harnesses/impls/hermes-agent/index.js +7 -0
  215. package/dist/harnesses/impls/hermes-agent/index.js.map +1 -0
  216. package/dist/harnesses/impls/hermes-agent/prompt.d.ts +15 -0
  217. package/dist/harnesses/impls/hermes-agent/prompt.js +37 -0
  218. package/dist/harnesses/impls/hermes-agent/prompt.js.map +1 -0
  219. package/dist/harnesses/impls/index.d.ts +13 -0
  220. package/dist/harnesses/impls/index.js +32 -4
  221. package/dist/harnesses/impls/index.js.map +1 -1
  222. package/dist/harnesses/impls/learner/adapters/claude-code.js.map +1 -0
  223. package/dist/harnesses/impls/{claude-code-learner → learner}/adapters/codex-code.js +13 -34
  224. package/dist/harnesses/impls/learner/adapters/codex-code.js.map +1 -0
  225. package/dist/harnesses/impls/learner/adapters/codex-workspace.js.map +1 -0
  226. package/dist/harnesses/impls/learner/harness.d.ts +62 -0
  227. package/dist/harnesses/impls/learner/harness.js +179 -0
  228. package/dist/harnesses/impls/learner/harness.js.map +1 -0
  229. package/dist/harnesses/impls/{claude-code-learner → learner}/harvest.js +15 -3
  230. package/dist/harnesses/impls/learner/harvest.js.map +1 -0
  231. package/dist/harnesses/impls/{claude-code-learner → learner}/index.d.ts +5 -5
  232. package/dist/harnesses/impls/{claude-code-learner → learner}/index.js +4 -4
  233. package/dist/harnesses/impls/learner/index.js.map +1 -0
  234. package/dist/harnesses/impls/{claude-code-learner → learner}/plugin-path.d.ts +4 -4
  235. package/dist/harnesses/impls/{claude-code-learner → learner}/plugin-path.js +7 -7
  236. package/dist/harnesses/impls/learner/plugin-path.js.map +1 -0
  237. package/dist/harnesses/impls/{claude-code-learner → learner}/restoration-patch.js +3 -1
  238. package/dist/harnesses/impls/learner/restoration-patch.js.map +1 -0
  239. package/dist/harnesses/impls/learner/test-utils/fake-plugin-outputs.js.map +1 -0
  240. package/dist/harnesses/impls/learner/test-utils/noop-adapter.js.map +1 -0
  241. package/dist/harnesses/impls/{claude-code-learner → learner}/types.d.ts +23 -2
  242. package/dist/harnesses/impls/learner/types.js.map +1 -0
  243. package/dist/harnesses/impls/portfolio-v0-evaluator/index.js +13 -12
  244. package/dist/harnesses/impls/portfolio-v0-evaluator/index.js.map +1 -1
  245. package/dist/harnesses/impls/prediction-apy-v0-evaluator/index.js +7 -7
  246. package/dist/harnesses/impls/prediction-apy-v0-evaluator/index.js.map +1 -1
  247. package/dist/harnesses/impls/prediction-apy-v0-evaluator/parse-submission.d.ts +3 -3
  248. package/dist/harnesses/impls/prediction-apy-v0-evaluator/parse-submission.js +7 -6
  249. package/dist/harnesses/impls/prediction-apy-v0-evaluator/parse-submission.js.map +1 -1
  250. package/dist/harnesses/impls/prediction-v0-evaluator/checks/integrity.js +1 -1
  251. package/dist/harnesses/impls/prediction-v0-evaluator/checks/integrity.js.map +1 -1
  252. package/dist/harnesses/impls/prediction-v0-evaluator/index.js +11 -10
  253. package/dist/harnesses/impls/prediction-v0-evaluator/index.js.map +1 -1
  254. package/dist/harnesses/impls/prediction-v1-evaluator/index.js +11 -10
  255. package/dist/harnesses/impls/prediction-v1-evaluator/index.js.map +1 -1
  256. package/dist/harnesses/impls/stub.d.ts +58 -0
  257. package/dist/harnesses/impls/stub.js +89 -0
  258. package/dist/harnesses/impls/stub.js.map +1 -0
  259. package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.d.ts +1 -0
  260. package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.js +10 -2
  261. package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.js.map +1 -1
  262. package/dist/harnesses/impls/swe-rebench-v2-evaluator/harness.d.ts +24 -5
  263. package/dist/harnesses/impls/swe-rebench-v2-evaluator/harness.js +104 -4
  264. package/dist/harnesses/impls/swe-rebench-v2-evaluator/harness.js.map +1 -1
  265. package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.d.ts +9 -0
  266. package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.js +25 -1
  267. package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.js.map +1 -1
  268. package/dist/harnesses/names.d.ts +1 -0
  269. package/dist/harnesses/names.js +3 -0
  270. package/dist/harnesses/names.js.map +1 -1
  271. package/dist/harnesses/readiness-registry.d.ts +48 -0
  272. package/dist/harnesses/readiness-registry.js +144 -0
  273. package/dist/harnesses/readiness-registry.js.map +1 -0
  274. package/dist/harnesses/types.d.ts +7 -0
  275. package/dist/main.d.ts +14 -0
  276. package/dist/main.js +372 -90
  277. package/dist/main.js.map +1 -1
  278. package/dist/mcp/server.js +14 -13
  279. package/dist/mcp/server.js.map +1 -1
  280. package/dist/operator-errors.d.ts +7 -0
  281. package/dist/operator-errors.js +26 -2
  282. package/dist/operator-errors.js.map +1 -1
  283. package/dist/preflight/claude-auth.d.ts +18 -0
  284. package/dist/preflight/claude-auth.js +38 -0
  285. package/dist/preflight/claude-auth.js.map +1 -1
  286. package/dist/restart-daemon.d.ts +71 -0
  287. package/dist/restart-daemon.js +82 -0
  288. package/dist/restart-daemon.js.map +1 -0
  289. package/dist/scripts/donation-consumption-acceptance.js +1 -1
  290. package/dist/scripts/donation-consumption-acceptance.js.map +1 -1
  291. package/dist/scripts/swe-rebench-v2-known-bad.json +12 -0
  292. package/dist/scripts/swe-rebench-v2-seed-pool.json +26 -0
  293. package/dist/setup/halt-mode.d.ts +14 -0
  294. package/dist/setup/halt-mode.js +17 -0
  295. package/dist/setup/halt-mode.js.map +1 -0
  296. package/dist/solver-nets/prediction-operator-ux.js +43 -3
  297. package/dist/solver-nets/prediction-operator-ux.js.map +1 -1
  298. package/dist/solver-nets/registry.d.ts +1 -0
  299. package/dist/solver-nets/registry.js +1 -1
  300. package/dist/solver-nets/registry.js.map +1 -1
  301. package/dist/solver-types/_swe-rebench-v2-substrate.d.ts +52 -0
  302. package/dist/solver-types/_swe-rebench-v2-substrate.js +76 -0
  303. package/dist/solver-types/_swe-rebench-v2-substrate.js.map +1 -0
  304. package/dist/solver-types/_swe-rebench-v2-validated-pool.d.ts +38 -12
  305. package/dist/solver-types/_swe-rebench-v2-validated-pool.js +136 -27
  306. package/dist/solver-types/_swe-rebench-v2-validated-pool.js.map +1 -1
  307. package/dist/solver-types/swe-rebench-v2-auto.d.ts +6 -0
  308. package/dist/solver-types/swe-rebench-v2-auto.js.map +1 -1
  309. package/dist/solver-types/swe-rebench-v2.d.ts +1 -0
  310. package/dist/solver-types/swe-rebench-v2.js +19 -6
  311. package/dist/solver-types/swe-rebench-v2.js.map +1 -1
  312. package/dist/solvernets/daemon-init.d.ts +10 -2
  313. package/dist/solvernets/daemon-init.js +22 -2
  314. package/dist/solvernets/daemon-init.js.map +1 -1
  315. package/dist/store/store.js +12 -4
  316. package/dist/store/store.js.map +1 -1
  317. package/dist/templates/plugins/runtime-plugin/.mcp.json.tmpl +8 -0
  318. package/dist/templates/plugins/runtime-plugin/README.md.tmpl +30 -0
  319. package/dist/templates/plugins/runtime-plugin/gitignore.tmpl +3 -0
  320. package/dist/templates/plugins/runtime-plugin/jinn.plugin.json.tmpl +21 -0
  321. package/dist/templates/plugins/runtime-plugin/mcp/server.mjs.tmpl +33 -0
  322. package/dist/templates/plugins/runtime-plugin/package.json.tmpl +15 -0
  323. package/dist/templates/plugins/runtime-plugin/test/plugin.test.ts.tmpl +35 -0
  324. package/dist/templates/plugins/runtime-plugin/tsconfig.json.tmpl +11 -0
  325. package/dist/templates/plugins/solver-type-plugin/README.md.tmpl +35 -0
  326. package/dist/templates/plugins/solver-type-plugin/gitignore.tmpl +3 -0
  327. package/dist/templates/plugins/solver-type-plugin/jinn.plugin.json.tmpl +11 -0
  328. package/dist/templates/plugins/solver-type-plugin/package.json.tmpl +15 -0
  329. package/dist/templates/plugins/solver-type-plugin/skills/example/SKILL.md.tmpl +10 -0
  330. package/dist/templates/plugins/solver-type-plugin/test/plugin.test.ts.tmpl +25 -0
  331. package/dist/templates/plugins/solver-type-plugin/tsconfig.json.tmpl +11 -0
  332. package/dist/tx-retry.d.ts +13 -0
  333. package/dist/tx-retry.js +22 -0
  334. package/dist/tx-retry.js.map +1 -1
  335. package/dist/types/envelope.d.ts +28 -21
  336. package/dist/types/envelope.js +8 -3
  337. package/dist/types/envelope.js.map +1 -1
  338. package/dist/types/payloads/index.d.ts +2 -2
  339. package/dist/types/payloads/index.js +13 -12
  340. package/dist/types/payloads/index.js.map +1 -1
  341. package/dist/types/payloads/portfolio-v0.d.ts +60 -10
  342. package/dist/types/payloads/portfolio-v0.js +16 -6
  343. package/dist/types/payloads/portfolio-v0.js.map +1 -1
  344. package/dist/types/payloads/prediction-apy-v0.d.ts +56 -14
  345. package/dist/types/payloads/prediction-apy-v0.js +16 -6
  346. package/dist/types/payloads/prediction-apy-v0.js.map +1 -1
  347. package/dist/types/payloads/prediction-v0.d.ts +53 -14
  348. package/dist/types/payloads/prediction-v0.js +16 -6
  349. package/dist/types/payloads/prediction-v0.js.map +1 -1
  350. package/dist/util/extract-tx-hash.d.ts +14 -0
  351. package/dist/util/extract-tx-hash.js +19 -0
  352. package/dist/util/extract-tx-hash.js.map +1 -0
  353. package/dist/vendor/@jinn-network/sdk/dist/payloads/prediction-v1.d.ts +45 -6
  354. package/dist/vendor/@jinn-network/sdk/dist/payloads/prediction-v1.js +16 -6
  355. package/dist/x402/handler.js +51 -20
  356. package/dist/x402/handler.js.map +1 -1
  357. package/package.json +38 -13
  358. package/plugins/swe-rebench-v2-diffmin/.claude-plugin/plugin.json +5 -0
  359. package/plugins/swe-rebench-v2-diffmin/.mcp.json +8 -0
  360. package/plugins/swe-rebench-v2-diffmin/README.md +69 -0
  361. package/plugins/swe-rebench-v2-diffmin/jinn.plugin.json +12 -0
  362. package/plugins/swe-rebench-v2-diffmin/mcp/diff-stats-server.mjs +72 -0
  363. package/plugins/swe-rebench-v2-diffmin/mcp/diff-stats.mjs +48 -0
  364. package/plugins/swe-rebench-v2-diffmin/package.json +19 -0
  365. package/plugins/swe-rebench-v2-diffmin/skills/diffmin/SKILL.md +116 -0
  366. package/plugins/swe-rebench-v2-diffmin/skills/test-map/SKILL.md +126 -0
  367. package/plugins/swe-rebench-v2-diffmin/test/diff-stats.test.ts +62 -0
  368. package/plugins/swe-rebench-v2-diffmin/test/manifest.test.ts +53 -0
  369. package/plugins/swe-rebench-v2-diffmin/tsconfig.json +12 -0
  370. package/plugins/swe-rebench-v2-runtime/README.md +13 -0
  371. package/plugins/swe-rebench-v2-runtime/skills/orient/SKILL.md +7 -3
  372. package/plugins/swe-rebench-v2-runtime/skills/plan/SKILL.md +6 -17
  373. package/templates/plugins/runtime-plugin/.mcp.json.tmpl +8 -0
  374. package/templates/plugins/runtime-plugin/README.md.tmpl +30 -0
  375. package/templates/plugins/runtime-plugin/gitignore.tmpl +3 -0
  376. package/templates/plugins/runtime-plugin/jinn.plugin.json.tmpl +21 -0
  377. package/templates/plugins/runtime-plugin/mcp/server.mjs.tmpl +33 -0
  378. package/templates/plugins/runtime-plugin/package.json.tmpl +15 -0
  379. package/templates/plugins/runtime-plugin/test/plugin.test.ts.tmpl +35 -0
  380. package/templates/plugins/runtime-plugin/tsconfig.json.tmpl +11 -0
  381. package/templates/plugins/solver-type-plugin/README.md.tmpl +35 -0
  382. package/templates/plugins/solver-type-plugin/gitignore.tmpl +3 -0
  383. package/templates/plugins/solver-type-plugin/jinn.plugin.json.tmpl +11 -0
  384. package/templates/plugins/solver-type-plugin/package.json.tmpl +15 -0
  385. package/templates/plugins/solver-type-plugin/skills/example/SKILL.md.tmpl +10 -0
  386. package/templates/plugins/solver-type-plugin/test/plugin.test.ts.tmpl +25 -0
  387. package/templates/plugins/solver-type-plugin/tsconfig.json.tmpl +11 -0
  388. package/dist/dashboard/assets/index-BjtltOGc.js +0 -76
  389. package/dist/harnesses/impls/claude-code-learner/adapters/claude-code.js.map +0 -1
  390. package/dist/harnesses/impls/claude-code-learner/adapters/codex-code.js.map +0 -1
  391. package/dist/harnesses/impls/claude-code-learner/adapters/codex-workspace.js.map +0 -1
  392. package/dist/harnesses/impls/claude-code-learner/harness.d.ts +0 -22
  393. package/dist/harnesses/impls/claude-code-learner/harness.js +0 -62
  394. package/dist/harnesses/impls/claude-code-learner/harness.js.map +0 -1
  395. package/dist/harnesses/impls/claude-code-learner/harvest.js.map +0 -1
  396. package/dist/harnesses/impls/claude-code-learner/index.js.map +0 -1
  397. package/dist/harnesses/impls/claude-code-learner/plugin-path.js.map +0 -1
  398. package/dist/harnesses/impls/claude-code-learner/restoration-patch.js.map +0 -1
  399. package/dist/harnesses/impls/claude-code-learner/test-utils/fake-plugin-outputs.js.map +0 -1
  400. package/dist/harnesses/impls/claude-code-learner/test-utils/noop-adapter.js.map +0 -1
  401. package/dist/harnesses/impls/claude-code-learner/types.js.map +0 -1
  402. package/dist/preflight/claude-required.d.ts +0 -8
  403. package/dist/preflight/claude-required.js +0 -17
  404. package/dist/preflight/claude-required.js.map +0 -1
  405. /package/dist/harnesses/impls/{claude-code-learner → learner}/adapters/claude-code.d.ts +0 -0
  406. /package/dist/harnesses/impls/{claude-code-learner → learner}/adapters/claude-code.js +0 -0
  407. /package/dist/harnesses/impls/{claude-code-learner → learner}/adapters/codex-code.d.ts +0 -0
  408. /package/dist/harnesses/impls/{claude-code-learner → learner}/adapters/codex-workspace.d.ts +0 -0
  409. /package/dist/harnesses/impls/{claude-code-learner → learner}/adapters/codex-workspace.js +0 -0
  410. /package/dist/harnesses/impls/{claude-code-learner → learner}/harvest.d.ts +0 -0
  411. /package/dist/harnesses/impls/{claude-code-learner → learner}/restoration-patch.d.ts +0 -0
  412. /package/dist/harnesses/impls/{claude-code-learner → learner}/test-utils/fake-plugin-outputs.d.ts +0 -0
  413. /package/dist/harnesses/impls/{claude-code-learner → learner}/test-utils/fake-plugin-outputs.js +0 -0
  414. /package/dist/harnesses/impls/{claude-code-learner → learner}/test-utils/noop-adapter.d.ts +0 -0
  415. /package/dist/harnesses/impls/{claude-code-learner → learner}/test-utils/noop-adapter.js +0 -0
  416. /package/dist/harnesses/impls/{claude-code-learner → learner}/types.js +0 -0
  417. /package/plugins/{claude-code-learner → learner}/.claude-plugin/plugin.json +0 -0
  418. /package/plugins/{claude-code-learner → learner}/.codex-plugin/plugin.json +0 -0
  419. /package/plugins/{claude-code-learner → learner}/AGENTS.md +0 -0
  420. /package/plugins/{claude-code-learner → learner}/CLAUDE.md +0 -0
  421. /package/plugins/{claude-code-learner → learner}/README.md +0 -0
  422. /package/plugins/{claude-code-learner → learner}/hooks/hooks.json +0 -0
  423. /package/plugins/{claude-code-learner → learner}/hooks/session-start +0 -0
  424. /package/plugins/{claude-code-learner → learner}/skills/learn/SKILL.md +0 -0
  425. /package/plugins/{claude-code-learner → learner}/skills/learn/analyst-prompt.md +0 -0
  426. /package/plugins/{claude-code-learner → learner}/skills/learn/consolidator-prompt.md +0 -0
  427. /package/plugins/{claude-code-learner → learner}/skills/learn/explorer-prompt.md +0 -0
  428. /package/plugins/{claude-code-learner → learner}/skills/learn/planner-prompt.md +0 -0
  429. /package/plugins/{claude-code-learner → learner}/skills/learn/promoter-prompt.md +0 -0
  430. /package/plugins/{claude-code-learner → learner}/skills/learn/step-worker-prompt.md +0 -0
  431. /package/plugins/{claude-code-learner → learner}/skills/learn/strategist-prompt.md +0 -0
@@ -15,20 +15,121 @@ import { formatBootstrapOperatorMessage, isJinnDebug, } from '../operator-errors
15
15
  import { reconcileServiceAgainstChain, } from './reconcile.js';
16
16
  import { previousSafeBeingAbandoned, sweepOrphanedServiceFunds, } from './orphan-sweep.js';
17
17
  import { DEFAULT_FAUCET_LOOP_TIMEOUT_MS, computeFaucetDripCap, requestTestnetFunding, } from './faucet.js';
18
- import { flattenErrorMessage, viemSendTransactionWithRetry, waitForTransactionReceiptWithRetry, } from '../tx-retry.js';
18
+ import { flattenErrorMessage, sleep, viemSendTransactionWithRetry, waitForContractCode, waitForTransactionReceiptWithRetry, } from '../tx-retry.js';
19
19
  import { isUnauthorizedAccountError } from '../errors/unauthorized-account.js';
20
20
  import { createJinnPublicClient, createJinnWalletClient } from './viem-clients.js';
21
21
  import { isTransientEthReadError } from '../chain-read-errors.js';
22
22
  import { nextFleetServiceIndex } from './next-service-index.js';
23
+ import { displayFleetServiceIndex } from './fleet-display-index.js';
23
24
  import { rpcHostForDisplay } from '../preflight/rpc-network.js';
24
25
  import { detectDeprecatedTestnetSetup, migrateDeprecatedTestnetSetup, } from './testnet-setup-migration.js';
25
26
  const addr = (value) => getAddress(value);
26
27
  const SAFE_TOKEN_BOOTSTRAP_MULTIPLIER = 2n;
27
- const STANDARD_MASTER_BOOTSTRAP_MULTIPLIER = 2n;
28
+ /**
29
+ * 2× cold-start headroom for master ETH target on a fresh bootstrap.
30
+ *
31
+ * Gas accounting for a single standard-mode service on first run:
32
+ * ~1.3M gas for the Safe deploy + stake + mech (at 2 gwei ≈ 0.0026 ETH)
33
+ * + 0.002 ETH Safe seed (sent to the Safe so it can pay mech fees)
34
+ * ≈ 0.0046 ETH minimum; 2× gives ≈ 0.009–0.010 ETH — the bootstrap
35
+ * `minEoaGasEth` default.
36
+ *
37
+ * The multiplier only applies when no service has a persisted `service_id`
38
+ * on-chain (i.e. the fleet is truly cold — OR migration-wiped, where
39
+ * services exist in shape but lost their on-chain anchor). Once any service
40
+ * has committed a `service_id`, the fleet is past the cold-start cliff and
41
+ * 1× suffices.
42
+ *
43
+ * Single source of truth: imported by funding-plan.ts.
44
+ */
45
+ export const STANDARD_MASTER_BOOTSTRAP_MULTIPLIER = 2n;
46
+ /**
47
+ * Compute the required master ETH gate for the standard staking mode.
48
+ *
49
+ * @param services Persisted service states
50
+ * @param minEoaGasEth Configured minimum EOA gas target (wei)
51
+ * @param standardMultiplier Cold-start multiplier (defaults to {@link STANDARD_MASTER_BOOTSTRAP_MULTIPLIER})
52
+ * @param pendingSetupMigration True when deprecated testnet setup detected
53
+ * @param targetServices How many services the fleet is aiming for
54
+ */
55
+ export function computeRequiredMasterEth({ services, minEoaGasEth, standardMultiplier = STANDARD_MASTER_BOOTSTRAP_MULTIPLIER, pendingSetupMigration = false, targetServices = 1, }) {
56
+ const completedCount = services.filter(s => s.step !== undefined && isOperationalServiceStep(s.step)).length;
57
+ const standardFleetAlreadyComplete = !pendingSetupMigration && completedCount >= targetServices;
58
+ if (standardFleetAlreadyComplete)
59
+ return 0n;
60
+ // "Cold in liability" = no service has an on-chain anchor yet.
61
+ // This includes:
62
+ // 1. Fresh fleet (services array is empty)
63
+ // 2. Migration-wiped fleet (services exist in shape but service_id === null)
64
+ // Apply the 2× headroom multiplier in both cases.
65
+ const hasPersistedOnChain = services.some(s => s.service_id != null);
66
+ return minEoaGasEth * (hasPersistedOnChain ? 1n : standardMultiplier);
67
+ }
68
+ /** Master ETH required to FINISH the whole bootstrap from a fresh start (not
69
+ * just to enter Stage 1). Centralized so the daemon's ensureStage1 gate,
70
+ * the read-side funding-plan, gather-status, and the panel faucet target
71
+ * all agree on a single "one-shot fund the operator" number.
72
+ *
73
+ * Components for a 1-service standard-mode bootstrap:
74
+ * - STAGE1_AGENT_ETH (0.010 ETH): master → HD-1 fleet-agent transfer.
75
+ * HD-1 pays for its own Safe deploy + register + setAgentWallet out
76
+ * of this; leftover covers service 1's Stage 2 work (mech deploy +
77
+ * rebind). May get a conditional minEoaGasEth top-up from master if
78
+ * Stage 1's gas eats too much of the original transfer.
79
+ * - minEoaGasEth × STANDARD_MASTER_BOOTSTRAP_MULTIPLIER (0.010 ETH):
80
+ * master gas budget across both stages. Real spend (~0.0025 ETH at
81
+ * typical 6 gwei Base Sepolia) leaves room for ~4× gas-spike margin
82
+ * plus the conditional HD-1 top-up.
83
+ * - minEoaGasEth × (targetServices - 1): per-extra-service top-up for
84
+ * services 2..N. Service 1 piggybacks on HD-1's Stage 1 funding.
85
+ *
86
+ * Total for N=1: 0.010 + 0.010 = 0.020 ETH.
87
+ * Total for N=2: 0.025 ETH.
88
+ * Total for N=3: 0.030 ETH.
89
+ *
90
+ * This number works ONLY when Stage 2's internal gate is `minEoaGasEth ×
91
+ * 1n` (0.005 ETH) — not the historic `× 2n`. After Stage 1 transfers 0.010
92
+ * out, master sits at ~0.0099 ETH (0.020 minus transfer minus Stage 1
93
+ * funding-tx gas). The 0.005 gate clears that with margin; the 0.010 gate
94
+ * did not. See the Stage 2 gate at ensureStage1And2 line ~484.
95
+ *
96
+ * Operator may see a "low runway" warning after bootstrap — that's
97
+ * intentional. The operator-facing 0.020 ETH covers bootstrap completion,
98
+ * not multi-week post-bootstrap runway. Top-up is a separate concern.
99
+ */
100
+ export const STAGE1_AGENT_ETH = 10000000000000000n; // 0.01 ETH (moved out of stepFleetSafeDeploy)
101
+ export function stage1MinMasterEth(config, targetServices = 1) {
102
+ const extraServiceTransfers = config.minEoaGasEth * BigInt(Math.max(0, targetServices - 1));
103
+ return (STAGE1_AGENT_ETH +
104
+ config.minEoaGasEth * STANDARD_MASTER_BOOTSTRAP_MULTIPLIER +
105
+ extraServiceTransfers);
106
+ }
28
107
  /** Conservative default: ~0.001 ETH/day master gas if not configured. */
29
108
  const DEFAULT_MASTER_ETH_DAILY_WEI = 1000000000000000n;
30
109
  /** Warn when ETH above the minimum would last fewer than this many days at the daily estimate. */
31
110
  const MASTER_ETH_RUNWAY_WARN_DAYS = 7n;
111
+ /**
112
+ * Safe → ERC-8004 agent NFT binding retry (jinn-mono-h74p).
113
+ *
114
+ * Empirical observation against fresh Base Sepolia 1/1 Safes: the first
115
+ * `IdentityRegistry.setAgentWallet` attempt reverts with a generic
116
+ * "Execution reverted for an unknown reason" — but the same Safe + same
117
+ * agentId + a freshly-signed message a few seconds later succeeds. The
118
+ * race window is likely freshly-deployed-Safe state lag on the public RPC
119
+ * (the simulator can't read the Safe's storage yet in the same block /
120
+ * eventual-consistency between sibling RPC nodes). A short bounded retry
121
+ * makes the operator-visible behaviour deterministic instead of relying on
122
+ * the "daemon exits → operator restarts → resume at safe_binding_pending"
123
+ * accidental safety net (which goes away when jinn-mono-vh74.2 removes the
124
+ * Claude-auth post-bootstrap exit gate).
125
+ *
126
+ * Defaults: 3 attempts × 3 s delay = at most ~6 s of in-process retry budget
127
+ * before falling through to the existing `safe_binding_pending` persisted
128
+ * state. Real (non-transient) failures still surface — just with a slightly
129
+ * higher latency tax for the diagnostic.
130
+ */
131
+ const DEFAULT_SAFE_BINDING_MAX_ATTEMPTS = 3;
132
+ const DEFAULT_SAFE_BINDING_RETRY_DELAY_MS = 3_000;
32
133
  export class FleetBootstrapper {
33
134
  store;
34
135
  config;
@@ -43,6 +144,8 @@ export class FleetBootstrapper {
43
144
  faucetLoopTimeoutMs;
44
145
  now;
45
146
  autoTestnetFaucet;
147
+ safeBindingMaxAttempts;
148
+ safeBindingRetryDelayMs;
46
149
  constructor(options = {}) {
47
150
  this.store = new FleetStateStore(options.earningDir);
48
151
  this.chain = options.chain ?? 'base';
@@ -55,6 +158,10 @@ export class FleetBootstrapper {
55
158
  this.now = options.now ?? Date.now;
56
159
  this.autoTestnetFaucet =
57
160
  options.autoTestnetFaucet ?? this.env['JINN_DISABLE_TESTNET_FAUCET'] !== '1';
161
+ this.safeBindingMaxAttempts =
162
+ options.safeBindingMaxAttempts ?? DEFAULT_SAFE_BINDING_MAX_ATTEMPTS;
163
+ this.safeBindingRetryDelayMs =
164
+ options.safeBindingRetryDelayMs ?? DEFAULT_SAFE_BINDING_RETRY_DELAY_MS;
58
165
  const dailyOpt = options.masterEthDailyEstimateWei;
59
166
  this.masterEthDailyEstimateWei =
60
167
  dailyOpt !== undefined
@@ -77,6 +184,71 @@ export class FleetBootstrapper {
77
184
  async getStatus() {
78
185
  return this.store.load(this.chain);
79
186
  }
187
+ /**
188
+ * Run `bindAgentWalletToSafe` with the freshly-deployed-Safe race retry
189
+ * (jinn-mono-h74p, corrected in jinn-mono-k1ng).
190
+ *
191
+ * The race: against a fresh 1/1 Safe on Base Sepolia, the first
192
+ * setAgentWallet attempt reverts with "Execution reverted for an unknown
193
+ * reason"; the same Safe + same agentId a few seconds later succeeds.
194
+ *
195
+ * The h74p version of the retry only wrapped the call in a try/catch.
196
+ * In production, `bindAgentWalletToSafe` doesn't throw on revert — it
197
+ * returns a `{ ok: false, error }` outcome — so the catch never fired
198
+ * and the retry was dead code. The per-service path "worked" anyway
199
+ * because it persists `safe_binding_pending` and the next bootstrap
200
+ * resumes the bind. Stage 1 had no equivalent safety net, so a single
201
+ * `ok: false` halted bootstrap (the 2026-05-18 canary failure).
202
+ *
203
+ * This wrapper:
204
+ * - Retries on returned `ok: false` (the way the race actually
205
+ * manifests in production).
206
+ * - Also retries on thrown exceptions (defense-in-depth in case viem
207
+ * error handling changes).
208
+ * - Returns the final outcome; callers decide whether to throw, mark
209
+ * pending, or proceed.
210
+ *
211
+ * Single attempt budget: `safeBindingMaxAttempts` × `safeBindingRetryDelayMs`
212
+ * (defaults: 3 × 3 s = ~9 s).
213
+ */
214
+ async bindAgentWalletWithRetry(args, label) {
215
+ const maxAttempts = Math.max(1, this.safeBindingMaxAttempts);
216
+ let lastResult;
217
+ let lastThrowError;
218
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
219
+ try {
220
+ lastResult = await bindAgentWalletToSafe(args);
221
+ if (lastResult.ok)
222
+ return lastResult;
223
+ if (attempt < maxAttempts) {
224
+ console.error(`[fleet-bootstrap] ${label}: setAgentWallet attempt ` +
225
+ `${attempt}/${maxAttempts} returned ok=false (${lastResult.error.shortMessage}); ` +
226
+ `retrying in ${this.safeBindingRetryDelayMs}ms...`);
227
+ if (this.safeBindingRetryDelayMs > 0) {
228
+ await sleep(this.safeBindingRetryDelayMs);
229
+ }
230
+ }
231
+ }
232
+ catch (err) {
233
+ lastThrowError = err;
234
+ if (attempt < maxAttempts) {
235
+ const reason = err instanceof Error ? err.message : String(err);
236
+ console.error(`[fleet-bootstrap] ${label}: setAgentWallet attempt ` +
237
+ `${attempt}/${maxAttempts} threw (${reason}); retrying in ` +
238
+ `${this.safeBindingRetryDelayMs}ms...`);
239
+ if (this.safeBindingRetryDelayMs > 0) {
240
+ await sleep(this.safeBindingRetryDelayMs);
241
+ }
242
+ }
243
+ }
244
+ }
245
+ if (lastResult)
246
+ return lastResult; // final ok:false propagated to caller
247
+ // Only thrown all the way through — no successful response or returned outcome.
248
+ throw lastThrowError instanceof Error
249
+ ? lastThrowError
250
+ : new Error(String(lastThrowError));
251
+ }
80
252
  /**
81
253
  * Conservative daily master gas (wei): max(DEFAULT, rough tx count from poll interval × cost).
82
254
  */
@@ -112,16 +284,149 @@ export class FleetBootstrapper {
112
284
  const mnemonic = await this.loadExistingMnemonic(state, password);
113
285
  return this.stepRegisterAgent(state, mnemonic, serviceIndex);
114
286
  }
115
- async bootstrap(password) {
116
- // Handle legacy keystore migration
287
+ /**
288
+ * Stage 1 Identity (universal). Walks: wallet → predict Safe (from
289
+ * HD-index-1 agent EOA) → ETH funding gate → deploy Safe → mint agentId
290
+ * + setAgentWallet via ERC-1271. Idempotent and re-entrant. Does NOT
291
+ * touch service rows or staking — those belong to Stage 2.
292
+ *
293
+ * Fleet-level fields written:
294
+ * - fleet_safe_address (after predict)
295
+ * - fleet_agent_id, fleet_identity_registry, fleet_stage='stage1'
296
+ * (after mint + bind)
297
+ *
298
+ * Funding gate: requires ETH on the master EOA only (no OLAS). On testnet,
299
+ * the existing CDP faucet loop drains as usual when `autoTestnetFaucet`
300
+ * is enabled.
301
+ *
302
+ * See docs/superpowers/specs/2026-05-13-plug-in-builder-entry-point-design.md §5.1.
303
+ */
304
+ async ensureStage1(password) {
305
+ // Legacy keystore migration (same as bootstrap()).
117
306
  if (!this.store.hasMnemonicKeystore() && this.store.hasLegacyKeystore()) {
118
307
  await this.store.migrateLegacyFiles();
119
308
  }
120
309
  let state = await this.store.load(this.chain);
310
+ // Short-circuit if Stage 1 is already complete (or beyond).
311
+ if (state.fleet_stage === 'stage1' || state.fleet_stage === 'stage1_and_2') {
312
+ // Even when stage marker says complete, fleet identity may be empty for
313
+ // pre-j07 operators (`stage1_and_2` is set by the migration for
314
+ // services-complete-but-no-agent_id operators). In that case we leave
315
+ // Stage 1 alone — the legacy backfill in main.ts handles those rows
316
+ // and a future ensureStage1 call after backfill will promote.
317
+ return {
318
+ ok: true,
319
+ fleet_state: state,
320
+ message: state.fleet_agent_id !== null
321
+ ? `Stage 1 already complete (fleet_agent_id=${state.fleet_agent_id}, fleet_safe=${state.fleet_safe_address}).`
322
+ : 'Stage 1 marker present but fleet identity is empty (legacy operator). Skipping.',
323
+ };
324
+ }
121
325
  try {
122
- // Phase 1: Master wallet setup
123
326
  state = await this.ensureMasterWallet(state, password);
124
- // Phase 1b: Check master funding
327
+ // Stage 1 funding gate — ETH only (no OLAS). Stage 1 needs
328
+ // STAGE1_AGENT_ETH (0.01) for the master → agent transfer plus
329
+ // minEoaGasEth (0.005) reserved for the master's own gas to send that
330
+ // transfer. The agent EOA then pays for Safe deploy + ERC-8004 register
331
+ // + setAgentWallet out of the funds it just received.
332
+ // See jinn-mono-u34i: pre-fix this gate was 2×minEoaGasEth (= 0.01 ETH),
333
+ // which equaled the transfer amount and left no gas headroom, so a
334
+ // master holding the gate's minimum would fail eth_estimateGas with
335
+ // "gas required exceeds allowance (0)".
336
+ const requiredMasterEth = stage1MinMasterEth(this.config, this.targetServices);
337
+ const masterAddress = state.master_address;
338
+ const masterBalance = await this.publicClient.getBalance({
339
+ address: masterAddress,
340
+ });
341
+ if (masterBalance < requiredMasterEth) {
342
+ const shortfall = requiredMasterEth - masterBalance;
343
+ return {
344
+ ok: false,
345
+ fleet_state: state,
346
+ message: `Your master wallet needs more ETH (currently ${formatEther(masterBalance)} ETH, need ${formatEther(shortfall)} ETH more) to complete Stage 1. Please send ETH to: ${masterAddress}`,
347
+ funding: {
348
+ master_address: masterAddress,
349
+ eth_required: shortfall.toString(),
350
+ eth_balance: masterBalance.toString(),
351
+ },
352
+ };
353
+ }
354
+ const mnemonic = await this.loadExistingMnemonic(state, password);
355
+ // Step 1: predict fleet Safe from HD-index-1 agent EOA.
356
+ if (!state.fleet_safe_address) {
357
+ state = await this.stepFleetSafePredict(state, mnemonic);
358
+ }
359
+ // Step 2: deploy fleet Safe if bytecode absent.
360
+ const safeCode = await this.publicClient.getCode({
361
+ address: getAddress(state.fleet_safe_address),
362
+ });
363
+ if (safeCode === undefined || safeCode === '0x') {
364
+ state = await this.stepFleetSafeDeploy(state, mnemonic);
365
+ }
366
+ // Step 3: mint agentId + bind Safe via setAgentWallet.
367
+ if (!state.fleet_agent_id) {
368
+ state = await this.stepFleetIdentityRegister(state, mnemonic);
369
+ }
370
+ else if (state.fleet_stage !== 'stage1' && state.fleet_stage !== 'stage1_and_2') {
371
+ // Identity was minted but stage marker is stale; advance it.
372
+ state = await this.store.patchFleet({ fleet_stage: 'stage1' });
373
+ }
374
+ return {
375
+ ok: true,
376
+ fleet_state: state,
377
+ message: `Stage 1 complete. fleet_agent_id=${state.fleet_agent_id}, fleet_safe=${state.fleet_safe_address}.`,
378
+ };
379
+ }
380
+ catch (error) {
381
+ const { summary, hint, rawMessage } = formatBootstrapOperatorMessage(error);
382
+ const userMessage = hint !== undefined ? `${summary}\nHint: ${hint}` : summary;
383
+ if (this.debug) {
384
+ console.error(`[fleet-bootstrap] ensureStage1 failed:`, error);
385
+ }
386
+ else {
387
+ console.error(`[fleet-bootstrap] ${summary}`);
388
+ if (hint !== undefined)
389
+ console.error(`Hint: ${hint}`);
390
+ if (rawMessage && rawMessage !== summary) {
391
+ console.error(`[fleet-bootstrap] raw: ${rawMessage.split('\n')[0]}`);
392
+ }
393
+ }
394
+ return {
395
+ ok: false,
396
+ fleet_state: state,
397
+ message: userMessage,
398
+ rawErrorMessage: rawMessage,
399
+ };
400
+ }
401
+ }
402
+ /**
403
+ * Stage 1 + Stage 2 — full operator bootstrap. Calls `ensureStage1`
404
+ * first; on success, walks Stage 2 per service. Builder-only users who
405
+ * have completed Stage 1 and call this method later begin Stage 2 from
406
+ * `awaiting_stake` for the first service row (created lazily here).
407
+ *
408
+ * Two-Safe topology in standard mode: `fleet_safe_address !==
409
+ * services[0].safe_address` because Stage 2's `distributor.stake()`
410
+ * creates its own Safe. In self-bond mode the two converge (both
411
+ * derived from HD-index-1).
412
+ *
413
+ * See docs/superpowers/specs/2026-05-13-plug-in-builder-entry-point-design.md §5.1.
414
+ */
415
+ async ensureStage1And2(password) {
416
+ // Stage 1 first — establishes fleet identity. Short-circuits if already done.
417
+ const stage1Result = await this.ensureStage1(password);
418
+ if (!stage1Result.ok) {
419
+ return stage1Result;
420
+ }
421
+ // Original bootstrap body — copied verbatim from the previous bootstrap()
422
+ // method, with two changes:
423
+ // (a) the legacy-keystore migration and master-wallet-ensure are no-ops
424
+ // because ensureStage1 already ran them.
425
+ // (b) at the end, if any service reached `complete`/`safe_binding_pending`
426
+ // we advance `fleet_stage` to `'stage1_and_2'`.
427
+ let state = stage1Result.fleet_state;
428
+ try {
429
+ // Phase 1b: Check master funding for the full operator path.
125
430
  const masterAddress = state.master_address;
126
431
  let masterBalance = await this.publicClient.getBalance({ address: masterAddress });
127
432
  // Self-bond mode needs much more ETH than standard mode because the master
@@ -151,15 +456,24 @@ export class FleetBootstrapper {
151
456
  stakingMode: this.stakingMode,
152
457
  currentStakingContract: this.config.stakingContract,
153
458
  }).services.length > 0;
154
- const completedCountBeforeFunding = state.services.filter(s => isOperationalServiceStep(s.step)).length;
155
459
  const standardFleetAlreadyComplete = this.stakingMode === 'standard' &&
156
- !pendingSetupMigration &&
157
- completedCountBeforeFunding >= this.targetServices;
158
- const standardFleetHasInProgressServices = this.stakingMode === 'standard' && state.services.length > 0;
460
+ state.services.length >= this.targetServices &&
461
+ state.services.every(svc => svc.step !== undefined && isOperationalServiceStep(svc.step)) &&
462
+ !pendingSetupMigration;
159
463
  const requiredMasterEth = this.stakingMode === 'standard'
160
464
  ? (standardFleetAlreadyComplete
161
465
  ? 0n
162
- : this.config.minEoaGasEth * (standardFleetHasInProgressServices ? 1n : STANDARD_MASTER_BOOTSTRAP_MULTIPLIER))
466
+ // Stage 2 master gas budget covers distributor.stake() (~0.003
467
+ // ETH at typical 6 gwei Base Sepolia) plus per-extra-service
468
+ // top-up transfers. Previously this was `× 2` for fresh fleets,
469
+ // which double-counted a service-1 transfer that doesn't actually
470
+ // fire (HD-1 carries Stage 1 leftover, gets a conditional top-up
471
+ // only if needed). See jinn-mono-u34i: the `× 2` figure also
472
+ // exceeded the post-Stage-1 master balance at the operator-facing
473
+ // 0.020 ETH budget by ~127k gwei of Stage 1 gas burn, causing a
474
+ // second funding prompt.
475
+ : this.config.minEoaGasEth +
476
+ this.config.minEoaGasEth * BigInt(Math.max(0, this.targetServices - 1)))
163
477
  : SELF_BOND_ETH_PER_SERVICE * BigInt(this.targetServices);
164
478
  const autoFaucetEnabled = this.autoTestnetFaucet;
165
479
  // Re-sum system ETH (master + agent/safe balances for self-bond mode).
@@ -291,6 +605,11 @@ export class FleetBootstrapper {
291
605
  const nextIndex = nextFleetServiceIndex(state.services);
292
606
  state = await this.bootstrapService(state, mnemonic, nextIndex);
293
607
  }
608
+ // Advance fleet_stage to 'stage1_and_2' if any service is operational.
609
+ const anyOperationalAfter = state.services.some(s => isOperationalServiceStep(s.step));
610
+ if (anyOperationalAfter && state.fleet_stage !== 'stage1_and_2') {
611
+ state = await this.store.patchFleet({ fleet_stage: 'stage1_and_2' });
612
+ }
294
613
  return {
295
614
  ok: true,
296
615
  fleet_state: state,
@@ -298,7 +617,7 @@ export class FleetBootstrapper {
298
617
  };
299
618
  }
300
619
  catch (error) {
301
- const { summary, hint, rawMessage } = formatBootstrapOperatorMessage(error);
620
+ const { summary, hint, rawMessage, category } = formatBootstrapOperatorMessage(error);
302
621
  const userMessage = hint !== undefined ? `${summary}\nHint: ${hint}` : summary;
303
622
  if (this.debug) {
304
623
  console.error(`[fleet-bootstrap] Bootstrap failed:`, error);
@@ -314,14 +633,32 @@ export class FleetBootstrapper {
314
633
  console.error(`[fleet-bootstrap] raw: ${rawMessage.split('\n')[0]}`);
315
634
  }
316
635
  }
636
+ // Extract a tx hash embedded in the error message by the on-chain revert
637
+ // paths (format: "...tx failed for service N: 0x<hash>" or
638
+ // "...tx reverted: 0x<hash>"). Surfaced in the fatal envelope so the SPA
639
+ // can render a block-explorer link. jinn-mono-hjex reviewer fix.
640
+ const txHashMatch = /(0x[a-fA-F0-9]{64})/.exec(rawMessage);
641
+ const txHash = txHashMatch ? txHashMatch[1] : null;
317
642
  return {
318
643
  ok: false,
319
644
  fleet_state: state,
320
645
  message: userMessage,
321
646
  rawErrorMessage: rawMessage,
647
+ // Preserve the structured category so the error envelope in main.ts
648
+ // can surface it in `details.category` for SPA consumers. jinn-mono-hjex.6
649
+ ...(category !== undefined ? { errorCategory: category } : {}),
650
+ ...(txHash !== null ? { txHash } : {}),
322
651
  };
323
652
  }
324
653
  }
654
+ /**
655
+ * Back-compat alias. Existing call sites in `client/src/cli/commands/bootstrap.ts`
656
+ * and `client/src/cli/commands/fleet-scale.ts` continue to call `bootstrap()`;
657
+ * forwarding to `ensureStage1And2` preserves their semantics without churn.
658
+ */
659
+ async bootstrap(password) {
660
+ return this.ensureStage1And2(password);
661
+ }
325
662
  /**
326
663
  * If the master is only slightly above the minimum, warn about gas runway (heuristic days).
327
664
  */
@@ -382,6 +719,133 @@ export class FleetBootstrapper {
382
719
  return freshMnemonic;
383
720
  }
384
721
  }
722
+ // ── Stage 1: fleet-level identity steps (nghf) ────────────────────────
723
+ /** Deterministic Safe predict from the HD-index-1 agent EOA. */
724
+ async stepFleetSafePredict(state, mnemonic) {
725
+ const agentAddress = deriveAgentAddress(mnemonic, 1);
726
+ const agentKey = walletPrivateKeyAtIndex(mnemonic, 1);
727
+ console.error(`[fleet-bootstrap] Stage 1: predicting fleet Safe (owner=${agentAddress})`);
728
+ const { address } = await initPredictedSafe({
729
+ rpcUrl: this.config.rpcUrl,
730
+ signerKey: agentKey,
731
+ owners: [agentAddress],
732
+ threshold: 1,
733
+ });
734
+ void state;
735
+ return this.store.patchFleet({ fleet_safe_address: getAddress(address) });
736
+ }
737
+ /** Deploy the predicted fleet Safe. Funds the agent EOA from master if needed. */
738
+ async stepFleetSafeDeploy(state, mnemonic) {
739
+ const agentAddress = deriveAgentAddress(mnemonic, 1);
740
+ const agentKey = walletPrivateKeyAtIndex(mnemonic, 1);
741
+ const agentSigner = deriveAgentSigner(mnemonic, 1);
742
+ const fleetSafe = state.fleet_safe_address;
743
+ // Fund agent EOA so it can pay for Safe deploy + setAgentWallet gas.
744
+ // 0.01 ETH covers Safe deploy (~250k gas) + register (~80k) + setAgentWallet
745
+ // (~200k) at testnet gas prices comfortably. STAGE1_AGENT_ETH is the
746
+ // module-level constant used both here and in `stage1MinMasterEth` so the
747
+ // gate and the transfer agree (jinn-mono-u34i).
748
+ const masterAccount = deriveMasterSigner(mnemonic);
749
+ const masterWallet = createJinnWalletClient(this.config.rpcUrl, this.chain, masterAccount);
750
+ const agentBalance = await this.publicClient.getBalance({
751
+ address: getAddress(agentAddress),
752
+ });
753
+ if (agentBalance < STAGE1_AGENT_ETH) {
754
+ const fundAmount = STAGE1_AGENT_ETH - agentBalance;
755
+ console.error(`[fleet-bootstrap] Stage 1: funding fleet agent EOA with ${fundAmount} wei from master`);
756
+ const fundHash = await viemSendTransactionWithRetry(masterWallet, this.publicClient, {
757
+ account: masterAccount,
758
+ to: addr(agentAddress),
759
+ value: fundAmount,
760
+ });
761
+ await waitForTransactionReceiptWithRetry(this.publicClient, fundHash);
762
+ }
763
+ console.error(`[fleet-bootstrap] Stage 1: deploying fleet Safe at ${fleetSafe}`);
764
+ const { safe } = await initPredictedSafe({
765
+ rpcUrl: this.config.rpcUrl,
766
+ signerKey: agentKey,
767
+ owners: [agentAddress],
768
+ threshold: 1,
769
+ });
770
+ const deployTx = await safe.createSafeDeploymentTransaction();
771
+ const agentWallet = createJinnWalletClient(this.config.rpcUrl, this.chain, agentSigner);
772
+ const deployHash = await viemSendTransactionWithRetry(agentWallet, this.publicClient, {
773
+ account: agentSigner,
774
+ to: deployTx.to,
775
+ value: BigInt(deployTx.value),
776
+ data: deployTx.data,
777
+ });
778
+ const receipt = await waitForTransactionReceiptWithRetry(this.publicClient, deployHash);
779
+ if (receipt.status !== 'success') {
780
+ throw new Error(`Fleet Safe deployment tx failed: ${deployHash}`);
781
+ }
782
+ try {
783
+ await waitForContractCode(this.publicClient, getAddress(fleetSafe));
784
+ }
785
+ catch {
786
+ throw new Error(`Fleet Safe deployment succeeded but no code at ${fleetSafe}`);
787
+ }
788
+ console.error(`[fleet-bootstrap] Stage 1: fleet Safe deployed (tx=${deployHash})`);
789
+ return this.store.load(this.chain);
790
+ }
791
+ /** Mint the fleet agentId + bind Safe via setAgentWallet (ERC-1271). */
792
+ async stepFleetIdentityRegister(state, mnemonic) {
793
+ const identityRegistry = this.config.identityRegistry ?? IDENTITY_REGISTRY_ADDRESSES[this.config.chainId];
794
+ if (!identityRegistry) {
795
+ throw new Error(`IdentityRegistry address not configured for chainId=${this.config.chainId}.`);
796
+ }
797
+ const fleetSafe = state.fleet_safe_address;
798
+ const agentSigner = deriveAgentSigner(mnemonic, 1);
799
+ const agentWallet = createJinnWalletClient(this.config.rpcUrl, this.chain, agentSigner);
800
+ // Mint agentId — empty agent URI for v0 (matches stepRegisterAgent §6.1 in spec).
801
+ const registerData = encodeFunctionData({
802
+ abi: IDENTITY_REGISTRY_ABI,
803
+ functionName: 'register',
804
+ args: [''],
805
+ });
806
+ console.error(`[fleet-bootstrap] Stage 1: minting fleet agentId ` +
807
+ `(IdentityRegistry=${identityRegistry}, agentEOA=${agentSigner.address})`);
808
+ const mintTxHash = await viemSendTransactionWithRetry(agentWallet, this.publicClient, {
809
+ account: agentSigner,
810
+ to: addr(identityRegistry),
811
+ data: registerData,
812
+ });
813
+ const mintReceipt = await waitForTransactionReceiptWithRetry(this.publicClient, mintTxHash);
814
+ if (mintReceipt.status !== 'success') {
815
+ throw new Error(`Fleet IdentityRegistry.register() failed: ${mintTxHash}`);
816
+ }
817
+ const fleetAgentId = this.parseAgentIdFromReceipt(mintReceipt, identityRegistry);
818
+ if (fleetAgentId === null) {
819
+ throw new Error(`Fleet IdentityRegistry.register() succeeded but Registered event missing (tx=${mintTxHash})`);
820
+ }
821
+ // Persist agentId IMMEDIATELY so a crash between mint and bind doesn't lose it.
822
+ await this.store.patchFleet({
823
+ fleet_agent_id: fleetAgentId,
824
+ fleet_identity_registry: getAddress(identityRegistry),
825
+ });
826
+ // Bind the Safe via setAgentWallet (ERC-1271).
827
+ // Wrapped in the freshly-deployed-Safe retry (jinn-mono-k1ng). The
828
+ // pre-k1ng single-attempt version halted bootstrap when the documented
829
+ // race fired on the first attempt — exactly the 2026-05-18 canary's
830
+ // second-time-around failure mode.
831
+ console.error(`[fleet-bootstrap] Stage 1: binding fleet Safe ${fleetSafe} to agentId=${fleetAgentId}`);
832
+ const bindResult = await this.bindAgentWalletWithRetry({
833
+ identityRegistryAddress: addr(identityRegistry),
834
+ agentId: BigInt(fleetAgentId),
835
+ safeAddress: addr(fleetSafe),
836
+ agentEoaAccount: agentSigner,
837
+ agentEoaWalletClient: agentWallet,
838
+ publicClient: this.publicClient,
839
+ chainId: this.config.chainId,
840
+ }, 'Stage 1');
841
+ if (!bindResult.ok) {
842
+ const bindErr = bindResult.error;
843
+ throw new Error(`Fleet setAgentWallet failed after ${this.safeBindingMaxAttempts} attempts: ${bindErr.shortMessage}` +
844
+ (bindErr.revertReason ? ` (revert: ${bindErr.revertReason})` : ''));
845
+ }
846
+ console.error(`[fleet-bootstrap] Stage 1: setAgentWallet succeeded (tx=${bindResult.txHash})`);
847
+ return this.store.patchFleet({ fleet_stage: 'stage1' });
848
+ }
385
849
  // ── Phase 2: Per-service bootstrap ───────────────────────────────────
386
850
  async bootstrapService(state, mnemonic, index) {
387
851
  const agentAddress = deriveAgentAddress(mnemonic, index);
@@ -629,6 +1093,39 @@ export class FleetBootstrapper {
629
1093
  return this.store.updateService(index, { step: 'staked' });
630
1094
  }
631
1095
  }
1096
+ // Pre-stake precondition: if migration cleared service_id but kept agent_address,
1097
+ // check the EOA is not already registered on-chain as an agent instance. If it is,
1098
+ // calling stake() again would revert with AgentInstanceRegistered (selector 0x631695bd)
1099
+ // and there is nothing useful the operator can do without rotating the agent EOA.
1100
+ // Fail fast with a typed error instead of letting the contract revert.
1101
+ //
1102
+ // ServiceRegistryL2 exposes the `mapAgentInstanceOperators(address) → address` mapping:
1103
+ // it returns the operator address that registered the given agent instance, or the
1104
+ // zero address when no operator has bound that instance. A non-zero return means the
1105
+ // EOA is already bound. (There is no `mapAgentInstances(address) → uint256` getter on
1106
+ // the deployed registry — an earlier draft of this guard referenced one and was a
1107
+ // permanent no-op.)
1108
+ if (svc.agent_address && svc.service_id === null && this.config.serviceRegistry) {
1109
+ let alreadyBound = false;
1110
+ try {
1111
+ const boundOperator = (await this.publicClient.readContract({
1112
+ address: getAddress(this.config.serviceRegistry),
1113
+ abi: SERVICE_REGISTRY_L2_ABI,
1114
+ functionName: 'mapAgentInstanceOperators',
1115
+ args: [getAddress(svc.agent_address)],
1116
+ }));
1117
+ alreadyBound = boundOperator !== '0x0000000000000000000000000000000000000000';
1118
+ }
1119
+ catch {
1120
+ // Registry read failure is non-fatal — proceed and let stake() surface
1121
+ // the error if the agent really is bound.
1122
+ }
1123
+ if (alreadyBound) {
1124
+ throw new Error(`agent_already_bound: agent EOA ${svc.agent_address} is already registered as an agent instance on-chain. ` +
1125
+ `The previous setup retirement may have been incomplete. ` +
1126
+ `Contact support or rotate the agent EOA to continue.`);
1127
+ }
1128
+ }
632
1129
  // Fresh distributor stake() creates a new on-chain service. If state still
633
1130
  // references an old Safe (e.g. hand-edited JSON), sweep it before replacing.
634
1131
  if (svc.service_id === null && svc.safe_address && state.master_address) {
@@ -694,42 +1191,19 @@ export class FleetBootstrapper {
694
1191
  const svc = state.services.find(s => s.index === index);
695
1192
  const serviceId = svc.service_id;
696
1193
  const stakingAddress = this.stakingAddressForService(svc);
697
- // `reStake()` is operator-scoped: the master EOA must match the
698
- // distributor's recorded `mapServiceIdCuratingAgents[serviceId]` entry.
699
- // If it doesn't, the operator is likely using the wrong earning dir or
700
- // password, or the service needs owner / managing-agent recovery.
701
- const masterAccount = deriveMasterSigner(mnemonic);
702
- const masterWallet = createJinnWalletClient(this.config.rpcUrl, this.chain, masterAccount);
703
- const reStakeData = encodeFunctionData({
704
- abi: STOLAS_DISTRIBUTOR_ABI,
705
- functionName: 'reStake',
706
- args: [stakingAddress, BigInt(serviceId)],
1194
+ const di = displayFleetServiceIndex(svc);
1195
+ // Delegate to the standalone exported helper (shared with EvictionLoop /
1196
+ // the dashboard "Re-stake now" CTA). This eliminates the duplicate
1197
+ // implementation (jinn-mono-hjex.3).
1198
+ await recoverEvictedService({
1199
+ serviceDisplayIndex: di,
1200
+ serviceId,
1201
+ stakingAddress: stakingAddress,
1202
+ distributorAddress: this.config.distributorAddress,
1203
+ rpcUrl: this.config.rpcUrl,
1204
+ chain: this.chain,
1205
+ mnemonic,
707
1206
  });
708
- console.error(`[fleet-bootstrap] Service ${index}: calling distributor.reStake() for evicted service ${serviceId}`);
709
- let reStakeHash;
710
- try {
711
- reStakeHash = await viemSendTransactionWithRetry(masterWallet, this.publicClient, {
712
- account: masterAccount,
713
- to: addr(this.config.distributorAddress),
714
- data: reStakeData,
715
- gas: 1500000n,
716
- });
717
- }
718
- catch (err) {
719
- const message = flattenErrorMessage(err);
720
- if (isUnauthorizedAccountError(message)) {
721
- throw new Error(`Service ${index} (service_id ${serviceId}) is evicted on the staking proxy, but master EOA ${masterAccount.address} is not authorized to reStake it. ` +
722
- `The distributor only permits the recorded service operator, a managing agent, or the owner. ` +
723
- `Verify JINN_EARNING_DIR and JINN_PASSWORD derive the original master EOA for this service, then re-run jinn bootstrap; otherwise request owner / managing-agent recovery or abandon-and-rebootstrap. ` +
724
- `reStake revert: ${message}`);
725
- }
726
- throw err;
727
- }
728
- const receipt = await waitForTransactionReceiptWithRetry(this.publicClient, reStakeHash);
729
- if (receipt.status !== 'success') {
730
- throw new Error(`reStake failed for service ${index}: ${reStakeHash}`);
731
- }
732
- console.error(`[fleet-bootstrap] Service ${index}: reStake confirmed (tx: ${reStakeHash})`);
733
1207
  // Service is now Staked again with the same service_id, safe_address, and mech_address.
734
1208
  // Step back to `mech_deployed` so the resume loop advances through
735
1209
  // `stepRegisterAgent` (idempotent — short-circuits if `agent_id` is
@@ -837,6 +1311,7 @@ export class FleetBootstrapper {
837
1311
  let svc = (await this.store.load(this.chain)).services.find(s => s.index === index);
838
1312
  if (!svc)
839
1313
  throw new Error(`Service ${index} not found in state`);
1314
+ const fleetSnapshot = await this.store.load(this.chain);
840
1315
  const identityRegistry = this.config.identityRegistry
841
1316
  ?? IDENTITY_REGISTRY_ADDRESSES[this.config.chainId];
842
1317
  if (!identityRegistry) {
@@ -845,7 +1320,7 @@ export class FleetBootstrapper {
845
1320
  }
846
1321
  const agentSigner = deriveAgentSigner(mnemonic, index);
847
1322
  const agentWallet = createJinnWalletClient(this.config.rpcUrl, this.chain, agentSigner);
848
- // ── Sub-step A: mint NFT (skip if agent_id is already set). ─────────
1323
+ // ── Sub-step A: mint NFT (skip if agent_id is already set OR fleet identity exists).
849
1324
  let agentId;
850
1325
  if (svc.agent_id) {
851
1326
  console.error(`[fleet-bootstrap] Service ${index}: ERC-8004 agent already registered ` +
@@ -856,6 +1331,22 @@ export class FleetBootstrapper {
856
1331
  step: svc.step === 'safe_binding_pending' ? 'safe_binding_pending' : 'agent_registered',
857
1332
  });
858
1333
  }
1334
+ else if (fleetSnapshot.fleet_agent_id) {
1335
+ // nghf: reuse the fleet-level agentId minted by ensureStage1 instead of
1336
+ // minting a second one. This collapses the "one agentId per user"
1337
+ // invariant in spec §5.1 for the standard-mode two-Safe topology.
1338
+ console.error(`[fleet-bootstrap] Service ${index}: reusing fleet agentId=${fleetSnapshot.fleet_agent_id} ` +
1339
+ `(no second mint needed).`);
1340
+ agentId = fleetSnapshot.fleet_agent_id;
1341
+ svc = await this.firstServiceUpdate(index, {
1342
+ agent_id: fleetSnapshot.fleet_agent_id,
1343
+ agent_uri: '',
1344
+ identity_registry_address: fleetSnapshot.fleet_identity_registry ?? getAddress(identityRegistry),
1345
+ agent_registered_tx: null,
1346
+ step: 'agent_registered',
1347
+ error: null,
1348
+ });
1349
+ }
859
1350
  else {
860
1351
  // v0: empty agentURI. The richer agent card (per §6 of the spec) is
861
1352
  // future work — operators may later call `setAgentURI`.
@@ -924,8 +1415,18 @@ export class FleetBootstrapper {
924
1415
  step: 'safe_binding_pending',
925
1416
  error: null,
926
1417
  });
1418
+ // Unified retry policy (jinn-mono-k1ng — supersedes h74p's throw-only
1419
+ // retry): retry on `ok: false` and on thrown exceptions. The h74p
1420
+ // version only caught throws, but `bindAgentWalletToSafe` returns
1421
+ // `ok: false` for the documented freshly-deployed-Safe revert race —
1422
+ // so the retry was dead code under real RPC. Per-service "worked"
1423
+ // anyway because the next bootstrap resumes from `safe_binding_pending`;
1424
+ // k1ng makes the in-process retry actually do its job so we don't
1425
+ // depend on the operator restarting the daemon.
1426
+ let bindResult;
1427
+ let lastBindError;
927
1428
  try {
928
- const result = await bindAgentWalletToSafe({
1429
+ bindResult = await this.bindAgentWalletWithRetry({
929
1430
  identityRegistryAddress: addr(identityRegistry),
930
1431
  agentId: BigInt(agentId),
931
1432
  safeAddress: addr(safeAddress),
@@ -933,23 +1434,45 @@ export class FleetBootstrapper {
933
1434
  agentEoaWalletClient: agentWallet,
934
1435
  publicClient: this.publicClient,
935
1436
  chainId: this.config.chainId,
936
- });
1437
+ }, `Service ${index}`);
1438
+ }
1439
+ catch (err) {
1440
+ lastBindError = err;
1441
+ }
1442
+ if (bindResult?.ok === true) {
937
1443
  console.error(`[fleet-bootstrap] Service ${index}: setAgentWallet succeeded ` +
938
- `(tx=${result.txHash}, safe=${safeAddress}).`);
1444
+ `(tx=${bindResult.txHash}, safe=${safeAddress}).`);
939
1445
  svc = await this.firstServiceUpdate(index, {
940
1446
  safe_bound_to_agent: true,
941
1447
  step: 'complete',
942
1448
  error: null,
1449
+ error_revert_reason: null,
1450
+ error_short_message: null,
943
1451
  });
944
1452
  }
945
- catch (err) {
946
- const reason = err instanceof Error ? err.message : String(err);
947
- console.error(`[fleet-bootstrap] Service ${index}: setAgentWallet failed; continuing with ` +
948
- `safe_bound_to_agent=false (${reason}).`);
1453
+ else if (bindResult && !bindResult.ok) {
1454
+ const bindErr = bindResult.error;
1455
+ console.error(`[fleet-bootstrap] Service ${index}: setAgentWallet failed after retries; ` +
1456
+ `continuing with safe_bound_to_agent=false (${bindErr.shortMessage}` +
1457
+ `${bindErr.revertReason ? `, revert: ${bindErr.revertReason}` : ''}).`);
1458
+ svc = await this.firstServiceUpdate(index, {
1459
+ safe_bound_to_agent: false,
1460
+ step: 'safe_binding_pending',
1461
+ error: `safe_binding_failed: ${bindErr.shortMessage}`,
1462
+ error_revert_reason: bindErr.revertReason,
1463
+ error_short_message: bindErr.shortMessage,
1464
+ });
1465
+ }
1466
+ else {
1467
+ const reason = lastBindError instanceof Error ? lastBindError.message : String(lastBindError);
1468
+ console.error(`[fleet-bootstrap] Service ${index}: setAgentWallet threw on every attempt; ` +
1469
+ `continuing with safe_bound_to_agent=false (${reason}).`);
949
1470
  svc = await this.firstServiceUpdate(index, {
950
1471
  safe_bound_to_agent: false,
951
1472
  step: 'safe_binding_pending',
952
1473
  error: `safe_binding_failed: ${reason}`,
1474
+ error_revert_reason: null,
1475
+ error_short_message: null,
953
1476
  });
954
1477
  }
955
1478
  }
@@ -1066,8 +1589,10 @@ export class FleetBootstrapper {
1066
1589
  if (receipt.status !== 'success') {
1067
1590
  throw new Error(`Safe deployment tx failed for service ${index}: ${deployHash}`);
1068
1591
  }
1069
- const deployedCode = await this.publicClient.getCode({ address: getAddress(safeAddress) });
1070
- if (deployedCode === undefined || deployedCode === '0x') {
1592
+ try {
1593
+ await waitForContractCode(this.publicClient, getAddress(safeAddress));
1594
+ }
1595
+ catch {
1071
1596
  throw new Error(`Safe deployment succeeded but no code at ${safeAddress}`);
1072
1597
  }
1073
1598
  console.error(`[fleet-bootstrap] Service ${index}: Safe deployed (tx: ${deployHash})`);
@@ -1431,4 +1956,50 @@ export class FleetBootstrapper {
1431
1956
  }
1432
1957
  /** @deprecated Use FleetBootstrapper */
1433
1958
  export const EarningBootstrapper = FleetBootstrapper;
1959
+ /**
1960
+ * Re-stake an evicted service by calling `distributor.reStake(stakingProxy, serviceId)`.
1961
+ *
1962
+ * Extracted from `FleetBootstrapper.recoverEvictedService` so it can be called
1963
+ * from the in-process `EvictionLoop` without requiring a full bootstrapper
1964
+ * context (jinn-mono-hjex.3).
1965
+ *
1966
+ * The caller is responsible for advancing the local service step back to
1967
+ * `mech_deployed` after this returns (just like the bootstrapper resume path does).
1968
+ */
1969
+ export async function recoverEvictedService(opts) {
1970
+ const { serviceDisplayIndex, serviceId, stakingAddress, distributorAddress, rpcUrl, chain, mnemonic, } = opts;
1971
+ const masterAccount = deriveMasterSigner(mnemonic);
1972
+ const publicClient = createJinnPublicClient(rpcUrl, chain);
1973
+ const masterWallet = createJinnWalletClient(rpcUrl, chain, masterAccount);
1974
+ const reStakeData = encodeFunctionData({
1975
+ abi: STOLAS_DISTRIBUTOR_ABI,
1976
+ functionName: 'reStake',
1977
+ args: [addr(stakingAddress), BigInt(serviceId)],
1978
+ });
1979
+ console.error(`[eviction-recovery] Service ${serviceDisplayIndex}: calling distributor.reStake() for evicted service ${serviceId}`);
1980
+ let reStakeHash;
1981
+ try {
1982
+ reStakeHash = await viemSendTransactionWithRetry(masterWallet, publicClient, {
1983
+ account: masterAccount,
1984
+ to: addr(distributorAddress),
1985
+ data: reStakeData,
1986
+ gas: 1500000n,
1987
+ });
1988
+ }
1989
+ catch (err) {
1990
+ const message = flattenErrorMessage(err);
1991
+ if (isUnauthorizedAccountError(message)) {
1992
+ throw new Error(`Service ${serviceDisplayIndex} (service_id ${serviceId}) is evicted on the staking proxy, but master EOA ` +
1993
+ `${masterAccount.address} is not authorized to reStake it. ` +
1994
+ `Verify JINN_EARNING_DIR and JINN_PASSWORD derive the original master EOA for this service. ` +
1995
+ `reStake revert: ${message}`);
1996
+ }
1997
+ throw err;
1998
+ }
1999
+ const receipt = await waitForTransactionReceiptWithRetry(publicClient, reStakeHash);
2000
+ if (receipt.status !== 'success') {
2001
+ throw new Error(`reStake failed for service ${serviceDisplayIndex}: ${reStakeHash}`);
2002
+ }
2003
+ console.error(`[eviction-recovery] Service ${serviceDisplayIndex}: reStake confirmed (tx: ${reStakeHash})`);
2004
+ }
1434
2005
  //# sourceMappingURL=bootstrap.js.map