opencandle 0.11.0 → 0.12.0

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 (663) hide show
  1. package/README.md +25 -28
  2. package/dist/analysts/contracts.js +0 -1
  3. package/dist/analysts/orchestrator.d.ts +2 -0
  4. package/dist/analysts/orchestrator.js +19 -1
  5. package/dist/cli-main.js +13 -7
  6. package/dist/cli-options.d.ts +4 -0
  7. package/dist/cli-options.js +33 -0
  8. package/dist/cli.js +8 -3
  9. package/dist/config.js +0 -1
  10. package/dist/doctor/cli-command.js +2 -1
  11. package/dist/doctor/render.js +0 -1
  12. package/dist/doctor/report.js +41 -12
  13. package/dist/gui/server/ask-user-bridge.d.ts +19 -0
  14. package/dist/gui/server/ask-user-bridge.js +55 -0
  15. package/dist/gui/server/automation-heartbeat.d.ts +25 -0
  16. package/dist/gui/server/automation-heartbeat.js +61 -0
  17. package/dist/gui/server/background-quotes.d.ts +39 -0
  18. package/dist/gui/server/background-quotes.js +71 -0
  19. package/dist/gui/server/chat-event-adapter.d.ts +16 -0
  20. package/dist/gui/server/chat-event-adapter.js +263 -0
  21. package/dist/gui/server/gui-session-manager.d.ts +2 -0
  22. package/dist/gui/server/gui-session-manager.js +4 -0
  23. package/dist/gui/server/http-routes.d.ts +69 -0
  24. package/dist/gui/server/http-routes.js +1008 -0
  25. package/dist/gui/server/invoke-tool.d.ts +47 -0
  26. package/dist/gui/server/invoke-tool.js +373 -0
  27. package/dist/gui/server/live-chat-event-adapter.d.ts +19 -0
  28. package/dist/gui/server/live-chat-event-adapter.js +206 -0
  29. package/dist/gui/server/local-session-coordinator.d.ts +26 -0
  30. package/dist/gui/server/local-session-coordinator.js +53 -0
  31. package/dist/gui/server/market-state-api.d.ts +138 -0
  32. package/dist/gui/server/market-state-api.js +355 -0
  33. package/dist/gui/server/model-setup.d.ts +64 -0
  34. package/dist/gui/server/model-setup.js +150 -0
  35. package/dist/gui/server/private-api-access.d.ts +6 -0
  36. package/dist/gui/server/private-api-access.js +53 -0
  37. package/dist/gui/server/projector.d.ts +49 -0
  38. package/dist/gui/server/projector.js +296 -0
  39. package/dist/gui/server/prompt-observation.d.ts +8 -0
  40. package/dist/gui/server/prompt-observation.js +43 -0
  41. package/dist/gui/server/quote-snapshot-store.d.ts +14 -0
  42. package/dist/gui/server/quote-snapshot-store.js +49 -0
  43. package/dist/gui/server/server.d.ts +1 -0
  44. package/dist/gui/server/server.js +259 -0
  45. package/dist/gui/server/session-actions.d.ts +60 -0
  46. package/dist/gui/server/session-actions.js +218 -0
  47. package/dist/gui/server/session-entry-wait.d.ts +27 -0
  48. package/dist/gui/server/session-entry-wait.js +129 -0
  49. package/dist/gui/server/session-list.d.ts +2 -0
  50. package/dist/gui/server/session-list.js +11 -0
  51. package/dist/gui/server/shutdown.d.ts +10 -0
  52. package/dist/gui/server/shutdown.js +29 -0
  53. package/dist/gui/server/tool-invoke-ack.d.ts +17 -0
  54. package/dist/gui/server/tool-invoke-ack.js +26 -0
  55. package/dist/gui/server/tool-metadata.d.ts +93 -0
  56. package/dist/gui/server/tool-metadata.js +148 -0
  57. package/dist/gui/server/websocket.d.ts +9 -0
  58. package/dist/gui/server/websocket.js +124 -0
  59. package/dist/gui/server/writer-lock.d.ts +1 -0
  60. package/dist/gui/server/writer-lock.js +1 -0
  61. package/dist/gui/server/ws-hub.d.ts +50 -0
  62. package/dist/gui/server/ws-hub.js +284 -0
  63. package/dist/gui/shared/chat-events.d.ts +174 -0
  64. package/dist/gui/shared/chat-events.js +13 -0
  65. package/dist/gui/shared/event-reducer.d.ts +3 -0
  66. package/dist/gui/shared/event-reducer.js +187 -0
  67. package/dist/index.js +0 -1
  68. package/dist/infra/cache.d.ts +6 -0
  69. package/dist/infra/cache.js +16 -7
  70. package/dist/infra/freshness.d.ts +21 -0
  71. package/dist/infra/freshness.js +118 -0
  72. package/dist/infra/http-client.js +1 -2
  73. package/dist/infra/index.js +0 -1
  74. package/dist/infra/market-calendar.d.ts +14 -0
  75. package/dist/infra/market-calendar.js +136 -0
  76. package/dist/infra/native-dependencies.js +0 -1
  77. package/dist/infra/node-version.js +0 -1
  78. package/dist/infra/open-url.js +0 -1
  79. package/dist/infra/opencandle-paths.js +0 -1
  80. package/dist/infra/rate-limiter.js +1 -1
  81. package/dist/market-state/alert-conditions.js +0 -1
  82. package/dist/market-state/alert-runner.d.ts +1 -0
  83. package/dist/market-state/alert-runner.js +5 -4
  84. package/dist/market-state/daily-report.js +0 -1
  85. package/dist/market-state/local-automation-service.js +0 -1
  86. package/dist/market-state/notification-delivery.js +0 -1
  87. package/dist/market-state/resolve-for-mutation.js +0 -1
  88. package/dist/market-state/resolve.js +0 -1
  89. package/dist/market-state/service.d.ts +26 -21
  90. package/dist/market-state/service.js +175 -36
  91. package/dist/market-state/summaries.d.ts +8 -0
  92. package/dist/market-state/summaries.js +58 -0
  93. package/dist/memory/index.js +0 -1
  94. package/dist/memory/manager.js +0 -1
  95. package/dist/memory/preference-extractor.js +0 -1
  96. package/dist/memory/retrieval.js +0 -1
  97. package/dist/memory/sqlite.js +60 -11
  98. package/dist/memory/storage.js +0 -1
  99. package/dist/memory/tool-defaults.js +0 -1
  100. package/dist/memory/types.js +0 -1
  101. package/dist/monitor.js +0 -1
  102. package/dist/onboarding/connect.js +0 -1
  103. package/dist/onboarding/credential-interceptor.js +0 -1
  104. package/dist/onboarding/degradation-accumulator.js +0 -1
  105. package/dist/onboarding/prompt-user.d.ts +1 -1
  106. package/dist/onboarding/prompt-user.js +0 -1
  107. package/dist/onboarding/provider-status.js +0 -1
  108. package/dist/onboarding/providers.d.ts +25 -1
  109. package/dist/onboarding/providers.js +26 -1
  110. package/dist/onboarding/state.js +0 -1
  111. package/dist/onboarding/tool-helpers.js +0 -1
  112. package/dist/onboarding/tool-tags.js +0 -1
  113. package/dist/onboarding/validate-model-key.d.ts +17 -0
  114. package/dist/onboarding/validate-model-key.js +54 -0
  115. package/dist/onboarding/validation.js +0 -1
  116. package/dist/pi/opencandle-extension.js +46 -7
  117. package/dist/pi/session-action-dedupe.js +0 -1
  118. package/dist/pi/session-storage.js +0 -1
  119. package/dist/pi/session-writer-lock.js +0 -1
  120. package/dist/pi/session.js +0 -1
  121. package/dist/pi/setup.d.ts +2 -0
  122. package/dist/pi/setup.js +10 -3
  123. package/dist/pi/tool-adapter.js +0 -1
  124. package/dist/pi/tui-session-coordinator.js +0 -1
  125. package/dist/prompts/context-builder.js +1 -2
  126. package/dist/prompts/disclaimer.js +0 -1
  127. package/dist/prompts/policy-cards.js +0 -1
  128. package/dist/prompts/sections.js +0 -1
  129. package/dist/prompts/symbol-preflight.js +0 -1
  130. package/dist/prompts/workflow-prompts.js +0 -1
  131. package/dist/providers/alpha-vantage.js +8 -3
  132. package/dist/providers/coingecko.js +1 -1
  133. package/dist/providers/errors.js +0 -1
  134. package/dist/providers/exa-search.js +11 -10
  135. package/dist/providers/external-tool-command.js +0 -1
  136. package/dist/providers/external-tool-error.js +0 -1
  137. package/dist/providers/fear-greed.js +0 -1
  138. package/dist/providers/finnhub.js +0 -1
  139. package/dist/providers/fred.js +0 -1
  140. package/dist/providers/index.js +0 -1
  141. package/dist/providers/polymarket.d.ts +2 -0
  142. package/dist/providers/polymarket.js +162 -0
  143. package/dist/providers/provider-credential-error.js +0 -1
  144. package/dist/providers/reddit-cli.js +0 -1
  145. package/dist/providers/reddit.js +0 -1
  146. package/dist/providers/sec-edgar.js +0 -1
  147. package/dist/providers/tradingview.js +1 -2
  148. package/dist/providers/twitter-cli.js +0 -1
  149. package/dist/providers/twitter.js +0 -1
  150. package/dist/providers/web-search.js +15 -12
  151. package/dist/providers/with-fallback.js +0 -1
  152. package/dist/providers/wrap-provider.js +5 -4
  153. package/dist/providers/yahoo-finance.d.ts +3 -0
  154. package/dist/providers/yahoo-finance.js +272 -13
  155. package/dist/routing/classify-intent.js +0 -1
  156. package/dist/routing/defaults.js +0 -1
  157. package/dist/routing/entity-extractor.js +18 -3
  158. package/dist/routing/fund-symbols.js +0 -1
  159. package/dist/routing/horizon.js +0 -1
  160. package/dist/routing/index.js +0 -1
  161. package/dist/routing/legacy-rule-router.js +0 -1
  162. package/dist/routing/planning.js +0 -1
  163. package/dist/routing/route-manifest.js +1 -2
  164. package/dist/routing/router-llm-client.d.ts +2 -2
  165. package/dist/routing/router-llm-client.js +18 -4
  166. package/dist/routing/router-prompt.js +0 -1
  167. package/dist/routing/router-types.js +0 -1
  168. package/dist/routing/router.d.ts +1 -1
  169. package/dist/routing/router.js +274 -6
  170. package/dist/routing/slot-resolver.d.ts +1 -0
  171. package/dist/routing/slot-resolver.js +1 -2
  172. package/dist/routing/symbol-disambiguator.js +0 -1
  173. package/dist/routing/turn-context.js +0 -1
  174. package/dist/routing/types.js +0 -1
  175. package/dist/runtime/answer-contracts.js +0 -1
  176. package/dist/runtime/artifact-contracts.js +0 -1
  177. package/dist/runtime/evidence.d.ts +1 -0
  178. package/dist/runtime/evidence.js +0 -1
  179. package/dist/runtime/numeric-claims.d.ts +23 -0
  180. package/dist/runtime/numeric-claims.js +99 -0
  181. package/dist/runtime/planning-evidence.js +1 -77
  182. package/dist/runtime/prompt-step.d.ts +10 -1
  183. package/dist/runtime/prompt-step.js +116 -3
  184. package/dist/runtime/provider-tracker.js +0 -1
  185. package/dist/runtime/run-context.js +0 -1
  186. package/dist/runtime/session-coordinator.d.ts +11 -1
  187. package/dist/runtime/session-coordinator.js +352 -50
  188. package/dist/runtime/session-title.js +0 -1
  189. package/dist/runtime/tool-defaults-wrapper.js +0 -1
  190. package/dist/runtime/validation.d.ts +3 -1
  191. package/dist/runtime/validation.js +0 -1
  192. package/dist/runtime/workflow-events.d.ts +1 -1
  193. package/dist/runtime/workflow-events.js +0 -1
  194. package/dist/runtime/workflow-runner.d.ts +1 -0
  195. package/dist/runtime/workflow-runner.js +8 -3
  196. package/dist/runtime/workflow-types.d.ts +3 -0
  197. package/dist/runtime/workflow-types.js +0 -1
  198. package/dist/sentiment/adapters/finnhub.js +0 -1
  199. package/dist/sentiment/adapters/reddit.js +0 -1
  200. package/dist/sentiment/adapters/twitter.js +0 -1
  201. package/dist/sentiment/adapters/web.js +0 -1
  202. package/dist/sentiment/index.js +4 -2
  203. package/dist/sentiment/insights.js +0 -1
  204. package/dist/sentiment/keywords.js +0 -1
  205. package/dist/sentiment/pipeline.js +0 -1
  206. package/dist/sentiment/scorer.js +0 -1
  207. package/dist/sentiment/store.js +0 -1
  208. package/dist/sentiment/trends.js +0 -1
  209. package/dist/sentiment/types.js +0 -1
  210. package/dist/system-prompt.js +0 -1
  211. package/dist/tool-kit.js +0 -1
  212. package/dist/tools/fundamentals/company-overview.d.ts +1 -1
  213. package/dist/tools/fundamentals/company-overview.js +2 -1
  214. package/dist/tools/fundamentals/comps.d.ts +1 -0
  215. package/dist/tools/fundamentals/comps.js +25 -12
  216. package/dist/tools/fundamentals/dcf.js +183 -113
  217. package/dist/tools/fundamentals/earnings.d.ts +1 -1
  218. package/dist/tools/fundamentals/earnings.js +2 -1
  219. package/dist/tools/fundamentals/financials.js +2 -1
  220. package/dist/tools/fundamentals/sec-filings.js +0 -1
  221. package/dist/tools/index.d.ts +117 -2
  222. package/dist/tools/index.js +3 -1
  223. package/dist/tools/interaction/ask-user.js +11 -1
  224. package/dist/tools/macro/event-probabilities.d.ts +8 -0
  225. package/dist/tools/macro/event-probabilities.js +120 -0
  226. package/dist/tools/macro/fear-greed.d.ts +1 -1
  227. package/dist/tools/macro/fear-greed.js +0 -1
  228. package/dist/tools/macro/fred-data.d.ts +1 -1
  229. package/dist/tools/macro/fred-data.js +2 -1
  230. package/dist/tools/market/crypto-history.js +0 -1
  231. package/dist/tools/market/crypto-price.d.ts +4 -1
  232. package/dist/tools/market/crypto-price.js +10 -2
  233. package/dist/tools/market/screen-stocks.js +14 -4
  234. package/dist/tools/market/search-ticker.js +1 -2
  235. package/dist/tools/market/stock-history.js +0 -1
  236. package/dist/tools/market/stock-quote.d.ts +4 -1
  237. package/dist/tools/market/stock-quote.js +9 -5
  238. package/dist/tools/options/greeks.js +0 -1
  239. package/dist/tools/options/option-chain.d.ts +4 -1
  240. package/dist/tools/options/option-chain.js +12 -2
  241. package/dist/tools/portfolio/alerts.d.ts +2 -1
  242. package/dist/tools/portfolio/alerts.js +228 -2
  243. package/dist/tools/portfolio/correlation.js +23 -5
  244. package/dist/tools/portfolio/daily-report.js +0 -1
  245. package/dist/tools/portfolio/holdings-overlap.js +0 -1
  246. package/dist/tools/portfolio/notifications.js +0 -1
  247. package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
  248. package/dist/tools/portfolio/risk-analysis.js +1 -2
  249. package/dist/tools/portfolio/tracker.d.ts +3 -1
  250. package/dist/tools/portfolio/tracker.js +57 -10
  251. package/dist/tools/portfolio/watchlist.d.ts +3 -6
  252. package/dist/tools/portfolio/watchlist.js +101 -110
  253. package/dist/tools/sentiment/insight-format.js +0 -1
  254. package/dist/tools/sentiment/query-match.js +0 -1
  255. package/dist/tools/sentiment/reddit-sentiment.js +0 -1
  256. package/dist/tools/sentiment/sentiment-summary.js +8 -4
  257. package/dist/tools/sentiment/sentiment-trend.d.ts +5 -1
  258. package/dist/tools/sentiment/sentiment-trend.js +6 -2
  259. package/dist/tools/sentiment/twitter-sentiment.js +0 -1
  260. package/dist/tools/sentiment/untrusted-text.js +0 -1
  261. package/dist/tools/sentiment/web-search.d.ts +1 -1
  262. package/dist/tools/sentiment/web-search.js +0 -1
  263. package/dist/tools/sentiment/web-sentiment.js +0 -1
  264. package/dist/tools/technical/backtest.js +0 -1
  265. package/dist/tools/technical/indicators.js +1 -2
  266. package/dist/types/fundamentals.d.ts +1 -0
  267. package/dist/types/fundamentals.js +0 -1
  268. package/dist/types/index.d.ts +1 -0
  269. package/dist/types/index.js +0 -1
  270. package/dist/types/macro.js +0 -1
  271. package/dist/types/market.d.ts +8 -0
  272. package/dist/types/market.js +0 -1
  273. package/dist/types/options.d.ts +1 -0
  274. package/dist/types/options.js +0 -1
  275. package/dist/types/portfolio.js +0 -1
  276. package/dist/types/prediction-markets.d.ts +13 -0
  277. package/dist/types/prediction-markets.js +1 -0
  278. package/dist/types/sentiment.js +0 -1
  279. package/dist/workflows/compare-assets.js +0 -1
  280. package/dist/workflows/index.js +0 -1
  281. package/dist/workflows/options-screener.js +0 -1
  282. package/dist/workflows/portfolio-builder.js +0 -1
  283. package/gui/web/dist/assets/CatalogOverlay-CAc7e3Pf.js +1 -0
  284. package/gui/web/dist/assets/index-BOEKd9wT.css +2 -0
  285. package/gui/web/dist/assets/index-C-H05cQ2.js +65 -0
  286. package/gui/web/dist/index.html +2 -2
  287. package/package.json +16 -22
  288. package/dist/analysts/contracts.js.map +0 -1
  289. package/dist/analysts/orchestrator.js.map +0 -1
  290. package/dist/cli-main.js.map +0 -1
  291. package/dist/cli.js.map +0 -1
  292. package/dist/config.js.map +0 -1
  293. package/dist/doctor/cli-command.js.map +0 -1
  294. package/dist/doctor/render.js.map +0 -1
  295. package/dist/doctor/report.js.map +0 -1
  296. package/dist/index.js.map +0 -1
  297. package/dist/infra/cache.js.map +0 -1
  298. package/dist/infra/http-client.js.map +0 -1
  299. package/dist/infra/index.js.map +0 -1
  300. package/dist/infra/native-dependencies.js.map +0 -1
  301. package/dist/infra/node-version.js.map +0 -1
  302. package/dist/infra/open-url.js.map +0 -1
  303. package/dist/infra/opencandle-paths.js.map +0 -1
  304. package/dist/infra/rate-limiter.js.map +0 -1
  305. package/dist/market-state/alert-conditions.js.map +0 -1
  306. package/dist/market-state/alert-runner.js.map +0 -1
  307. package/dist/market-state/daily-report.js.map +0 -1
  308. package/dist/market-state/local-automation-service.js.map +0 -1
  309. package/dist/market-state/notification-delivery.js.map +0 -1
  310. package/dist/market-state/resolve-for-mutation.js.map +0 -1
  311. package/dist/market-state/resolve.js.map +0 -1
  312. package/dist/market-state/service.js.map +0 -1
  313. package/dist/memory/index.js.map +0 -1
  314. package/dist/memory/manager.js.map +0 -1
  315. package/dist/memory/preference-extractor.js.map +0 -1
  316. package/dist/memory/retrieval.js.map +0 -1
  317. package/dist/memory/sqlite.js.map +0 -1
  318. package/dist/memory/storage.js.map +0 -1
  319. package/dist/memory/tool-defaults.js.map +0 -1
  320. package/dist/memory/types.js.map +0 -1
  321. package/dist/monitor.js.map +0 -1
  322. package/dist/onboarding/connect.js.map +0 -1
  323. package/dist/onboarding/credential-interceptor.js.map +0 -1
  324. package/dist/onboarding/degradation-accumulator.js.map +0 -1
  325. package/dist/onboarding/prompt-user.js.map +0 -1
  326. package/dist/onboarding/provider-status.js.map +0 -1
  327. package/dist/onboarding/providers.js.map +0 -1
  328. package/dist/onboarding/state.js.map +0 -1
  329. package/dist/onboarding/tool-helpers.js.map +0 -1
  330. package/dist/onboarding/tool-tags.js.map +0 -1
  331. package/dist/onboarding/validation.js.map +0 -1
  332. package/dist/pi/opencandle-extension.js.map +0 -1
  333. package/dist/pi/session-action-dedupe.js.map +0 -1
  334. package/dist/pi/session-storage.js.map +0 -1
  335. package/dist/pi/session-writer-lock.js.map +0 -1
  336. package/dist/pi/session.js.map +0 -1
  337. package/dist/pi/setup.js.map +0 -1
  338. package/dist/pi/tool-adapter.js.map +0 -1
  339. package/dist/pi/tui-session-coordinator.js.map +0 -1
  340. package/dist/prompts/context-builder.js.map +0 -1
  341. package/dist/prompts/disclaimer.js.map +0 -1
  342. package/dist/prompts/policy-cards.js.map +0 -1
  343. package/dist/prompts/sections.js.map +0 -1
  344. package/dist/prompts/symbol-preflight.js.map +0 -1
  345. package/dist/prompts/workflow-prompts.js.map +0 -1
  346. package/dist/providers/alpha-vantage.js.map +0 -1
  347. package/dist/providers/coingecko.js.map +0 -1
  348. package/dist/providers/errors.js.map +0 -1
  349. package/dist/providers/exa-search.js.map +0 -1
  350. package/dist/providers/external-tool-command.js.map +0 -1
  351. package/dist/providers/external-tool-error.js.map +0 -1
  352. package/dist/providers/fear-greed.js.map +0 -1
  353. package/dist/providers/finnhub.js.map +0 -1
  354. package/dist/providers/fred.js.map +0 -1
  355. package/dist/providers/index.js.map +0 -1
  356. package/dist/providers/provider-credential-error.js.map +0 -1
  357. package/dist/providers/reddit-cli.js.map +0 -1
  358. package/dist/providers/reddit.js.map +0 -1
  359. package/dist/providers/sec-edgar.js.map +0 -1
  360. package/dist/providers/tradingview.js.map +0 -1
  361. package/dist/providers/twitter-cli.js.map +0 -1
  362. package/dist/providers/twitter.js.map +0 -1
  363. package/dist/providers/web-search.js.map +0 -1
  364. package/dist/providers/with-fallback.js.map +0 -1
  365. package/dist/providers/wrap-provider.js.map +0 -1
  366. package/dist/providers/yahoo-finance.js.map +0 -1
  367. package/dist/routing/classify-intent.js.map +0 -1
  368. package/dist/routing/defaults.js.map +0 -1
  369. package/dist/routing/entity-extractor.js.map +0 -1
  370. package/dist/routing/fund-symbols.js.map +0 -1
  371. package/dist/routing/horizon.js.map +0 -1
  372. package/dist/routing/index.js.map +0 -1
  373. package/dist/routing/legacy-rule-router.js.map +0 -1
  374. package/dist/routing/planning.js.map +0 -1
  375. package/dist/routing/route-manifest.js.map +0 -1
  376. package/dist/routing/router-llm-client.js.map +0 -1
  377. package/dist/routing/router-prompt.js.map +0 -1
  378. package/dist/routing/router-types.js.map +0 -1
  379. package/dist/routing/router.js.map +0 -1
  380. package/dist/routing/slot-resolver.js.map +0 -1
  381. package/dist/routing/symbol-disambiguator.js.map +0 -1
  382. package/dist/routing/turn-context.js.map +0 -1
  383. package/dist/routing/types.js.map +0 -1
  384. package/dist/runtime/answer-contracts.js.map +0 -1
  385. package/dist/runtime/artifact-contracts.js.map +0 -1
  386. package/dist/runtime/evidence.js.map +0 -1
  387. package/dist/runtime/planning-evidence.js.map +0 -1
  388. package/dist/runtime/prompt-step.js.map +0 -1
  389. package/dist/runtime/provider-tracker.js.map +0 -1
  390. package/dist/runtime/run-context.js.map +0 -1
  391. package/dist/runtime/session-coordinator.js.map +0 -1
  392. package/dist/runtime/session-title.js.map +0 -1
  393. package/dist/runtime/tool-defaults-wrapper.js.map +0 -1
  394. package/dist/runtime/validation.js.map +0 -1
  395. package/dist/runtime/workflow-events.js.map +0 -1
  396. package/dist/runtime/workflow-runner.js.map +0 -1
  397. package/dist/runtime/workflow-types.js.map +0 -1
  398. package/dist/sentiment/adapters/finnhub.js.map +0 -1
  399. package/dist/sentiment/adapters/reddit.js.map +0 -1
  400. package/dist/sentiment/adapters/twitter.js.map +0 -1
  401. package/dist/sentiment/adapters/web.js.map +0 -1
  402. package/dist/sentiment/index.js.map +0 -1
  403. package/dist/sentiment/insights.js.map +0 -1
  404. package/dist/sentiment/keywords.js.map +0 -1
  405. package/dist/sentiment/pipeline.js.map +0 -1
  406. package/dist/sentiment/scorer.js.map +0 -1
  407. package/dist/sentiment/store.js.map +0 -1
  408. package/dist/sentiment/trends.js.map +0 -1
  409. package/dist/sentiment/types.js.map +0 -1
  410. package/dist/system-prompt.js.map +0 -1
  411. package/dist/tool-kit.js.map +0 -1
  412. package/dist/tools/fundamentals/company-overview.js.map +0 -1
  413. package/dist/tools/fundamentals/comps.js.map +0 -1
  414. package/dist/tools/fundamentals/dcf.js.map +0 -1
  415. package/dist/tools/fundamentals/earnings.js.map +0 -1
  416. package/dist/tools/fundamentals/financials.js.map +0 -1
  417. package/dist/tools/fundamentals/sec-filings.js.map +0 -1
  418. package/dist/tools/index.js.map +0 -1
  419. package/dist/tools/interaction/ask-user.js.map +0 -1
  420. package/dist/tools/macro/fear-greed.js.map +0 -1
  421. package/dist/tools/macro/fred-data.js.map +0 -1
  422. package/dist/tools/market/crypto-history.js.map +0 -1
  423. package/dist/tools/market/crypto-price.js.map +0 -1
  424. package/dist/tools/market/screen-stocks.js.map +0 -1
  425. package/dist/tools/market/search-ticker.js.map +0 -1
  426. package/dist/tools/market/stock-history.js.map +0 -1
  427. package/dist/tools/market/stock-quote.js.map +0 -1
  428. package/dist/tools/options/greeks.js.map +0 -1
  429. package/dist/tools/options/option-chain.js.map +0 -1
  430. package/dist/tools/portfolio/alerts.js.map +0 -1
  431. package/dist/tools/portfolio/correlation.js.map +0 -1
  432. package/dist/tools/portfolio/daily-report.js.map +0 -1
  433. package/dist/tools/portfolio/holdings-overlap.js.map +0 -1
  434. package/dist/tools/portfolio/notifications.js.map +0 -1
  435. package/dist/tools/portfolio/risk-analysis.js.map +0 -1
  436. package/dist/tools/portfolio/tracker.js.map +0 -1
  437. package/dist/tools/portfolio/watchlist.js.map +0 -1
  438. package/dist/tools/sentiment/insight-format.js.map +0 -1
  439. package/dist/tools/sentiment/query-match.js.map +0 -1
  440. package/dist/tools/sentiment/reddit-sentiment.js.map +0 -1
  441. package/dist/tools/sentiment/sentiment-summary.js.map +0 -1
  442. package/dist/tools/sentiment/sentiment-trend.js.map +0 -1
  443. package/dist/tools/sentiment/twitter-sentiment.js.map +0 -1
  444. package/dist/tools/sentiment/untrusted-text.js.map +0 -1
  445. package/dist/tools/sentiment/web-search.js.map +0 -1
  446. package/dist/tools/sentiment/web-sentiment.js.map +0 -1
  447. package/dist/tools/technical/backtest.js.map +0 -1
  448. package/dist/tools/technical/indicators.js.map +0 -1
  449. package/dist/types/fundamentals.js.map +0 -1
  450. package/dist/types/index.js.map +0 -1
  451. package/dist/types/macro.js.map +0 -1
  452. package/dist/types/market.js.map +0 -1
  453. package/dist/types/options.js.map +0 -1
  454. package/dist/types/portfolio.js.map +0 -1
  455. package/dist/types/sentiment.js.map +0 -1
  456. package/dist/workflows/compare-assets.js.map +0 -1
  457. package/dist/workflows/index.js.map +0 -1
  458. package/dist/workflows/options-screener.js.map +0 -1
  459. package/dist/workflows/portfolio-builder.js.map +0 -1
  460. package/gui/server/ask-user-bridge.ts +0 -89
  461. package/gui/server/automation-heartbeat.ts +0 -97
  462. package/gui/server/background-quotes.ts +0 -127
  463. package/gui/server/chat-event-adapter.ts +0 -210
  464. package/gui/server/chat-run-session.ts +0 -16
  465. package/gui/server/gui-session-manager.ts +0 -5
  466. package/gui/server/http-routes.ts +0 -953
  467. package/gui/server/invoke-tool.ts +0 -489
  468. package/gui/server/live-chat-event-adapter.ts +0 -208
  469. package/gui/server/local-session-coordinator.ts +0 -97
  470. package/gui/server/market-state-api.ts +0 -273
  471. package/gui/server/model-setup.ts +0 -254
  472. package/gui/server/package.json +0 -5
  473. package/gui/server/private-api-access.ts +0 -62
  474. package/gui/server/projector.ts +0 -263
  475. package/gui/server/prompt-observation.ts +0 -58
  476. package/gui/server/quote-snapshot-store.ts +0 -50
  477. package/gui/server/server.ts +0 -289
  478. package/gui/server/session-actions.ts +0 -331
  479. package/gui/server/session-entry-wait.ts +0 -160
  480. package/gui/server/shutdown.ts +0 -47
  481. package/gui/server/tool-invoke-ack.ts +0 -49
  482. package/gui/server/tool-metadata.ts +0 -167
  483. package/gui/server/websocket.ts +0 -138
  484. package/gui/server/writer-lock.ts +0 -1
  485. package/gui/server/ws-hub.ts +0 -391
  486. package/gui/shared/chat-events.ts +0 -156
  487. package/gui/shared/event-reducer.ts +0 -219
  488. package/gui/web/dist/assets/CatalogOverlay-ChRTKNlf.js +0 -1
  489. package/gui/web/dist/assets/index-BzyqyVnd.css +0 -2
  490. package/gui/web/dist/assets/index-DvMjkOP9.js +0 -65
  491. package/src/analysts/contracts.ts +0 -176
  492. package/src/analysts/orchestrator.ts +0 -224
  493. package/src/cli-main.ts +0 -510
  494. package/src/cli.ts +0 -17
  495. package/src/config.ts +0 -272
  496. package/src/doctor/cli-command.ts +0 -83
  497. package/src/doctor/render.ts +0 -37
  498. package/src/doctor/report.ts +0 -638
  499. package/src/index.ts +0 -5
  500. package/src/infra/cache.ts +0 -114
  501. package/src/infra/http-client.ts +0 -134
  502. package/src/infra/index.ts +0 -14
  503. package/src/infra/native-dependencies.ts +0 -84
  504. package/src/infra/node-version.ts +0 -23
  505. package/src/infra/open-url.ts +0 -28
  506. package/src/infra/opencandle-paths.ts +0 -53
  507. package/src/infra/rate-limiter.ts +0 -76
  508. package/src/market-state/alert-conditions.ts +0 -82
  509. package/src/market-state/alert-runner.ts +0 -863
  510. package/src/market-state/daily-report.ts +0 -237
  511. package/src/market-state/local-automation-service.ts +0 -162
  512. package/src/market-state/notification-delivery.ts +0 -158
  513. package/src/market-state/resolve-for-mutation.ts +0 -24
  514. package/src/market-state/resolve.ts +0 -112
  515. package/src/market-state/service.ts +0 -2208
  516. package/src/memory/index.ts +0 -10
  517. package/src/memory/manager.ts +0 -190
  518. package/src/memory/preference-extractor.ts +0 -106
  519. package/src/memory/retrieval.ts +0 -71
  520. package/src/memory/sqlite.ts +0 -577
  521. package/src/memory/storage.ts +0 -195
  522. package/src/memory/tool-defaults.ts +0 -108
  523. package/src/memory/types.ts +0 -71
  524. package/src/monitor.ts +0 -123
  525. package/src/onboarding/connect.ts +0 -177
  526. package/src/onboarding/credential-interceptor.ts +0 -122
  527. package/src/onboarding/degradation-accumulator.ts +0 -77
  528. package/src/onboarding/prompt-user.ts +0 -85
  529. package/src/onboarding/provider-status.ts +0 -382
  530. package/src/onboarding/providers.ts +0 -414
  531. package/src/onboarding/state.ts +0 -216
  532. package/src/onboarding/tool-helpers.ts +0 -104
  533. package/src/onboarding/tool-tags.ts +0 -244
  534. package/src/onboarding/validation.ts +0 -152
  535. package/src/pi/opencandle-extension.ts +0 -1244
  536. package/src/pi/session-action-dedupe.ts +0 -155
  537. package/src/pi/session-storage.ts +0 -5
  538. package/src/pi/session-writer-lock.ts +0 -426
  539. package/src/pi/session.ts +0 -84
  540. package/src/pi/setup.ts +0 -399
  541. package/src/pi/tool-adapter.ts +0 -49
  542. package/src/pi/tui-session-coordinator.ts +0 -351
  543. package/src/prompts/context-builder.ts +0 -326
  544. package/src/prompts/disclaimer.ts +0 -9
  545. package/src/prompts/policy-cards.ts +0 -228
  546. package/src/prompts/sections.ts +0 -46
  547. package/src/prompts/symbol-preflight.ts +0 -80
  548. package/src/prompts/workflow-prompts.ts +0 -504
  549. package/src/providers/alpha-vantage.ts +0 -334
  550. package/src/providers/coingecko.ts +0 -93
  551. package/src/providers/errors.ts +0 -9
  552. package/src/providers/exa-search.ts +0 -375
  553. package/src/providers/external-tool-command.ts +0 -164
  554. package/src/providers/external-tool-error.ts +0 -20
  555. package/src/providers/fear-greed.ts +0 -45
  556. package/src/providers/finnhub.ts +0 -125
  557. package/src/providers/fred.ts +0 -83
  558. package/src/providers/index.ts +0 -17
  559. package/src/providers/provider-credential-error.ts +0 -23
  560. package/src/providers/reddit-cli.ts +0 -286
  561. package/src/providers/reddit.ts +0 -106
  562. package/src/providers/sec-edgar.ts +0 -326
  563. package/src/providers/tradingview.ts +0 -399
  564. package/src/providers/twitter-cli.ts +0 -202
  565. package/src/providers/twitter.ts +0 -102
  566. package/src/providers/web-search.ts +0 -303
  567. package/src/providers/with-fallback.ts +0 -42
  568. package/src/providers/wrap-provider.ts +0 -103
  569. package/src/providers/yahoo-finance.ts +0 -672
  570. package/src/routing/classify-intent.ts +0 -380
  571. package/src/routing/defaults.ts +0 -29
  572. package/src/routing/entity-extractor.ts +0 -540
  573. package/src/routing/fund-symbols.ts +0 -58
  574. package/src/routing/horizon.ts +0 -7
  575. package/src/routing/index.ts +0 -70
  576. package/src/routing/legacy-rule-router.ts +0 -13
  577. package/src/routing/planning.ts +0 -829
  578. package/src/routing/route-manifest.ts +0 -308
  579. package/src/routing/router-llm-client.ts +0 -52
  580. package/src/routing/router-prompt.ts +0 -159
  581. package/src/routing/router-types.ts +0 -84
  582. package/src/routing/router.ts +0 -997
  583. package/src/routing/slot-resolver.ts +0 -213
  584. package/src/routing/symbol-disambiguator.ts +0 -72
  585. package/src/routing/turn-context.ts +0 -108
  586. package/src/routing/types.ts +0 -77
  587. package/src/runtime/answer-contracts.ts +0 -693
  588. package/src/runtime/artifact-contracts.ts +0 -77
  589. package/src/runtime/evidence.ts +0 -77
  590. package/src/runtime/planning-evidence.ts +0 -682
  591. package/src/runtime/prompt-step.ts +0 -60
  592. package/src/runtime/provider-tracker.ts +0 -40
  593. package/src/runtime/run-context.ts +0 -32
  594. package/src/runtime/session-coordinator.ts +0 -632
  595. package/src/runtime/session-title.ts +0 -60
  596. package/src/runtime/tool-defaults-wrapper.ts +0 -43
  597. package/src/runtime/validation.ts +0 -211
  598. package/src/runtime/workflow-events.ts +0 -75
  599. package/src/runtime/workflow-runner.ts +0 -182
  600. package/src/runtime/workflow-types.ts +0 -102
  601. package/src/sentiment/adapters/finnhub.ts +0 -49
  602. package/src/sentiment/adapters/reddit.ts +0 -65
  603. package/src/sentiment/adapters/twitter.ts +0 -36
  604. package/src/sentiment/adapters/web.ts +0 -44
  605. package/src/sentiment/index.ts +0 -49
  606. package/src/sentiment/insights.ts +0 -269
  607. package/src/sentiment/keywords.ts +0 -31
  608. package/src/sentiment/pipeline.ts +0 -91
  609. package/src/sentiment/scorer.ts +0 -89
  610. package/src/sentiment/store.ts +0 -260
  611. package/src/sentiment/trends.ts +0 -96
  612. package/src/sentiment/types.ts +0 -112
  613. package/src/system-prompt.ts +0 -115
  614. package/src/tool-kit.ts +0 -69
  615. package/src/tools/AGENTS.md +0 -36
  616. package/src/tools/fundamentals/company-overview.ts +0 -64
  617. package/src/tools/fundamentals/comps.ts +0 -169
  618. package/src/tools/fundamentals/dcf.ts +0 -367
  619. package/src/tools/fundamentals/earnings.ts +0 -57
  620. package/src/tools/fundamentals/financials.ts +0 -63
  621. package/src/tools/fundamentals/sec-filings.ts +0 -105
  622. package/src/tools/index.ts +0 -101
  623. package/src/tools/interaction/ask-user.ts +0 -93
  624. package/src/tools/macro/fear-greed.ts +0 -41
  625. package/src/tools/macro/fred-data.ts +0 -92
  626. package/src/tools/market/crypto-history.ts +0 -72
  627. package/src/tools/market/crypto-price.ts +0 -53
  628. package/src/tools/market/screen-stocks.ts +0 -279
  629. package/src/tools/market/search-ticker.ts +0 -254
  630. package/src/tools/market/stock-history.ts +0 -104
  631. package/src/tools/market/stock-quote.ts +0 -67
  632. package/src/tools/options/greeks.ts +0 -81
  633. package/src/tools/options/option-chain.ts +0 -120
  634. package/src/tools/portfolio/alerts.ts +0 -457
  635. package/src/tools/portfolio/correlation.ts +0 -189
  636. package/src/tools/portfolio/daily-report.ts +0 -107
  637. package/src/tools/portfolio/holdings-overlap.ts +0 -139
  638. package/src/tools/portfolio/notifications.ts +0 -45
  639. package/src/tools/portfolio/risk-analysis.ts +0 -173
  640. package/src/tools/portfolio/tracker.ts +0 -308
  641. package/src/tools/portfolio/watchlist.ts +0 -288
  642. package/src/tools/sentiment/insight-format.ts +0 -50
  643. package/src/tools/sentiment/query-match.ts +0 -117
  644. package/src/tools/sentiment/reddit-sentiment.ts +0 -403
  645. package/src/tools/sentiment/sentiment-summary.ts +0 -383
  646. package/src/tools/sentiment/sentiment-trend.ts +0 -75
  647. package/src/tools/sentiment/twitter-sentiment.ts +0 -287
  648. package/src/tools/sentiment/untrusted-text.ts +0 -21
  649. package/src/tools/sentiment/web-search.ts +0 -186
  650. package/src/tools/sentiment/web-sentiment.ts +0 -96
  651. package/src/tools/technical/backtest.ts +0 -336
  652. package/src/tools/technical/indicators.ts +0 -281
  653. package/src/types/fundamentals.ts +0 -46
  654. package/src/types/index.ts +0 -25
  655. package/src/types/macro.ts +0 -27
  656. package/src/types/market.ts +0 -44
  657. package/src/types/options.ts +0 -52
  658. package/src/types/portfolio.ts +0 -83
  659. package/src/types/sentiment.ts +0 -129
  660. package/src/workflows/compare-assets.ts +0 -89
  661. package/src/workflows/index.ts +0 -3
  662. package/src/workflows/options-screener.ts +0 -125
  663. package/src/workflows/portfolio-builder.ts +0 -63
