@jinn-network/client 0.1.9-canary.5c19fc6d → 0.1.9-canary.5ff3d8cb

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 (295) hide show
  1. package/CONTRIBUTING.md +2 -2
  2. package/dist/adapters/mech/adapter.d.ts +10 -0
  3. package/dist/adapters/mech/adapter.js +19 -1
  4. package/dist/adapters/mech/adapter.js.map +1 -1
  5. package/dist/adapters/mech/types.d.ts +1 -88
  6. package/dist/adapters/mech/types.js +1 -45
  7. package/dist/adapters/mech/types.js.map +1 -1
  8. package/dist/api/bootstrap-endpoint.d.ts +18 -3
  9. package/dist/api/bootstrap-endpoint.js +3 -0
  10. package/dist/api/bootstrap-endpoint.js.map +1 -1
  11. package/dist/api/gather-status.js +6 -5
  12. package/dist/api/gather-status.js.map +1 -1
  13. package/dist/api/launcher-endpoints.js +6 -1
  14. package/dist/api/launcher-endpoints.js.map +1 -1
  15. package/dist/api/launcher-status.d.ts +2 -0
  16. package/dist/api/launcher-status.js.map +1 -1
  17. package/dist/api/launcher-tasks.d.ts +47 -0
  18. package/dist/api/launcher-tasks.js +88 -3
  19. package/dist/api/launcher-tasks.js.map +1 -1
  20. package/dist/api/loop-completion-build.d.ts +2 -2
  21. package/dist/api/loop-completion-build.js +2 -3
  22. package/dist/api/loop-completion-build.js.map +1 -1
  23. package/dist/api/portfolio-v0-build.d.ts +2 -1
  24. package/dist/api/portfolio-v0-build.js +5 -7
  25. package/dist/api/portfolio-v0-build.js.map +1 -1
  26. package/dist/api/prediction-v1-build.d.ts +2 -2
  27. package/dist/api/prediction-v1-build.js +5 -7
  28. package/dist/api/prediction-v1-build.js.map +1 -1
  29. package/dist/api/setup-endpoints.d.ts +4 -0
  30. package/dist/api/setup-endpoints.js +13 -7
  31. package/dist/api/setup-endpoints.js.map +1 -1
  32. package/dist/api/status-build.js +1 -4
  33. package/dist/api/status-build.js.map +1 -1
  34. package/dist/api/task-run-routing.d.ts +1 -1
  35. package/dist/api/task-runs-build.d.ts +2 -2
  36. package/dist/api/task-runs-build.js +5 -7
  37. package/dist/api/task-runs-build.js.map +1 -1
  38. package/dist/bin/jinn-distill-mcp.js +813 -0
  39. package/dist/bin/jinn-layer.js +4992 -1151
  40. package/dist/build-info.json +4 -4
  41. package/dist/build-meta.json +1 -1
  42. package/dist/captures/distil-export.d.ts +54 -0
  43. package/dist/captures/distil-export.js +135 -0
  44. package/dist/captures/distil-export.js.map +1 -0
  45. package/dist/captures/ingest.d.ts +21 -0
  46. package/dist/captures/ingest.js +144 -0
  47. package/dist/captures/ingest.js.map +1 -0
  48. package/dist/captures/live-publisher.js +4 -3
  49. package/dist/captures/live-publisher.js.map +1 -1
  50. package/dist/captures/publish.d.ts +4 -0
  51. package/dist/captures/publish.js +2 -2
  52. package/dist/captures/publish.js.map +1 -1
  53. package/dist/cli/execution-context.js +2 -1
  54. package/dist/cli/execution-context.js.map +1 -1
  55. package/dist/config.js +34 -1
  56. package/dist/config.js.map +1 -1
  57. package/dist/contracts/abis.d.ts +100 -0
  58. package/dist/contracts/abis.js +58 -0
  59. package/dist/contracts/abis.js.map +1 -0
  60. package/dist/contracts/addresses.d.ts +36 -0
  61. package/dist/contracts/addresses.js +60 -0
  62. package/dist/contracts/addresses.js.map +1 -0
  63. package/dist/corpus/onchain-query.d.ts +0 -1
  64. package/dist/corpus/onchain-query.js +2 -5
  65. package/dist/corpus/onchain-query.js.map +1 -1
  66. package/dist/daemon/balance-topup-loop.js +27 -12
  67. package/dist/daemon/balance-topup-loop.js.map +1 -1
  68. package/dist/daemon/creator.js +15 -13
  69. package/dist/daemon/creator.js.map +1 -1
  70. package/dist/daemon/daemon.js +35 -13
  71. package/dist/daemon/daemon.js.map +1 -1
  72. package/dist/daemon/loop-heartbeat.d.ts +62 -7
  73. package/dist/daemon/loop-heartbeat.js +50 -13
  74. package/dist/daemon/loop-heartbeat.js.map +1 -1
  75. package/dist/{api/peers.js → daemon/peer-sync.js} +2 -2
  76. package/dist/daemon/peer-sync.js.map +1 -0
  77. package/dist/daemon/reward-claim-loop.js +27 -12
  78. package/dist/daemon/reward-claim-loop.js.map +1 -1
  79. package/dist/daemon/spend-cap-gate.d.ts +0 -6
  80. package/dist/daemon/spend-cap-gate.js +1 -8
  81. package/dist/daemon/spend-cap-gate.js.map +1 -1
  82. package/dist/dashboard/assets/index-BdGCkGPF.js +157 -0
  83. package/dist/dashboard/assets/index-D7rvJRNa.css +1 -0
  84. package/dist/dashboard/index.html +2 -2
  85. package/dist/discovery/http.js +83 -1
  86. package/dist/discovery/http.js.map +1 -1
  87. package/dist/discovery/onchain.js +16 -8
  88. package/dist/discovery/onchain.js.map +1 -1
  89. package/dist/discovery/types.d.ts +43 -0
  90. package/dist/discovery/types.js.map +1 -1
  91. package/dist/discovery/with-fallback.js +10 -0
  92. package/dist/discovery/with-fallback.js.map +1 -1
  93. package/dist/earning/bootstrap-run.d.ts +25 -0
  94. package/dist/earning/bootstrap-run.js +315 -0
  95. package/dist/earning/bootstrap-run.js.map +1 -0
  96. package/dist/earning/bootstrap.d.ts +6 -11
  97. package/dist/earning/bootstrap.js +45 -195
  98. package/dist/earning/bootstrap.js.map +1 -1
  99. package/dist/earning/contracts.d.ts +2 -3
  100. package/dist/earning/contracts.js +12 -21
  101. package/dist/earning/contracts.js.map +1 -1
  102. package/dist/earning/master-gas.d.ts +10 -0
  103. package/dist/earning/master-gas.js +11 -0
  104. package/dist/earning/master-gas.js.map +1 -0
  105. package/dist/earning/safe-adapter.js +1 -45
  106. package/dist/earning/safe-adapter.js.map +1 -1
  107. package/dist/earning/steps/context.d.ts +42 -0
  108. package/dist/earning/steps/context.js +4 -0
  109. package/dist/earning/steps/context.js.map +1 -0
  110. package/dist/earning/steps/fleet-identity-register.d.ts +4 -0
  111. package/dist/earning/steps/fleet-identity-register.js +65 -0
  112. package/dist/earning/steps/fleet-identity-register.js.map +1 -0
  113. package/dist/earning/steps/fleet-safe-deploy.d.ts +4 -0
  114. package/dist/earning/steps/fleet-safe-deploy.js +62 -0
  115. package/dist/earning/steps/fleet-safe-deploy.js.map +1 -0
  116. package/dist/earning/steps/fleet-safe-predict.d.ts +4 -0
  117. package/dist/earning/steps/fleet-safe-predict.js +18 -0
  118. package/dist/earning/steps/fleet-safe-predict.js.map +1 -0
  119. package/dist/earning/steps/receipt-parsing.d.ts +12 -0
  120. package/dist/earning/steps/receipt-parsing.js +71 -0
  121. package/dist/earning/steps/receipt-parsing.js.map +1 -0
  122. package/dist/earning/stolas-staking.js +20 -14
  123. package/dist/earning/stolas-staking.js.map +1 -1
  124. package/dist/erc8004/addresses.d.ts +6 -5
  125. package/dist/erc8004/addresses.js +7 -5
  126. package/dist/erc8004/addresses.js.map +1 -1
  127. package/dist/erc8004/identity.d.ts +12 -1
  128. package/dist/erc8004/identity.js +31 -0
  129. package/dist/erc8004/identity.js.map +1 -1
  130. package/dist/erc8004/index.d.ts +1 -1
  131. package/dist/erc8004/index.js +1 -1
  132. package/dist/erc8004/index.js.map +1 -1
  133. package/dist/erc8004/reputation.js +16 -12
  134. package/dist/erc8004/reputation.js.map +1 -1
  135. package/dist/erc8004/validation.js +5 -4
  136. package/dist/erc8004/validation.js.map +1 -1
  137. package/dist/eval/capability-slate.d.ts +54 -0
  138. package/dist/eval/capability-slate.js +116 -0
  139. package/dist/eval/capability-slate.js.map +1 -0
  140. package/dist/eval/capability-stats.d.ts +39 -0
  141. package/dist/eval/capability-stats.js +123 -0
  142. package/dist/eval/capability-stats.js.map +1 -0
  143. package/dist/eval/contested-band.d.ts +18 -0
  144. package/dist/eval/contested-band.js +19 -0
  145. package/dist/eval/contested-band.js.map +1 -0
  146. package/dist/eval/corpus-index.d.ts +21 -0
  147. package/dist/eval/corpus-index.js +55 -0
  148. package/dist/eval/corpus-index.js.map +1 -0
  149. package/dist/eval/disjointness.d.ts +26 -0
  150. package/dist/eval/disjointness.js +67 -0
  151. package/dist/eval/disjointness.js.map +1 -0
  152. package/dist/eval/mcnemar-power.d.ts +14 -0
  153. package/dist/eval/mcnemar-power.js +40 -0
  154. package/dist/eval/mcnemar-power.js.map +1 -0
  155. package/dist/harnesses/engine/engine.d.ts +2 -1
  156. package/dist/harnesses/engine/engine.js +15 -2
  157. package/dist/harnesses/engine/engine.js.map +1 -1
  158. package/dist/harnesses/engine/persistence.d.ts +2 -61
  159. package/dist/harnesses/engine/persistence.js.map +1 -1
  160. package/dist/harnesses/impls/_evaluator-base.d.ts +48 -0
  161. package/dist/harnesses/impls/_evaluator-base.js +35 -0
  162. package/dist/harnesses/impls/_evaluator-base.js.map +1 -0
  163. package/dist/harnesses/impls/claude-mcp-prediction/index.d.ts +2 -3
  164. package/dist/harnesses/impls/claude-mcp-prediction/index.js +29 -99
  165. package/dist/harnesses/impls/claude-mcp-prediction/index.js.map +1 -1
  166. package/dist/harnesses/impls/claude-mcp-prediction/mcp-tools.js +4 -21
  167. package/dist/harnesses/impls/claude-mcp-prediction/mcp-tools.js.map +1 -1
  168. package/dist/harnesses/impls/claude-mcp-prediction/session-orchestrator.d.ts +2 -0
  169. package/dist/harnesses/impls/claude-mcp-prediction/session-orchestrator.js +1 -1
  170. package/dist/harnesses/impls/claude-mcp-prediction/session-orchestrator.js.map +1 -1
  171. package/dist/harnesses/impls/claude-mcp-prediction-apy/index.js +30 -79
  172. package/dist/harnesses/impls/claude-mcp-prediction-apy/index.js.map +1 -1
  173. package/dist/harnesses/impls/claude-mcp-prediction-apy/mcp-tools.js +4 -19
  174. package/dist/harnesses/impls/claude-mcp-prediction-apy/mcp-tools.js.map +1 -1
  175. package/dist/harnesses/impls/claude-mcp-prediction-apy/session-orchestrator.d.ts +2 -0
  176. package/dist/harnesses/impls/claude-mcp-prediction-apy/session-orchestrator.js +1 -1
  177. package/dist/harnesses/impls/claude-mcp-prediction-apy/session-orchestrator.js.map +1 -1
  178. package/dist/harnesses/impls/claude-mcp-shared/mcp-server-script.d.ts +39 -0
  179. package/dist/harnesses/impls/claude-mcp-shared/mcp-server-script.js +62 -0
  180. package/dist/harnesses/impls/claude-mcp-shared/mcp-server-script.js.map +1 -0
  181. package/dist/harnesses/impls/claude-mcp-shared/mcp-server.d.ts +40 -0
  182. package/dist/harnesses/impls/claude-mcp-shared/mcp-server.js +44 -0
  183. package/dist/harnesses/impls/claude-mcp-shared/mcp-server.js.map +1 -0
  184. package/dist/harnesses/impls/claude-mcp-shared/single-session-harness.d.ts +56 -0
  185. package/dist/harnesses/impls/claude-mcp-shared/single-session-harness.js +79 -0
  186. package/dist/harnesses/impls/claude-mcp-shared/single-session-harness.js.map +1 -0
  187. package/dist/harnesses/impls/learner/adapters/codex-code.d.ts +3 -1
  188. package/dist/harnesses/impls/learner/adapters/codex-code.js +39 -4
  189. package/dist/harnesses/impls/learner/adapters/codex-code.js.map +1 -1
  190. package/dist/harnesses/impls/portfolio-v0-evaluator/index.d.ts +5 -15
  191. package/dist/harnesses/impls/portfolio-v0-evaluator/index.js +10 -29
  192. package/dist/harnesses/impls/portfolio-v0-evaluator/index.js.map +1 -1
  193. package/dist/harnesses/impls/prediction-apy-v0-evaluator/index.d.ts +5 -15
  194. package/dist/harnesses/impls/prediction-apy-v0-evaluator/index.js +10 -23
  195. package/dist/harnesses/impls/prediction-apy-v0-evaluator/index.js.map +1 -1
  196. package/dist/harnesses/impls/prediction-v0-evaluator/index.d.ts +5 -15
  197. package/dist/harnesses/impls/prediction-v0-evaluator/index.js +10 -25
  198. package/dist/harnesses/impls/prediction-v0-evaluator/index.js.map +1 -1
  199. package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.js +8 -0
  200. package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.js.map +1 -1
  201. package/dist/learner/verification-gate.d.ts +53 -0
  202. package/dist/learner/verification-gate.js +80 -0
  203. package/dist/learner/verification-gate.js.map +1 -0
  204. package/dist/main.js +74 -457
  205. package/dist/main.js.map +1 -1
  206. package/dist/monitoring/net-liveness.d.ts +143 -0
  207. package/dist/monitoring/net-liveness.js +301 -0
  208. package/dist/monitoring/net-liveness.js.map +1 -0
  209. package/dist/observability/progress.d.ts +11 -0
  210. package/dist/observability/progress.js +16 -0
  211. package/dist/observability/progress.js.map +1 -0
  212. package/dist/pilot/cost.d.ts +19 -0
  213. package/dist/pilot/cost.js +31 -0
  214. package/dist/pilot/cost.js.map +1 -0
  215. package/dist/pilot/instance.d.ts +20 -0
  216. package/dist/pilot/instance.js +19 -0
  217. package/dist/pilot/instance.js.map +1 -0
  218. package/dist/pilot/repo.d.ts +31 -0
  219. package/dist/pilot/repo.js +42 -0
  220. package/dist/pilot/repo.js.map +1 -0
  221. package/dist/pilot/solve.d.ts +22 -0
  222. package/dist/pilot/solve.js +37 -0
  223. package/dist/pilot/solve.js.map +1 -0
  224. package/dist/pilot/tally.d.ts +35 -0
  225. package/dist/pilot/tally.js +61 -0
  226. package/dist/pilot/tally.js.map +1 -0
  227. package/dist/plugins/local-trace-distiller/.claude-plugin/plugin.json +9 -0
  228. package/dist/plugins/local-trace-distiller/.codex-plugin/plugin.json +41 -0
  229. package/dist/plugins/local-trace-distiller/.mcp.json +8 -0
  230. package/dist/plugins/local-trace-distiller/README.md +27 -0
  231. package/dist/plugins/local-trace-distiller/jinn.plugin.json +36 -0
  232. package/dist/plugins/local-trace-distiller/skills/distill/SKILL.md +51 -0
  233. package/dist/preflight/rpc-network.d.ts +10 -3
  234. package/dist/preflight/rpc-network.js +46 -13
  235. package/dist/preflight/rpc-network.js.map +1 -1
  236. package/dist/preflight/stable-cwd.d.ts +29 -0
  237. package/dist/preflight/stable-cwd.js +98 -0
  238. package/dist/preflight/stable-cwd.js.map +1 -0
  239. package/dist/solvernets/launched-record-dispatcher.js +6 -0
  240. package/dist/solvernets/launched-record-dispatcher.js.map +1 -1
  241. package/dist/spend/credential.d.ts +2 -2
  242. package/dist/spend/credential.js +9 -3
  243. package/dist/spend/credential.js.map +1 -1
  244. package/dist/spend/spend-cap.d.ts +10 -0
  245. package/dist/spend/spend-cap.js +13 -0
  246. package/dist/spend/spend-cap.js.map +1 -0
  247. package/dist/store/store.d.ts +14 -0
  248. package/dist/store/store.js +10 -1
  249. package/dist/store/store.js.map +1 -1
  250. package/dist/trajectory/processors/identity-scrub.js +22 -3
  251. package/dist/trajectory/processors/identity-scrub.js.map +1 -1
  252. package/dist/trajectory/scrub/build.d.ts +19 -0
  253. package/dist/trajectory/scrub/build.js +25 -0
  254. package/dist/trajectory/scrub/build.js.map +1 -1
  255. package/dist/trajectory/scrub/layer2.d.ts +23 -0
  256. package/dist/trajectory/scrub/layer2.js +33 -0
  257. package/dist/trajectory/scrub/layer2.js.map +1 -0
  258. package/dist/trajectory/scrub/openredaction-stage.d.ts +127 -0
  259. package/dist/trajectory/scrub/openredaction-stage.js +186 -2
  260. package/dist/trajectory/scrub/openredaction-stage.js.map +1 -1
  261. package/dist/trajectory/scrub/pipeline.d.ts +21 -0
  262. package/dist/trajectory/scrub/pipeline.js +57 -0
  263. package/dist/trajectory/scrub/pipeline.js.map +1 -1
  264. package/dist/trajectory/scrub/plain-patterns-stage.d.ts +14 -1
  265. package/dist/trajectory/scrub/plain-patterns-stage.js +35 -5
  266. package/dist/trajectory/scrub/plain-patterns-stage.js.map +1 -1
  267. package/dist/trajectory/scrub/secretlint-stage.d.ts +22 -3
  268. package/dist/trajectory/scrub/secretlint-stage.js +118 -27
  269. package/dist/trajectory/scrub/secretlint-stage.js.map +1 -1
  270. package/dist/trajectory/transcript-watcher.d.ts +26 -0
  271. package/dist/trajectory/transcript-watcher.js +33 -0
  272. package/dist/trajectory/transcript-watcher.js.map +1 -1
  273. package/dist/types/skill-artifact.d.ts +442 -0
  274. package/dist/types/skill-artifact.js +137 -0
  275. package/dist/types/skill-artifact.js.map +1 -0
  276. package/dist/types/task-run-read-model.d.ts +21 -0
  277. package/dist/types/task-run-read-model.js +2 -0
  278. package/dist/types/task-run-read-model.js.map +1 -0
  279. package/dist/types/task-run.d.ts +75 -0
  280. package/dist/types/task-run.js +2 -0
  281. package/dist/types/task-run.js.map +1 -0
  282. package/dist/util/redact-rpc-urls.d.ts +1 -1
  283. package/dist/util/redact-rpc-urls.js +57 -2
  284. package/dist/util/redact-rpc-urls.js.map +1 -1
  285. package/package.json +11 -8
  286. package/plugins/local-trace-distiller/.claude-plugin/plugin.json +9 -0
  287. package/plugins/local-trace-distiller/.codex-plugin/plugin.json +41 -0
  288. package/plugins/local-trace-distiller/.mcp.json +8 -0
  289. package/plugins/local-trace-distiller/README.md +27 -0
  290. package/plugins/local-trace-distiller/jinn.plugin.json +36 -0
  291. package/plugins/local-trace-distiller/skills/distill/SKILL.md +51 -0
  292. package/dist/api/peers.js.map +0 -1
  293. package/dist/dashboard/assets/index-BsrAAmvC.js +0 -171
  294. package/dist/dashboard/assets/index-DQOWJ2SF.css +0 -1
  295. /package/dist/{api/peers.d.ts → daemon/peer-sync.d.ts} +0 -0
