opencandle 0.5.0 → 0.7.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 (574) hide show
  1. package/README.md +170 -186
  2. package/dist/analysts/contracts.d.ts +1 -3
  3. package/dist/analysts/contracts.js +1 -11
  4. package/dist/analysts/contracts.js.map +1 -1
  5. package/dist/analysts/orchestrator.d.ts +1 -3
  6. package/dist/analysts/orchestrator.js +1 -26
  7. package/dist/analysts/orchestrator.js.map +1 -1
  8. package/dist/cli.js +66 -7
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config.d.ts +13 -3
  11. package/dist/config.js +25 -5
  12. package/dist/config.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/infra/cache.d.ts +8 -11
  17. package/dist/infra/cache.js +17 -15
  18. package/dist/infra/cache.js.map +1 -1
  19. package/dist/infra/http-client.d.ts +4 -1
  20. package/dist/infra/http-client.js +59 -6
  21. package/dist/infra/http-client.js.map +1 -1
  22. package/dist/infra/index.d.ts +2 -3
  23. package/dist/infra/index.js +2 -3
  24. package/dist/infra/index.js.map +1 -1
  25. package/dist/infra/native-dependencies.js +2 -2
  26. package/dist/infra/native-dependencies.js.map +1 -1
  27. package/dist/infra/node-version.js.map +1 -1
  28. package/dist/infra/opencandle-paths.d.ts +0 -3
  29. package/dist/infra/opencandle-paths.js +4 -11
  30. package/dist/infra/opencandle-paths.js.map +1 -1
  31. package/dist/infra/rate-limiter.js +12 -9
  32. package/dist/infra/rate-limiter.js.map +1 -1
  33. package/dist/market-state/alert-conditions.d.ts +34 -0
  34. package/dist/market-state/alert-conditions.js +23 -0
  35. package/dist/market-state/alert-conditions.js.map +1 -0
  36. package/dist/market-state/alert-runner.d.ts +55 -0
  37. package/dist/market-state/alert-runner.js +634 -0
  38. package/dist/market-state/alert-runner.js.map +1 -0
  39. package/dist/market-state/daily-report.d.ts +26 -0
  40. package/dist/market-state/daily-report.js +179 -0
  41. package/dist/market-state/daily-report.js.map +1 -0
  42. package/dist/market-state/local-automation-service.d.ts +25 -0
  43. package/dist/market-state/local-automation-service.js +119 -0
  44. package/dist/market-state/local-automation-service.js.map +1 -0
  45. package/dist/market-state/notification-delivery.d.ts +14 -0
  46. package/dist/market-state/notification-delivery.js +139 -0
  47. package/dist/market-state/notification-delivery.js.map +1 -0
  48. package/dist/market-state/resolve-for-mutation.d.ts +10 -0
  49. package/dist/market-state/resolve-for-mutation.js +15 -0
  50. package/dist/market-state/resolve-for-mutation.js.map +1 -0
  51. package/dist/market-state/resolve.d.ts +14 -0
  52. package/dist/market-state/resolve.js +89 -0
  53. package/dist/market-state/resolve.js.map +1 -0
  54. package/dist/market-state/service.d.ts +527 -0
  55. package/dist/market-state/service.js +1099 -0
  56. package/dist/market-state/service.js.map +1 -0
  57. package/dist/memory/index.d.ts +7 -7
  58. package/dist/memory/index.js +6 -6
  59. package/dist/memory/index.js.map +1 -1
  60. package/dist/memory/manager.js +11 -11
  61. package/dist/memory/manager.js.map +1 -1
  62. package/dist/memory/retrieval.js +7 -4
  63. package/dist/memory/retrieval.js.map +1 -1
  64. package/dist/memory/sqlite.js +385 -3
  65. package/dist/memory/sqlite.js.map +1 -1
  66. package/dist/memory/storage.js +1 -2
  67. package/dist/memory/storage.js.map +1 -1
  68. package/dist/memory/tool-defaults.js +64 -28
  69. package/dist/memory/tool-defaults.js.map +1 -1
  70. package/dist/memory/types.js.map +1 -1
  71. package/dist/monitor.d.ts +2 -0
  72. package/dist/monitor.js +104 -0
  73. package/dist/monitor.js.map +1 -0
  74. package/dist/onboarding/connect.d.ts +2 -2
  75. package/dist/onboarding/connect.js +13 -8
  76. package/dist/onboarding/connect.js.map +1 -1
  77. package/dist/onboarding/credential-interceptor.js +1 -1
  78. package/dist/onboarding/credential-interceptor.js.map +1 -1
  79. package/dist/onboarding/degradation-accumulator.js +1 -3
  80. package/dist/onboarding/degradation-accumulator.js.map +1 -1
  81. package/dist/onboarding/provider-status.d.ts +48 -0
  82. package/dist/onboarding/provider-status.js +285 -0
  83. package/dist/onboarding/provider-status.js.map +1 -0
  84. package/dist/onboarding/providers.d.ts +85 -8
  85. package/dist/onboarding/providers.js +83 -18
  86. package/dist/onboarding/providers.js.map +1 -1
  87. package/dist/onboarding/state.d.ts +1 -0
  88. package/dist/onboarding/state.js +5 -0
  89. package/dist/onboarding/state.js.map +1 -1
  90. package/dist/onboarding/tool-helpers.js +1 -1
  91. package/dist/onboarding/tool-helpers.js.map +1 -1
  92. package/dist/onboarding/tool-tags.d.ts +12 -1
  93. package/dist/onboarding/tool-tags.js +37 -5
  94. package/dist/onboarding/tool-tags.js.map +1 -1
  95. package/dist/onboarding/validation.d.ts +2 -2
  96. package/dist/onboarding/validation.js +1 -1
  97. package/dist/onboarding/validation.js.map +1 -1
  98. package/dist/pi/opencandle-extension.d.ts +8 -0
  99. package/dist/pi/opencandle-extension.js +502 -42
  100. package/dist/pi/opencandle-extension.js.map +1 -1
  101. package/dist/pi/session.d.ts +1 -1
  102. package/dist/pi/session.js +3 -1
  103. package/dist/pi/session.js.map +1 -1
  104. package/dist/pi/setup.js +8 -3
  105. package/dist/pi/setup.js.map +1 -1
  106. package/dist/pi/tool-adapter.d.ts +4 -1
  107. package/dist/pi/tool-adapter.js +10 -6
  108. package/dist/pi/tool-adapter.js.map +1 -1
  109. package/dist/prompts/context-builder.d.ts +1 -1
  110. package/dist/prompts/context-builder.js +20 -7
  111. package/dist/prompts/context-builder.js.map +1 -1
  112. package/dist/prompts/policy-cards.d.ts +1 -1
  113. package/dist/prompts/policy-cards.js +2 -2
  114. package/dist/prompts/policy-cards.js.map +1 -1
  115. package/dist/prompts/sections.d.ts +1 -1
  116. package/dist/prompts/symbol-preflight.d.ts +20 -0
  117. package/dist/prompts/symbol-preflight.js +49 -0
  118. package/dist/prompts/symbol-preflight.js.map +1 -0
  119. package/dist/prompts/workflow-prompts.d.ts +1 -1
  120. package/dist/prompts/workflow-prompts.js +54 -16
  121. package/dist/prompts/workflow-prompts.js.map +1 -1
  122. package/dist/providers/alpha-vantage.d.ts +1 -1
  123. package/dist/providers/alpha-vantage.js +26 -7
  124. package/dist/providers/alpha-vantage.js.map +1 -1
  125. package/dist/providers/coingecko.js +1 -1
  126. package/dist/providers/coingecko.js.map +1 -1
  127. package/dist/providers/errors.d.ts +5 -0
  128. package/dist/providers/errors.js +11 -0
  129. package/dist/providers/errors.js.map +1 -0
  130. package/dist/providers/exa-search.d.ts +2 -2
  131. package/dist/providers/exa-search.js +19 -11
  132. package/dist/providers/exa-search.js.map +1 -1
  133. package/dist/providers/external-tool-error.d.ts +10 -0
  134. package/dist/providers/external-tool-error.js +21 -0
  135. package/dist/providers/external-tool-error.js.map +1 -0
  136. package/dist/providers/fear-greed.js +1 -1
  137. package/dist/providers/fear-greed.js.map +1 -1
  138. package/dist/providers/finnhub.js +3 -5
  139. package/dist/providers/finnhub.js.map +1 -1
  140. package/dist/providers/fred.js +2 -2
  141. package/dist/providers/fred.js.map +1 -1
  142. package/dist/providers/index.d.ts +7 -6
  143. package/dist/providers/index.js +6 -5
  144. package/dist/providers/index.js.map +1 -1
  145. package/dist/providers/reddit-cli.d.ts +36 -0
  146. package/dist/providers/reddit-cli.js +201 -0
  147. package/dist/providers/reddit-cli.js.map +1 -0
  148. package/dist/providers/reddit.d.ts +1 -1
  149. package/dist/providers/reddit.js +9 -37
  150. package/dist/providers/reddit.js.map +1 -1
  151. package/dist/providers/sec-edgar.d.ts +1 -0
  152. package/dist/providers/sec-edgar.js +12 -4
  153. package/dist/providers/sec-edgar.js.map +1 -1
  154. package/dist/providers/tradingview.d.ts +47 -0
  155. package/dist/providers/tradingview.js +275 -0
  156. package/dist/providers/tradingview.js.map +1 -0
  157. package/dist/providers/twitter-cli.d.ts +40 -0
  158. package/dist/providers/twitter-cli.js +153 -0
  159. package/dist/providers/twitter-cli.js.map +1 -0
  160. package/dist/providers/twitter.d.ts +0 -8
  161. package/dist/providers/twitter.js +8 -60
  162. package/dist/providers/twitter.js.map +1 -1
  163. package/dist/providers/web-search.js +26 -12
  164. package/dist/providers/web-search.js.map +1 -1
  165. package/dist/providers/with-fallback.js +4 -2
  166. package/dist/providers/with-fallback.js.map +1 -1
  167. package/dist/providers/wrap-provider.d.ts +2 -3
  168. package/dist/providers/wrap-provider.js +44 -8
  169. package/dist/providers/wrap-provider.js.map +1 -1
  170. package/dist/providers/yahoo-finance.d.ts +1 -1
  171. package/dist/providers/yahoo-finance.js +153 -48
  172. package/dist/providers/yahoo-finance.js.map +1 -1
  173. package/dist/routing/classify-intent.d.ts +6 -0
  174. package/dist/routing/classify-intent.js +78 -7
  175. package/dist/routing/classify-intent.js.map +1 -1
  176. package/dist/routing/defaults.d.ts +1 -1
  177. package/dist/routing/entity-extractor.d.ts +1 -0
  178. package/dist/routing/entity-extractor.js +234 -29
  179. package/dist/routing/entity-extractor.js.map +1 -1
  180. package/dist/routing/fund-symbols.d.ts +2 -0
  181. package/dist/routing/fund-symbols.js +55 -0
  182. package/dist/routing/fund-symbols.js.map +1 -0
  183. package/dist/routing/horizon.d.ts +1 -0
  184. package/dist/routing/horizon.js +10 -0
  185. package/dist/routing/horizon.js.map +1 -0
  186. package/dist/routing/index.d.ts +10 -10
  187. package/dist/routing/index.js +6 -6
  188. package/dist/routing/index.js.map +1 -1
  189. package/dist/routing/planning.d.ts +2 -2
  190. package/dist/routing/planning.js +65 -34
  191. package/dist/routing/planning.js.map +1 -1
  192. package/dist/routing/route-manifest.d.ts +2 -2
  193. package/dist/routing/route-manifest.js +25 -4
  194. package/dist/routing/route-manifest.js.map +1 -1
  195. package/dist/routing/router-llm-client.js.map +1 -1
  196. package/dist/routing/router-prompt.js +7 -9
  197. package/dist/routing/router-prompt.js.map +1 -1
  198. package/dist/routing/router-types.d.ts +1 -0
  199. package/dist/routing/router.js +137 -22
  200. package/dist/routing/router.js.map +1 -1
  201. package/dist/routing/slot-resolver.d.ts +1 -1
  202. package/dist/routing/slot-resolver.js +2 -4
  203. package/dist/routing/slot-resolver.js.map +1 -1
  204. package/dist/routing/symbol-disambiguator.d.ts +11 -0
  205. package/dist/routing/symbol-disambiguator.js +52 -0
  206. package/dist/routing/symbol-disambiguator.js.map +1 -0
  207. package/dist/routing/turn-context.d.ts +1 -1
  208. package/dist/routing/turn-context.js +1 -1
  209. package/dist/routing/turn-context.js.map +1 -1
  210. package/dist/routing/types.d.ts +2 -0
  211. package/dist/runtime/answer-contracts.d.ts +1 -1
  212. package/dist/runtime/answer-contracts.js +48 -9
  213. package/dist/runtime/answer-contracts.js.map +1 -1
  214. package/dist/runtime/artifact-contracts.js.map +1 -1
  215. package/dist/runtime/planning-evidence.js +47 -26
  216. package/dist/runtime/planning-evidence.js.map +1 -1
  217. package/dist/runtime/prompt-step.d.ts +1 -9
  218. package/dist/runtime/prompt-step.js +0 -10
  219. package/dist/runtime/prompt-step.js.map +1 -1
  220. package/dist/runtime/run-context.d.ts +5 -2
  221. package/dist/runtime/run-context.js +8 -1
  222. package/dist/runtime/run-context.js.map +1 -1
  223. package/dist/runtime/session-coordinator.d.ts +13 -5
  224. package/dist/runtime/session-coordinator.js +160 -20
  225. package/dist/runtime/session-coordinator.js.map +1 -1
  226. package/dist/runtime/session-title.d.ts +14 -0
  227. package/dist/runtime/session-title.js +50 -0
  228. package/dist/runtime/session-title.js.map +1 -0
  229. package/dist/runtime/tool-defaults-wrapper.js +7 -5
  230. package/dist/runtime/tool-defaults-wrapper.js.map +1 -1
  231. package/dist/runtime/validation.js.map +1 -1
  232. package/dist/runtime/workflow-events.js.map +1 -1
  233. package/dist/runtime/workflow-runner.d.ts +3 -3
  234. package/dist/runtime/workflow-runner.js +1 -1
  235. package/dist/runtime/workflow-runner.js.map +1 -1
  236. package/dist/sentiment/adapters/finnhub.d.ts +1 -1
  237. package/dist/sentiment/adapters/finnhub.js +6 -1
  238. package/dist/sentiment/adapters/finnhub.js.map +1 -1
  239. package/dist/sentiment/adapters/reddit.d.ts +2 -2
  240. package/dist/sentiment/adapters/twitter.d.ts +1 -1
  241. package/dist/sentiment/adapters/web.d.ts +1 -1
  242. package/dist/sentiment/index.d.ts +10 -11
  243. package/dist/sentiment/index.js +10 -20
  244. package/dist/sentiment/index.js.map +1 -1
  245. package/dist/sentiment/insights.d.ts +17 -0
  246. package/dist/sentiment/insights.js +206 -0
  247. package/dist/sentiment/insights.js.map +1 -0
  248. package/dist/sentiment/keywords.js +26 -4
  249. package/dist/sentiment/keywords.js.map +1 -1
  250. package/dist/sentiment/pipeline.d.ts +2 -2
  251. package/dist/sentiment/pipeline.js +14 -2
  252. package/dist/sentiment/pipeline.js.map +1 -1
  253. package/dist/sentiment/scorer.d.ts +2 -0
  254. package/dist/sentiment/scorer.js +11 -2
  255. package/dist/sentiment/scorer.js.map +1 -1
  256. package/dist/sentiment/store.d.ts +1 -1
  257. package/dist/sentiment/store.js +1 -1
  258. package/dist/sentiment/store.js.map +1 -1
  259. package/dist/sentiment/trends.d.ts +1 -1
  260. package/dist/sentiment/trends.js.map +1 -1
  261. package/dist/sentiment/types.d.ts +2 -0
  262. package/dist/sentiment/types.js.map +1 -1
  263. package/dist/system-prompt.js +6 -9
  264. package/dist/system-prompt.js.map +1 -1
  265. package/dist/tool-kit.d.ts +7 -7
  266. package/dist/tool-kit.js +4 -4
  267. package/dist/tool-kit.js.map +1 -1
  268. package/dist/tools/fundamentals/company-overview.js +11 -6
  269. package/dist/tools/fundamentals/company-overview.js.map +1 -1
  270. package/dist/tools/fundamentals/comps.js +18 -9
  271. package/dist/tools/fundamentals/comps.js.map +1 -1
  272. package/dist/tools/fundamentals/dcf.js +23 -11
  273. package/dist/tools/fundamentals/dcf.js.map +1 -1
  274. package/dist/tools/fundamentals/earnings.js +8 -3
  275. package/dist/tools/fundamentals/earnings.js.map +1 -1
  276. package/dist/tools/fundamentals/financials.js +8 -3
  277. package/dist/tools/fundamentals/financials.js.map +1 -1
  278. package/dist/tools/fundamentals/sec-filings.js +21 -6
  279. package/dist/tools/fundamentals/sec-filings.js.map +1 -1
  280. package/dist/tools/index.d.ts +27 -20
  281. package/dist/tools/index.js +55 -43
  282. package/dist/tools/index.js.map +1 -1
  283. package/dist/tools/interaction/ask-user.js +15 -3
  284. package/dist/tools/interaction/ask-user.js.map +1 -1
  285. package/dist/tools/macro/fear-greed.js.map +1 -1
  286. package/dist/tools/macro/fred-data.d.ts +1 -1
  287. package/dist/tools/macro/fred-data.js +17 -6
  288. package/dist/tools/macro/fred-data.js.map +1 -1
  289. package/dist/tools/market/crypto-history.js +3 -1
  290. package/dist/tools/market/crypto-history.js.map +1 -1
  291. package/dist/tools/market/crypto-price.js +3 -1
  292. package/dist/tools/market/crypto-price.js.map +1 -1
  293. package/dist/tools/market/screen-stocks.d.ts +18 -0
  294. package/dist/tools/market/screen-stocks.js +252 -0
  295. package/dist/tools/market/screen-stocks.js.map +1 -0
  296. package/dist/tools/market/search-ticker.js +160 -8
  297. package/dist/tools/market/search-ticker.js.map +1 -1
  298. package/dist/tools/market/stock-history.d.ts +2 -2
  299. package/dist/tools/market/stock-history.js +26 -7
  300. package/dist/tools/market/stock-history.js.map +1 -1
  301. package/dist/tools/market/stock-quote.js +5 -3
  302. package/dist/tools/market/stock-quote.js.map +1 -1
  303. package/dist/tools/options/greeks.js +1 -1
  304. package/dist/tools/options/greeks.js.map +1 -1
  305. package/dist/tools/options/option-chain.js +19 -6
  306. package/dist/tools/options/option-chain.js.map +1 -1
  307. package/dist/tools/portfolio/alerts.d.ts +15 -0
  308. package/dist/tools/portfolio/alerts.js +357 -0
  309. package/dist/tools/portfolio/alerts.js.map +1 -0
  310. package/dist/tools/portfolio/correlation.d.ts +1 -1
  311. package/dist/tools/portfolio/correlation.js +33 -13
  312. package/dist/tools/portfolio/correlation.js.map +1 -1
  313. package/dist/tools/portfolio/daily-report.d.ts +8 -0
  314. package/dist/tools/portfolio/daily-report.js +83 -0
  315. package/dist/tools/portfolio/daily-report.js.map +1 -0
  316. package/dist/tools/portfolio/holdings-overlap.js +10 -3
  317. package/dist/tools/portfolio/holdings-overlap.js.map +1 -1
  318. package/dist/tools/portfolio/notifications.d.ts +7 -0
  319. package/dist/tools/portfolio/notifications.js +43 -0
  320. package/dist/tools/portfolio/notifications.js.map +1 -0
  321. package/dist/tools/portfolio/predictions.d.ts +12 -6
  322. package/dist/tools/portfolio/predictions.js +337 -87
  323. package/dist/tools/portfolio/predictions.js.map +1 -1
  324. package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
  325. package/dist/tools/portfolio/risk-analysis.js +45 -6
  326. package/dist/tools/portfolio/risk-analysis.js.map +1 -1
  327. package/dist/tools/portfolio/tracker.d.ts +4 -3
  328. package/dist/tools/portfolio/tracker.js +246 -101
  329. package/dist/tools/portfolio/tracker.js.map +1 -1
  330. package/dist/tools/portfolio/watchlist.d.ts +6 -4
  331. package/dist/tools/portfolio/watchlist.js +208 -108
  332. package/dist/tools/portfolio/watchlist.js.map +1 -1
  333. package/dist/tools/sentiment/insight-format.d.ts +2 -0
  334. package/dist/tools/sentiment/insight-format.js +36 -0
  335. package/dist/tools/sentiment/insight-format.js.map +1 -0
  336. package/dist/tools/sentiment/query-match.d.ts +3 -0
  337. package/dist/tools/sentiment/query-match.js +113 -0
  338. package/dist/tools/sentiment/query-match.js.map +1 -0
  339. package/dist/tools/sentiment/reddit-sentiment.d.ts +12 -1
  340. package/dist/tools/sentiment/reddit-sentiment.js +266 -107
  341. package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
  342. package/dist/tools/sentiment/sentiment-summary.d.ts +9 -1
  343. package/dist/tools/sentiment/sentiment-summary.js +223 -205
  344. package/dist/tools/sentiment/sentiment-summary.js.map +1 -1
  345. package/dist/tools/sentiment/sentiment-trend.d.ts +1 -1
  346. package/dist/tools/sentiment/sentiment-trend.js +12 -2
  347. package/dist/tools/sentiment/sentiment-trend.js.map +1 -1
  348. package/dist/tools/sentiment/twitter-sentiment.d.ts +11 -1
  349. package/dist/tools/sentiment/twitter-sentiment.js +188 -58
  350. package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
  351. package/dist/tools/sentiment/untrusted-text.d.ts +2 -0
  352. package/dist/tools/sentiment/untrusted-text.js +17 -0
  353. package/dist/tools/sentiment/untrusted-text.js.map +1 -0
  354. package/dist/tools/sentiment/web-search.js +9 -13
  355. package/dist/tools/sentiment/web-search.js.map +1 -1
  356. package/dist/tools/sentiment/web-sentiment.js +19 -3
  357. package/dist/tools/sentiment/web-sentiment.js.map +1 -1
  358. package/dist/tools/technical/backtest.d.ts +1 -1
  359. package/dist/tools/technical/backtest.js +27 -20
  360. package/dist/tools/technical/backtest.js.map +1 -1
  361. package/dist/tools/technical/indicators.js +23 -5
  362. package/dist/tools/technical/indicators.js.map +1 -1
  363. package/dist/types/index.d.ts +3 -3
  364. package/dist/types/index.js.map +1 -1
  365. package/dist/types/market.d.ts +1 -0
  366. package/dist/types/portfolio.d.ts +14 -4
  367. package/dist/types/sentiment.d.ts +52 -0
  368. package/dist/workflows/compare-assets.d.ts +0 -3
  369. package/dist/workflows/compare-assets.js +20 -11
  370. package/dist/workflows/compare-assets.js.map +1 -1
  371. package/dist/workflows/index.d.ts +3 -4
  372. package/dist/workflows/index.js +3 -3
  373. package/dist/workflows/index.js.map +1 -1
  374. package/dist/workflows/options-screener.d.ts +0 -3
  375. package/dist/workflows/options-screener.js +4 -11
  376. package/dist/workflows/options-screener.js.map +1 -1
  377. package/dist/workflows/portfolio-builder.d.ts +0 -3
  378. package/dist/workflows/portfolio-builder.js +0 -8
  379. package/dist/workflows/portfolio-builder.js.map +1 -1
  380. package/gui/server/ask-user-bridge.ts +1 -1
  381. package/gui/server/automation-heartbeat.ts +97 -0
  382. package/gui/server/background-quotes.ts +97 -1
  383. package/gui/server/chat-event-adapter.ts +32 -10
  384. package/gui/server/chat-run-session.ts +16 -0
  385. package/gui/server/invoke-tool.ts +160 -3
  386. package/gui/server/live-chat-event-adapter.ts +21 -6
  387. package/gui/server/market-state-api.ts +315 -0
  388. package/gui/server/model-setup.ts +156 -2
  389. package/gui/server/private-api-access.ts +62 -0
  390. package/gui/server/projector.ts +18 -9
  391. package/gui/server/prompt-observation.ts +4 -7
  392. package/gui/server/quote-snapshot-store.ts +50 -0
  393. package/gui/server/server.ts +218 -451
  394. package/gui/server/session-actions.ts +186 -1
  395. package/gui/server/shutdown.ts +47 -0
  396. package/gui/server/tool-invoke-ack.ts +49 -0
  397. package/gui/server/tool-metadata.ts +101 -24
  398. package/gui/server/websocket.ts +13 -3
  399. package/gui/server/writer-lock.ts +6 -2
  400. package/gui/server/ws-hub.ts +311 -0
  401. package/gui/shared/chat-events.ts +16 -1
  402. package/gui/shared/event-reducer.ts +24 -6
  403. package/gui/web/dist/assets/CatalogOverlay-CgeY5Pkp.js +1 -0
  404. package/gui/web/dist/assets/index-C6W_2eAn.js +69 -0
  405. package/gui/web/dist/assets/index-hwbx24a5.css +1 -0
  406. package/gui/web/dist/index.html +2 -2
  407. package/package.json +9 -6
  408. package/src/analysts/contracts.ts +10 -23
  409. package/src/analysts/orchestrator.ts +8 -43
  410. package/src/cli.ts +76 -12
  411. package/src/config.ts +44 -9
  412. package/src/index.ts +1 -1
  413. package/src/infra/cache.ts +41 -30
  414. package/src/infra/http-client.ts +72 -6
  415. package/src/infra/index.ts +6 -10
  416. package/src/infra/native-dependencies.ts +8 -3
  417. package/src/infra/node-version.ts +3 -1
  418. package/src/infra/opencandle-paths.ts +3 -14
  419. package/src/infra/rate-limiter.ts +22 -19
  420. package/src/market-state/alert-conditions.ts +82 -0
  421. package/src/market-state/alert-runner.ts +863 -0
  422. package/src/market-state/daily-report.ts +247 -0
  423. package/src/market-state/local-automation-service.ts +162 -0
  424. package/src/market-state/notification-delivery.ts +158 -0
  425. package/src/market-state/resolve-for-mutation.ts +24 -0
  426. package/src/market-state/resolve.ts +112 -0
  427. package/src/market-state/service.ts +2344 -0
  428. package/src/memory/index.ts +7 -7
  429. package/src/memory/manager.ts +14 -16
  430. package/src/memory/retrieval.ts +8 -7
  431. package/src/memory/sqlite.ts +407 -6
  432. package/src/memory/storage.ts +5 -15
  433. package/src/memory/tool-defaults.ts +60 -39
  434. package/src/memory/types.ts +3 -3
  435. package/src/monitor.ts +121 -0
  436. package/src/onboarding/connect.ts +24 -31
  437. package/src/onboarding/credential-interceptor.ts +3 -15
  438. package/src/onboarding/degradation-accumulator.ts +1 -3
  439. package/src/onboarding/provider-status.ts +410 -0
  440. package/src/onboarding/providers.ts +144 -45
  441. package/src/onboarding/state.ts +13 -15
  442. package/src/onboarding/tool-helpers.ts +2 -9
  443. package/src/onboarding/tool-tags.ts +51 -8
  444. package/src/onboarding/validation.ts +16 -22
  445. package/src/pi/opencandle-extension.ts +643 -101
  446. package/src/pi/session.ts +7 -5
  447. package/src/pi/setup.ts +61 -43
  448. package/src/pi/tool-adapter.ts +19 -6
  449. package/src/prompts/context-builder.ts +24 -13
  450. package/src/prompts/policy-cards.ts +3 -3
  451. package/src/prompts/sections.ts +1 -1
  452. package/src/prompts/symbol-preflight.ts +80 -0
  453. package/src/prompts/workflow-prompts.ts +77 -28
  454. package/src/providers/alpha-vantage.ts +58 -39
  455. package/src/providers/coingecko.ts +2 -5
  456. package/src/providers/errors.ts +9 -0
  457. package/src/providers/exa-search.ts +24 -22
  458. package/src/providers/external-tool-error.ts +20 -0
  459. package/src/providers/fear-greed.ts +1 -1
  460. package/src/providers/finnhub.ts +7 -6
  461. package/src/providers/fred.ts +3 -3
  462. package/src/providers/index.ts +14 -6
  463. package/src/providers/reddit-cli.ts +317 -0
  464. package/src/providers/reddit.ts +14 -59
  465. package/src/providers/sec-edgar.ts +20 -6
  466. package/src/providers/tradingview.ts +399 -0
  467. package/src/providers/twitter-cli.ts +233 -0
  468. package/src/providers/twitter.ts +8 -79
  469. package/src/providers/web-search.ts +30 -20
  470. package/src/providers/with-fallback.ts +8 -7
  471. package/src/providers/wrap-provider.ts +49 -10
  472. package/src/providers/yahoo-finance.ts +204 -66
  473. package/src/routing/classify-intent.ts +101 -10
  474. package/src/routing/defaults.ts +1 -1
  475. package/src/routing/entity-extractor.ts +287 -38
  476. package/src/routing/fund-symbols.ts +58 -0
  477. package/src/routing/horizon.ts +7 -0
  478. package/src/routing/index.ts +48 -48
  479. package/src/routing/planning.ts +145 -53
  480. package/src/routing/route-manifest.ts +37 -15
  481. package/src/routing/router-llm-client.ts +4 -4
  482. package/src/routing/router-prompt.ts +15 -19
  483. package/src/routing/router-types.ts +2 -5
  484. package/src/routing/router.ts +251 -53
  485. package/src/routing/slot-resolver.ts +34 -11
  486. package/src/routing/symbol-disambiguator.ts +72 -0
  487. package/src/routing/turn-context.ts +6 -9
  488. package/src/routing/types.ts +2 -0
  489. package/src/runtime/answer-contracts.ts +105 -45
  490. package/src/runtime/artifact-contracts.ts +2 -1
  491. package/src/runtime/planning-evidence.ts +157 -66
  492. package/src/runtime/prompt-step.ts +1 -16
  493. package/src/runtime/run-context.ts +12 -2
  494. package/src/runtime/session-coordinator.ts +238 -63
  495. package/src/runtime/session-title.ts +60 -0
  496. package/src/runtime/tool-defaults-wrapper.ts +13 -5
  497. package/src/runtime/validation.ts +1 -4
  498. package/src/runtime/workflow-events.ts +7 -7
  499. package/src/runtime/workflow-runner.ts +5 -11
  500. package/src/sentiment/adapters/finnhub.ts +7 -2
  501. package/src/sentiment/adapters/reddit.ts +2 -2
  502. package/src/sentiment/adapters/twitter.ts +1 -1
  503. package/src/sentiment/adapters/web.ts +1 -1
  504. package/src/sentiment/index.ts +17 -26
  505. package/src/sentiment/insights.ts +269 -0
  506. package/src/sentiment/keywords.ts +26 -4
  507. package/src/sentiment/pipeline.ts +28 -5
  508. package/src/sentiment/scorer.ts +13 -2
  509. package/src/sentiment/store.ts +2 -2
  510. package/src/sentiment/trends.ts +9 -3
  511. package/src/sentiment/types.ts +8 -4
  512. package/src/system-prompt.ts +6 -9
  513. package/src/tool-kit.ts +10 -9
  514. package/src/tools/fundamentals/company-overview.ts +19 -9
  515. package/src/tools/fundamentals/comps.ts +68 -55
  516. package/src/tools/fundamentals/dcf.ts +145 -95
  517. package/src/tools/fundamentals/earnings.ts +16 -6
  518. package/src/tools/fundamentals/financials.ts +16 -7
  519. package/src/tools/fundamentals/sec-filings.ts +37 -16
  520. package/src/tools/index.ts +56 -43
  521. package/src/tools/interaction/ask-user.ts +22 -10
  522. package/src/tools/macro/fear-greed.ts +1 -1
  523. package/src/tools/macro/fred-data.ts +58 -46
  524. package/src/tools/market/crypto-history.ts +8 -3
  525. package/src/tools/market/crypto-price.ts +6 -6
  526. package/src/tools/market/screen-stocks.ts +279 -0
  527. package/src/tools/market/search-ticker.ts +218 -17
  528. package/src/tools/market/stock-history.ts +37 -12
  529. package/src/tools/market/stock-quote.ts +10 -7
  530. package/src/tools/options/greeks.ts +5 -5
  531. package/src/tools/options/option-chain.ts +41 -17
  532. package/src/tools/portfolio/alerts.ts +457 -0
  533. package/src/tools/portfolio/correlation.ts +47 -20
  534. package/src/tools/portfolio/daily-report.ts +101 -0
  535. package/src/tools/portfolio/holdings-overlap.ts +31 -15
  536. package/src/tools/portfolio/notifications.ts +45 -0
  537. package/src/tools/portfolio/predictions.ts +406 -106
  538. package/src/tools/portfolio/risk-analysis.ts +46 -7
  539. package/src/tools/portfolio/tracker.ts +270 -109
  540. package/src/tools/portfolio/watchlist.ts +250 -121
  541. package/src/tools/sentiment/insight-format.ts +50 -0
  542. package/src/tools/sentiment/query-match.ts +117 -0
  543. package/src/tools/sentiment/reddit-sentiment.ts +360 -121
  544. package/src/tools/sentiment/sentiment-summary.ts +302 -235
  545. package/src/tools/sentiment/sentiment-trend.ts +24 -7
  546. package/src/tools/sentiment/twitter-sentiment.ts +264 -73
  547. package/src/tools/sentiment/untrusted-text.ts +21 -0
  548. package/src/tools/sentiment/web-search.ts +21 -18
  549. package/src/tools/sentiment/web-sentiment.ts +30 -10
  550. package/src/tools/technical/backtest.ts +32 -22
  551. package/src/tools/technical/indicators.ts +39 -14
  552. package/src/types/index.ts +8 -3
  553. package/src/types/market.ts +1 -0
  554. package/src/types/portfolio.ts +14 -4
  555. package/src/types/sentiment.ts +61 -2
  556. package/src/workflows/compare-assets.ts +33 -21
  557. package/src/workflows/index.ts +3 -4
  558. package/src/workflows/options-screener.ts +27 -29
  559. package/src/workflows/portfolio-builder.ts +34 -27
  560. package/dist/infra/browser.d.ts +0 -35
  561. package/dist/infra/browser.js +0 -103
  562. package/dist/infra/browser.js.map +0 -1
  563. package/dist/tools/interaction/twitter-login.d.ts +0 -8
  564. package/dist/tools/interaction/twitter-login.js +0 -77
  565. package/dist/tools/interaction/twitter-login.js.map +0 -1
  566. package/dist/workflows/types.d.ts +0 -4
  567. package/dist/workflows/types.js +0 -2
  568. package/dist/workflows/types.js.map +0 -1
  569. package/gui/web/dist/assets/CatalogOverlay-Bmp6Knu7.js +0 -1
  570. package/gui/web/dist/assets/index-Bxt9QpLX.css +0 -1
  571. package/gui/web/dist/assets/index-CZ9DHZYy.js +0 -67
  572. package/src/infra/browser.ts +0 -111
  573. package/src/tools/interaction/twitter-login.ts +0 -93
  574. package/src/workflows/types.ts +0 -4
