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,9 +1,9 @@
1
- import { Type } from "@sinclair/typebox";
2
1
  import type { AgentTool } from "@earendil-works/pi-agent-core";
3
- import { getHistory } from "../../providers/yahoo-finance.js";
2
+ import { Type } from "@sinclair/typebox";
4
3
  import { wrapProvider } from "../../providers/wrap-provider.js";
5
- import { computeSMA, computeRSI } from "./indicators.js";
4
+ import { getHistory } from "../../providers/yahoo-finance.js";
6
5
  import type { OHLCV } from "../../types/market.js";
6
+ import { computeRSI, computeSMA } from "./indicators.js";
7
7
 
8
8
  export type Strategy = "sma_crossover" | "sma_50_200_crossover" | "rsi_mean_reversion";
9
9
 
@@ -61,7 +61,7 @@ function backtestSMACrossover(
61
61
  const sLong = longSma[i];
62
62
  const price = closes[barIdx];
63
63
 
64
- if (!position && sShort > sLong) {
64
+ if (!position && sShort > sLong && price > 0) {
65
65
  // Buy signal
66
66
  position = true;
67
67
  entryPrice = price;
@@ -75,9 +75,7 @@ function backtestSMACrossover(
75
75
  }
76
76
 
77
77
  // Track mark-to-market equity for accurate drawdown
78
- const currentEquity = position
79
- ? equity * (1 + (price - entryPrice) / entryPrice)
80
- : equity;
78
+ const currentEquity = position ? equity * (1 + (price - entryPrice) / entryPrice) : equity;
81
79
  if (currentEquity > peak) peak = currentEquity;
82
80
  const dd = (peak - currentEquity) / peak;
83
81
  if (dd > maxDd) maxDd = dd;
@@ -115,7 +113,7 @@ function backtestRSIMeanReversion(bars: OHLCV[], closes: number[]): BacktestResu
115
113
  const r = rsi[i];
116
114
  const price = closes[barIdx];
117
115
 
118
- if (!position && r < 30) {
116
+ if (!position && r < 30 && price > 0) {
119
117
  // RSI oversold → buy
120
118
  position = true;
121
119
  entryPrice = price;
@@ -129,9 +127,7 @@ function backtestRSIMeanReversion(bars: OHLCV[], closes: number[]): BacktestResu
129
127
  }
130
128
 
131
129
  // Track mark-to-market equity for accurate drawdown
132
- const currentEquity = position
133
- ? equity * (1 + (price - entryPrice) / entryPrice)
134
- : equity;
130
+ const currentEquity = position ? equity * (1 + (price - entryPrice) / entryPrice) : equity;
135
131
  if (currentEquity > peak) peak = currentEquity;
136
132
  const dd = (peak - currentEquity) / peak;
137
133
  if (dd > maxDd) maxDd = dd;
@@ -157,9 +153,8 @@ function buildResult(
157
153
  ): BacktestResult {
158
154
  const sellTrades = tradeLog.filter((t) => t.type === "sell" && t.pnl != null);
159
155
  const wins = sellTrades.filter((t) => t.pnl! > 0).length;
160
- const buyAndHoldReturn = closes.length > 1
161
- ? (closes[closes.length - 1] - closes[0]) / closes[0]
162
- : 0;
156
+ const buyAndHoldReturn =
157
+ closes.length > 1 && closes[0] > 0 ? (closes[closes.length - 1] - closes[0]) / closes[0] : 0;
163
158
 
164
159
  return {
165
160
  strategy,
@@ -177,9 +172,8 @@ function emptyResult(strategy: string, closes: number[]): BacktestResult {
177
172
  return {
178
173
  strategy,
179
174
  totalReturn: 0,
180
- buyAndHoldReturn: closes.length > 1
181
- ? (closes[closes.length - 1] - closes[0]) / closes[0]
182
- : 0,
175
+ buyAndHoldReturn:
176
+ closes.length > 1 && closes[0] > 0 ? (closes[closes.length - 1] - closes[0]) / closes[0] : 0,
183
177
  trades: 0,
184
178
  wins: 0,
185
179
  winRate: 0,
@@ -191,11 +185,20 @@ function emptyResult(strategy: string, closes: number[]): BacktestResult {
191
185
  const params = Type.Object({
192
186
  symbol: Type.String({ description: "Stock ticker symbol (e.g. AAPL, MSFT, SPY)" }),
193
187
  strategy: Type.Union(
194
- [Type.Literal("sma_crossover"), Type.Literal("sma_50_200_crossover"), Type.Literal("rsi_mean_reversion")],
195
- { description: "Strategy: sma_crossover (buy when SMA20 > SMA50, sell on reverse), sma_50_200_crossover (buy when SMA50 > SMA200, sell on reverse), or rsi_mean_reversion (buy when RSI < 30, sell when RSI > 70)" },
188
+ [
189
+ Type.Literal("sma_crossover"),
190
+ Type.Literal("sma_50_200_crossover"),
191
+ Type.Literal("rsi_mean_reversion"),
192
+ ],
193
+ {
194
+ description:
195
+ "Strategy: sma_crossover (buy when SMA20 > SMA50, sell on reverse), sma_50_200_crossover (buy when SMA50 > SMA200, sell on reverse), or rsi_mean_reversion (buy when RSI < 30, sell when RSI > 70)",
196
+ },
196
197
  ),
197
198
  period: Type.Optional(
198
- Type.String({ description: "Historical period to backtest: 1y, 2y, 5y. Default: 2y" }),
199
+ Type.Union([Type.Literal("1y"), Type.Literal("2y"), Type.Literal("5y")], {
200
+ description: "Historical period to backtest: 1y, 2y, 5y. Default: 2y",
201
+ }),
199
202
  ),
200
203
  });
201
204
 
@@ -211,7 +214,9 @@ export const backtestTool: AgentTool<typeof params> = {
211
214
  const historyResult = await wrapProvider("yahoo", () => getHistory(symbol, period, "1d"));
212
215
  if (historyResult.status === "unavailable") {
213
216
  return {
214
- content: [{ type: "text", text: `⚠ Backtest unavailable for ${symbol} (${historyResult.reason}).` }],
217
+ content: [
218
+ { type: "text", text: `⚠ Backtest unavailable for ${symbol} (${historyResult.reason}).` },
219
+ ],
215
220
  details: null as any,
216
221
  };
217
222
  }
@@ -220,7 +225,12 @@ export const backtestTool: AgentTool<typeof params> = {
220
225
  const minBars = requiredBarsForStrategy(args.strategy);
221
226
  if (bars.length < minBars) {
222
227
  return {
223
- content: [{ type: "text", text: `Insufficient data for backtesting ${symbol} (need ${minBars}+ days, got ${bars.length})` }],
228
+ content: [
229
+ {
230
+ type: "text",
231
+ text: `Insufficient data for backtesting ${symbol} (need ${minBars}+ days, got ${bars.length})`,
232
+ },
233
+ ],
224
234
  details: null,
225
235
  };
226
236
  }
@@ -1,7 +1,7 @@
1
- import { Type } from "@sinclair/typebox";
2
1
  import type { AgentTool } from "@earendil-works/pi-agent-core";
3
- import { getHistory } from "../../providers/yahoo-finance.js";
2
+ import { Type } from "@sinclair/typebox";
4
3
  import { wrapProvider } from "../../providers/wrap-provider.js";
4
+ import { getHistory } from "../../providers/yahoo-finance.js";
5
5
  import type { OHLCV } from "../../types/market.js";
6
6
 
7
7
  // --- Volume-based indicators ---
@@ -52,7 +52,12 @@ export const technicalIndicatorsTool: AgentTool<typeof params> = {
52
52
  const result = await wrapProvider("yahoo", () => getHistory(symbol, range, "1d"));
53
53
  if (result.status === "unavailable") {
54
54
  return {
55
- content: [{ type: "text", text: `⚠ Technical indicators unavailable for ${symbol} (${result.reason}).` }],
55
+ content: [
56
+ {
57
+ type: "text",
58
+ text: `⚠ Technical indicators unavailable for ${symbol} (${result.reason}).`,
59
+ },
60
+ ],
56
61
  details: null as any,
57
62
  };
58
63
  }
@@ -61,7 +66,12 @@ export const technicalIndicatorsTool: AgentTool<typeof params> = {
61
66
 
62
67
  if (closes.length < 26) {
63
68
  return {
64
- content: [{ type: "text", text: `Insufficient data for ${symbol} (need 26+ bars, got ${closes.length})` }],
69
+ content: [
70
+ {
71
+ type: "text",
72
+ text: `Insufficient data for ${symbol} (need 26+ bars, got ${closes.length})`,
73
+ },
74
+ ],
65
75
  details: null,
66
76
  };
67
77
  }
@@ -79,9 +89,12 @@ export const technicalIndicatorsTool: AgentTool<typeof params> = {
79
89
  const latestMacd = macd[macd.length - 1];
80
90
  const latestBB = bb[bb.length - 1];
81
91
  const latestVwap = vwap[vwap.length - 1];
82
- const obvTrend = obv.length >= 20
83
- ? (obv[obv.length - 1] > obv[obv.length - 20] ? "Rising" : "Falling")
84
- : "N/A";
92
+ const obvTrend =
93
+ obv.length >= 20
94
+ ? obv[obv.length - 1] > obv[obv.length - 20]
95
+ ? "Rising"
96
+ : "Falling"
97
+ : "N/A";
85
98
 
86
99
  const lines = [
87
100
  `**${symbol} Technical Analysis** (${bars[0].date} to ${bars[bars.length - 1].date})`,
@@ -103,7 +116,13 @@ export const technicalIndicatorsTool: AgentTool<typeof params> = {
103
116
  range,
104
117
  prices: closes,
105
118
  dates: bars.map((b) => b.date),
106
- sma20, sma50, rsi, macd, bb, obv, vwap,
119
+ sma20,
120
+ sma50,
121
+ rsi,
122
+ macd,
123
+ bb,
124
+ obv,
125
+ vwap,
107
126
  },
108
127
  };
109
128
  },
@@ -241,16 +260,22 @@ function trendSummary(
241
260
 
242
261
  if (latestSma20 && price > latestSma20) signals.push("Price above SMA(20) — short-term bullish");
243
262
  if (latestSma20 && price < latestSma20) signals.push("Price below SMA(20) — short-term bearish");
244
- if (latestSma20 && latestSma50 && latestSma20 > latestSma50) signals.push("Golden cross pattern (SMA20 > SMA50)");
245
- if (latestSma20 && latestSma50 && latestSma20 < latestSma50) signals.push("Death cross pattern (SMA20 < SMA50)");
263
+ if (latestSma20 && latestSma50 && latestSma20 > latestSma50)
264
+ signals.push("Golden cross pattern (SMA20 > SMA50)");
265
+ if (latestSma20 && latestSma50 && latestSma20 < latestSma50)
266
+ signals.push("Death cross pattern (SMA20 < SMA50)");
246
267
  if (rsi != null && rsi >= 70) signals.push("RSI overbought — potential reversal");
247
268
  if (rsi != null && rsi <= 30) signals.push("RSI oversold — potential bounce");
248
269
  if (macd && macd.histogram > 0) signals.push("MACD bullish (histogram positive)");
249
270
  if (macd && macd.histogram < 0) signals.push("MACD bearish (histogram negative)");
250
- if (obvTrend === "Rising" && price > (latestSma20 ?? 0)) signals.push("Volume confirming price advance (OBV rising)");
251
- if (obvTrend === "Falling" && price < (latestSma20 ?? Infinity)) signals.push("Volume confirming price decline (OBV falling)");
252
- if (vwap != null && price > vwap) signals.push("Price above cumulative VWAP — bullish volume-weighted bias");
253
- if (vwap != null && price < vwap) signals.push("Price below cumulative VWAP bearish volume-weighted bias");
271
+ if (obvTrend === "Rising" && price > (latestSma20 ?? 0))
272
+ signals.push("Volume confirming price advance (OBV rising)");
273
+ if (obvTrend === "Falling" && price < (latestSma20 ?? Infinity))
274
+ signals.push("Volume confirming price decline (OBV falling)");
275
+ if (vwap != null && price > vwap)
276
+ signals.push("Price above cumulative VWAP — bullish volume-weighted bias");
277
+ if (vwap != null && price < vwap)
278
+ signals.push("Price below cumulative VWAP — bearish volume-weighted bias");
254
279
 
255
280
  return signals.length > 0 ? "Signals: " + signals.join(" | ") : "No strong signals";
256
281
  }
@@ -1,10 +1,15 @@
1
- export type { StockQuote, OHLCV, CryptoPrice } from "./market.js";
2
1
  export type { CompanyOverview, EarningsData, FinancialStatement } from "./fundamentals.js";
3
2
  export type { FredObservation, FredSeries } from "./macro.js";
4
3
  export { FRED_SERIES } from "./macro.js";
4
+ export type { CryptoPrice, OHLCV, StockQuote } from "./market.js";
5
5
  export type { Greeks, OptionContract, OptionsChain } from "./options.js";
6
- export type { Position, PortfolioSummary, RiskMetrics, TechnicalIndicators } from "./portfolio.js";
7
- export type { FearGreedData, RedditSentimentResult, WebSearchResult, WebSearchEnvelope } from "./sentiment.js";
6
+ export type { PortfolioSummary, Position, RiskMetrics, TechnicalIndicators } from "./portfolio.js";
7
+ export type {
8
+ FearGreedData,
9
+ RedditSentimentResult,
10
+ WebSearchEnvelope,
11
+ WebSearchResult,
12
+ } from "./sentiment.js";
8
13
 
9
14
  /**
10
15
  * Handler for `ask_user` tool invocations in non-UI contexts (e.g. test harness).
@@ -13,6 +13,7 @@ export interface StockQuote {
13
13
  week52High: number;
14
14
  week52Low: number;
15
15
  timestamp: number;
16
+ currency?: string | null;
16
17
  }
17
18
 
18
19
  export interface OHLCV {
@@ -2,23 +2,33 @@ export interface Position {
2
2
  symbol: string;
3
3
  shares: number;
4
4
  avgCost: number;
5
+ currency: string;
5
6
  addedAt: string;
6
7
  }
7
8
 
8
9
  export interface PortfolioSummary {
9
10
  positions: Array<
10
11
  Position & {
11
- currentPrice: number;
12
- marketValue: number;
12
+ currentPrice: number | null;
13
+ marketValue: number | null;
13
14
  totalCost: number;
14
- pnl: number;
15
- pnlPercent: number;
15
+ pnl: number | null;
16
+ pnlPercent: number | null;
17
+ includedInTotals: boolean;
18
+ quoteStatus?: "ok" | "unavailable";
19
+ exclusionReason?: string;
16
20
  }
17
21
  >;
22
+ baseCurrency: string;
18
23
  totalValue: number;
19
24
  totalCost: number;
20
25
  totalPnl: number;
21
26
  totalPnlPercent: number;
27
+ excludedFromTotals: Array<{
28
+ symbol: string;
29
+ currency: string;
30
+ reason: string;
31
+ }>;
22
32
  }
23
33
 
24
34
  export interface RiskMetrics {
@@ -1,3 +1,5 @@
1
+ import type { SentinelRecord } from "../sentiment/types.js";
2
+
1
3
  export interface FearGreedData {
2
4
  value: number;
3
5
  label: string; // "Extreme Fear" | "Fear" | "Neutral" | "Greed" | "Extreme Greed"
@@ -19,15 +21,70 @@ export interface TwitterTweet {
19
21
  created: string;
20
22
  }
21
23
 
24
+ export type SentimentInsightMethod = "deterministic-keyword-v1" | "llm";
25
+ export type SentimentConfidenceLevel = "low" | "medium" | "high";
26
+
27
+ export interface SentimentInsightConfidence {
28
+ level: SentimentConfidenceLevel;
29
+ score: number; // 0.0 to 1.0
30
+ reasons: string[];
31
+ }
32
+
33
+ export interface SentimentInsightDriver {
34
+ label: string;
35
+ count: number;
36
+ polarity: "positive" | "negative" | "mixed";
37
+ terms: string[];
38
+ sourceIds: string[];
39
+ }
40
+
41
+ export interface SentimentRepresentativeItem {
42
+ source: "twitter" | "reddit" | "web" | "finnhub" | "aggregate";
43
+ sourceId: string;
44
+ title: string | null;
45
+ excerpt: string;
46
+ url: string | null;
47
+ author: string | null;
48
+ publishedAt: string | null;
49
+ engagement: number | null;
50
+ score: number;
51
+ matchedTerms: string[];
52
+ metadata?: Record<string, unknown>;
53
+ }
54
+
55
+ export interface SentimentSourceCoverage {
56
+ sources: string[];
57
+ counts: Record<string, number>;
58
+ missingSources?: string[];
59
+ notes?: string[];
60
+ }
61
+
62
+ export interface SentimentInsight {
63
+ label: string;
64
+ score: number;
65
+ sampleSize: number;
66
+ scoredSampleSize: number;
67
+ confidence: SentimentInsightConfidence;
68
+ positiveDrivers: SentimentInsightDriver[];
69
+ negativeDrivers: SentimentInsightDriver[];
70
+ mixedDrivers: SentimentInsightDriver[];
71
+ notableClaims: string[];
72
+ representativeItems: SentimentRepresentativeItem[];
73
+ sourceCoverage: SentimentSourceCoverage;
74
+ caveats: string[];
75
+ method: SentimentInsightMethod;
76
+ }
77
+
22
78
  export interface TwitterSentimentResult {
23
79
  query: string;
24
80
  tweetCount: number;
25
81
  tweets: TwitterTweet[];
26
- sentimentScore: number; // -1.0 (fully bearish) to +1.0 (fully bullish)
82
+ sentimentScore: number; // -1.0 (fully bearish) to +1.0 (fully bullish)
27
83
  bullishCount: number;
28
84
  bearishCount: number;
29
85
  topMentions: string[];
30
86
  fetchedAt: string;
87
+ insight?: SentimentInsight;
31
88
  }
32
89
 
33
90
  export interface WebSearchResult {
@@ -63,8 +120,10 @@ export interface RedditSentimentResult {
63
120
  created: string;
64
121
  }>;
65
122
  topMentions: string[];
66
- sentimentScore: number; // -1.0 (fully bearish) to +1.0 (fully bullish)
123
+ sentimentScore: number; // -1.0 (fully bearish) to +1.0 (fully bullish)
67
124
  bullishCount: number;
68
125
  bearishCount: number;
69
126
  fetchedAt: string;
127
+ insight?: SentimentInsight;
128
+ records?: SentinelRecord[];
70
129
  }
@@ -1,6 +1,7 @@
1
- import type { CompareAssetsSlots, SlotResolution } from "../routing/types.js";
2
1
  import { buildCompareAssetsPrompt } from "../prompts/workflow-prompts.js";
3
- import type { WorkflowPlan } from "./types.js";
2
+ import { areLikelyFundOrIndexSymbols, isFundOrIndexAssetScope } from "../routing/fund-symbols.js";
3
+ import { isLongInvestmentHorizon } from "../routing/horizon.js";
4
+ import type { CompareAssetsSlots, SlotResolution } from "../routing/types.js";
4
5
  import type { WorkflowDefinition } from "../runtime/prompt-step.js";
5
6
  import { promptStep } from "../runtime/prompt-step.js";
6
7
 
@@ -12,11 +13,18 @@ export function buildCompareAssetsWorkflowDefinition(
12
13
  const isMacroHedge = resolution.resolved.metrics?.includes("macro_hedge") ?? false;
13
14
  const isInterestRateSensitive = resolution.resolved.metrics?.includes("interest_rates") ?? false;
14
15
  const isOverlapComparison = resolution.resolved.metrics?.includes("overlap") ?? false;
16
+ const hasFundContext =
17
+ isFundOrIndexAssetScope(resolution.resolved.assetScope) ||
18
+ areLikelyFundOrIndexSymbols(resolution.resolved.symbols);
19
+ const shouldProbeFundOverlap =
20
+ !isOverlapComparison && isLongInvestmentHorizon(timeHorizon) && hasFundContext;
15
21
  const evidenceList = resolution.resolved.metrics?.includes("sentiment")
16
22
  ? "price, technical, risk, and sentiment data"
17
23
  : isOverlapComparison
18
24
  ? "quote, holdings-overlap, and correlation data"
19
- : "price, technical, and risk data";
25
+ : shouldProbeFundOverlap
26
+ ? "price, technical, risk, correlation, and holdings-overlap data when applicable"
27
+ : "price, technical, and risk data";
20
28
  const horizonGuidance = timeHorizon
21
29
  ? `
22
30
  - Start by directly answering whether these assets are reasonable to compare for a ${timeHorizon} horizon.
@@ -40,10 +48,20 @@ export function buildCompareAssetsWorkflowDefinition(
40
48
  - For ETF overlap prompts, synthesize the holdings-overlap and shared-exposure evidence first, with correlation only as supporting diversification context.
41
49
  - State the diversification implication directly: deliberate factor tilt, accidental duplication, or genuinely differentiated exposure.
42
50
  - If provider holdings coverage was partial or unavailable, say that before giving the practical next step.`
43
- : "";
51
+ : shouldProbeFundOverlap
52
+ ? `
53
+ - If these assets are ETFs, funds, or index products, synthesize holdings-overlap and shared-exposure evidence before correlation when that provider evidence is available.
54
+ - Compare fund role, broad style or sector tilt, dividend/income versus growth tradeoffs, taxable-account dividend drag, and any fetched expense ratio, yield, or AUM evidence.
55
+ - If expense ratio, yield, AUM, or constituent detail is unavailable, name that verification gap instead of filling it with approximate fund facts.
56
+ - If provider holdings coverage was partial or unavailable, say that before giving the practical next step.`
57
+ : "";
44
58
  const verdictInstruction = isOverlapComparison
45
59
  ? "End with a concise verdict on whether the added fund improves diversification or mostly duplicates existing exposure."
46
- : "End with a concise verdict on which asset looks strongest right now and why.";
60
+ : shouldProbeFundOverlap
61
+ ? "End with a concise verdict tied to the user's horizon, fund roles, and diversification needs rather than short-term timing."
62
+ : timeHorizon
63
+ ? `End with a concise verdict on which asset best fits the ${timeHorizon} horizon and why.`
64
+ : "End with a concise verdict on which asset looks strongest right now and why.";
47
65
 
48
66
  return {
49
67
  workflowType: "compare_assets",
@@ -52,24 +70,18 @@ export function buildCompareAssetsWorkflowDefinition(
52
70
  requiredInputs: ["symbols"],
53
71
  expectedOutputs: ["asset_data"],
54
72
  }),
55
- promptStep("compare_and_present", "Present side-by-side comparison", `Now present the side-by-side comparison for ${symbols}:
73
+ promptStep(
74
+ "compare_and_present",
75
+ "Present side-by-side comparison",
76
+ `Now present the side-by-side comparison for ${symbols}:
56
77
  - Keep any unavailable fundamentals marked as unavailable instead of retrying the same failed provider calls.
57
78
  - Use the ${evidenceList} you already fetched to finish the comparison even if some fundamentals are missing.
58
- - ${verdictInstruction}${horizonGuidance}${macroHedgeGuidance}${interestRateGuidance}${overlapGuidance}`, {
59
- requiredInputs: ["asset_data"],
60
- expectedOutputs: ["comparison_summary"],
61
- }),
79
+ - ${verdictInstruction}${horizonGuidance}${macroHedgeGuidance}${interestRateGuidance}${overlapGuidance}`,
80
+ {
81
+ requiredInputs: ["asset_data"],
82
+ expectedOutputs: ["comparison_summary"],
83
+ },
84
+ ),
62
85
  ],
63
86
  };
64
87
  }
65
-
66
- /** @deprecated Use buildCompareAssetsWorkflowDefinition instead */
67
- export function buildCompareAssetsWorkflow(
68
- resolution: SlotResolution<CompareAssetsSlots>,
69
- ): WorkflowPlan {
70
- const def = buildCompareAssetsWorkflowDefinition(resolution);
71
- return {
72
- initialPrompt: def.steps[0].prompt,
73
- followUps: def.steps.slice(1).map((s) => s.prompt),
74
- };
75
- }
@@ -1,4 +1,3 @@
1
- export { buildPortfolioWorkflow, buildPortfolioWorkflowDefinition } from "./portfolio-builder.js";
2
- export { buildCompareAssetsWorkflow, buildCompareAssetsWorkflowDefinition } from "./compare-assets.js";
3
- export { buildOptionsScreenerWorkflow, buildOptionsScreenerWorkflowDefinition } from "./options-screener.js";
4
- export type { WorkflowPlan } from "./types.js";
1
+ export { buildCompareAssetsWorkflowDefinition } from "./compare-assets.js";
2
+ export { buildOptionsScreenerWorkflowDefinition } from "./options-screener.js";
3
+ export { buildPortfolioWorkflowDefinition } from "./portfolio-builder.js";
@@ -1,32 +1,34 @@
1
- import type { OptionsScreenerSlots, SlotResolution } from "../routing/types.js";
2
1
  import { buildOptionsScreenerPrompt } from "../prompts/workflow-prompts.js";
3
- import type { WorkflowPlan } from "./types.js";
2
+ import type { OptionsScreenerSlots, SlotResolution } from "../routing/types.js";
4
3
  import type { WorkflowDefinition } from "../runtime/prompt-step.js";
5
4
  import { promptStep } from "../runtime/prompt-step.js";
6
5
 
7
- export function buildOptionsScreenerWorkflowDefinition(resolution: SlotResolution<OptionsScreenerSlots>): WorkflowDefinition {
6
+ export function buildOptionsScreenerWorkflowDefinition(
7
+ resolution: SlotResolution<OptionsScreenerSlots>,
8
+ ): WorkflowDefinition {
8
9
  const s = resolution.resolved;
9
10
  const isProtectivePutContext = s.optionStrategy === "protective_put";
10
11
  const contractType = s.direction === "bullish" && !isProtectivePutContext ? "calls" : "puts";
11
12
  const horizonPhrase = describeDteTarget(s.dteTarget);
12
- const isCoveredCallContext = !isProtectivePutContext && (
13
- s.optionStrategy === "covered_call" ||
14
- s.costBasis !== undefined ||
15
- (s.catalystSymbols?.length ?? 0) > 0
16
- );
13
+ const isCoveredCallContext =
14
+ !isProtectivePutContext &&
15
+ (s.optionStrategy === "covered_call" ||
16
+ s.costBasis !== undefined ||
17
+ (s.catalystSymbols?.length ?? 0) > 0);
17
18
  const rankingInstruction = isProtectivePutContext
18
19
  ? "Rank by protection per dollar of premium, expiration fit, moneyness, hedge floor, live liquidity, and premium as a percent of the stock position."
19
20
  : isCoveredCallContext
20
- ? "Rank by premium collected, strike above cost basis, assignment risk, event risk, live liquidity, and probability of expiring out of the money."
21
- : `Rank by ${s.objective}: balance premium cost, delta exposure, and probability of profit. Only include contracts with |delta| >= 0.20.`;
22
- const maxPremiumInstruction = s.maxPremium !== undefined
23
- ? ` Do not rank contracts above the user's max premium of $${s.maxPremium.toLocaleString("en-US")} unless no contracts under that cap are liquid; if so, say the cap could not be met.`
24
- : "";
21
+ ? "Rank by premium collected, strike above cost basis, assignment risk, event risk, live liquidity, and probability of expiring out of the money."
22
+ : `Rank by ${s.objective}: balance premium cost, delta exposure, and probability of profit. Only include contracts with |delta| >= 0.20.`;
23
+ const maxPremiumInstruction =
24
+ s.maxPremium !== undefined
25
+ ? ` Do not rank contracts above the user's max premium of $${s.maxPremium.toLocaleString("en-US")} unless no contracts under that cap are liquid; if so, say the cap could not be met.`
26
+ : "";
25
27
  const riskInstruction = isProtectivePutContext
26
28
  ? "Include protective-put hedge risks: premium decay/cost, imperfect hedge before the strike, liquidity, and opportunity cost. Long protective puts do not have short-option assignment risk."
27
29
  : isCoveredCallContext
28
- ? "Include covered-call sale risks: assignment risk, upside is capped at the strike plus premium, share-price downside in the owned stock less premium received, IV/event risk, and exit liquidity. Do not say max loss = premium or describe max loss as the option premium paid."
29
- : "Include risk caveats: max loss = premium, IV crush risk, time decay (theta).";
30
+ ? "Include covered-call sale risks: assignment risk, upside is capped at the strike plus premium, share-price downside in the owned stock less premium received, IV/event risk, and exit liquidity. Do not say max loss = premium or describe max loss as the option premium paid."
31
+ : "Include risk caveats: max loss = premium, IV crush risk, time decay (theta).";
30
32
  const coveredCallFallback = isCoveredCallContext
31
33
  ? `
32
34
  Covered-call fallback:
@@ -77,7 +79,10 @@ Protective-put requirements:
77
79
  requiredInputs: ["symbol"],
78
80
  expectedOutputs: ["option_chain"],
79
81
  }),
80
- promptStep("rank_and_present", "Rank and present top contracts", `Now rank and present the top ${contractType} for ${s.symbol}. You MUST produce a final text response — never end this turn with only tool calls.
82
+ promptStep(
83
+ "rank_and_present",
84
+ "Rank and present top contracts",
85
+ `Now rank and present the top ${contractType} for ${s.symbol}. You MUST produce a final text response — never end this turn with only tool calls.
81
86
 
82
87
  1. From the option chain data already fetched, select the top 3-5 contracts matching: ${s.moneynessPreference} strikes, DTE near ${s.dteTarget}, with ${s.liquidityMinimum}.
83
88
  2. ${rankingInstruction}${maxPremiumInstruction}
@@ -97,10 +102,12 @@ ${protectivePutFallback}
97
102
  Length constraints:
98
103
  - Max 1 sentence explaining the #1 pick.
99
104
  - Risk section: max 3 bullets.
100
- - Keep total response under 30 lines.`, {
101
- requiredInputs: ["option_chain"],
102
- expectedOutputs: ["ranked_contracts"],
103
- }),
105
+ - Keep total response under 30 lines.`,
106
+ {
107
+ requiredInputs: ["option_chain"],
108
+ expectedOutputs: ["ranked_contracts"],
109
+ },
110
+ ),
104
111
  ],
105
112
  };
106
113
  }
@@ -116,12 +123,3 @@ function describeDteTarget(dteTarget: string): string {
116
123
  if (plus) return `${plus[1]}+ day horizon`;
117
124
  return `${dteTarget} horizon`;
118
125
  }
119
-
120
- /** @deprecated Use buildOptionsScreenerWorkflowDefinition instead */
121
- export function buildOptionsScreenerWorkflow(resolution: SlotResolution<OptionsScreenerSlots>): WorkflowPlan {
122
- const def = buildOptionsScreenerWorkflowDefinition(resolution);
123
- return {
124
- initialPrompt: def.steps[0].prompt,
125
- followUps: def.steps.slice(1).map((s) => s.prompt),
126
- };
127
- }
@@ -1,30 +1,44 @@
1
- import type { PortfolioSlots, SlotResolution } from "../routing/types.js";
2
1
  import { buildPortfolioPrompt } from "../prompts/workflow-prompts.js";
3
- import type { WorkflowPlan } from "./types.js";
2
+ import type { PortfolioSlots, SlotResolution } from "../routing/types.js";
4
3
  import type { WorkflowDefinition } from "../runtime/prompt-step.js";
5
4
  import { promptStep } from "../runtime/prompt-step.js";
6
5
 
7
- export function buildPortfolioWorkflowDefinition(resolution: SlotResolution<PortfolioSlots>): WorkflowDefinition {
6
+ export function buildPortfolioWorkflowDefinition(
7
+ resolution: SlotResolution<PortfolioSlots>,
8
+ ): WorkflowDefinition {
8
9
  const s = resolution.resolved;
9
10
 
10
11
  return {
11
12
  workflowType: "portfolio_builder",
12
13
  steps: [
13
- promptStep("fetch_candidates", "Fetch market data and select candidates", buildPortfolioPrompt(resolution), {
14
- requiredInputs: ["symbols"],
15
- expectedOutputs: ["candidate_positions"],
16
- }),
17
- promptStep("risk_review", "Review risk and diversification", `Now review the risk and diversification of this draft portfolio:
14
+ promptStep(
15
+ "fetch_candidates",
16
+ "Fetch market data and select candidates",
17
+ buildPortfolioPrompt(resolution),
18
+ {
19
+ requiredInputs: ["symbols"],
20
+ expectedOutputs: ["candidate_positions"],
21
+ },
22
+ ),
23
+ promptStep(
24
+ "risk_review",
25
+ "Review risk and diversification",
26
+ `Now review the risk and diversification of this draft portfolio:
18
27
  1. Use analyze_correlation across all ${s.positionCount} candidates to check for concentration risk.
19
28
  2. Use analyze_risk on each position for volatility and max drawdown.
20
29
  3. If correlation is too high (>0.7 between any pair), suggest a replacement to improve diversification.
21
30
  4. If any position's risk metrics undermine its intended role, lower its allocation, replace it with a role-equivalent candidate, or explicitly justify why it remains.
22
- 5. Confirm the portfolio fits a ${s.riskProfile} risk profile with ${s.timeHorizon} horizon.`, {
23
- skippable: true,
24
- requiredInputs: ["candidate_positions"],
25
- expectedOutputs: ["risk_assessment"],
26
- }),
27
- promptStep("synthesize", "Present final portfolio draft", `Present the final portfolio draft as a structured summary:
31
+ 5. Confirm the portfolio fits a ${s.riskProfile} risk profile with ${s.timeHorizon} horizon.`,
32
+ {
33
+ skippable: true,
34
+ requiredInputs: ["candidate_positions"],
35
+ expectedOutputs: ["risk_assessment"],
36
+ },
37
+ ),
38
+ promptStep(
39
+ "synthesize",
40
+ "Present final portfolio draft",
41
+ `Present the final portfolio draft as a structured summary:
28
42
  - State all assumptions at the top (which parameters were defaults vs user-specified vs saved preferences).
29
43
  - Commit to the allocation: concrete percentages per position, not ranges.
30
44
  - Present an allocation table: symbol, allocation %, dollar amount ($${s.budget.toLocaleString("en-US")} total), current price used, estimated shares, role, and one-line analyst rationale per position.
@@ -38,19 +52,12 @@ Length constraints:
38
52
  - Risk summary: max 3 bullet points.
39
53
  - Implementation notes: max 3 bullets.
40
54
  - Growth/safety suggestions: max 2 bullet points each.
41
- - Keep total response under 40 lines.`, {
42
- requiredInputs: ["risk_assessment"],
43
- expectedOutputs: ["portfolio_summary"],
44
- }),
55
+ - Keep total response under 40 lines.`,
56
+ {
57
+ requiredInputs: ["risk_assessment"],
58
+ expectedOutputs: ["portfolio_summary"],
59
+ },
60
+ ),
45
61
  ],
46
62
  };
47
63
  }
48
-
49
- /** @deprecated Use buildPortfolioWorkflowDefinition instead */
50
- export function buildPortfolioWorkflow(resolution: SlotResolution<PortfolioSlots>): WorkflowPlan {
51
- const def = buildPortfolioWorkflowDefinition(resolution);
52
- return {
53
- initialPrompt: def.steps[0].prompt,
54
- followUps: def.steps.slice(1).map((s) => s.prompt),
55
- };
56
- }