package/dist/main.js CHANGED
@@ -24,33 +24,35 @@ import { homedir, hostname, userInfo } from 'node:os';
24
24
  import { randomBytes as cryptoRandomBytes } from 'node:crypto';
25
25
  import { dirname, join } from 'node:path';
26
26
  import { fileURLToPath } from 'node:url';
27
- import { loadConfig, getConfigPathFromArgs, DEFAULT_CONFIG_PATH } from './config.js';
27
+ import { loadConfig, getConfigPathFromArgs, DEFAULT_CONFIG_PATH, DEFAULT_TESTNET_RPC_URLS } from './config.js';
28
28
  import { Store } from './store/store.js';
29
29
  import { startApiServer, isEmbeddedAgentEnabled } from './api/server.js';
30
- import { setDefaultTxSubmissionLedger } from './tx-retry.js';
30
+ import { setDefaultTxSubmissionLedger, withEoaBroadcastLock } from './tx-retry.js';
31
31
  // addHarnessReadinessRoutes is wired through startApiServer's holder ref now
32
32
  // (jinn-mono-u34i). No direct import needed.
33
33
  import { CapturePublishUnavailableError } from './api/captures.js';
34
34
  import { invalidatePredictionOperatorStatusCache } from './api/gather-status.js';
35
35
  import { ensureUiToken } from './api/ui-token.js';
