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
@@ -1,12 +1,14 @@
1
+ import { parseDteTarget } from "../routing/defaults.js";
2
+ import { areLikelyFundOrIndexSymbols, isFundOrIndexAssetScope } from "../routing/fund-symbols.js";
3
+ import { isLongInvestmentHorizon } from "../routing/horizon.js";
4
+ import type { RouterOutput } from "../routing/router-types.js";
1
5
  import type {
2
- PortfolioSlots,
3
- OptionsScreenerSlots,
4
6
  CompareAssetsSlots,
7
+ OptionsScreenerSlots,
8
+ PortfolioSlots,
5
9
  SlotResolution,
6
10
  SlotSource,
7
11
  } from "../routing/types.js";
8
- import type { RouterOutput } from "../routing/router-types.js";
9
- import { parseDteTarget } from "../routing/defaults.js";
10
12
 
11
13
  function tag(source: string | undefined): string {
12
14
  switch (source) {
@@ -14,7 +16,6 @@ function tag(source: string | undefined): string {
14
16
  return " [DEFAULT]";
15
17
  case "preference":
16
18
  return " [SAVED PREFERENCE]";
17
- case "user":
18
19
  default:
19
20
  return "";
20
21
  }
@@ -96,8 +97,10 @@ export function buildDisclosureBlock(
96
97
  const lines: string[] = [];
97
98
  lines.push("Assumptions (reproduce this block exactly — do not relabel sources):");
98
99
  if (userSpecified.length > 0) lines.push(` User-specified: ${userSpecified.join(", ")}`);
99
- if (fromPreferences.length > 0) lines.push(` From saved preferences: ${fromPreferences.join(", ")}`);
100
- if (fromPriorContext.length > 0) lines.push(` From prior context: ${fromPriorContext.join(", ")}`);
100
+ if (fromPreferences.length > 0)
101
+ lines.push(` From saved preferences: ${fromPreferences.join(", ")}`);
102
+ if (fromPriorContext.length > 0)
103
+ lines.push(` From prior context: ${fromPriorContext.join(", ")}`);
101
104
  if (fromMemory.length > 0) lines.push(` From memory: ${fromMemory.join(", ")}`);
102
105
  if (defaults.length > 0) lines.push(` Defaults: ${defaults.join(", ")}`);
103
106
  if (workflowConstraints && workflowConstraints.length > 0) {
@@ -139,7 +142,10 @@ function formatSlotValue(value: unknown): string {
139
142
  export function buildPortfolioPrompt(resolution: SlotResolution<PortfolioSlots>): string {
140
143
  const { resolved: s, sources } = resolution;
141
144
  const normalizedScope = s.assetScope.toLowerCase();
142
- const isFundBuildingBlocks = normalizedScope.includes("etf") || normalizedScope.includes("fund") || normalizedScope.includes("building_blocks");
145
+ const isFundBuildingBlocks =
146
+ normalizedScope.includes("etf") ||
147
+ normalizedScope.includes("fund") ||
148
+ normalizedScope.includes("building_blocks");
143
149
 
144
150
  const disclosureBlock = buildDisclosureBlock(
145
151
  {
@@ -198,7 +204,9 @@ Response format:
198
204
  - Suggest what to change for more growth or more safety.`;
199
205
  }
200
206
 
201
- export function buildOptionsScreenerPrompt(resolution: SlotResolution<OptionsScreenerSlots>): string {
207
+ export function buildOptionsScreenerPrompt(
208
+ resolution: SlotResolution<OptionsScreenerSlots>,
209
+ ): string {
202
210
  const { resolved: s, sources } = resolution;
203
211
 
204
212
  const dateStr = todayStr();
@@ -225,14 +233,15 @@ Ranking constraints:
225
233
  - Do NOT rank ultra-cheap near-zero-delta contracts as "best."
226
234
  `
227
235
  : "";
228
- const longDatedInstructions = s.dteTarget === "180_plus_days"
229
- ? `
236
+ const longDatedInstructions =
237
+ s.dteTarget === "180_plus_days"
238
+ ? `
230
239
  For LEAPS / long-dated options:
231
240
  - First call get_option_chain without an expiration to inspect available expirations.
232
241
  - Choose available expirations inside the target window, then call get_option_chain again with explicit \`expiration\` dates before ranking contracts.
233
242
  - Do not rank the nearest-expiration chain as a LEAPS result.
234
243
  `
235
- : "";
244
+ : "";
236
245
 
237
246
  const disclosureBlock = buildDisclosureBlock(
238
247
  {
@@ -253,18 +262,26 @@ For LEAPS / long-dated options:
253
262
  );
254
263
 
255
264
  const coveredCallContext = [
256
- s.optionStrategy ? `\n- Option strategy: ${s.optionStrategy}${tag(sources.optionStrategy)}` : "",
257
- s.costBasis !== undefined ? `\n- Cost basis: ${formatBudget(s.costBasis)} (Position cost basis: ${formatBudget(s.costBasis)})${tag(sources.costBasis)}` : "",
258
- s.shareQuantity !== undefined ? `\n- Share quantity: ${s.shareQuantity} shares${tag(sources.shareQuantity)}` : "",
259
- s.catalystSymbols?.length ? `\n- Catalyst/context tickers: ${s.catalystSymbols.join(", ")}${tag(sources.catalystSymbols)}` : "",
265
+ s.optionStrategy
266
+ ? `\n- Option strategy: ${s.optionStrategy}${tag(sources.optionStrategy)}`
267
+ : "",
268
+ s.costBasis !== undefined
269
+ ? `\n- Cost basis: ${formatBudget(s.costBasis)} (Position cost basis: ${formatBudget(s.costBasis)})${tag(sources.costBasis)}`
270
+ : "",
271
+ s.shareQuantity !== undefined
272
+ ? `\n- Share quantity: ${s.shareQuantity} shares${tag(sources.shareQuantity)}`
273
+ : "",
274
+ s.catalystSymbols?.length
275
+ ? `\n- Catalyst/context tickers: ${s.catalystSymbols.join(", ")}${tag(sources.catalystSymbols)}`
276
+ : "",
260
277
  ].join("");
261
278
 
262
279
  const isProtectivePutContext = s.optionStrategy === "protective_put";
263
- const isCoveredCallContext = !isProtectivePutContext && (
264
- s.optionStrategy === "covered_call" ||
265
- s.costBasis !== undefined ||
266
- (s.catalystSymbols?.length ?? 0) > 0
267
- );
280
+ const isCoveredCallContext =
281
+ !isProtectivePutContext &&
282
+ (s.optionStrategy === "covered_call" ||
283
+ s.costBasis !== undefined ||
284
+ (s.catalystSymbols?.length ?? 0) > 0);
268
285
  const coveredCallInstructions = isCoveredCallContext
269
286
  ? `
270
287
  Covered-call sale guidance:
@@ -314,9 +331,17 @@ Steps:
314
331
  3. Filter contracts matching: ${s.direction === "bullish" && !isProtectivePutContext ? "calls" : "puts"}, DTE near ${s.dteTarget}, ${s.moneynessPreference} strikes.
315
332
  4. ${isProtectivePutContext ? "Rank by hedge quality: protection per dollar of premium, expiration fit, moneyness, liquidity, and hedge floor." : `Rank by ${s.objective}: balance premium cost, delta exposure, and probability of profit.`}${s.maxPremium !== undefined ? ` Do not rank contracts above the user's max premium of ${formatBudget(s.maxPremium)} unless no contracts under that cap are liquid; if so, say the cap could not be met.` : ""}
316
333
  5. Filter for ${s.liquidityMinimum}: high open interest and tight bid-ask spread.
317
- ${s.optionStrategy === "covered_call" ? `6. Covered call framing: treat option premium as premium received, not paid. Use the user's cost basis when provided, and include return-if-assigned and assignment/downside risk instead of long-call max-loss framing.
318
- ` : ""}${isCoveredCallContext && s.costBasis !== undefined ? `Cost-basis math: if assigned, share gain/loss is strike minus ${formatBudget(s.costBasis)} before premium. Total return if assigned is (strike - cost basis + premium received) / cost basis.
319
- ` : ""}
334
+ ${
335
+ s.optionStrategy === "covered_call"
336
+ ? `6. Covered call framing: treat option premium as premium received, not paid. Use the user's cost basis when provided, and include return-if-assigned and assignment/downside risk instead of long-call max-loss framing.
337
+ `
338
+ : ""
339
+ }${
340
+ isCoveredCallContext && s.costBasis !== undefined
341
+ ? `Cost-basis math: if assigned, share gain/loss is strike minus ${formatBudget(s.costBasis)} before premium. Total return if assigned is (strike - cost basis + premium received) / cost basis.
342
+ `
343
+ : ""
344
+ }
320
345
  ${longDatedInstructions}
321
346
  ${coveredCallInstructions}
322
347
  ${protectivePutInstructions}
@@ -336,10 +361,15 @@ export function buildCompareAssetsPrompt(resolution: SlotResolution<CompareAsset
336
361
  const symbols = resolution.resolved.symbols;
337
362
  const symbolList = symbols.join(", ");
338
363
  const timeHorizon = resolution.resolved.timeHorizon;
364
+ const budget = resolution.resolved.budget;
339
365
  const includeSentiment = resolution.resolved.metrics?.includes("sentiment") ?? false;
340
366
  const isMacroHedge = resolution.resolved.metrics?.includes("macro_hedge") ?? false;
341
367
  const isInterestRateSensitive = resolution.resolved.metrics?.includes("interest_rates") ?? false;
342
368
  const isOverlapComparison = resolution.resolved.metrics?.includes("overlap") ?? false;
369
+ const hasFundContext =
370
+ isFundOrIndexAssetScope(resolution.resolved.assetScope) || areLikelyFundOrIndexSymbols(symbols);
371
+ const shouldProbeFundOverlap =
372
+ !isOverlapComparison && isLongInvestmentHorizon(timeHorizon) && hasFundContext;
343
373
  const sentimentStep = includeSentiment
344
374
  ? `\n6. Use get_sentiment_summary for each of: ${symbolList} to compare retail/news sentiment and note source availability.`
345
375
  : "";
@@ -365,7 +395,16 @@ ETF overlap guidance:
365
395
  - Use provider top holdings and overlap weights when available. If provider coverage is partial or unavailable, say so directly and fall back to plain-language fund structure.
366
396
  - Discuss top holdings, shared mega-cap names, sector concentration, and whether the position is a deliberate tilt or accidental duplication.
367
397
  - avoid treating price, RSI, or generic risk metrics as the main answer.`
368
- : "";
398
+ : shouldProbeFundOverlap
399
+ ? `
400
+ ETF/fund overlap check:
401
+ - If these assets are ETFs, funds, or index products, use provider-backed holdings-overlap evidence before making diversification claims.
402
+ - Compare fund role, style/factor tilt, concentration, and broad sector exposure when available; do not invent exact holdings or weights.
403
+ - For dividend/income funds versus growth funds over multi-year horizons, explain taxable account dividend drag: dividends can be taxed annually even when reinvested, while more return may be deferred as capital gains in growth-oriented funds. Contrast that with tax-advantaged accounts.
404
+ - Include expense ratios, dividend yields, and AUM only when fetched evidence supports them; otherwise tell the user to verify current fund facts before acting.
405
+ - Treat holdings overlap and sector concentration as different from correlation; correlation is supporting evidence, not a substitute for constituent exposure.
406
+ - If provider holdings coverage is partial or unavailable, say so directly and continue with the available price, risk, and correlation evidence.`
407
+ : "";
369
408
  const macroHedgeSteps = isMacroHedge
370
409
  ? `
371
410
  macro hedge decision guidance:
@@ -380,16 +419,24 @@ macro hedge decision guidance:
380
419
  ? "- Present a comparison table with hedge-relevant columns: hedge role, macro drivers, volatility/drawdown evidence, correlation regime, liquidity/risk-on sensitivity, current data, and missing evidence."
381
420
  : isOverlapComparison
382
421
  ? "- Present an ETF overlap table with columns: fund role, shared top holdings/overlap weight from provider when available, sector concentration, what exposure is duplicated, what exposure is new, and diversification implication."
383
- : `- Present a comparison table with key metrics: price, P/E, revenue growth, profit margin, RSI, Sharpe, max drawdown${sentimentMetric}.
422
+ : shouldProbeFundOverlap
423
+ ? "- Present a long-horizon fund comparison table with columns: fund role/style, dividend/income versus growth tradeoff, risk evidence, holdings-overlap availability, tax and expense/yield/AUM verification gaps, and horizon fit."
424
+ : `- Present a comparison table with key metrics: price, P/E, revenue growth, profit margin, RSI, Sharpe, max drawdown${sentimentMetric}.
384
425
  - Highlight which asset is stronger on each metric.`;
385
426
  const technicalRiskSteps = isOverlapComparison
386
427
  ? `3. Use analyze_holdings_overlap with symbols [${symbolList}] to fetch provider top holdings and compute pairwise overlap by weight.
387
428
  4. Use analyze_correlation across [${symbolList}] only as supporting diversification evidence; do not substitute correlation for holdings overlap.
388
429
  5. Skip momentum/risk tool calls unless the user asks about timing or trade setup; the core question is top holdings and sector overlap.`
389
- : `3. Use get_technical_indicators for each to compare momentum and trend.
430
+ : shouldProbeFundOverlap
431
+ ? `3. Use analyze_holdings_overlap with symbols [${symbolList}] to fetch provider top holdings and compute pairwise overlap by weight.
432
+ 4. Use analyze_correlation across [${symbolList}] as supporting diversification evidence.
433
+ 5. Use analyze_risk for each to compare long-horizon risk context.
434
+ 6. Use get_technical_indicators only as secondary timing context; do not let RSI or short-term momentum dominate the long-horizon fund decision.`
435
+ : `3. Use get_technical_indicators for each to compare momentum and trend.
390
436
  4. Use analyze_risk for each to compare risk metrics.
391
437
  5. Use analyze_correlation across [${symbolList}] to check diversification.`;
392
438
  const horizonLine = timeHorizon ? `\nTime horizon: ${timeHorizon}` : "";
439
+ const budgetLine = budget !== undefined ? `\nBudget: ${formatBudget(budget)}` : "";
393
440
  const horizonSteps = timeHorizon
394
441
  ? `
395
442
  6. Adapt the comparison to the ${timeHorizon} horizon: prioritize near-term catalysts, earnings/guidance, estimate revisions, sentiment, and forward-looking valuation evidence over long-term historical averages.
@@ -406,6 +453,8 @@ macro hedge decision guidance:
406
453
  {
407
454
  symbols: symbolList,
408
455
  ...(timeHorizon ? { timeHorizon } : {}),
456
+ ...(budget !== undefined ? { budget: formatBudget(budget) } : {}),
457
+ ...(resolution.resolved.assetScope ? { assetScope: resolution.resolved.assetScope } : {}),
409
458
  ...(resolution.resolved.metrics ? { metrics: resolution.resolved.metrics.join(", ") } : {}),
410
459
  },
411
460
  resolution.sources as Record<string, SlotSource | undefined>,
@@ -413,7 +462,7 @@ macro hedge decision guidance:
413
462
 
414
463
  return `Current date: ${todayStr()}
415
464
 
416
- Compare these assets side by side: ${symbolList}${horizonLine}
465
+ Compare these assets side by side: ${symbolList}${horizonLine}${budgetLine}
417
466
 
418
467
  Steps:
419
468
  1. Use get_stock_quote for each of: ${symbolList}.
@@ -1,9 +1,9 @@
1
- import { httpGet, HttpError } 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";
2
+ import { HttpError, httpGet } from "../infra/http-client.js";
3
3
  import { rateLimiter } from "../infra/rate-limiter.js";
4
- import { ProviderCredentialError } from "./provider-credential-error.js";
5
4
  import type { CompanyOverview, EarningsData, FinancialStatement } from "../types/fundamentals.js";
6
- import type { StockQuote, OHLCV } from "../types/market.js";
5
+ import type { OHLCV, StockQuote } from "../types/market.js";
6
+ import { ProviderCredentialError } from "./provider-credential-error.js";
7
7
 
8
8
  const BASE_URL = "https://www.alphavantage.co/query";
9
9
  const MISSING_OVERVIEW_TTL = 15 * 60_000;
@@ -44,10 +44,7 @@ function throwIfApiMessage(data: unknown): void {
44
44
  throw new Error(`Alpha Vantage error: ${message}`);
45
45
  }
46
46
 
47
- export async function getOverview(
48
- symbol: string,
49
- apiKey: string,
50
- ): Promise<CompanyOverview> {
47
+ export async function getOverview(symbol: string, apiKey: string): Promise<CompanyOverview> {
51
48
  const cacheKey = `av:overview:${symbol}`;
52
49
  const missingCacheKey = `${cacheKey}:missing`;
53
50
  const cached = cache.get<CompanyOverview>(cacheKey);
@@ -98,10 +95,7 @@ export async function getOverview(
98
95
  }
99
96
  }
100
97
 
101
- export async function getEarnings(
102
- symbol: string,
103
- apiKey: string,
104
- ): Promise<EarningsData> {
98
+ export async function getEarnings(symbol: string, apiKey: string): Promise<EarningsData> {
105
99
  const cacheKey = `av:earnings:${symbol}`;
106
100
  const cached = cache.get<EarningsData>(cacheKey);
107
101
  if (cached) return cached;
@@ -132,31 +126,36 @@ export async function getEarnings(
132
126
  }
133
127
  }
134
128
 
135
- export async function getFinancials(
136
- symbol: string,
137
- apiKey: string,
138
- ): Promise<FinancialStatement[]> {
129
+ export async function getFinancials(symbol: string, apiKey: string): Promise<FinancialStatement[]> {
139
130
  const cacheKey = `av:financials:${symbol}`;
140
131
  const cached = cache.get<FinancialStatement[]>(cacheKey);
141
132
  if (cached) return cached;
142
133
 
143
134
  try {
144
135
  // Fetch sequentially to respect Alpha Vantage rate limits (5 req/min free tier)
145
- const incomeData = await fetchStatement<{ annualReports: any[] }>("INCOME_STATEMENT", symbol, apiKey);
146
- const balanceData = await fetchStatement<{ annualReports: any[] }>("BALANCE_SHEET", symbol, apiKey);
147
- const cashFlowData = await fetchStatement<{ annualReports: any[] }>("CASH_FLOW", symbol, apiKey);
136
+ const incomeData = await fetchStatement<{ annualReports: any[] }>(
137
+ "INCOME_STATEMENT",
138
+ symbol,
139
+ apiKey,
140
+ );
141
+ const balanceData = await fetchStatement<{ annualReports: any[] }>(
142
+ "BALANCE_SHEET",
143
+ symbol,
144
+ apiKey,
145
+ );
146
+ const cashFlowData = await fetchStatement<{ annualReports: any[] }>(
147
+ "CASH_FLOW",
148
+ symbol,
149
+ apiKey,
150
+ );
148
151
 
149
152
  const incomeReports = incomeData.annualReports ?? [];
150
153
  const balanceReports = balanceData.annualReports ?? [];
151
154
  const cashFlowReports = cashFlowData.annualReports ?? [];
152
155
 
153
156
  // Index balance sheet and cash flow by fiscal date for merging
154
- const balanceByDate = new Map(
155
- balanceReports.map((r: any) => [r.fiscalDateEnding, r]),
156
- );
157
- const cashFlowByDate = new Map(
158
- cashFlowReports.map((r: any) => [r.fiscalDateEnding, r]),
159
- );
157
+ const balanceByDate = new Map(balanceReports.map((r: any) => [r.fiscalDateEnding, r]));
158
+ const cashFlowByDate = new Map(cashFlowReports.map((r: any) => [r.fiscalDateEnding, r]));
160
159
 
161
160
  const statements = incomeReports.slice(0, 4).map((r: any) => {
162
161
  const balance = balanceByDate.get(r.fiscalDateEnding) ?? {};
@@ -202,10 +201,7 @@ async function fetchStatement<T>(fn: string, symbol: string, apiKey: string): Pr
202
201
  return data;
203
202
  }
204
203
 
205
- export async function getGlobalQuote(
206
- symbol: string,
207
- apiKey: string,
208
- ): Promise<StockQuote> {
204
+ export async function getGlobalQuote(symbol: string, apiKey: string): Promise<StockQuote> {
209
205
  const cacheKey = `av:globalquote:${symbol}`;
210
206
  const cached = cache.get<StockQuote>(cacheKey);
211
207
  if (cached) return cached;
@@ -233,10 +229,10 @@ export async function getGlobalQuote(
233
229
  low: parseFloat(gq["04. low"]) || 0,
234
230
  previousClose: parseFloat(gq["08. previous close"]) || 0,
235
231
  volume: parseInt(gq["06. volume"], 10) || 0,
236
- marketCap: 0, // Not available from GLOBAL_QUOTE
237
- pe: null, // Not available from GLOBAL_QUOTE
238
- week52High: 0, // Not available from GLOBAL_QUOTE
239
- week52Low: 0, // Not available from GLOBAL_QUOTE
232
+ marketCap: 0, // Not available from GLOBAL_QUOTE
233
+ pe: null, // Not available from GLOBAL_QUOTE
234
+ week52High: 0, // Not available from GLOBAL_QUOTE
235
+ week52Low: 0, // Not available from GLOBAL_QUOTE
240
236
  timestamp: Date.now(),
241
237
  };
242
238
 
@@ -266,7 +262,9 @@ export async function getDailyHistory(
266
262
  const daysNeeded = rangeToDays(range);
267
263
  const outputsize = daysNeeded > 100 ? "full" : "compact";
268
264
  const url = buildUrl("TIME_SERIES_DAILY", { symbol, outputsize }, apiKey);
269
- const data = await httpGet<{ "Time Series (Daily)": Record<string, Record<string, string>> }>(url);
265
+ const data = await httpGet<{ "Time Series (Daily)": Record<string, Record<string, string>> }>(
266
+ url,
267
+ );
270
268
  throwIfApiMessage(data);
271
269
 
272
270
  const timeSeries = data["Time Series (Daily)"];
@@ -274,7 +272,7 @@ export async function getDailyHistory(
274
272
  throw new Error(`Alpha Vantage: No daily history for ${symbol}`);
275
273
  }
276
274
 
277
- const ohlcv: OHLCV[] = Object.entries(timeSeries)
275
+ const sorted = Object.entries(timeSeries)
278
276
  .map(([date, bar]) => ({
279
277
  date,
280
278
  open: parseFloat(bar["1. open"]) || 0,
@@ -283,8 +281,14 @@ export async function getDailyHistory(
283
281
  close: parseFloat(bar["4. close"]) || 0,
284
282
  volume: parseInt(bar["5. volume"], 10) || 0,
285
283
  }))
286
- .sort((a, b) => a.date.localeCompare(b.date))
287
- .slice(-daysNeeded);
284
+ .sort((a, b) => a.date.localeCompare(b.date));
285
+
286
+ // Count-based slicing for ytd is only an estimate (ignores holidays and
287
+ // the starting weekday) and can leak prior-year bars; filter by date.
288
+ const ohlcv: OHLCV[] =
289
+ range === "ytd"
290
+ ? sorted.filter((bar) => bar.date >= `${new Date().getFullYear()}-01-01`)
291
+ : sorted.slice(-daysNeeded);
288
292
 
289
293
  cache.set(cacheKey, ohlcv, TTL.HISTORY);
290
294
  return ohlcv;
@@ -298,12 +302,27 @@ export async function getDailyHistory(
298
302
 
299
303
  function rangeToDays(range: string): number {
300
304
  const map: Record<string, number> = {
301
- "1d": 1, "5d": 5, "1mo": 22, "3mo": 66, "6mo": 130,
302
- "1y": 252, "2y": 504, "5y": 1260, "max": 5000,
305
+ "1d": 1,
306
+ "5d": 5,
307
+ "1mo": 22,
308
+ "3mo": 66,
309
+ "6mo": 130,
310
+ "1y": 252,
311
+ "2y": 504,
312
+ "5y": 1260,
313
+ "10y": 2520,
314
+ max: 5000,
303
315
  };
316
+ if (range === "ytd") return tradingDaysSinceStartOfYear();
304
317
  return map[range] ?? 130;
305
318
  }
306
319
 
320
+ function tradingDaysSinceStartOfYear(date = new Date()): number {
321
+ const start = new Date(date.getFullYear(), 0, 1);
322
+ const calendarDays = Math.max(1, Math.ceil((date.getTime() - start.getTime()) / 86_400_000) + 1);
323
+ return Math.max(1, Math.ceil((calendarDays / 7) * 5));
324
+ }
325
+
307
326
  function parseNum(s: string | undefined): number {
308
327
  return parseFloat(s ?? "0") || 0;
309
328
  }
@@ -1,5 +1,5 @@
1
+ import { cache, STALE_LIMIT, TTL } from "../infra/cache.js";
1
2
  import { httpGet } from "../infra/http-client.js";
2
- import { cache, TTL, STALE_LIMIT } from "../infra/cache.js";
3
3
  import { rateLimiter } from "../infra/rate-limiter.js";
4
4
  import type { CryptoPrice, OHLCV } from "../types/market.js";
5
5
 
@@ -63,10 +63,7 @@ export async function getCryptoPrice(id: string): Promise<CryptoPrice> {
63
63
  }
64
64
  }
65
65
 
66
- export async function getCryptoHistory(
67
- id: string,
68
- days: number = 180,
69
- ): Promise<OHLCV[]> {
66
+ export async function getCryptoHistory(id: string, days: number = 180): Promise<OHLCV[]> {
70
67
  const cacheKey = `coingecko:history:${id}:${days}`;
71
68
  const cached = cache.get<OHLCV[]>(cacheKey);
72
69
  if (cached) return cached;
@@ -0,0 +1,9 @@
1
+ export class InvalidSymbolError extends Error {
2
+ constructor(
3
+ public readonly symbol: string,
4
+ public readonly provider: string,
5
+ ) {
6
+ super(`Invalid symbol ${symbol} for ${provider}`);
7
+ this.name = "InvalidSymbolError";
8
+ }
9
+ }
@@ -1,8 +1,8 @@
1
- import { cache, TTL, STALE_LIMIT } from "../infra/cache.js";
2
- import { rateLimiter } from "../infra/rate-limiter.js";
3
1
  import { getConfig } from "../config.js";
2
+ import { cache, STALE_LIMIT, TTL } from "../infra/cache.js";
3
+ import { rateLimiter } from "../infra/rate-limiter.js";
4
+ import type { WebSearchEnvelope, WebSearchResult } from "../types/sentiment.js";
4
5
  import { ProviderCredentialError } from "./provider-credential-error.js";
5
- import type { WebSearchResult, WebSearchEnvelope } from "../types/sentiment.js";
6
6
  import type { WebSearchOpts } from "./web-search.js";
7
7
 
8
8
  const EXA_MCP_URL = "https://mcp.exa.ai/mcp";
@@ -19,19 +19,27 @@ let requestIdCounter = 0;
19
19
 
20
20
  function freshnessToMs(freshness: WebSearchOpts["freshness"]): number {
21
21
  switch (freshness) {
22
- case "hours": return 60 * 60 * 1000;
23
- case "day": return 24 * 60 * 60 * 1000;
24
- case "week": return 7 * 24 * 60 * 60 * 1000;
25
- case "month": return 30 * 24 * 60 * 60 * 1000;
22
+ case "hours":
23
+ return 60 * 60 * 1000;
24
+ case "day":
25
+ return 24 * 60 * 60 * 1000;
26
+ case "week":
27
+ return 7 * 24 * 60 * 60 * 1000;
28
+ case "month":
29
+ return 30 * 24 * 60 * 60 * 1000;
26
30
  }
27
31
  }
28
32
 
29
33
  function enrichQueryForMcp(query: string, freshness: WebSearchOpts["freshness"]): string {
30
34
  switch (freshness) {
31
- case "hours": return `${query} past hour`;
32
- case "day": return `${query} past 24 hours`;
33
- case "week": return `${query} past week`;
34
- case "month": return `${query} past month`;
35
+ case "hours":
36
+ return `${query} past hour`;
37
+ case "day":
38
+ return `${query} past 24 hours`;
39
+ case "week":
40
+ return `${query} past week`;
41
+ case "month":
42
+ return `${query} past month`;
35
43
  }
36
44
  }
37
45
 
@@ -192,10 +200,7 @@ function exaCacheKey(query: string, opts: WebSearchOpts): string {
192
200
  // MCP path
193
201
  // ---------------------------------------------------------------------------
194
202
 
195
- async function exaMcpSearch(
196
- query: string,
197
- opts: WebSearchOpts,
198
- ): Promise<WebSearchEnvelope> {
203
+ async function exaMcpSearch(query: string, opts: WebSearchOpts): Promise<WebSearchEnvelope> {
199
204
  const enrichedQuery = enrichQueryForMcp(query, opts.freshness);
200
205
 
201
206
  const response = await fetch(EXA_MCP_URL, {
@@ -330,10 +335,7 @@ async function exaApiSearch(
330
335
  // Public entry point
331
336
  // ---------------------------------------------------------------------------
332
337
 
333
- export async function exaSearch(
334
- query: string,
335
- opts: WebSearchOpts,
336
- ): Promise<WebSearchEnvelope> {
338
+ export async function exaSearch(query: string, opts: WebSearchOpts): Promise<WebSearchEnvelope> {
337
339
  const key = exaCacheKey(query, opts);
338
340
  const cached = cache.get<WebSearchEnvelope>(key);
339
341
  if (cached) return cached;
@@ -362,12 +364,12 @@ export async function exaSearch(
362
364
  }
363
365
  }
364
366
 
367
+ export type { ExaApiResponse, McpRpcResponse, ParsedResult };
365
368
  // Exported for testing
366
369
  export {
367
- parseMcpResultBlocks,
368
- extractJsonRpcPayload,
369
370
  enrichQueryForMcp,
371
+ extractJsonRpcPayload,
370
372
  filterByFreshness,
371
373
  mapApiResults,
374
+ parseMcpResultBlocks,
372
375
  };
373
- export type { ParsedResult, McpRpcResponse, ExaApiResponse };
@@ -0,0 +1,20 @@
1
+ export class ExternalToolError extends Error {
2
+ constructor(
3
+ public readonly toolName: string,
4
+ message: string,
5
+ public readonly code?: string,
6
+ ) {
7
+ super(message);
8
+ this.name = "ExternalToolError";
9
+ }
10
+ }
11
+
12
+ export class ExternalToolNotInstalled extends Error {
13
+ constructor(
14
+ public readonly toolName: string,
15
+ public readonly installCmd: string,
16
+ ) {
17
+ super(`${toolName} is not installed. Install it with: ${installCmd}`);
18
+ this.name = "ExternalToolNotInstalled";
19
+ }
20
+ }
@@ -1,5 +1,5 @@
1
+ import { cache, STALE_LIMIT, TTL } from "../infra/cache.js";
1
2
  import { httpGet } from "../infra/http-client.js";
2
- import { cache, TTL, STALE_LIMIT } from "../infra/cache.js";
3
3
  import type { FearGreedData } from "../types/sentiment.js";
4
4
 
5
5
  // alternative.me provides a free crypto Fear & Greed index
@@ -1,5 +1,5 @@
1
- import { httpGet, HttpError } 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";
2
+ import { HttpError, httpGet } from "../infra/http-client.js";
3
3
  import { rateLimiter } from "../infra/rate-limiter.js";
4
4
  import { ProviderCredentialError } from "./provider-credential-error.js";
5
5
 
@@ -36,7 +36,10 @@ const TICKER_NAMES: Record<string, string> = {
36
36
  ORCL: "oracle",
37
37
  };
38
38
 
39
- export function finnhubDateRange(freshness: "hours" | "day" | "week" | "month"): { from: string; to: string } {
39
+ export function finnhubDateRange(freshness: "hours" | "day" | "week" | "month"): {
40
+ from: string;
41
+ to: string;
42
+ } {
40
43
  const now = new Date();
41
44
  const to = formatDate(now);
42
45
 
@@ -86,9 +89,7 @@ export function filterByRelevance(
86
89
  });
87
90
 
88
91
  // Most recent first, capped
89
- return filtered
90
- .sort((a, b) => b.datetime - a.datetime)
91
- .slice(0, limit);
92
+ return filtered.sort((a, b) => b.datetime - a.datetime).slice(0, limit);
92
93
  }
93
94
 
94
95
  export async function getCompanyNews(
@@ -1,8 +1,8 @@
1
- import { httpGet, HttpError } 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";
2
+ import { HttpError, httpGet } from "../infra/http-client.js";
3
3
  import { rateLimiter } from "../infra/rate-limiter.js";
4
+ import type { FredObservation, FredSeries } from "../types/macro.js";
4
5
  import { ProviderCredentialError } from "./provider-credential-error.js";
5
- import type { FredSeries, FredObservation } from "../types/macro.js";
6
6
 
7
7
  const BASE_URL = "https://api.stlouisfed.org/fred";
8
8
 
@@ -1,9 +1,17 @@
1
- export { getQuote, getHistory, getOptionsChain, clearCrumbCache, getYahooCrumb, computeTimeToExpiry } from "./yahoo-finance.js";
2
- export { getOverview, getEarnings, getFinancials } from "./alpha-vantage.js";
1
+ export { getEarnings, getFinancials, getOverview } from "./alpha-vantage.js";
2
+ export { getCryptoHistory, getCryptoPrice } from "./coingecko.js";
3
+ export { getFearGreedIndex } from "./fear-greed.js";
3
4
  export { getSeries } from "./fred.js";
4
- export { getCryptoPrice, getCryptoHistory } from "./coingecko.js";
5
5
  export { getSubredditPosts, scoreSentiment } from "./reddit.js";
6
- export { searchFilings, type SECFiling } from "./sec-edgar.js";
7
- export { getFearGreedIndex } from "./fear-greed.js";
8
- export { searchWeb, ddgSearch, braveSearch, normalizeFinancialQuery } from "./web-search.js";
6
+ export { type SECFiling, searchFilings } from "./sec-edgar.js";
7
+ export { getQuotes, screenStocks } from "./tradingview.js";
9
8
  export type { WebSearchOpts } from "./web-search.js";
9
+ export { braveSearch, ddgSearch, normalizeFinancialQuery, searchWeb } from "./web-search.js";
10
+ export {
11
+ clearCrumbCache,
12
+ computeTimeToExpiry,
13
+ getHistory,
14
+ getOptionsChain,
15
+ getQuote,
16
+ getYahooCrumb,
17
+ } from "./yahoo-finance.js";