@@ -0,0 +1,317 @@
1
+ import { spawn } from "node:child_process";
2
+ import { ExternalToolError, ExternalToolNotInstalled } from "./external-tool-error.js";
3
+ import type { RedditComment } from "./reddit.js";
4
+
5
+ const RDT_BINARY = "rdt";
6
+ const RDT_INSTALL_CMD = "uv tool install rdt-cli";
7
+ const COMMAND_TIMEOUT_MS = 20_000;
8
+ const MAX_OUTPUT_CHARS = 2_000_000;
9
+
10
+ interface RdtEnvelope<T> {
11
+ readonly ok: boolean;
12
+ readonly schema_version: string;
13
+ readonly data?: T;
14
+ readonly error?: {
15
+ readonly code?: string;
16
+ readonly message?: string;
17
+ };
18
+ }
19
+
20
+ export interface RdtPost {
21
+ readonly id: string;
22
+ readonly title: string;
23
+ readonly selftext: string;
24
+ readonly author: string;
25
+ readonly score: number;
26
+ readonly comments: number;
27
+ readonly subreddit?: string;
28
+ readonly url: string;
29
+ readonly permalink?: string;
30
+ readonly created: string;
31
+ }
32
+
33
+ interface RawRdtPost {
34
+ readonly id?: unknown;
35
+ readonly title?: unknown;
36
+ readonly selftext?: unknown;
37
+ readonly author?: unknown;
38
+ readonly score?: unknown;
39
+ readonly num_comments?: unknown;
40
+ readonly subreddit?: unknown;
41
+ readonly url?: unknown;
42
+ readonly permalink?: unknown;
43
+ readonly created_utc?: unknown;
44
+ }
45
+
46
+ interface RawRdtComment {
47
+ readonly id?: unknown;
48
+ readonly body?: unknown;
49
+ readonly author?: unknown;
50
+ readonly score?: unknown;
51
+ readonly permalink?: unknown;
52
+ }
53
+
54
+ interface RdtListingChild<T> {
55
+ readonly kind?: string;
56
+ readonly data?: T;
57
+ }
58
+
59
+ interface RdtListing<T> {
60
+ readonly data?: {
61
+ readonly children?: Array<RdtListingChild<T>>;
62
+ };
63
+ }
64
+
65
+ interface CommandResult {
66
+ readonly code: number | null;
67
+ readonly stdout: string;
68
+ readonly stderr: string;
69
+ }
70
+
71
+ type RdtCommandRunner = (command: string, args: readonly string[]) => Promise<CommandResult>;
72
+
73
+ let commandRunner: RdtCommandRunner = runCommand;
74
+
75
+ export function setRdtCommandRunnerForTests(runner: RdtCommandRunner): void {
76
+ commandRunner = runner;
77
+ }
78
+
79
+ export function resetRdtCommandRunnerForTests(): void {
80
+ commandRunner = runCommand;
81
+ }
82
+
83
+ export async function searchRedditPosts(
84
+ query: string,
85
+ opts: { subreddit?: string; limit: number },
86
+ ): Promise<RdtPost[]> {
87
+ const args = ["search", query];
88
+ if (opts.subreddit) {
89
+ args.push("--subreddit", opts.subreddit);
90
+ }
91
+ args.push("--json", "--compact", "-n", String(opts.limit));
92
+ const data = await runRdt<RawRdtPost[]>(args);
93
+ if (!Array.isArray(data)) {
94
+ throw new ExternalToolError(RDT_BINARY, "rdt-cli returned invalid search data");
95
+ }
96
+ return data.map(adaptPost).filter((post) => post.id.length > 0);
97
+ }
98
+
99
+ export async function listSubredditPosts(
100
+ subreddit: string,
101
+ opts: { limit: number },
102
+ ): Promise<RdtPost[]> {
103
+ const data = await runRdt<RawRdtPost[]>([
104
+ "sub",
105
+ subreddit,
106
+ "--json",
107
+ "--compact",
108
+ "-n",
109
+ String(opts.limit),
110
+ ]);
111
+ if (!Array.isArray(data)) {
112
+ throw new ExternalToolError(RDT_BINARY, "rdt-cli returned invalid subreddit data");
113
+ }
114
+ return data.map((raw) => adaptPost({ ...raw, subreddit })).filter((post) => post.id.length > 0);
115
+ }
116
+
117
+ export async function readRedditPost(
118
+ postId: string,
119
+ opts: { limit: number },
120
+ ): Promise<{ post: RdtPost; comments: RedditComment[] }> {
121
+ const data = await runRdt<Array<RdtListing<RawRdtPost | RawRdtComment>>>([
122
+ "read",
123
+ postId,
124
+ "--json",
125
+ "-n",
126
+ String(opts.limit),
127
+ ]);
128
+ if (!Array.isArray(data)) {
129
+ throw new ExternalToolError(RDT_BINARY, "rdt-cli returned invalid read data");
130
+ }
131
+
132
+ const rawPost = data[0]?.data?.children?.find((child) => child.kind === "t3")?.data;
133
+ if (!rawPost) {
134
+ throw new ExternalToolError(RDT_BINARY, "rdt-cli read output did not include a post");
135
+ }
136
+
137
+ const post = adaptPost(rawPost as RawRdtPost);
138
+ const commentListing = data[1]?.data?.children ?? [];
139
+ const comments = commentListing
140
+ .filter((child): child is RdtListingChild<RawRdtComment> => child.kind === "t1")
141
+ .map((child) => adaptComment(child.data, post.permalink))
142
+ .filter((comment): comment is RedditComment => comment !== null)
143
+ .sort((a, b) => b.score - a.score)
144
+ .slice(0, opts.limit);
145
+
146
+ return { post, comments };
147
+ }
148
+
149
+ async function runRdt<T>(args: readonly string[]): Promise<T> {
150
+ let result: CommandResult;
151
+ try {
152
+ result = await commandRunner(RDT_BINARY, args);
153
+ } catch (err) {
154
+ const nodeError = err as NodeJS.ErrnoException;
155
+ if (nodeError.code === "ENOENT") {
156
+ throw new ExternalToolNotInstalled(RDT_BINARY, RDT_INSTALL_CMD);
157
+ }
158
+ throw new ExternalToolError(
159
+ RDT_BINARY,
160
+ redactSensitiveOutput(err instanceof Error ? err.message : String(err)),
161
+ );
162
+ }
163
+
164
+ if (result.code !== 0) {
165
+ const envelopeError = parseCliErrorEnvelope(result.stdout);
166
+ if (envelopeError) {
167
+ throw new ExternalToolError(
168
+ RDT_BINARY,
169
+ redactSensitiveOutput(envelopeError.message),
170
+ envelopeError.code,
171
+ );
172
+ }
173
+ throw new ExternalToolError(
174
+ RDT_BINARY,
175
+ redactSensitiveOutput(result.stderr.trim() || `rdt-cli exited with code ${result.code}`),
176
+ );
177
+ }
178
+
179
+ let envelope: RdtEnvelope<T>;
180
+ try {
181
+ envelope = JSON.parse(result.stdout) as RdtEnvelope<T>;
182
+ } catch {
183
+ throw new ExternalToolError(
184
+ RDT_BINARY,
185
+ `rdt-cli returned non-JSON output: ${redactSensitiveOutput(result.stdout.slice(0, 200))}`,
186
+ );
187
+ }
188
+
189
+ if (!envelope.ok) {
190
+ throw new ExternalToolError(
191
+ RDT_BINARY,
192
+ redactSensitiveOutput(envelope.error?.message ?? "rdt-cli returned an error"),
193
+ envelope.error?.code,
194
+ );
195
+ }
196
+ if (envelope.data === undefined) {
197
+ throw new ExternalToolError(RDT_BINARY, "rdt-cli returned no data");
198
+ }
199
+ return envelope.data;
200
+ }
201
+
202
+ function parseCliErrorEnvelope(stdout: string): { code?: string; message: string } | null {
203
+ try {
204
+ const parsed = JSON.parse(stdout) as {
205
+ ok?: unknown;
206
+ error?: { code?: unknown; message?: unknown };
207
+ };
208
+ if (parsed.ok !== false || typeof parsed.error?.message !== "string") return null;
209
+ return {
210
+ code: typeof parsed.error.code === "string" ? parsed.error.code : undefined,
211
+ message: parsed.error.message,
212
+ };
213
+ } catch {
214
+ return null;
215
+ }
216
+ }
217
+
218
+ function adaptPost(raw: RawRdtPost): RdtPost {
219
+ const permalink = stringValue(raw.permalink);
220
+ const url = stringValue(raw.url) || (permalink ? `https://reddit.com${permalink}` : "");
221
+ return {
222
+ id: stringValue(raw.id),
223
+ title: stringValue(raw.title),
224
+ selftext: stringValue(raw.selftext),
225
+ author: stringValue(raw.author) || "unknown",
226
+ score: numberValue(raw.score),
227
+ comments: numberValue(raw.num_comments),
228
+ subreddit: stringValue(raw.subreddit) || undefined,
229
+ url,
230
+ permalink: permalink || undefined,
231
+ created: normalizeCreatedAt(raw.created_utc),
232
+ };
233
+ }
234
+
235
+ function adaptComment(
236
+ raw: RawRdtComment | undefined,
237
+ postPermalink?: string,
238
+ ): RedditComment | null {
239
+ if (!raw || typeof raw.body !== "string" || raw.body.length === 0) return null;
240
+ const permalink = stringValue(raw.permalink) || postPermalink || "";
241
+ return {
242
+ id: stringValue(raw.id),
243
+ body: raw.body,
244
+ author: stringValue(raw.author) || "unknown",
245
+ score: numberValue(raw.score),
246
+ permalink: permalink ? `https://reddit.com${permalink}` : "",
247
+ };
248
+ }
249
+
250
+ function normalizeCreatedAt(value: unknown): string {
251
+ if (typeof value === "number" && Number.isFinite(value)) {
252
+ const millis = value > 1_000_000_000_000 ? value : value * 1000;
253
+ return new Date(millis).toISOString();
254
+ }
255
+ if (typeof value === "string" && value.length > 0) {
256
+ const millis = Date.parse(value);
257
+ if (!Number.isNaN(millis)) return new Date(millis).toISOString();
258
+ }
259
+ return new Date(0).toISOString();
260
+ }
261
+
262
+ function stringValue(value: unknown): string {
263
+ return typeof value === "string" ? value : "";
264
+ }
265
+
266
+ function numberValue(value: unknown): number {
267
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
268
+ }
269
+
270
+ export function redactSensitiveOutput(input: string): string {
271
+ return input
272
+ .slice(0, MAX_OUTPUT_CHARS)
273
+ .replace(
274
+ /\b([a-z0-9_]*(?:cookie|session|token)[a-z0-9_]*)\b\s*[:=]\s*[^;\s,)]+/gi,
275
+ "$1=[redacted]",
276
+ )
277
+ .replace(/\b([a-z0-9_]*(?:cookie|session|token)[a-z0-9_]*)=([^;\s,)]+)/gi, "$1=[redacted]")
278
+ .replace(
279
+ /(?:~|\/Users\/[^/\s]+|\/home\/[^/\s]+)?\/\.config\/rdt-cli\/credential\.json/g,
280
+ "[redacted-credential-path]",
281
+ );
282
+ }
283
+
284
+ function runCommand(command: string, args: readonly string[]): Promise<CommandResult> {
285
+ return new Promise((resolve, reject) => {
286
+ const child = spawn(command, [...args], { stdio: ["ignore", "pipe", "pipe"] });
287
+ let stdout = "";
288
+ let stderr = "";
289
+ let settled = false;
290
+
291
+ const timeout = setTimeout(() => {
292
+ if (settled) return;
293
+ settled = true;
294
+ child.kill("SIGTERM");
295
+ reject(new Error(`${command} timed out after ${COMMAND_TIMEOUT_MS}ms`));
296
+ }, COMMAND_TIMEOUT_MS);
297
+
298
+ child.stdout.on("data", (chunk: Buffer) => {
299
+ stdout = (stdout + chunk.toString("utf8")).slice(0, MAX_OUTPUT_CHARS);
300
+ });
301
+ child.stderr.on("data", (chunk: Buffer) => {
302
+ stderr = (stderr + chunk.toString("utf8")).slice(0, MAX_OUTPUT_CHARS);
303
+ });
304
+ child.on("error", (err) => {
305
+ if (settled) return;
306
+ settled = true;
307
+ clearTimeout(timeout);
308
+ reject(err);
309
+ });
310
+ child.on("close", (code) => {
311
+ if (settled) return;
312
+ settled = true;
313
+ clearTimeout(timeout);
314
+ resolve({ code, stdout, stderr });
315
+ });
316
+ });
317
+ }
@@ -1,53 +1,23 @@
1
- import { httpGet } from "../infra/http-client.js";
2
- import { cache, TTL, STALE_LIMIT } from "../infra/cache.js";
1
+ import { cache, STALE_LIMIT, TTL } from "../infra/cache.js";
3
2
  import { rateLimiter } from "../infra/rate-limiter.js";