36
36
  import { getFileLogger, closeFileLogger } from './observability/file-logger.js';
37
+ import { emitProgress } from './observability/progress.js';
37
38
  import { hashImplStateDir } from './harnesses/freeze.js';
38
39
  import { readModeState } from './harnesses/mode-state.js';
39
40
  import { attachAgentWs, updateAgentClaudePath } from './agent/agent-ws.js';
40
41
  import { createSetupModeController } from './setup-mode.js';
41
42
  import { requestDaemonRestart } from './restart-daemon.js';
42
- import { buildEnvelope, emitEnvelope } from './errors/envelope.js';
43
- import { clearBootstrapError, persistBootstrapError, } from './errors/persisted-bootstrap-error.js';
43
+ import { emitEnvelope } from './errors/envelope.js';
44
44
  import { emitStructured } from './events/emitter.js';
45
45
  import { applyPidfileLivenessGate } from './preflight/pidfile-liveness.js';
46
46
  import { applyDeploymentReadinessGate } from './preflight/deployment-readiness.js';
47
+ import { ensureStableCwd } from './preflight/stable-cwd.js';
47
48
  import { detectAuthContext } from './preflight/claude-auth.js';
48
49
  import { FleetBootstrapper, recoverEvictedService as recoverEvictedServiceFn } from './earning/bootstrap.js';