package/README.md CHANGED
@@ -1,13 +1,15 @@
1
1
  # OpenCandle
2
2
 
3
+ OpenCandle is an open source financial investigator: a terminal agent and local browser workbench for market research that starts from real provider data, shows source gaps, and keeps risk visible.
4
+
5
+ Requires Node.js 22.19+ or 24–26. macOS and Linux are fully supported; Windows is best-effort (WSL recommended).
6
+
3
7
  ```bash
4
8
  npx opencandle
5
9
  # or
6
10
  npx opencandle gui
7
11
  ```
8
12
 
9
- OpenCandle is an open source financial investigator: a terminal agent and local browser workbench for market research that starts from real provider data, shows source gaps, and keeps risk visible.
10
-
11
13
  [![CI](https://github.com/Kahtaf/OpenCandle/actions/workflows/ci.yml/badge.svg)](https://github.com/Kahtaf/OpenCandle/actions/workflows/ci.yml)
12
14
  [![npm](https://img.shields.io/npm/v/opencandle)](https://www.npmjs.com/package/opencandle)
13
15
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/Kahtaf/OpenCandle/blob/main/LICENSE)
@@ -18,15 +20,15 @@ OpenCandle is an open source financial investigator: a terminal agent and local
18
20
 
19
21
  ### Terminal UI
20
22
 
21
- [![OpenCandle terminal UI demo](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@main/assets/opencandle-tui-poster.png)](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@main/assets/opencandle-tui.mp4)
23
+ [![OpenCandle terminal UI demo](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@v0.11.1/assets/opencandle-tui-poster.png)](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@v0.11.1/assets/opencandle-tui.mp4)
22
24
 
23
- [Watch the terminal UI demo](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@main/assets/opencandle-tui.mp4) | [Download MP4](https://github.com/Kahtaf/OpenCandle/raw/refs/heads/main/assets/opencandle-tui.mp4)
25
+ [Watch the terminal UI demo](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@v0.11.1/assets/opencandle-tui.mp4) | [Download MP4](https://github.com/Kahtaf/OpenCandle/raw/refs/heads/main/assets/opencandle-tui.mp4)
24
26
 
25
27
  ### Local GUI
26
28
 
27
- [![OpenCandle local GUI demo](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@main/assets/opencandle-gui-poster.png)](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@main/assets/opencandle-gui.mp4)
29
+ [![OpenCandle local GUI demo](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@v0.11.1/assets/opencandle-gui-poster.png)](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@v0.11.1/assets/opencandle-gui.mp4)
28
30
 
29
- [Watch the local GUI demo](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@main/assets/opencandle-gui.mp4) | [Download MP4](https://github.com/Kahtaf/OpenCandle/raw/refs/heads/main/assets/opencandle-gui.mp4)
31
+ [Watch the local GUI demo](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@v0.11.1/assets/opencandle-gui.mp4) | [Download MP4](https://github.com/Kahtaf/OpenCandle/raw/refs/heads/main/assets/opencandle-gui.mp4)
30
32
 
31
33
  ## Why OpenCandle
32
34
 
@@ -46,24 +48,18 @@ OpenCandle is read-only research software. It does not place trades, route order
46
48
 
47
49
  | Capability | What it gives you |
48
50
  | --- | --- |
49
- | Terminal agent | Fast keyboard-driven research inside Pi's local TUI, with sessions, slash commands, model setup, and saved transcripts. |
51
+ | Terminal agent | Fast keyboard-driven research inside the bundled [Pi](https://github.com/earendil-works/pi) local TUI, with sessions, slash commands, model setup, and saved transcripts. Pi is the bundled agent runtime; no separate Pi install is needed. |
50
52
  | Local browser GUI | Chat, session history, provider setup, tool discovery, workflow launches, and richer financial result cards at `http://127.0.0.1:14567`. |
51
53
  | Evidence-first answers | Tools fetch and format data; the model synthesizes only after evidence is gathered. |
52
54
  | Finance routing | Quote lookup, comparison, portfolio review, options strategy, filing checks, macro questions, sentiment reads, and educational prompts route differently. |
53
55
  | Provider transparency | Missing keys, degraded sources, stale cache, and unavailable data are surfaced instead of hidden. |
54
- | Local state | OpenCandle user state lives under `~/.opencandle/` unless `OPENCANDLE_HOME` is set; durable market state is stored in SQLite at `state.db`. |
56
+ | Local state | OpenCandle user state lives under `~/.opencandle/` unless `OPENCANDLE_HOME` is set; durable market state is stored in SQLite at `~/.opencandle/state.db` (or `$OPENCANDLE_HOME/state.db`). |
55
57
  | Extensible tools | TypeScript tool APIs, provider boundaries, workflow builders, and package exports for add-on tools. |
56
58
  | Eval harness | Unit tests, live provider checks, CLI e2e, GUI browser smoke tests, and competitive finance evals. |
57
59
 
58
60
  ## Quick Start
59
61
 
60
- Requires Node.js `>=22.19.0 <27`.
61
-
62
- ```bash
63
- npx opencandle
64
- ```
65
-
66
- On first run, OpenCandle walks you through model setup. In the terminal, you can use Pi sign-in when available or provide a model API key. In the GUI, use the API-key setup panel or complete terminal `/setup` first and refresh the browser. Data-provider keys are separate and optional.
62
+ On first run, OpenCandle walks you through model setup. [Pi](https://github.com/earendil-works/pi) is the bundled agent runtime that handles model setup, the terminal shell, and saved sessions; no separate Pi install is needed. In the terminal, you can use Pi sign-in when available or provide a model API key. In the GUI, use the API-key setup panel or complete terminal `/setup` first and refresh the browser. Data-provider keys are separate and optional.
67
63
 
68
64
  Start the GUI instead:
69
65
 
@@ -100,11 +96,11 @@ Useful slash commands:
100
96
  Health diagnostics run from your shell:
101
97
 
102
98
  ```bash
103
- opencandle doctor
104
- opencandle doctor --json
105
- opencandle doctor --sessions
106
- opencandle doctor --full
107
- opencandle doctor --enable twitter
99
+ npx opencandle doctor
100
+ npx opencandle doctor --json
101
+ npx opencandle doctor --sessions
102
+ npx opencandle doctor --full
103
+ npx opencandle doctor --enable twitter
108
104
  ```
109
105
 
110
106
  The GUI also has a Diagnostics page at `/diagnostics` with the same health report, provider setup links, model setup actions, and an explicit browser-session check for Reddit and Twitter/X.
@@ -113,16 +109,16 @@ The GUI also has a Diagnostics page at `/diagnostics` with the same health repor
113
109
 
114
110
  | Area | Examples | Source |
115
111
  | --- | --- | --- |
116
- | Market data | Quotes, history, ticker search, crypto price and history | Yahoo Finance, Alpha Vantage fallback when configured, CoinGecko |
112
+ | Market data | Quotes, history, ticker search, screeners, crypto price and history | Yahoo Finance, TradingView scanner, Alpha Vantage fallback when configured, CoinGecko |
117
113
  | Options | Chains, open interest, IV, locally computed Greeks | Yahoo Finance plus local calculations |
118
114
  | Fundamentals | Overview, financials, earnings, DCF, comparable companies | Alpha Vantage |
119
- | Macro | Rates, CPI, GDP, unemployment, crypto Fear & Greed | FRED, alternative.me |
115
+ | Macro | Rates, CPI, GDP, unemployment, event probabilities, crypto Fear & Greed | FRED, Polymarket Gamma API, alternative.me |
120
116
  | Technical | Indicators and strategy backtests | Local calculations over market history |
121
117
  | Sentiment | Reddit, Twitter/X, Finnhub news, and web sentiment | `rdt-cli` and `twitter-cli` using your normal browser sessions, Finnhub, Exa, Brave, DuckDuckGo |
122
118
  | Filings | SEC filing search | SEC EDGAR |
123
119
  | Portfolio | Watchlists, holdings, correlation, risk | Local state plus market data |
124
120
 
125
- Yahoo Finance, CoinGecko, SEC EDGAR, DuckDuckGo search, and the alternative.me crypto Fear & Greed index do not require OpenCandle provider keys. Reddit sentiment requires `rdt-cli` (`uv tool install rdt-cli`) plus an active Reddit session in your normal browser. Twitter/X sentiment requires `twitter-cli` (`uv tool install twitter-cli`) plus an active x.com session in your normal browser. Alpha Vantage, FRED, Brave, Exa, and Finnhub unlock deeper coverage when configured.
121
+ Yahoo Finance, TradingView scanner, Polymarket Gamma, CoinGecko, SEC EDGAR, DuckDuckGo search, and the alternative.me crypto Fear & Greed index do not require OpenCandle provider keys. Reddit sentiment requires `rdt-cli` (`uv tool install rdt-cli`) plus an active Reddit session in your normal browser. Twitter/X sentiment requires `twitter-cli` (`uv tool install twitter-cli`) plus an active x.com session in your normal browser. Alpha Vantage, FRED, Brave, Exa, and Finnhub unlock deeper coverage when configured.
126
122
 
127
123
  ## Configuration
128
124
 
@@ -165,7 +161,7 @@ npm run gui
165
161
 
166
162
  ## How It Fits Together
167
163
 
168
- ![OpenCandle architecture diagram showing the user prompt flowing through OpenCandle routing, saved market state, finance data tools, evidence trace, a configured AI model, and external data sources.](assets/opencandle-architecture.png)
164
+ ![OpenCandle architecture diagram showing the user prompt flowing through OpenCandle routing, saved market state, finance data tools, evidence trace, a configured AI model, and external data sources.](https://cdn.jsdelivr.net/gh/Kahtaf/OpenCandle@main/assets/opencandle-architecture.png)
169
165
 
170
166
  ```text
171
167
  User prompt
@@ -220,9 +216,10 @@ npm run test:watch
220
216
  npm run test:e2e
221
217
  npm run test:e2e:cli
222
218
  npm run test:e2e:providers
223
- npm run test:evals
224
- npm run test:evals:product
225
- npm run test:evals:competitive
219
+ npm run eval
220
+ npm run eval -- cases
221
+ npm run eval -- product
222
+ npm run eval -- competitive
226
223
  ```
227
224
 
228
225
  Baseline check:
@@ -231,7 +228,7 @@ Baseline check:
231
228
  npm test
232
229
  ```
233
230
 
234
- The e2e, provider, and eval commands can hit live APIs, live model providers, or local agent CLIs. Run them intentionally; see [Testing and Evals](https://opencandle.app/docs/testing-and-evals.html).
231
+ The e2e, provider, and eval commands can hit live APIs, live model providers, or local agent CLIs. `npm run eval` lists every eval suite; run suites through `npm run eval -- <suite>`. Run them intentionally; see [Testing and Evals](https://opencandle.app/docs/testing-and-evals.html).
235
232
 
236
233
  Repository rules that matter:
237
234
 
@@ -145,4 +145,3 @@ export function collectEvidence(outputs) {
145
145
  }
146
146
  return evidence;
147
147
  }
148
- //# sourceMappingURL=contracts.js.map
@@ -1,8 +1,10 @@
1
1
  import type { WorkflowDefinition } from "../runtime/prompt-step.js";
2
+ import type { AnalystOutput } from "../runtime/workflow-types.js";
2
3
  export type AnalystRole = "valuation" | "momentum" | "options" | "contrarian" | "risk";
3
4
  export declare function buildBullPrompt(symbol: string): string;
4
5
  export declare function buildBearPrompt(symbol: string): string;
5
6
  export declare function buildRebuttalPrompt(symbol: string): string;
7
+ export declare function buildAnalystVoteTallyBlock(outputs: AnalystOutput[]): string | null;
6
8
  export declare function buildSynthesisPrompt(symbol: string): string;
7
9
  export declare function getInitialAnalysisPrompt(symbol: string): string;
8
10
  export declare function buildComprehensiveAnalysisDefinition(symbol: string): WorkflowDefinition;
@@ -1,4 +1,5 @@
1
1
  import { promptStep } from "../runtime/prompt-step.js";
2
+ import { isAnalystSplit, tallyVotes } from "./contracts.js";
2
3
  const SYMBOL_CAPTURE = "(\\$?[A-Za-z]{1,5}(?:[./-][A-Za-z]{1,2})?)";
3
4
  const NORMALIZED_SYMBOL_PATTERN = /^[A-Z]{1,5}(?:[./-][A-Z]{1,2})?$/;
4
5
  const VOTING_INSTRUCTION = `
@@ -95,7 +96,24 @@ End with this exact format:
95
96
  CONCESSIONS: [bullet list of points you concede]
96
97
  REMAINING CONVICTION: [1-10, where 10 = fully confident despite bear case]`;
97
98
  }
99
+ export function buildAnalystVoteTallyBlock(outputs) {
100
+ if (outputs.length < 2)
101
+ return null;
102
+ const tally = tallyVotes(outputs);
103
+ const split = isAnalystSplit(outputs);
104
+ return `## Deterministic Analyst Vote Tally
105
+ - Parsed analyst outputs: ${outputs.length}
106
+ - BUY: ${tally.buy}
107
+ - HOLD: ${tally.hold}
108
+ - SELL: ${tally.sell}
109
+ - Weighted average conviction: ${tally.weightedConviction}
110
+ - Computed verdict: ${tally.verdict}
111
+ - BUY+SELL split: ${split ? "yes" : "no"}`;
112
+ }
98
113
  export function buildSynthesisPrompt(symbol) {
114
+ // The runtime tally block is injected at dispatch time by
115
+ // SessionCoordinator.prepareWorkflowPrompt, where parsed analyst outputs
116
+ // exist; this builder runs at workflow-definition time and stays static.
99
117
  return `**[Synthesis]** You have received five analyst signals with conviction scores for ${symbol}, a bull case arguing FOR the position, and a bear case arguing AGAINST.
100
118
  If a bull rebuttal with concessions appears above (not a line starting with "REBUTTAL SKIPPED"), treat the concessions as validated risks that must be addressed.
101
119
 
@@ -154,6 +172,7 @@ export function buildComprehensiveAnalysisDefinition(symbol) {
154
172
  expectedOutputs: ["bear_thesis"],
155
173
  }),
156
174
  promptStep("debate_rebuttal", "Bull rebuttal (self-gating)", buildRebuttalPrompt(symbol), {
175
+ skippable: true,
157
176
  requiredInputs: [...analystOutputs, "bull_thesis", "bear_thesis"],
158
177
  expectedOutputs: ["rebuttal"],
159
178
  }),
@@ -190,4 +209,3 @@ export function normalizeSymbol(input) {
190
209
  const candidate = trimmed.replace(/\$/g, "").toUpperCase();
191
210
  return NORMALIZED_SYMBOL_PATTERN.test(candidate) ? candidate : undefined;
192
211
  }
193
- //# sourceMappingURL=orchestrator.js.map
package/dist/cli-main.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { randomUUID } from "node:crypto";
3
+ import { existsSync } from "node:fs";
3
4
  import { createRequire } from "node:module";
4
5
  import { dirname, resolve } from "node:path";
5
6
  import { createInterface } from "node:readline/promises";
@@ -98,9 +99,12 @@ async function handlePackageCommand(args, cwd, agentDir) {
98
99
  async function handleGuiCommand(args, cwd) {
99
100
  if (args[0] !== "gui")
100
101
  return false;
101
- const tsxCli = require.resolve("tsx/cli");
102
- const serverPath = resolve(packageRoot, "gui/server/server.ts");
103
- const child = spawn(process.execPath, [tsxCli, serverPath, ...args.slice(1)], {
102
+ const compiledServerPath = resolve(packageRoot, "dist/gui/server/server.js");
103
+ const sourceServerPath = resolve(packageRoot, "gui/server/server.ts");
104
+ const commandArgs = existsSync(compiledServerPath)
105
+ ? [compiledServerPath, ...args.slice(1)]
106
+ : [require.resolve("tsx/cli"), sourceServerPath, ...args.slice(1)];
107
+ const child = spawn(process.execPath, commandArgs, {
104
108
  cwd,
105
109
  env: process.env,
106
110
  stdio: "inherit",
@@ -121,9 +125,12 @@ async function handleGuiCommand(args, cwd) {
121
125
  async function handleMonitorCommand(args, cwd) {
122
126
  if (args[0] !== "monitor")
123
127
  return false;
124
- const tsxCli = require.resolve("tsx/cli");
125
- const monitorPath = resolve(packageRoot, "src/monitor.ts");
126
- const child = spawn(process.execPath, [tsxCli, monitorPath, ...args.slice(1)], {
128
+ const compiledMonitorPath = resolve(packageRoot, "dist/monitor.js");
129
+ const sourceMonitorPath = resolve(packageRoot, "src/monitor.ts");
130
+ const commandArgs = existsSync(compiledMonitorPath)
131
+ ? [compiledMonitorPath, ...args.slice(1)]
132
+ : [require.resolve("tsx/cli"), sourceMonitorPath, ...args.slice(1)];
133
+ const child = spawn(process.execPath, commandArgs, {
127
134
  cwd,
128
135
  env: process.env,
129
136
  stdio: "inherit",
@@ -454,4 +461,3 @@ function asRecord(value) {
454
461
  : {};
455
462
  }
456
463
  await main();
457
- //# sourceMappingURL=cli-main.js.map
@@ -0,0 +1,4 @@
1
+ type FrontDoorCommand = "help" | "version";
2
+ export declare function frontDoorCommand(args: readonly string[]): FrontDoorCommand | undefined;
3
+ export declare function renderFrontDoorCommand(command: FrontDoorCommand): string;
4
+ export {};
@@ -0,0 +1,33 @@
1
+ import { createRequire } from "node:module";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const require = createRequire(import.meta.url);
5
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
6
+ export function frontDoorCommand(args) {
7
+ if (args[0] === "--help" || args[0] === "-h")
8
+ return "help";
9
+ if (args[0] === "--version" || args[0] === "-v")
10
+ return "version";
11
+ return undefined;
12
+ }
13
+ export function renderFrontDoorCommand(command) {
14
+ if (command === "version")
15
+ return readPackageMetadata().version ?? "unknown";
16
+ return [
17
+ "Usage: opencandle [command]",
18
+ "",
19
+ "Commands:",
20
+ " (default) Start the interactive terminal TUI",
21
+ " gui Start the local GUI",
22
+ " monitor [--once] Run local alert/report automation",
23
+ " doctor [--json|--full|--sessions|--enable <provider>]",
24
+ " Check OpenCandle health",
25
+ " install <source> [-l] Install a package",
26
+ " remove|uninstall <source> [-l] Remove a package",
27
+ " list List installed packages",
28
+ " update [source] Update packages",
29
+ ].join("\n");
30
+ }
31
+ function readPackageMetadata() {
32
+ return require(resolve(packageRoot, "package.json"));
33
+ }
package/dist/cli.js CHANGED
@@ -1,18 +1,23 @@
1
1
  #!/usr/bin/env node
2
+ import { frontDoorCommand, renderFrontDoorCommand } from "./cli-options.js";
2
3
  import { ensureOpenCandleNativeDependencies } from "./infra/native-dependencies.js";
3
4
  import { assertSupportedNodeVersion } from "./infra/node-version.js";
4
- if (process.argv[2] === "doctor") {
5
+ const args = process.argv.slice(2);
6
+ const command = frontDoorCommand(args);
7
+ if (command) {
8
+ console.log(renderFrontDoorCommand(command));
9
+ }
10
+ else if (args[0] === "doctor") {
5
11
  const [{ loadEnv }, { handleDoctorCommand }, { getAgentDir }] = await Promise.all([
6
12
  import("./config.js"),
7
13
  import("./doctor/cli-command.js"),
8
14
  import("@earendil-works/pi-coding-agent"),
9
15
  ]);
10
16
  loadEnv();
11
- await handleDoctorCommand(process.argv.slice(2), process.cwd(), getAgentDir());
17
+ await handleDoctorCommand(args, process.cwd(), getAgentDir());
12
18
  }
13
19
  else {
14
20
  assertSupportedNodeVersion();
15
21
  await ensureOpenCandleNativeDependencies();
16
22
  await import("./cli-main.js");
17
23
  }
18
- //# sourceMappingURL=cli.js.map
package/dist/config.js CHANGED
@@ -172,4 +172,3 @@ export function getConfig() {
172
172
  export function resetConfigCache() {
173
173
  cachedConfig = null;
174
174
  }
175
- //# sourceMappingURL=config.js.map
@@ -42,6 +42,8 @@ export async function handleDoctorCommand(args, cwd, agentDir) {
42
42
  modelSetup: buildCliModelSetupState(modelRegistry, settingsManager),
43
43
  });
44
44
  console.log(json ? JSON.stringify(report, null, 2) : renderDoctorReport(report));
45
+ if (report.status === "blocked")
46
+ process.exitCode = 1;
45
47
  return true;
46
48
  }
47
49
  function buildCliModelSetupState(modelRegistry, settingsManager) {
@@ -66,4 +68,3 @@ function buildCliModelSetupState(modelRegistry, settingsManager) {
66
68
  availableModels,
67
69
  };
68
70
  }
69
- //# sourceMappingURL=cli-command.js.map
@@ -29,4 +29,3 @@ export function renderDoctorReport(report) {
29
29
  function isActionable(check) {
30
30
  return check.status !== "pass" && Boolean(check.remediation);
31
31
  }
32
- //# sourceMappingURL=render.js.map
@@ -6,6 +6,7 @@ import { getUnsupportedNodeVersionMessage } from "../infra/node-version.js";
6
6
  import { getConfigPath, getOnboardingPath, getOpenCandleHomeDir, getStateDbPath, resolveOpenCandlePath, } from "../infra/opencandle-paths.js";
7
7
  import { probeAllProviderStatuses, probeProviderStatus, } from "../onboarding/provider-status.js";
8
8
  import { getProvider, isExternalToolProvider, listAllProviders, } from "../onboarding/providers.js";
9
+ import { loadOnboardingState } from "../onboarding/state.js";
9
10
  export const DOCTOR_REPORT_SCHEMA_VERSION = 1;
10
11
  const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
11
12
  const require = createRequire(import.meta.url);
@@ -48,8 +49,8 @@ export function deriveDoctorStatus(checks) {
48
49
  return "blocked";
49
50
  }
50
51
  if (checks.some((candidate) => candidate.status === "warn" ||
51
- candidate.status === "unknown" ||
52
- (candidate.capability === "optional" && candidate.status === "fail"))) {
52
+ (candidate.capability === "optional" && candidate.status === "fail") ||
53
+ (candidate.capability === "core" && candidate.status === "unknown"))) {
53
54
  return "degraded";
54
55
  }
55
56
  return "ready";
@@ -301,16 +302,22 @@ function providerStatusCheck(status, modeOverride) {
301
302
  tier: provider.tier,
302
303
  kind: status.kind,
303
304
  state: status.state,
305
+ unlocks: provider.unlocks,
304
306
  };
305
307
  if (status.kind === "api-key") {
308
+ const configuredBefore = wasProviderConfigured(status.providerId);
309
+ const missing = status.state === "missing";
310
+ const neverConnected = missing && !configuredBefore;
306
311
  return {
307
312
  id: `provider.${status.providerId}.credential`,
308
313
  label: provider.displayName,
309
- status: status.state === "configured" ? "pass" : "warn",
314
+ status: status.state === "configured" ? "pass" : neverConnected ? "skip" : "warn",
310
315
  capability,
311
316
  summary: status.state === "configured"
312
317
  ? `Configured via ${status.credentialSource}`
313
- : "Credential is missing",
318
+ : neverConnected
319
+ ? "Not connected — optional."
320
+ : "Credential is missing",
314
321
  remediation: status.state === "configured"
315
322
  ? undefined
316
323
  : `${provider.instructionsHint}. Run /connect ${provider.id}.`,
@@ -327,7 +334,7 @@ function providerStatusCheck(status, modeOverride) {
327
334
  summary: ok ? "Reachable" : `Not reachable (${status.state})`,
328
335
  remediation: ok
329
336
  ? undefined
330
- : "Check network connectivity and retry. Core quote and market-data workflows depend on Yahoo Finance reachability.",
337
+ : `Check network connectivity and retry. OpenCandle uses ${provider.displayName} through a public HTTP endpoint.`,
331
338
  metadata: { ...metadata, statusCode: status.statusCode ?? null },
332
339
  };
333
340
  }
@@ -344,6 +351,7 @@ function providerStatusCheck(status, modeOverride) {
344
351
  };
345
352
  }
346
353
  const installed = status.state === "installed";
354
+ const missingBeforeSetup = status.state === "missing" && !wasProviderConfigured(status.providerId);
347
355
  return {
348
356
  id: `provider.${status.providerId}.binary`,
349
357
  label: `${provider.displayName} CLI`,
@@ -352,10 +360,16 @@ function providerStatusCheck(status, modeOverride) {
352
360
  : status.state === "skipped"
353
361
  ? "skip"
354
362
  : status.state === "missing"
355
- ? "warn"
363
+ ? missingBeforeSetup
364
+ ? "skip"
365
+ : "warn"
356
366
  : "unknown",
357
367
  capability: "optional",
358
- summary: installed ? "Installed" : (status.message ?? `CLI ${status.state}`),
368
+ summary: installed
369
+ ? "Installed"
370
+ : missingBeforeSetup
371
+ ? "Not connected — optional."
372
+ : (status.message ?? `CLI ${status.state}`),
359
373
  remediation: installed
360
374
  ? undefined
361
375
  : status.state === "skipped"
@@ -364,6 +378,9 @@ function providerStatusCheck(status, modeOverride) {
364
378
  metadata,
365
379
  };
366
380
  }
381
+ function wasProviderConfigured(providerId) {
382
+ return loadOnboardingState().providers[providerId]?.status === "completed";
383
+ }
367
384
  function sessionNotCheckedCheck(provider) {
368
385
  return {
369
386
  id: `provider.${provider.id}.session`,
@@ -385,7 +402,11 @@ async function buildGuiChecks(options) {
385
402
  label: "GUI server",
386
403
  status: reachable ? (role === "follower" ? "warn" : "pass") : "skip",
387
404
  capability: "optional",
388
- summary: reachable ? `GUI server is ${role}` : "GUI server is not running",
405
+ summary: reachable
406
+ ? role === "writer"
407
+ ? "GUI server is running and can run sessions"
408
+ : `GUI server is ${role}`
409
+ : "GUI server is not running",
389
410
  remediation: role === "follower"
390
411
  ? "Open the writer GUI process to perform setup mutations."
391
412
  : reachable
@@ -415,7 +436,9 @@ async function buildGuiChecks(options) {
415
436
  status: verified ? (role === "follower" ? "warn" : "pass") : "warn",
416
437
  capability: "optional",
417
438
  summary: verified
418
- ? `GUI server is ${role}`
439
+ ? role === "writer"
440
+ ? "GUI server is running and can run sessions"
441
+ : `GUI server is ${role}`
419
442
  : response.ok
420
443
  ? "OpenCandle GUI health payload was not verified"
421
444
  : `GUI health returned HTTP ${response.status}`,
@@ -449,8 +472,15 @@ function sessionRemediation(providerId, installCmd) {
449
472
  return installCmd ?? "Install and authenticate the external tool.";
450
473
  }
451
474
  function buildSummary(status, checks) {
452
- if (status === "ready")
453
- return "OpenCandle core health is ready.";
475
+ if (status === "ready") {
476
+ const unchecked = checks.filter((candidate) => candidate.status === "unknown");
477
+ if (unchecked.length === 0)
478
+ return "OpenCandle core health is ready.";
479
+ if (unchecked.every((candidate) => candidate.capability === "optional")) {
480
+ return `OpenCandle core health is ready with ${unchecked.length} optional ${unchecked.length === 1 ? "capability" : "capabilities"} unchecked.`;
481
+ }
482
+ return `OpenCandle is usable with ${unchecked.length} unchecked ${unchecked.length === 1 ? "capability" : "capabilities"}.`;
483
+ }
454
484
  const firstBlocking = checks.find((candidate) => candidate.capability === "core" && candidate.status === "fail");
455
485
  if (firstBlocking)
456
486
  return `OpenCandle is blocked: ${firstBlocking.summary}.`;
@@ -497,4 +527,3 @@ function canAccess(path, mode = constants.R_OK | constants.W_OK) {
497
527
  return false;
498
528
  }
499
529
  }
500
- //# sourceMappingURL=report.js.map
@@ -0,0 +1,19 @@
1
+ import type { AskUserHandler } from "../../types/index.js";
2
+ type AskUserParams = Parameters<AskUserHandler>[0];
3
+ export interface GuiAskUserPrompt extends AskUserParams {
4
+ id: string;
5
+ sessionId: string;
6
+ status: "pending" | "answered" | "cancelled";
7
+ answer: string | null;
8
+ }
9
+ export declare function createAskUserBridge({ broadcast, getSessionId, }: {
10
+ broadcast: (message: unknown) => void;
11
+ getSessionId: () => string;
12
+ }): {
13
+ ask: AskUserHandler;
14
+ askForSession: (sessionId: string) => AskUserHandler;
15
+ answer: (id: string, answer: string) => boolean;
16
+ cancel: (id: string) => boolean;
17
+ getPrompts: () => GuiAskUserPrompt[];
18
+ };
19
+ export {};
@@ -0,0 +1,55 @@
1
+ export function createAskUserBridge({ broadcast, getSessionId, }) {
2
+ let nextId = 1;
3
+ const prompts = new Map();
4
+ const pending = new Map();
5
+ const createAskHandler = (resolveSessionId) => async (params) => {
6
+ const id = `ask-user-${Date.now()}-${nextId++}`;
7
+ const prompt = {
8
+ id,
9
+ sessionId: resolveSessionId(),
10
+ question: params.question,
11
+ questionType: params.questionType,
12
+ options: params.options,
13
+ placeholder: params.placeholder,
14
+ reason: params.reason,
15
+ status: "pending",
16
+ answer: null,
17
+ };
18
+ prompts.set(id, prompt);
19
+ broadcast({ type: "ask_user.prompt", prompt });
20
+ return new Promise((resolve) => {
21
+ pending.set(id, { prompt, resolve });
22
+ });
23
+ };
24
+ const ask = createAskHandler(getSessionId);
25
+ const resolvePrompt = (id, result) => {
26
+ const item = pending.get(id);
27
+ if (!item)
28
+ return false;
29
+ pending.delete(id);
30
+ const prompt = {
31
+ ...item.prompt,
32
+ status: result.cancelled ? "cancelled" : "answered",
33
+ answer: result.cancelled ? null : (result.answer ?? null),
34
+ };
35
+ prompts.set(id, prompt);
36
+ broadcast({ type: "ask_user.resolved", prompt });
37
+ item.resolve(result);
38
+ return true;
39
+ };
40
+ return {
41
+ ask,
42
+ askForSession(sessionId) {
43
+ return createAskHandler(() => sessionId);
44
+ },
45
+ answer(id, answer) {
46
+ return resolvePrompt(id, { answer, cancelled: false });
47
+ },
48
+ cancel(id) {
49
+ return resolvePrompt(id, { answer: null, cancelled: true });
50
+ },
51
+ getPrompts() {
52
+ return [...prompts.values()];
53
+ },
54
+ };
55
+ }
@@ -0,0 +1,25 @@
1
+ import { runLocalAutomationHeartbeat } from "../../market-state/local-automation-service.js";
2
+ import { initDefaultDatabase } from "../../memory/sqlite.js";
3
+ export declare const DEFAULT_AUTOMATION_HEARTBEAT_MS = 60000;
4
+ export declare const MIN_AUTOMATION_HEARTBEAT_MS = 5000;
5
+ export declare function normalizeAutomationHeartbeatMs(raw: string | undefined): number;
6
+ export declare function createAutomationHeartbeatRunner(run: (checkAlerts: boolean) => Promise<void>): (checkAlerts: boolean) => Promise<boolean>;
7
+ type IntervalHandle = ReturnType<typeof setInterval>;
8
+ type SetIntervalFn = (callback: () => void, ms: number) => IntervalHandle;
9
+ type ClearIntervalFn = (handle: IntervalHandle) => void;
10
+ export interface LocalAutomationHeartbeat {
11
+ start(): void;
12
+ stop(): void;
13
+ }
14
+ export interface LocalAutomationHeartbeatOptions {
15
+ role: string;
16
+ getSessionId: () => string;
17
+ intervalMs: number;
18
+ initDatabase?: typeof initDefaultDatabase;
19
+ runHeartbeat?: typeof runLocalAutomationHeartbeat;
20
+ setIntervalFn?: SetIntervalFn;
21
+ clearIntervalFn?: ClearIntervalFn;
22
+ warn?: (message: string) => void;
23
+ }
24
+ export declare function createLocalAutomationHeartbeat({ role, getSessionId, intervalMs, initDatabase, runHeartbeat, setIntervalFn, clearIntervalFn, warn, }: LocalAutomationHeartbeatOptions): LocalAutomationHeartbeat;
25
+ export {};
@@ -0,0 +1,61 @@
1
+ import { runLocalAutomationHeartbeat } from "../../market-state/local-automation-service.js";
2
+ import { initDefaultDatabase } from "../../memory/sqlite.js";
3
+ export const DEFAULT_AUTOMATION_HEARTBEAT_MS = 60_000;
4
+ export const MIN_AUTOMATION_HEARTBEAT_MS = 5_000;
5
+ export function normalizeAutomationHeartbeatMs(raw) {
6
+ if (raw == null || raw.trim() === "")
7
+ return DEFAULT_AUTOMATION_HEARTBEAT_MS;
8
+ const parsed = Number(raw);
9
+ if (!Number.isFinite(parsed) || parsed < MIN_AUTOMATION_HEARTBEAT_MS)
10
+ return DEFAULT_AUTOMATION_HEARTBEAT_MS;
11
+ return Math.floor(parsed);
12
+ }
13
+ export function createAutomationHeartbeatRunner(run) {
14
+ let inFlight = false;
15
+ return async (checkAlerts) => {
16
+ if (inFlight)
17
+ return false;
18
+ inFlight = true;
19
+ try {
20
+ await run(checkAlerts);
21
+ return true;
22
+ }
23
+ finally {
24
+ inFlight = false;
25
+ }
26
+ };
27
+ }
28
+ export function createLocalAutomationHeartbeat({ role, getSessionId, intervalMs, initDatabase = initDefaultDatabase, runHeartbeat = runLocalAutomationHeartbeat, setIntervalFn = setInterval, clearIntervalFn = clearInterval, warn = (message) => console.warn(message), }) {
29
+ let automationHeartbeat = null;
30
+ async function executeGuiAutomationHeartbeat(checkAlerts) {
31
+ const db = initDatabase();
32
+ try {
33
+ await runHeartbeat(db, {
34
+ ownerId: `gui:${getSessionId()}`,
35
+ ownerKind: "writer",
36
+ ttlSeconds: Math.max(90, Math.ceil((intervalMs * 2) / 1000)),
37
+ checkAlerts,
38
+ });
39
+ }
40
+ catch (error) {
41
+ warn(`Local automation heartbeat failed: ${error instanceof Error ? error.message : String(error)}`);
42
+ }
43
+ finally {
44
+ db.close();
45
+ }
46
+ }
47
+ const runGuiAutomationHeartbeat = createAutomationHeartbeatRunner(executeGuiAutomationHeartbeat);
48
+ function start() {
49
+ if (role !== "writer")
50
+ return;
51
+ void runGuiAutomationHeartbeat(true);
52
+ automationHeartbeat = setIntervalFn(() => void runGuiAutomationHeartbeat(true), intervalMs);
53
+ }
54
+ function stop() {
55
+ if (automationHeartbeat) {
56
+ clearIntervalFn(automationHeartbeat);
57
+ automationHeartbeat = null;
58
+ }
59
+ }
60
+ return { start, stop };
61
+ }