3
+ import { BEARISH_TERMS, BULLISH_TERMS } from "../sentiment/keywords.js";
4
4
  import type { RedditSentimentResult } from "../types/sentiment.js";
5
- import { BULLISH_TERMS, BEARISH_TERMS } from "../sentiment/keywords.js";
6
-
7
- interface RedditListingResponse {
8
- data: {
9
- children: Array<{
10
- data: {
11
- id: string;
12
- title: string;
13
- selftext: string;
14
- author: string;
15
- score: number;
16
- num_comments: number;
17
- permalink: string;
18
- created_utc: number;
19
- };
20
- }>;
21
- };
22
- }
23
-
24
- const REDDIT_HEADERS = { "User-Agent": "OpenCandle/1.0 (financial analysis agent)" };
5
+ import { listSubredditPosts, readRedditPost, searchRedditPosts } from "./reddit-cli.js";
25
6
 
26
7
  export async function getSubredditPosts(
27
8
  subreddit: string,
28
9
  limit: number = 25,
10
+ query?: string,
29
11
  ): Promise<RedditSentimentResult> {
30
- const cacheKey = `reddit:${subreddit}:${limit}`;
12
+ const cacheKey = `reddit:${subreddit}:${query ?? "hot"}:${limit}`;
31
13
  const cached = cache.get<RedditSentimentResult>(cacheKey);
32
14
  if (cached) return cached;
33
15
 
34
16
  try {
35
17
  await rateLimiter.acquire("reddit");
36
- const url = `https://www.reddit.com/r/${encodeURIComponent(subreddit)}/hot.json?limit=${limit}`;
37
- const data = await httpGet<RedditListingResponse>(url, {
38
- headers: REDDIT_HEADERS,
39
- });
40
-
41
- const posts = data.data.children.map((child) => ({
42
- id: child.data.id,
43
- title: child.data.title,
44
- selftext: child.data.selftext ?? "",
45
- author: child.data.author ?? "unknown",
46
- score: child.data.score,
47
- comments: child.data.num_comments,
48
- url: `https://reddit.com${child.data.permalink}`,
49
- created: new Date(child.data.created_utc * 1000).toISOString(),
50
- }));
18
+ const posts = query
19
+ ? await searchRedditPosts(query, { subreddit, limit })
20
+ : await listSubredditPosts(subreddit, { limit });
51
21
 
52
22
  // Extract ticker-like mentions ($AAPL, $TSLA, etc.)
53
23
  const tickerRegex = /\$([A-Z]{1,5})\b/g;
@@ -107,24 +77,7 @@ export async function getPostComments(
107
77
  if (cached) return cached;
108
78
 
109
79
  await rateLimiter.acquire("reddit_comments");
110
- const url = `https://www.reddit.com/r/${encodeURIComponent(subreddit)}/comments/${postId}.json`;
111
- const data = await httpGet<Array<{ data: { children: Array<{ kind: string; data: { id: string; body?: string; author?: string; score?: number; permalink?: string } }> } }>>(url, {
112
- headers: REDDIT_HEADERS,
113
- });
114
-
115
- // Comments are in the second listing element
116
- const commentListing = data[1]?.data?.children ?? [];
117
- const comments: RedditComment[] = commentListing
118
- .filter((c) => c.kind === "t1" && c.data.body)
119
- .sort((a, b) => (b.data.score ?? 0) - (a.data.score ?? 0))
120
- .slice(0, limit)
121
- .map((c) => ({
122
- id: c.data.id,
123
- body: c.data.body!,
124
- author: c.data.author ?? "unknown",
125
- score: c.data.score ?? 0,
126
- permalink: `https://reddit.com${c.data.permalink ?? ""}`,
127
- }));
80
+ const { comments } = await readRedditPost(postId, { limit });
128
81
 
129
82
  cache.set(cacheKey, comments, COMMENT_TTL);
130
83
  return comments;
@@ -132,9 +85,11 @@ export async function getPostComments(
132
85
 
133
86
  // ── Sentiment scoring ───────────────────────────────────
134
87
 
135
- export function scoreSentiment(
136
- posts: Array<{ title: string }>,
137
- ): { score: number; bullish: number; bearish: number } {
88
+ export function scoreSentiment(posts: Array<{ title: string }>): {
89
+ score: number;
90
+ bullish: number;
91
+ bearish: number;
92
+ } {
138
93
  let bullish = 0;
139
94
  let bearish = 0;
140
95
  for (const post of posts) {
@@ -1,9 +1,11 @@
1
- import { httpGet } from "../infra/http-client.js";
2
1
  import { cache, TTL } from "../infra/cache.js";
2
+ import { httpGet } from "../infra/http-client.js";
3
+ import { rateLimiter } from "../infra/rate-limiter.js";
3
4
 
4
5
  const EFTS_BASE = "https://efts.sec.gov/LATEST/search-index";
5
6
  const COMPANY_TICKERS_URL = "https://www.sec.gov/files/company_tickers.json";
6
7
  const SUBMISSIONS_BASE = "https://data.sec.gov/submissions";
8
+ const SEC_DOCUMENT_FETCH_TIMEOUT_MS = 10_000;
7
9
 
8
10
  export interface SECFiling {
9
11
  formType: string;
@@ -15,6 +17,7 @@ export interface SECFiling {
15
17
  primaryDocumentUrl?: string;
16
18
  items?: string[];
17
19
  evidenceSnippets?: string[];
20
+ evidenceWarning?: string;
18
21
  }
19
22
 
20
23
  export interface SearchFilingsOptions {
@@ -71,7 +74,9 @@ export async function searchFilings(
71
74
  if (cached) return cached;
72
75
 
73
76
  if (options.includeSnippets) {
74
- const submissions = await searchFilingsFromCompanySubmissions(ticker, formTypes, limit).catch(() => []);
77
+ const submissions = await searchFilingsFromCompanySubmissions(ticker, formTypes, limit).catch(
78
+ () => [],
79
+ );
75
80
  if (submissions.length > 0) {
76
81
  await enrichWithEvidenceSnippets(submissions, options.snippetLimitPerFiling ?? 3);
77
82
  cache.set(cacheKey, submissions, TTL.FUNDAMENTALS);
@@ -186,7 +191,10 @@ async function resolveCompanyTicker(ticker: string): Promise<CompanyTickerEntry
186
191
 
187
192
  function splitFilingItems(raw: string | undefined): string[] {
188
193
  return raw
189
- ? raw.split(",").map((item) => item.trim()).filter(Boolean)
194
+ ? raw
195
+ .split(",")
196
+ .map((item) => item.trim())
197
+ .filter(Boolean)
190
198
  : [];
191
199
  }
192
200
 
@@ -222,23 +230,29 @@ function getDateYearsAgo(years: number): string {
222
230
  return d.toISOString().split("T")[0];
223
231
  }
224
232
 
225
- async function enrichWithEvidenceSnippets(filings: SECFiling[], limitPerFiling: number): Promise<void> {
233
+ async function enrichWithEvidenceSnippets(
234
+ filings: SECFiling[],
235
+ limitPerFiling: number,
236
+ ): Promise<void> {
226
237
  await Promise.all(
227
238
  filings.map(async (filing) => {
228
239
  if (!filing.primaryDocumentUrl) return;
229
240
  try {
230
241
  const raw = await fetchText(filing.primaryDocumentUrl);
231
242
  filing.evidenceSnippets = extractEvidenceSnippets(raw, limitPerFiling);
232
- } catch {
243
+ } catch (error) {
233
244
  filing.evidenceSnippets = [];
245
+ filing.evidenceWarning = `Evidence fetch failed for primary document: ${error instanceof Error ? error.message : String(error)}`;
234
246
  }
235
247
  }),
236
248
  );
237
249
  }
238
250
 
239
251
  async function fetchText(url: string): Promise<string> {
252
+ await rateLimiter.acquire("sec_edgar");
240
253
  const response = await fetch(url, {
241
254
  headers: { "User-Agent": "OpenCandle/1.0 (financial analysis agent)" },
255
+ signal: AbortSignal.timeout(SEC_DOCUMENT_FETCH_TIMEOUT_MS),
242
256
  });
243
257
  if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
244
258
  return response.text();
@@ -300,7 +314,7 @@ function stripFilingMarkup(raw: string): string {
300
314
  .replace(/&amp;/gi, "&")
301
315
  .replace(/&#160;/g, " ")
302
316
  .replace(/&#8217;/g, "'")
303
- .replace(/&#8220;|&#8221;/g, "\"")
317
+ .replace(/&#8220;|&#8221;/g, '"')
304
318
  .replace(/\s+/g, " ")
305
319
  .trim();
306
320
  }