50
+ import { runFleetBootstrap, SetupBootstrapHalted } from './earning/bootstrap-run.js';
49
51
  import { applyChainGasOverrides, getChainConfig } from './earning/contracts.js';
50
- import { runLegacyAgentIdMigration } from './earning/migrate-agent-id.js';
52
+ import { getJinnRouterAddress } from './contracts/addresses.js';
51
53
  import { FleetStateStore } from './earning/store.js';
52
- import { isOperationalServiceStep, } from './earning/types.js';
53
- import { decryptMnemonic, deriveMasterSigner, walletPrivateKeyAtIndex } from './earning/wallet.js';
54
+ import { isOperationalServiceStep } from './earning/types.js';
55
+ import { decryptMnemonic, deriveMasterSigner } from './earning/wallet.js';
54
56
  import { MechAdapter } from './adapters/mech/adapter.js';
55
57
  import { ClaudeRunner } from './runner/claude.js';
56
58
  import { Daemon } from './daemon/daemon.js';
@@ -74,6 +76,7 @@ import { createCorpus } from './corpus/index.js';
74
76
  import { DEFAULT_EXECUTION_DISCOVERY_FROM_BLOCK } from './corpus/onchain-query.js';
75
77
  import { CapturesStore } from './store/captures.js';
76
78
  import { createLiveCapturePublisher } from './captures/live-publisher.js';
79
+ import { EnsurePendingCaptureProcessor, ensurePendingCapture, ingestStopHookCapture } from './captures/ingest.js';
77
80
  import { startReceiver } from './trajectory/receiver.js';
78
81
  import { startSyntheticSpanProvider, emitSyntheticSpan } from './trajectory/synthetic-span-builder.js';
79
82
  import { CredentialScrubProcessor } from './trajectory/processors/credential-scrub.js';
@@ -83,10 +86,6 @@ import { PathScrubProcessor } from './trajectory/processors/path-scrub.js';
83
86
  import { buildScrubPipeline } from './trajectory/scrub/build.js';
84
87
  import { maybeBuildPiiDetector } from './trajectory/scrub/pii-build.js';
85
88
  import { SqliteExporterProcessor } from './trajectory/processors/sqlite-exporter.js';
86
- import { ClaudeCodeJsonlParser } from './trajectory/transcript-parsers/claude-code-jsonl.js';
87
- import { CodexSessionParser } from './trajectory/transcript-parsers/codex-session.js';
88
- import { GeminiSessionParser } from './trajectory/transcript-parsers/gemini-session.js';
89
- import { CursorSqliteParser } from './trajectory/transcript-parsers/cursor-sqlite.js';
90
89
  import { startTranscriptWatcher, } from './trajectory/transcript-watcher.js';
91
90
  import { defaultTranscriptWatchDirectories } from './trajectory/transcript-session-dirs.js';
92
91
  import { buildInfo } from './build-info.js';
@@ -95,7 +94,6 @@ import { GeneratedTaskSource, StaticConfiguredTaskSource, filterBindableTasks }
95
94
  import { checkRpcNetwork, logRpcLocalDevToStderr, probeFallbackChain, rpcNetworkFailureHint, summarizeFallbackChain, } from './preflight/rpc-network.js';
96
95
  import { apiPortFailureMessage, checkApiPortAvailable } from './preflight/api-port.js';
97
96
  import { openBrowser } from './cli/open-browser.js';
98
- import { keepSetupUiOnBootstrapError } from './setup/halt-mode.js';
99
97
  if (process.env['JINN_LOAD_DEV_ENV'] === '1' || process.env['NODE_ENV'] === 'development') {
100
98
  dotenvConfig({ path: join(dirname(fileURLToPath(import.meta.url)), '..', '.env') });
101
99
  }
@@ -183,456 +181,40 @@ const CHAIN_CONFIG = applyChainGasOverrides(getChainConfig(NETWORK_CHAIN, {
183
181
  minSafeEthWei: config.minSafeEthWei,
184
182
  });
185
183
  const MARKETPLACE_ADDRESS = CHAIN_CONFIG.mechMarketplace;
186
- const ROUTER_ADDRESS = (CHAIN_CONFIG.jinnRouter ?? '0xfFa7118A3D820cd4E820010837D65FAfF463181B');
187
- class EnsurePendingCaptureProcessor {
188
- captures;
189
- constructor(captures) {
190
- this.captures = captures;
191
- }
192
- forceFlush() { return Promise.resolve(); }
193
- shutdown() { return Promise.resolve(); }
194
- onStart() { }
195
- onEnd(span) {
196
- const sessionId = stringAttribute(span.attributes['jinn.session.id']);
197
- if (!sessionId)
198
- return;
199
- ensurePendingCapture(this.captures, {
200
- sessionId,
201
- capturedAt: hrTimeToIso(span.startTime),
202
- tool: inferCaptureTool(span),
203
- capturePath: inferCapturePath(span),
204
- ...repoMetadataFromSpan(span),
205
- });
206
- }
207
- }
208
- function stringAttribute(value) {
209
- return typeof value === 'string' && value.trim() !== '' ? value : undefined;
210
- }
211
- function hrTimeToIso(time) {
212
- const millis = (time[0] * 1000) + Math.floor(time[1] / 1_000_000);
213
- return new Date(millis).toISOString();
214
- }
215
- function inferCaptureTool(span) {
216
- return stringAttribute(span.attributes['transcript.tool'])
217
- ?? stringAttribute(span.resource.attributes['service.name'])
218
- ?? 'otel';
219
- }
220
- function inferCapturePath(span) {
221
- if (stringAttribute(span.attributes['transcript.tool']))
222
- return 'B';
223
- return 'A';
224
- }
225
- function repoMetadataFromSpan(span) {
226
- const attrs = span.attributes;
227
- const repoRemoteUrl = stringAttribute(attrs['repo.remote_url'])
228
- ?? stringAttribute(attrs['vcs.repository.url'])
229
- ?? stringAttribute(attrs['git.remote_url']);
230
- const repoCommitHash = stringAttribute(attrs['repo.commit_hash'])
231
- ?? stringAttribute(attrs['vcs.ref.head.revision'])
232
- ?? stringAttribute(attrs['git.commit']);
233
- return {
234
- ...(repoRemoteUrl ? { repoRemoteUrl } : {}),
235
- ...(repoCommitHash ? { repoCommitHash } : {}),
236
- };
237
- }
238
- function parserForStopHookTool(tool) {
239
- switch (tool) {
240
- case 'claude-code':
241
- return new ClaudeCodeJsonlParser();
242
- case 'codex':
243
- return new CodexSessionParser();
244
- case 'gemini-cli':
245
- return new GeminiSessionParser();
246
- case 'cursor':
247
- return new CursorSqliteParser();
248
- default: {
249
- const exhaustive = tool;
250
- throw new Error(`No transcript parser for stop-hook tool: ${String(exhaustive)}`);
251
- }
252
- }
253
- }
254
- function ensurePendingCapture(captures, params) {
255
- if (captures.getBySession(params.sessionId))
256
- return;
257
- try {
258
- captures.savePending({
259
- sessionId: params.sessionId,
260
- capturedAt: params.capturedAt,
261
- originatingTool: { name: params.tool },
262
- capturePath: params.capturePath,
263
- status: 'pending',
264
- spanCount: 0,
265
- durationMs: 0,
266
- redactedSpanCount: 0,
267
- ...(params.repoRemoteUrl ? { repoRemoteUrl: params.repoRemoteUrl } : {}),
268
- ...(params.repoCommitHash ? { repoCommitHash: params.repoCommitHash } : {}),
269
- });
270
- }
271
- catch (err) {
272
- if (!captures.getBySession(params.sessionId))
273
- throw err;
274
- }
275
- }
276
- async function ingestStopHookCapture(captures, receiver, payload) {
277
- ensurePendingCapture(captures, {
278
- sessionId: payload.sessionId,
279
- capturedAt: payload.stoppedAt,
280
- tool: payload.tool,
281
- capturePath: 'D',
282
- });
283
- if (!payload.transcriptPath)
284
- return;
285
- if (!receiver) {
286
- console.warn('[main] stop-hook capture received but OTLP receiver is unavailable; pending capture has no transcript spans.');
287
- return;
288
- }
289
- const parser = parserForStopHookTool(payload.tool);
290
- let events;
291
- try {
292
- events = await parser.parseFull({ sessionId: payload.sessionId, path: payload.transcriptPath });
293
- }
294
- catch (err) {
295
- console.warn(`[main] stop-hook transcript import failed for ${payload.transcriptPath}: ` +
296
- `${err instanceof Error ? err.message : String(err)}`);
297
- return;
298
- }
299
- if (events.length === 0)
300
- return;
301
- const provider = startSyntheticSpanProvider({
302
- otlpHttpEndpoint: `http://127.0.0.1:${receiver.httpPort}/v1/traces`,
303
- });
184
+ const ROUTER_ADDRESS = (CHAIN_CONFIG.jinnRouter ?? getJinnRouterAddress(CHAIN_CONFIG.chainId));
185
+ /** #913: the bundled public RPC fallback chain for the current network. The
186
+ * setup/network endpoint persists `[primary, ...RPC_PUBLIC_DEFAULTS]` so the
187
+ * operator keeps the public backup chain when they set a primary, and the
188
+ * Settings → Network UI renders these as the trailing read-only slots. */
189
+ const RPC_PUBLIC_DEFAULTS = NETWORK_CHAIN === 'base-sepolia' ? DEFAULT_TESTNET_RPC_URLS : [CHAIN_CONFIG.rpcUrl];
190
+ /** #913: last L2 boot-probe result per RPC slot. Captured at boot, surfaced
191
+ * via /v1/bootstrap so Settings → Network can render per-slot health. Hosts
192
+ * are already masked by probeFallbackChain. The RPC chain is restart-required,
193
+ * so this never drifts without a re-probing restart. */
194
+ let lastL2Probe = [];
195
+ function configFileHasTopLevelKey(configPath, key) {
196
+ const filePath = configPath ?? join(process.env['HOME'] ?? '', '.jinn-client', 'config.json');
197
+ if (!filePath || !existsSync(filePath))
198
+ return false;
304
199
  try {
305
- for (const event of events) {
306
- emitSyntheticSpan(provider, { tool: parser.tool, sessionId: payload.sessionId, event });
307
- }
308
- await provider.flush();
200
+ const raw = JSON.parse(readFileSync(filePath, 'utf8'));
201
+ return !!raw && typeof raw === 'object' && Object.prototype.hasOwnProperty.call(raw, key);
309
202
  }
310
- finally {
311
- await provider.shutdown();
312
- }
313
- }
314
- const STANDARD_SERVICE_PROGRESSION = [
315
- 'awaiting_stake',
316
- 'staked',
317
- 'mech_deployed',
318
- 'agent_registered',
319
- 'safe_binding_pending',
320
- 'complete',
321
- ];
322
- const SELF_BOND_SERVICE_PROGRESSION = [
323
- 'awaiting_stake',
324
- 'service_created',
325
- 'service_activated',
326
- 'agents_registered',
327
- 'service_deployed',
328
- 'service_staked',
329
- 'mech_deployed',
330
- 'agent_registered',
331
- 'safe_binding_pending',
332
- 'complete',
333
- ];
334
- /** §6.2 `bootstrap_incomplete` — `{ currentStep, nextStep }` from persisted fleet state. */
335
- function bootstrapIncompleteSteps(state) {
336
- const progression = state.staking_mode === 'self-bond'
337
- ? SELF_BOND_SERVICE_PROGRESSION
338
- : STANDARD_SERVICE_PROGRESSION;
339
- const byIndex = [...state.services].sort((a, b) => a.index - b.index);
340
- const focus = byIndex.find(s => isOperationalServiceStep(s.step) && !s.safe_address) ??
341
- byIndex.find(s => !isOperationalServiceStep(s.step)) ??
342
- byIndex.find(s => s.step === 'safe_binding_pending') ??
343
- byIndex[0];
344
- if (!focus) {
345
- return { currentStep: 'awaiting_service', nextStep: 'awaiting_stake' };
346
- }
347
- if (isOperationalServiceStep(focus.step) && !focus.safe_address) {
348
- return { currentStep: 'complete', nextStep: 'bootstrap' };
349
- }
350
- const i = progression.indexOf(focus.step);
351
- if (i === -1) {
352
- return { currentStep: focus.step, nextStep: 'bootstrap' };
353
- }
354
- if (i < progression.length - 1) {
355
- return { currentStep: focus.step, nextStep: progression[i + 1] };
356
- }
357
- return { currentStep: focus.step, nextStep: 'bootstrap' };
358
- }
359
- // ── Bootstrap ───────────────────────────────────────────────────────────────
360
- async function bootstrap() {
361
- console.log('[main] Running fleet bootstrap...');
362
- // A fresh bootstrap attempt clears any stale error breadcrumb. If this run
363
- // hits the same failure, it'll be re-persisted below; if it succeeds (or
364
- // proceeds past the previously-failed step), the panel returns to a clean
365
- // state on the next /v1/bootstrap poll.
366
- clearBootstrapError(config.earningDir);
367
- // Persist the envelope to disk before emitEnvelope's process.exit fires,
368
- // so /v1/bootstrap can surface it to the panel after the daemon has
369
- // exited (operator restart → panel reload → error visible).
370
- function failBootstrap(input) {
371
- const envelope = buildEnvelope(input);
372
- persistBootstrapError(envelope, config.earningDir);
373
- if (keepSetupUiOnBootstrapError()) {
374
- emitStructured({
375
- kind: 'error',
376
- message: envelope.message,
377
- errorCode: envelope.code,
378
- details: {
379
- phase: 'bootstrap',
380
- exitCode: envelope.exitCode,
381
- ...(envelope.details ?? {}),
382
- },
383
- });
384
- console.error(`[main] Bootstrap halted (${envelope.code}); setup UI remains available. ` +
385
- `Resolve the issue and run jinn run again.`);
386
- throw new SetupBootstrapHalted(envelope);
387
- }
388
- emitEnvelope(input);
389
- // Unreachable in production (emitEnvelope exits); satisfies `never`.
390
- throw new Error('unreachable');
391
- }
392
- const bootstrapper = new FleetBootstrapper({
393
- earningDir: config.earningDir,
394
- chain: NETWORK_CHAIN,
395
- rpcUrl: config.rpcUrl,
396
- stakingMode: config.stakingMode,
397
- targetServices: config.targetServices,
398
- testnetL2DeploymentPath: config.testnetL2DeploymentPath,
399
- testnetL2TokenDeploymentPath: config.testnetL2TokenDeploymentPath,
400
- testnetMechDeploymentPath: config.testnetMechDeploymentPath,
401
- testnetStolasDeploymentPath: config.testnetStolasDeploymentPath,
402
- debug: config.debug,
403
- masterEthDailyEstimateWei: config.masterEthDailyEstimateWei,
404
- minEoaGasWei: config.minEoaGasWei,
405
- minSafeEthWei: config.minSafeEthWei,
406
- pollIntervalMs: config.pollIntervalMs,
407
- autoTestnetFaucet: process.env['JINN_AUTO_TESTNET_FAUCET'] === '1',
408
- });
409
- // Funding poll: stay up while waiting for the operator to fund the wallet.
410
- // The setup-mode API server is already running; the panel renders the
411
- // funding card and auto-advances when funds land. Daemon-side, we poll
412
- // bootstrap on a 15s tick and only escalate (exit) if a non-funding error
413
- // occurs or the configured timeout elapses. Each tick re-runs the full
414
- // bootstrap state machine — completed steps are no-ops, so this is safe.
415
- // Testnet faucet funding is panel-driven: bootstrap reports the funding gate,
416
- // and the operator clicks the funding action before this loop can advance.
417
- const FUNDING_POLL_INTERVAL_MS = 15_000;
418
- const fundingTimeoutMs = (() => {
419
- const raw = process.env['JINN_FUNDING_TIMEOUT_MS'];
420
- if (!raw)
421
- return Number.POSITIVE_INFINITY;
422
- const n = Number.parseInt(raw, 10);
423
- return Number.isFinite(n) && n > 0 ? n : Number.POSITIVE_INFINITY;
424
- })();
425
- emitProgress({
426
- type: 'progress',
427
- phase: 'bootstrap',
428
- step: 'advance_state_machine',
429
- estimatedWaitMs: 60_000,
430
- });
431
- const fundingStartedAt = Date.now();
432
- let result;
433
- let lastFundingMessage = '';
434
- const fundingGatePath = join(config.earningDir, 'bootstrap-funding.json');
435
- const persistFundingGate = (funding) => {
436
- mkdirSync(config.earningDir, { recursive: true, mode: 0o700 });
437
- writeFileSyncMain(fundingGatePath, `${JSON.stringify({
438
- schemaVersion: 1,
439
- generatedAt: new Date().toISOString(),
440
- ...funding,
441
- }, null, 2)}\n`, { mode: 0o600 });
442
- };
443
- const clearFundingGate = () => {
444
- try {
445
- unlinkSync(fundingGatePath);
446
- }
447
- catch {
448
- // best-effort: absent/stale funding gate should not affect bootstrap
449
- }
450
- };
451
- // eslint-disable-next-line no-constant-condition
452
- while (true) {
453
- result = await bootstrapper.ensureStage1And2(PASSWORD);
454
- if (!result.funding) {
455
- clearFundingGate();
456
- break;
457
- }
458
- persistFundingGate(result.funding);
459
- // Emit a structured event so the panel's Visibility region shows the gate.
460
- // Dedupe by message to avoid spamming the ring buffer on each poll.
461
- const fundingMsg = result.message ?? 'awaiting funding';
462
- if (fundingMsg !== lastFundingMessage) {
463
- emitStructured({
464
- kind: 'fleet',
465
- message: fundingMsg,
466
- details: {
467
- phase: 'awaiting_funding',
468
- role: 'master',
469
- address: result.funding.master_address,
470
- asset: 'native',
471
- needWei: result.funding.eth_required,
472
- haveWei: result.funding.eth_balance,
473
- },
474
- });
475
- // Mirror the structured event to NDJSON progress for --json-progress
476
- // consumers (CI, agents). Same dedup gate so we don't spam stdout.
477
- emitProgress({
478
- type: 'progress',
479
- phase: 'bootstrap',
480
- step: 'awaiting_funding',
481
- blocking: true,
482
- nextAction: 'Fund the address shown in addresses.fundingAddress, then wait for automatic retry.',
483
- addresses: { fundingAddress: result.funding.master_address },
484
- estimatedWaitMs: 1_800_000,
485
- });
486
- lastFundingMessage = fundingMsg;
487
- }
488
- const elapsed = Date.now() - fundingStartedAt;
489
- if (elapsed >= fundingTimeoutMs) {
490
- failBootstrap({
491
- code: 'funding_required',
492
- message: `${result.message} (timeout after ${Math.round(elapsed / 1000)}s)`,
493
- hint: 'Fund the listed address and re-run this command.',
494
- exampleCli: 'jinn fund-requirements --json',
495
- details: {
496
- // jinn-mono-hjex.6: structured envelope so SPA can render the
497
- // specific address + amount instead of a prose disjunction.
498
- category: 'insufficient_funds',
499
- step: 'awaiting_funding',
500
- address: result.funding.master_address,
501
- requiredWei: result.funding.eth_required,
502
- haveWei: result.funding.eth_balance,
503
- // Legacy aliases kept for any external consumers that read these.
504
- role: 'master',
505
- asset: 'native',
506
- needWei: result.funding.eth_required,
507
- },
508
- });
509
- }
510
- console.log(`[main] Awaiting funding... (${Math.round(elapsed / 1000)}s elapsed; ` +
511
- `will retry in ${FUNDING_POLL_INTERVAL_MS / 1000}s)`);
512
- await new Promise((r) => setTimeout(r, FUNDING_POLL_INTERVAL_MS));
513
- }
514
- if (!result.ok) {
515
- failBootstrap({
516
- code: 'fatal',
517
- message: result.message,
518
- hint: 'Bootstrap failed before the fleet reached a runnable state.',
519
- details: {
520
- cause: result.message,
521
- // jinn-mono-hjex.6: propagate structured category from the bootstrapper
522
- // so the SPA can render category-specific UI (e.g. funding shortfall).
523
- ...(result.errorCategory !== undefined ? { category: result.errorCategory } : {}),
524
- // Preserve the raw underlying error so a misclassified summary can
525
- // be diagnosed without re-running with JINN_DEBUG. See jinn-mono-jz9f.
526
- ...(result.rawErrorMessage && result.rawErrorMessage !== result.message
527
- ? { rawErrorMessage: result.rawErrorMessage }
528
- : {}),
529
- // jinn-mono-hjex reviewer fix: propagate tx hash so the SPA can render
530
- // a block-explorer link for failed on-chain revert transactions.
531
- ...(result.txHash != null ? { txHash: result.txHash } : {}),
532
- },
533
- });
534
- }
535
- // Legacy migration (jinn-mono-jgp): backfill `agent_id` on `complete`
536
- // services that pre-date j07. Idempotent + per-service failure-isolated;
537
- // a failure here does not abort daemon startup, but we surface counts so
538
- // operators notice. Disabled via `runLegacyMigrations: false` /
539
- // JINN_RUN_LEGACY_MIGRATIONS=0 — operators can run `jinn migrate-agent-id`
540
- // explicitly instead.
541
- let state = result.fleet_state;
542
- if (config.runLegacyMigrations) {
543
- try {
544
- const migration = await runLegacyAgentIdMigration({
545
- earningDir: config.earningDir,
546
- network: NETWORK_CHAIN,
547
- rpcUrl: config.rpcUrl,
548
- password: PASSWORD,
549
- testnetL2DeploymentPath: config.testnetL2DeploymentPath,
550
- testnetL2TokenDeploymentPath: config.testnetL2TokenDeploymentPath,
551
- testnetMechDeploymentPath: config.testnetMechDeploymentPath,
552
- testnetStolasDeploymentPath: config.testnetStolasDeploymentPath,
553
- });
554
- if (migration.migrated.length > 0 || migration.failed.length > 0) {
555
- console.log(`[main] Legacy agent_id migration: migrated=${migration.migrated.length} ` +
556
- `skipped=${migration.skipped.length} failed=${migration.failed.length}`);
557
- for (const f of migration.failed) {
558
- console.log(`[main] service ${f.service.index} (agent ${f.service.agent_address}): ${f.error}`);
559
- }
560
- // Reload state so downstream wiring (agent_id, identityRegistry)
561
- // sees the migrated rows.
562
- state = await new FleetStateStore(config.earningDir).load(NETWORK_CHAIN);
563
- }
564
- }
565
- catch (err) {
566
- const message = err instanceof Error ? err.message : String(err);
567
- console.error(`[main] Legacy agent_id migration failed (non-fatal): ${message}`);
568
- }
569
- }
570
- // Use the first operational service for the daemon. `safe_binding_pending`
571
- // means staking/mech are live; only the ERC-8004 Safe→agent metadata bind
572
- // is still retrying.
573
- const firstComplete = state.services.find(s => isOperationalServiceStep(s.step));
574
- if (!firstComplete || !firstComplete.safe_address) {
575
- failBootstrap({
576
- code: 'bootstrap_incomplete',
577
- message: 'Bootstrap completed but no service is ready.',
578
- hint: 'Re-run to continue the state machine toward a running fleet.',
579
- exampleCli: 'jinn bootstrap --json',
580
- details: bootstrapIncompleteSteps(state),
581
- });
582
- }
583
- // Derive agent private key from mnemonic
584
- const store = new FleetStateStore(config.earningDir);
585
- const mnemonic = await decryptMnemonic(await store.loadMnemonicKeystore(), PASSWORD);
586
- const agentPrivateKey = walletPrivateKeyAtIndex(mnemonic, firstComplete.index);
587
- console.log(`[main] Fleet bootstrap complete.`);
588
- console.log(` Master: ${state.master_address}`);
589
- console.log(` Services: ${state.services.filter(s => isOperationalServiceStep(s.step)).length}/${config.targetServices}`);
590
- console.log(` Active: service ${firstComplete.service_id} (agent ${firstComplete.agent_address})`);
591
- if (firstComplete.mech_address) {
592
- console.log(` Mech: ${firstComplete.mech_address}`);
593
- }
594
- return {
595
- masterAddress: state.master_address,
596
- serviceIndex: firstComplete.index,
597
- serviceId: firstComplete.service_id ?? null,
598
- stakingAddress: firstComplete.staking_address ? firstComplete.staking_address : null,
599
- agentPrivateKey: agentPrivateKey,
600
- safeAddress: firstComplete.safe_address,
601
- mechAddress: firstComplete.mech_address ? firstComplete.mech_address : undefined,
602
- agentId: firstComplete.agent_id ?? null,
603
- identityRegistryAddress: firstComplete.identity_registry_address
604
- ? firstComplete.identity_registry_address
605
- : null,
606
- };
607
- }
608
- class SetupBootstrapHalted extends Error {
609
- envelope;
610
- constructor(envelope) {
611
- super(envelope.message);
612
- this.envelope = envelope;
613
- this.name = 'SetupBootstrapHalted';
203
+ catch {
204
+ return false;
614
205
  }
615
206
  }
616
207
  // hjex.6: gate for the halt-and-resume loop. Lives in ./setup/halt-mode.ts
617
208
  // so it can be unit-tested without dragging main.ts's top-level side
618
209
  // effects (password resolution, config load) into the test.
619
210
  // ── Main ────────────────────────────────────────────────────────────────────
620
- /**
621
- * --json-progress: emit NDJSON progress envelopes on stdout during long
622
- * phases (init, bootstrap, daemon startup). The `jinn run --json-progress`
623
- * flag flips JINN_JSON_PROGRESS=1 in run.ts before calling main(); when
624
- * unset this is a no-op so tests / non-flag invocations stay silent on
625
- * stdout.
626
- */
627
- function progressEnabled() {
628
- return process.env['JINN_JSON_PROGRESS'] === '1';
629
- }
630
- function emitProgress(envelope) {
631
- if (progressEnabled()) {
632
- process.stdout.write(JSON.stringify(envelope) + '\n');
633
- }
634
- }
635
211
  export async function main() {
212
+ // Issue #909: chdir to a stable directory before spawning any child process.
213
+ // `jinn run` inherits the launch shell's CWD; when daemonised from an
214
+ // ephemeral dir that is later deleted, the CWD becomes a dangling inode and
215
+ // child processes (the `claude` CLI harness probe) crash on getcwd() ENOENT.
216
+ // Must run first — before getFileLogger() and any subprocess spawn.
217
+ ensureStableCwd({ earningDir: config.earningDir });
636
218
  console.log(`[main] jinn-client starting on ${NETWORK_CHAIN}`);
637
219
  // Issue #420: initialise the rotating daemon file logger early so lifecycle
638
220
  // events (tapped in `observability/emit-event.ts`) accumulate durable,
@@ -682,7 +264,7 @@ export async function main() {
682
264
  // Boot-time RPC fallback-chain probe (issue #592, AC7 + AC9). Log-only —
683
265
  // per-slot 429s/5xx never gate startup. checkRpcNetwork above already
684
266
  // fail-loud on chain-id mismatch against the head provider.
685
- await probeFallbackChain(config.rpcUrls, config.network, 'L2');
267
+ lastL2Probe = await probeFallbackChain(config.rpcUrls, config.network, 'L2');
686
268
  console.error(summarizeFallbackChain('L2', config.rpcUrls));
687
269
  const portPreflight = await checkApiPortAvailable(config.apiPort);
688
270
  if (!portPreflight.ok) {
@@ -950,6 +532,25 @@ export async function main() {
950
532
  configReader: () => ({
951
533
  rpcUrl: config.rpcUrl,
952
534
  defaultRpcUrl: CHAIN_CONFIG.rpcUrl,
535
+ rpcUrls: config.rpcUrls,
536
+ publicDefaults: RPC_PUBLIC_DEFAULTS,
537
+ rpcSlotHealth: lastL2Probe.map((p) => p.ok
538
+ ? {
539
+ ok: true,
540
+ host: p.host,
541
+ latencyMs: p.latencyMs,
542
+ expectedChainId: p.expectedChainId,
543
+ actualChainId: p.actualChainId,
544
+ ...(p.localDev ? { localDev: true } : {}),
545
+ }
546
+ : {
547
+ ok: false,
548
+ host: p.host,
549
+ code: p.code,
550
+ reason: p.reason,
551
+ expectedChainId: p.expectedChainId,
552
+ actualChainId: p.actualChainId,
553
+ }),
953
554
  joinedSolverNets: config.joinedSolverNets,
954
555
  onboardingComplete: config.onboardingComplete,
955
556
  }),
@@ -1076,6 +677,7 @@ export async function main() {
1076
677
  getClaudePath: () => activeClaudePath,
1077
678
  configPath: CONFIG_PATH ?? DEFAULT_CONFIG_PATH,
1078
679
  defaultRpcUrlForChain: () => CHAIN_CONFIG.rpcUrl,
680
+ defaultRpcUrlsForChain: () => RPC_PUBLIC_DEFAULTS,
1079
681
  onClaudePathSelected: selectClaudePath,
1080
682
  // Issue #421 retired the legacy `solverNets` write target. Setup
1081
683
  // endpoints no longer call back into the daemon to mutate operator
@@ -1225,6 +827,11 @@ export async function main() {
1225
827
  const rows = sharedStore.listPostedTasksByCreator(opts);
1226
828
  return rows.map((r) => ({
1227
829
  taskId: r.taskId,
830
+ // On-chain decimal id (#579) — the launcher status chip keys its
831
+ // indexer lookup on this, not the off-chain display taskId. Empty
832
+ // string (no on-chain id recorded) maps to undefined so the
833
+ // gatherer falls back to taskId.
834
+ ...(r.protocolTaskId ? { protocolTaskId: r.protocolTaskId } : {}),
1228
835
  taskCid: r.taskCid,
1229
836
  solverType: r.solverType ?? undefined,
1230
837
  postedAt: r.postedAt,
@@ -1232,6 +839,16 @@ export async function main() {
1232
839
  ...(r.claims ? { claims: r.claims } : {}),
1233
840
  }));
1234
841
  },
842
+ // #579: on-chain finalized/open/expired status for the Recent posted
843
+ // Tasks chip. discoveryApiHolder is populated post-bootstrap; before
844
+ // that (or with no discovery API) we return an empty Map → all chips
845
+ // render 'unknown', the safe degraded default.
846
+ fetchTaskStatuses: async (manifestCid) => {
847
+ const api = discoveryApiHolder.current;
848
+ if (!api)
849
+ return new Map();
850
+ return api.getTaskStatuses({ manifestCid });
851
+ },
1235
852
  },
1236
853
  },
1237
854
  });
@@ -1355,7 +972,7 @@ export async function main() {
1355
972
  // eslint-disable-next-line no-constant-condition
1356
973
  while (true) {
1357
974
  try {
1358
- bootstrapResult = await bootstrap();
975
+ bootstrapResult = await runFleetBootstrap({ config, password: PASSWORD, network: NETWORK_CHAIN, emitProgress });
1359
976
  break; // success — exit the retry loop
1360
977
  }
1361
978
  catch (err) {
@@ -2261,7 +1878,7 @@ export async function main() {
2261
1878
  store: earningStore,
2262
1879
  chain: NETWORK_CHAIN,
2263
1880
  writeCheckpoint: async ({ stakingProxy }) => {
2264
- const txHash = await masterWallet.writeContract({
1881
+ const txHash = await withEoaBroadcastLock(masterAccount.address, () => masterWallet.writeContract({
2265
1882
  address: stakingProxy,
2266
1883
  abi: [
2267
1884
  {
@@ -2275,7 +1892,7 @@ export async function main() {
2275
1892
  functionName: 'checkpoint',
2276
1893
  account: masterAccount,
2277
1894
  chain: null,
2278
- });
1895
+ }));
2279
1896
  return { txHash };
2280
1897
  },
2281
1898
  }