opencandle 0.5.0 → 0.6.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 (527) hide show
  1. package/README.md +164 -187
  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 +30 -7
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config.d.ts +3 -3
  11. package/dist/config.js +12 -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/browser.js +3 -1
  17. package/dist/infra/browser.js.map +1 -1
  18. package/dist/infra/cache.d.ts +8 -11
  19. package/dist/infra/cache.js +17 -15
  20. package/dist/infra/cache.js.map +1 -1
  21. package/dist/infra/http-client.d.ts +4 -1
  22. package/dist/infra/http-client.js +59 -6
  23. package/dist/infra/http-client.js.map +1 -1
  24. package/dist/infra/index.d.ts +3 -3
  25. package/dist/infra/index.js +3 -3
  26. package/dist/infra/index.js.map +1 -1
  27. package/dist/infra/native-dependencies.js +2 -2
  28. package/dist/infra/native-dependencies.js.map +1 -1
  29. package/dist/infra/node-version.js.map +1 -1
  30. package/dist/infra/opencandle-paths.d.ts +0 -3
  31. package/dist/infra/opencandle-paths.js +4 -11
  32. package/dist/infra/opencandle-paths.js.map +1 -1
  33. package/dist/infra/rate-limiter.js +12 -9
  34. package/dist/infra/rate-limiter.js.map +1 -1
  35. package/dist/market-state/alert-conditions.d.ts +34 -0
  36. package/dist/market-state/alert-conditions.js +23 -0
  37. package/dist/market-state/alert-conditions.js.map +1 -0
  38. package/dist/market-state/alert-runner.d.ts +55 -0
  39. package/dist/market-state/alert-runner.js +634 -0
  40. package/dist/market-state/alert-runner.js.map +1 -0
  41. package/dist/market-state/daily-report.d.ts +26 -0
  42. package/dist/market-state/daily-report.js +179 -0
  43. package/dist/market-state/daily-report.js.map +1 -0
  44. package/dist/market-state/local-automation-service.d.ts +25 -0
  45. package/dist/market-state/local-automation-service.js +119 -0
  46. package/dist/market-state/local-automation-service.js.map +1 -0
  47. package/dist/market-state/notification-delivery.d.ts +14 -0
  48. package/dist/market-state/notification-delivery.js +139 -0
  49. package/dist/market-state/notification-delivery.js.map +1 -0
  50. package/dist/market-state/resolve-for-mutation.d.ts +10 -0
  51. package/dist/market-state/resolve-for-mutation.js +15 -0
  52. package/dist/market-state/resolve-for-mutation.js.map +1 -0
  53. package/dist/market-state/resolve.d.ts +14 -0
  54. package/dist/market-state/resolve.js +89 -0
  55. package/dist/market-state/resolve.js.map +1 -0
  56. package/dist/market-state/service.d.ts +527 -0
  57. package/dist/market-state/service.js +1099 -0
  58. package/dist/market-state/service.js.map +1 -0
  59. package/dist/memory/index.d.ts +7 -7
  60. package/dist/memory/index.js +6 -6
  61. package/dist/memory/index.js.map +1 -1
  62. package/dist/memory/manager.js +11 -11
  63. package/dist/memory/manager.js.map +1 -1
  64. package/dist/memory/retrieval.js +7 -4
  65. package/dist/memory/retrieval.js.map +1 -1
  66. package/dist/memory/sqlite.js +385 -3
  67. package/dist/memory/sqlite.js.map +1 -1
  68. package/dist/memory/storage.js +1 -2
  69. package/dist/memory/storage.js.map +1 -1
  70. package/dist/memory/tool-defaults.js +64 -28
  71. package/dist/memory/tool-defaults.js.map +1 -1
  72. package/dist/memory/types.js.map +1 -1
  73. package/dist/monitor.d.ts +2 -0
  74. package/dist/monitor.js +104 -0
  75. package/dist/monitor.js.map +1 -0
  76. package/dist/onboarding/connect.js +4 -6
  77. package/dist/onboarding/connect.js.map +1 -1
  78. package/dist/onboarding/credential-interceptor.js +1 -1
  79. package/dist/onboarding/credential-interceptor.js.map +1 -1
  80. package/dist/onboarding/degradation-accumulator.js +1 -3
  81. package/dist/onboarding/degradation-accumulator.js.map +1 -1
  82. package/dist/onboarding/providers.js +3 -16
  83. package/dist/onboarding/providers.js.map +1 -1
  84. package/dist/onboarding/state.js.map +1 -1
  85. package/dist/onboarding/tool-helpers.js +1 -1
  86. package/dist/onboarding/tool-helpers.js.map +1 -1
  87. package/dist/onboarding/tool-tags.js +6 -4
  88. package/dist/onboarding/tool-tags.js.map +1 -1
  89. package/dist/onboarding/validation.js +1 -1
  90. package/dist/onboarding/validation.js.map +1 -1
  91. package/dist/pi/opencandle-extension.d.ts +8 -0
  92. package/dist/pi/opencandle-extension.js +412 -28
  93. package/dist/pi/opencandle-extension.js.map +1 -1
  94. package/dist/pi/session.d.ts +1 -1
  95. package/dist/pi/session.js +3 -1
  96. package/dist/pi/session.js.map +1 -1
  97. package/dist/pi/setup.js +8 -3
  98. package/dist/pi/setup.js.map +1 -1
  99. package/dist/pi/tool-adapter.js +5 -2
  100. package/dist/pi/tool-adapter.js.map +1 -1
  101. package/dist/prompts/context-builder.d.ts +1 -1
  102. package/dist/prompts/context-builder.js +19 -6
  103. package/dist/prompts/context-builder.js.map +1 -1
  104. package/dist/prompts/policy-cards.d.ts +1 -1
  105. package/dist/prompts/policy-cards.js +1 -1
  106. package/dist/prompts/policy-cards.js.map +1 -1
  107. package/dist/prompts/sections.d.ts +1 -1
  108. package/dist/prompts/symbol-preflight.d.ts +20 -0
  109. package/dist/prompts/symbol-preflight.js +49 -0
  110. package/dist/prompts/symbol-preflight.js.map +1 -0
  111. package/dist/prompts/workflow-prompts.d.ts +1 -1
  112. package/dist/prompts/workflow-prompts.js +54 -16
  113. package/dist/prompts/workflow-prompts.js.map +1 -1
  114. package/dist/providers/alpha-vantage.d.ts +1 -1
  115. package/dist/providers/alpha-vantage.js +26 -7
  116. package/dist/providers/alpha-vantage.js.map +1 -1
  117. package/dist/providers/coingecko.js +1 -1
  118. package/dist/providers/coingecko.js.map +1 -1
  119. package/dist/providers/errors.d.ts +5 -0
  120. package/dist/providers/errors.js +11 -0
  121. package/dist/providers/errors.js.map +1 -0
  122. package/dist/providers/exa-search.d.ts +2 -2
  123. package/dist/providers/exa-search.js +19 -11
  124. package/dist/providers/exa-search.js.map +1 -1
  125. package/dist/providers/fear-greed.js +1 -1
  126. package/dist/providers/fear-greed.js.map +1 -1
  127. package/dist/providers/finnhub.js +3 -5
  128. package/dist/providers/finnhub.js.map +1 -1
  129. package/dist/providers/fred.js +2 -2
  130. package/dist/providers/fred.js.map +1 -1
  131. package/dist/providers/index.d.ts +7 -6
  132. package/dist/providers/index.js +6 -5
  133. package/dist/providers/index.js.map +1 -1
  134. package/dist/providers/reddit.js +2 -2
  135. package/dist/providers/reddit.js.map +1 -1
  136. package/dist/providers/sec-edgar.d.ts +1 -0
  137. package/dist/providers/sec-edgar.js +12 -4
  138. package/dist/providers/sec-edgar.js.map +1 -1
  139. package/dist/providers/tradingview.d.ts +47 -0
  140. package/dist/providers/tradingview.js +275 -0
  141. package/dist/providers/tradingview.js.map +1 -0
  142. package/dist/providers/twitter.js +6 -8
  143. package/dist/providers/twitter.js.map +1 -1
  144. package/dist/providers/web-search.js +26 -12
  145. package/dist/providers/web-search.js.map +1 -1
  146. package/dist/providers/with-fallback.js +4 -2
  147. package/dist/providers/with-fallback.js.map +1 -1
  148. package/dist/providers/wrap-provider.d.ts +2 -3
  149. package/dist/providers/wrap-provider.js +14 -8
  150. package/dist/providers/wrap-provider.js.map +1 -1
  151. package/dist/providers/yahoo-finance.d.ts +1 -1
  152. package/dist/providers/yahoo-finance.js +101 -17
  153. package/dist/providers/yahoo-finance.js.map +1 -1
  154. package/dist/routing/classify-intent.d.ts +6 -0
  155. package/dist/routing/classify-intent.js +78 -7
  156. package/dist/routing/classify-intent.js.map +1 -1
  157. package/dist/routing/defaults.d.ts +1 -1
  158. package/dist/routing/entity-extractor.d.ts +1 -0
  159. package/dist/routing/entity-extractor.js +234 -29
  160. package/dist/routing/entity-extractor.js.map +1 -1
  161. package/dist/routing/fund-symbols.d.ts +2 -0
  162. package/dist/routing/fund-symbols.js +55 -0
  163. package/dist/routing/fund-symbols.js.map +1 -0
  164. package/dist/routing/horizon.d.ts +1 -0
  165. package/dist/routing/horizon.js +10 -0
  166. package/dist/routing/horizon.js.map +1 -0
  167. package/dist/routing/index.d.ts +10 -10
  168. package/dist/routing/index.js +6 -6
  169. package/dist/routing/index.js.map +1 -1
  170. package/dist/routing/planning.d.ts +1 -1
  171. package/dist/routing/planning.js +65 -34
  172. package/dist/routing/planning.js.map +1 -1
  173. package/dist/routing/route-manifest.d.ts +2 -2
  174. package/dist/routing/route-manifest.js +25 -4
  175. package/dist/routing/route-manifest.js.map +1 -1
  176. package/dist/routing/router-llm-client.js.map +1 -1
  177. package/dist/routing/router-prompt.js +7 -9
  178. package/dist/routing/router-prompt.js.map +1 -1
  179. package/dist/routing/router-types.d.ts +1 -0
  180. package/dist/routing/router.js +137 -22
  181. package/dist/routing/router.js.map +1 -1
  182. package/dist/routing/slot-resolver.d.ts +1 -1
  183. package/dist/routing/slot-resolver.js +2 -4
  184. package/dist/routing/slot-resolver.js.map +1 -1
  185. package/dist/routing/symbol-disambiguator.d.ts +11 -0
  186. package/dist/routing/symbol-disambiguator.js +52 -0
  187. package/dist/routing/symbol-disambiguator.js.map +1 -0
  188. package/dist/routing/turn-context.d.ts +1 -1
  189. package/dist/routing/turn-context.js +1 -1
  190. package/dist/routing/turn-context.js.map +1 -1
  191. package/dist/routing/types.d.ts +2 -0
  192. package/dist/runtime/answer-contracts.js +36 -8
  193. package/dist/runtime/answer-contracts.js.map +1 -1
  194. package/dist/runtime/artifact-contracts.js.map +1 -1
  195. package/dist/runtime/planning-evidence.js +47 -26
  196. package/dist/runtime/planning-evidence.js.map +1 -1
  197. package/dist/runtime/prompt-step.d.ts +1 -9
  198. package/dist/runtime/prompt-step.js +0 -10
  199. package/dist/runtime/prompt-step.js.map +1 -1
  200. package/dist/runtime/run-context.d.ts +5 -2
  201. package/dist/runtime/run-context.js +8 -1
  202. package/dist/runtime/run-context.js.map +1 -1
  203. package/dist/runtime/session-coordinator.d.ts +13 -5
  204. package/dist/runtime/session-coordinator.js +160 -20
  205. package/dist/runtime/session-coordinator.js.map +1 -1
  206. package/dist/runtime/session-title.d.ts +14 -0
  207. package/dist/runtime/session-title.js +50 -0
  208. package/dist/runtime/session-title.js.map +1 -0
  209. package/dist/runtime/tool-defaults-wrapper.js +1 -3
  210. package/dist/runtime/tool-defaults-wrapper.js.map +1 -1
  211. package/dist/runtime/validation.js.map +1 -1
  212. package/dist/runtime/workflow-events.js.map +1 -1
  213. package/dist/runtime/workflow-runner.d.ts +3 -3
  214. package/dist/runtime/workflow-runner.js +1 -1
  215. package/dist/runtime/workflow-runner.js.map +1 -1
  216. package/dist/sentiment/adapters/finnhub.d.ts +1 -1
  217. package/dist/sentiment/adapters/finnhub.js +6 -1
  218. package/dist/sentiment/adapters/finnhub.js.map +1 -1
  219. package/dist/sentiment/adapters/reddit.d.ts +2 -2
  220. package/dist/sentiment/adapters/twitter.d.ts +1 -1
  221. package/dist/sentiment/adapters/web.d.ts +1 -1
  222. package/dist/sentiment/index.d.ts +9 -11
  223. package/dist/sentiment/index.js +9 -20
  224. package/dist/sentiment/index.js.map +1 -1
  225. package/dist/sentiment/keywords.js +26 -4
  226. package/dist/sentiment/keywords.js.map +1 -1
  227. package/dist/sentiment/pipeline.d.ts +2 -2
  228. package/dist/sentiment/pipeline.js +1 -1
  229. package/dist/sentiment/pipeline.js.map +1 -1
  230. package/dist/sentiment/scorer.js +1 -1
  231. package/dist/sentiment/store.d.ts +1 -1
  232. package/dist/sentiment/store.js +1 -1
  233. package/dist/sentiment/store.js.map +1 -1
  234. package/dist/sentiment/trends.d.ts +1 -1
  235. package/dist/sentiment/trends.js.map +1 -1
  236. package/dist/sentiment/types.js.map +1 -1
  237. package/dist/system-prompt.js +3 -2
  238. package/dist/system-prompt.js.map +1 -1
  239. package/dist/tool-kit.d.ts +7 -7
  240. package/dist/tool-kit.js +4 -4
  241. package/dist/tool-kit.js.map +1 -1
  242. package/dist/tools/fundamentals/company-overview.js +11 -6
  243. package/dist/tools/fundamentals/company-overview.js.map +1 -1
  244. package/dist/tools/fundamentals/comps.js +18 -9
  245. package/dist/tools/fundamentals/comps.js.map +1 -1
  246. package/dist/tools/fundamentals/dcf.js +23 -11
  247. package/dist/tools/fundamentals/dcf.js.map +1 -1
  248. package/dist/tools/fundamentals/earnings.js +8 -3
  249. package/dist/tools/fundamentals/earnings.js.map +1 -1
  250. package/dist/tools/fundamentals/financials.js +8 -3
  251. package/dist/tools/fundamentals/financials.js.map +1 -1
  252. package/dist/tools/fundamentals/sec-filings.js +21 -6
  253. package/dist/tools/fundamentals/sec-filings.js.map +1 -1
  254. package/dist/tools/index.d.ts +23 -19
  255. package/dist/tools/index.js +51 -39
  256. package/dist/tools/index.js.map +1 -1
  257. package/dist/tools/interaction/ask-user.js +15 -3
  258. package/dist/tools/interaction/ask-user.js.map +1 -1
  259. package/dist/tools/interaction/twitter-login.js +13 -3
  260. package/dist/tools/interaction/twitter-login.js.map +1 -1
  261. package/dist/tools/macro/fear-greed.js.map +1 -1
  262. package/dist/tools/macro/fred-data.d.ts +1 -1
  263. package/dist/tools/macro/fred-data.js +17 -6
  264. package/dist/tools/macro/fred-data.js.map +1 -1
  265. package/dist/tools/market/crypto-history.js +3 -1
  266. package/dist/tools/market/crypto-history.js.map +1 -1
  267. package/dist/tools/market/crypto-price.js +3 -1
  268. package/dist/tools/market/crypto-price.js.map +1 -1
  269. package/dist/tools/market/screen-stocks.d.ts +18 -0
  270. package/dist/tools/market/screen-stocks.js +252 -0
  271. package/dist/tools/market/screen-stocks.js.map +1 -0
  272. package/dist/tools/market/search-ticker.js +160 -8
  273. package/dist/tools/market/search-ticker.js.map +1 -1
  274. package/dist/tools/market/stock-history.d.ts +2 -2
  275. package/dist/tools/market/stock-history.js +26 -7
  276. package/dist/tools/market/stock-history.js.map +1 -1
  277. package/dist/tools/market/stock-quote.js +5 -3
  278. package/dist/tools/market/stock-quote.js.map +1 -1
  279. package/dist/tools/options/greeks.js +1 -1
  280. package/dist/tools/options/greeks.js.map +1 -1
  281. package/dist/tools/options/option-chain.js +19 -6
  282. package/dist/tools/options/option-chain.js.map +1 -1
  283. package/dist/tools/portfolio/alerts.d.ts +15 -0
  284. package/dist/tools/portfolio/alerts.js +357 -0
  285. package/dist/tools/portfolio/alerts.js.map +1 -0
  286. package/dist/tools/portfolio/correlation.d.ts +1 -1
  287. package/dist/tools/portfolio/correlation.js +33 -13
  288. package/dist/tools/portfolio/correlation.js.map +1 -1
  289. package/dist/tools/portfolio/daily-report.d.ts +8 -0
  290. package/dist/tools/portfolio/daily-report.js +83 -0
  291. package/dist/tools/portfolio/daily-report.js.map +1 -0
  292. package/dist/tools/portfolio/holdings-overlap.js +10 -3
  293. package/dist/tools/portfolio/holdings-overlap.js.map +1 -1
  294. package/dist/tools/portfolio/notifications.d.ts +7 -0
  295. package/dist/tools/portfolio/notifications.js +43 -0
  296. package/dist/tools/portfolio/notifications.js.map +1 -0
  297. package/dist/tools/portfolio/predictions.d.ts +12 -6
  298. package/dist/tools/portfolio/predictions.js +337 -87
  299. package/dist/tools/portfolio/predictions.js.map +1 -1
  300. package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
  301. package/dist/tools/portfolio/risk-analysis.js +45 -6
  302. package/dist/tools/portfolio/risk-analysis.js.map +1 -1
  303. package/dist/tools/portfolio/tracker.d.ts +4 -3
  304. package/dist/tools/portfolio/tracker.js +246 -101
  305. package/dist/tools/portfolio/tracker.js.map +1 -1
  306. package/dist/tools/portfolio/watchlist.d.ts +6 -4
  307. package/dist/tools/portfolio/watchlist.js +208 -108
  308. package/dist/tools/portfolio/watchlist.js.map +1 -1
  309. package/dist/tools/sentiment/reddit-sentiment.js +23 -10
  310. package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
  311. package/dist/tools/sentiment/sentiment-summary.js +15 -13
  312. package/dist/tools/sentiment/sentiment-summary.js.map +1 -1
  313. package/dist/tools/sentiment/sentiment-trend.d.ts +1 -1
  314. package/dist/tools/sentiment/sentiment-trend.js +12 -2
  315. package/dist/tools/sentiment/sentiment-trend.js.map +1 -1
  316. package/dist/tools/sentiment/twitter-sentiment.js +12 -5
  317. package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
  318. package/dist/tools/sentiment/untrusted-text.d.ts +2 -0
  319. package/dist/tools/sentiment/untrusted-text.js +17 -0
  320. package/dist/tools/sentiment/untrusted-text.js.map +1 -0
  321. package/dist/tools/sentiment/web-search.js +9 -13
  322. package/dist/tools/sentiment/web-search.js.map +1 -1
  323. package/dist/tools/sentiment/web-sentiment.js +15 -3
  324. package/dist/tools/sentiment/web-sentiment.js.map +1 -1
  325. package/dist/tools/technical/backtest.d.ts +1 -1
  326. package/dist/tools/technical/backtest.js +27 -20
  327. package/dist/tools/technical/backtest.js.map +1 -1
  328. package/dist/tools/technical/indicators.js +23 -5
  329. package/dist/tools/technical/indicators.js.map +1 -1
  330. package/dist/types/index.d.ts +3 -3
  331. package/dist/types/index.js.map +1 -1
  332. package/dist/types/market.d.ts +1 -0
  333. package/dist/types/portfolio.d.ts +14 -4
  334. package/dist/workflows/compare-assets.d.ts +0 -3
  335. package/dist/workflows/compare-assets.js +20 -11
  336. package/dist/workflows/compare-assets.js.map +1 -1
  337. package/dist/workflows/index.d.ts +3 -4
  338. package/dist/workflows/index.js +3 -3
  339. package/dist/workflows/index.js.map +1 -1
  340. package/dist/workflows/options-screener.d.ts +0 -3
  341. package/dist/workflows/options-screener.js +4 -11
  342. package/dist/workflows/options-screener.js.map +1 -1
  343. package/dist/workflows/portfolio-builder.d.ts +0 -3
  344. package/dist/workflows/portfolio-builder.js +0 -8
  345. package/dist/workflows/portfolio-builder.js.map +1 -1
  346. package/gui/server/ask-user-bridge.ts +1 -1
  347. package/gui/server/automation-heartbeat.ts +97 -0
  348. package/gui/server/background-quotes.ts +97 -1
  349. package/gui/server/chat-event-adapter.ts +32 -10
  350. package/gui/server/chat-run-session.ts +16 -0
  351. package/gui/server/invoke-tool.ts +144 -1
  352. package/gui/server/live-chat-event-adapter.ts +21 -6
  353. package/gui/server/market-state-api.ts +315 -0
  354. package/gui/server/model-setup.ts +149 -2
  355. package/gui/server/private-api-access.ts +62 -0
  356. package/gui/server/projector.ts +12 -7
  357. package/gui/server/prompt-observation.ts +4 -7
  358. package/gui/server/quote-snapshot-store.ts +50 -0
  359. package/gui/server/server.ts +200 -451
  360. package/gui/server/session-actions.ts +186 -1
  361. package/gui/server/shutdown.ts +47 -0
  362. package/gui/server/tool-invoke-ack.ts +49 -0
  363. package/gui/server/tool-metadata.ts +23 -10
  364. package/gui/server/websocket.ts +13 -3
  365. package/gui/server/writer-lock.ts +6 -2
  366. package/gui/server/ws-hub.ts +292 -0
  367. package/gui/shared/chat-events.ts +16 -1
  368. package/gui/shared/event-reducer.ts +24 -6
  369. package/gui/web/dist/assets/CatalogOverlay-eJ2cBk33.js +1 -0
  370. package/gui/web/dist/assets/index-2KZtKBmu.css +1 -0
  371. package/gui/web/dist/assets/index-CveNgtDg.js +69 -0
  372. package/gui/web/dist/index.html +2 -2
  373. package/package.json +5 -1
  374. package/src/analysts/contracts.ts +10 -23
  375. package/src/analysts/orchestrator.ts +8 -43
  376. package/src/cli.ts +35 -12
  377. package/src/config.ts +17 -9
  378. package/src/index.ts +1 -1
  379. package/src/infra/browser.ts +3 -1
  380. package/src/infra/cache.ts +41 -30
  381. package/src/infra/http-client.ts +72 -6
  382. package/src/infra/index.ts +7 -10
  383. package/src/infra/native-dependencies.ts +8 -3
  384. package/src/infra/node-version.ts +3 -1
  385. package/src/infra/opencandle-paths.ts +3 -14
  386. package/src/infra/rate-limiter.ts +22 -19
  387. package/src/market-state/alert-conditions.ts +82 -0
  388. package/src/market-state/alert-runner.ts +863 -0
  389. package/src/market-state/daily-report.ts +247 -0
  390. package/src/market-state/local-automation-service.ts +162 -0
  391. package/src/market-state/notification-delivery.ts +158 -0
  392. package/src/market-state/resolve-for-mutation.ts +24 -0
  393. package/src/market-state/resolve.ts +112 -0
  394. package/src/market-state/service.ts +2344 -0
  395. package/src/memory/index.ts +7 -7
  396. package/src/memory/manager.ts +14 -16
  397. package/src/memory/retrieval.ts +8 -7
  398. package/src/memory/sqlite.ts +407 -6
  399. package/src/memory/storage.ts +5 -15
  400. package/src/memory/tool-defaults.ts +60 -39
  401. package/src/memory/types.ts +3 -3
  402. package/src/monitor.ts +121 -0
  403. package/src/onboarding/connect.ts +10 -33
  404. package/src/onboarding/credential-interceptor.ts +3 -15
  405. package/src/onboarding/degradation-accumulator.ts +1 -3
  406. package/src/onboarding/providers.ts +9 -40
  407. package/src/onboarding/state.ts +4 -15
  408. package/src/onboarding/tool-helpers.ts +2 -9
  409. package/src/onboarding/tool-tags.ts +6 -6
  410. package/src/onboarding/validation.ts +14 -20
  411. package/src/pi/opencandle-extension.ts +529 -85
  412. package/src/pi/session.ts +7 -5
  413. package/src/pi/setup.ts +61 -43
  414. package/src/pi/tool-adapter.ts +5 -2
  415. package/src/prompts/context-builder.ts +23 -12
  416. package/src/prompts/policy-cards.ts +2 -2
  417. package/src/prompts/sections.ts +1 -1
  418. package/src/prompts/symbol-preflight.ts +80 -0
  419. package/src/prompts/workflow-prompts.ts +77 -28
  420. package/src/providers/alpha-vantage.ts +58 -39
  421. package/src/providers/coingecko.ts +2 -5
  422. package/src/providers/errors.ts +9 -0
  423. package/src/providers/exa-search.ts +24 -22
  424. package/src/providers/fear-greed.ts +1 -1
  425. package/src/providers/finnhub.ts +7 -6
  426. package/src/providers/fred.ts +3 -3
  427. package/src/providers/index.ts +14 -6
  428. package/src/providers/reddit.ts +17 -6
  429. package/src/providers/sec-edgar.ts +20 -6
  430. package/src/providers/tradingview.ts +399 -0
  431. package/src/providers/twitter.ts +6 -8
  432. package/src/providers/web-search.ts +30 -20
  433. package/src/providers/with-fallback.ts +8 -7
  434. package/src/providers/wrap-provider.ts +15 -10
  435. package/src/providers/yahoo-finance.ts +140 -35
  436. package/src/routing/classify-intent.ts +101 -10
  437. package/src/routing/defaults.ts +1 -1
  438. package/src/routing/entity-extractor.ts +287 -38
  439. package/src/routing/fund-symbols.ts +58 -0
  440. package/src/routing/horizon.ts +7 -0
  441. package/src/routing/index.ts +48 -48
  442. package/src/routing/planning.ts +144 -53
  443. package/src/routing/route-manifest.ts +37 -15
  444. package/src/routing/router-llm-client.ts +4 -4
  445. package/src/routing/router-prompt.ts +15 -19
  446. package/src/routing/router-types.ts +2 -5
  447. package/src/routing/router.ts +251 -53
  448. package/src/routing/slot-resolver.ts +34 -11
  449. package/src/routing/symbol-disambiguator.ts +72 -0
  450. package/src/routing/turn-context.ts +6 -9
  451. package/src/routing/types.ts +2 -0
  452. package/src/runtime/answer-contracts.ts +82 -43
  453. package/src/runtime/artifact-contracts.ts +2 -1
  454. package/src/runtime/planning-evidence.ts +157 -66
  455. package/src/runtime/prompt-step.ts +1 -16
  456. package/src/runtime/run-context.ts +12 -2
  457. package/src/runtime/session-coordinator.ts +238 -63
  458. package/src/runtime/session-title.ts +60 -0
  459. package/src/runtime/tool-defaults-wrapper.ts +1 -3
  460. package/src/runtime/validation.ts +1 -4
  461. package/src/runtime/workflow-events.ts +7 -7
  462. package/src/runtime/workflow-runner.ts +5 -11
  463. package/src/sentiment/adapters/finnhub.ts +7 -2
  464. package/src/sentiment/adapters/reddit.ts +2 -2
  465. package/src/sentiment/adapters/twitter.ts +1 -1
  466. package/src/sentiment/adapters/web.ts +1 -1
  467. package/src/sentiment/index.ts +16 -26
  468. package/src/sentiment/keywords.ts +26 -4
  469. package/src/sentiment/pipeline.ts +15 -4
  470. package/src/sentiment/scorer.ts +1 -1
  471. package/src/sentiment/store.ts +2 -2
  472. package/src/sentiment/trends.ts +9 -3
  473. package/src/sentiment/types.ts +5 -4
  474. package/src/system-prompt.ts +3 -2
  475. package/src/tool-kit.ts +10 -9
  476. package/src/tools/fundamentals/company-overview.ts +19 -9
  477. package/src/tools/fundamentals/comps.ts +68 -55
  478. package/src/tools/fundamentals/dcf.ts +145 -95
  479. package/src/tools/fundamentals/earnings.ts +16 -6
  480. package/src/tools/fundamentals/financials.ts +16 -7
  481. package/src/tools/fundamentals/sec-filings.ts +37 -16
  482. package/src/tools/index.ts +51 -39
  483. package/src/tools/interaction/ask-user.ts +22 -10
  484. package/src/tools/interaction/twitter-login.ts +17 -5
  485. package/src/tools/macro/fear-greed.ts +1 -1
  486. package/src/tools/macro/fred-data.ts +58 -46
  487. package/src/tools/market/crypto-history.ts +8 -3
  488. package/src/tools/market/crypto-price.ts +6 -6
  489. package/src/tools/market/screen-stocks.ts +279 -0
  490. package/src/tools/market/search-ticker.ts +218 -17
  491. package/src/tools/market/stock-history.ts +37 -12
  492. package/src/tools/market/stock-quote.ts +10 -7
  493. package/src/tools/options/greeks.ts +5 -5
  494. package/src/tools/options/option-chain.ts +41 -17
  495. package/src/tools/portfolio/alerts.ts +457 -0
  496. package/src/tools/portfolio/correlation.ts +47 -20
  497. package/src/tools/portfolio/daily-report.ts +101 -0
  498. package/src/tools/portfolio/holdings-overlap.ts +31 -15
  499. package/src/tools/portfolio/notifications.ts +45 -0
  500. package/src/tools/portfolio/predictions.ts +406 -106
  501. package/src/tools/portfolio/risk-analysis.ts +46 -7
  502. package/src/tools/portfolio/tracker.ts +270 -109
  503. package/src/tools/portfolio/watchlist.ts +250 -121
  504. package/src/tools/sentiment/reddit-sentiment.ts +50 -24
  505. package/src/tools/sentiment/sentiment-summary.ts +62 -41
  506. package/src/tools/sentiment/sentiment-trend.ts +24 -7
  507. package/src/tools/sentiment/twitter-sentiment.ts +22 -15
  508. package/src/tools/sentiment/untrusted-text.ts +21 -0
  509. package/src/tools/sentiment/web-search.ts +21 -18
  510. package/src/tools/sentiment/web-sentiment.ts +26 -10
  511. package/src/tools/technical/backtest.ts +32 -22
  512. package/src/tools/technical/indicators.ts +39 -14
  513. package/src/types/index.ts +8 -3
  514. package/src/types/market.ts +1 -0
  515. package/src/types/portfolio.ts +14 -4
  516. package/src/types/sentiment.ts +2 -2
  517. package/src/workflows/compare-assets.ts +33 -21
  518. package/src/workflows/index.ts +3 -4
  519. package/src/workflows/options-screener.ts +27 -29
  520. package/src/workflows/portfolio-builder.ts +34 -27
  521. package/dist/workflows/types.d.ts +0 -4
  522. package/dist/workflows/types.js +0 -2
  523. package/dist/workflows/types.js.map +0 -1
  524. package/gui/web/dist/assets/CatalogOverlay-Bmp6Knu7.js +0 -1
  525. package/gui/web/dist/assets/index-Bxt9QpLX.css +0 -1
  526. package/gui/web/dist/assets/index-CZ9DHZYy.js +0 -67
  527. package/src/workflows/types.ts +0 -4
@@ -3,60 +3,81 @@ import { initDefaultDatabase } from "./sqlite.js";
3
3
  export type ToolDefaults = Record<string, unknown>;
4
4
  type SqliteDb = ReturnType<typeof initDefaultDatabase>;
5
5
 
6
- export function getDefaults(toolName: string, db: SqliteDb = initDefaultDatabase()): ToolDefaults {
7
- const rows = db
8
- .prepare(
9
- `SELECT param_path, value_json FROM tool_defaults WHERE tool_name = ? ORDER BY param_path`,
10
- )
11
- .all(toolName) as Array<{ param_path: string; value_json: string }>;
6
+ export function getDefaults(toolName: string, db?: SqliteDb): ToolDefaults {
7
+ const ownedDb = db == null;
8
+ const connection = db ?? initDefaultDatabase();
9
+ try {
10
+ const rows = connection
11
+ .prepare(
12
+ `SELECT param_path, value_json FROM tool_defaults WHERE tool_name = ? ORDER BY param_path`,
13
+ )
14
+ .all(toolName) as Array<{ param_path: string; value_json: string }>;
12
15
 
13
- const defaults: ToolDefaults = {};
14
- for (const row of rows) {
15
- setPath(defaults, row.param_path, parseStoredValue(row.value_json));
16
+ const defaults: ToolDefaults = {};
17
+ for (const row of rows) {
18
+ setPath(defaults, row.param_path, parseStoredValue(row.value_json));
19
+ }
20
+ return defaults;
21
+ } finally {
22
+ if (ownedDb) connection.close();
16
23
  }
17
- return defaults;
18
24
  }
19
25
 
20
- export function getAllDefaults(db: SqliteDb = initDefaultDatabase()): Map<string, ToolDefaults> {
21
- const rows = db
22
- .prepare(
23
- `SELECT tool_name, param_path, value_json FROM tool_defaults ORDER BY tool_name, param_path`,
24
- )
25
- .all() as Array<{ tool_name: string; param_path: string; value_json: string }>;
26
+ export function getAllDefaults(db?: SqliteDb): Map<string, ToolDefaults> {
27
+ const ownedDb = db == null;
28
+ const connection = db ?? initDefaultDatabase();
29
+ try {
30
+ const rows = connection
31
+ .prepare(
32
+ `SELECT tool_name, param_path, value_json FROM tool_defaults ORDER BY tool_name, param_path`,
33
+ )
34
+ .all() as Array<{ tool_name: string; param_path: string; value_json: string }>;
26
35
 
27
- const groups = new Map<string, ToolDefaults>();
28
- for (const row of rows) {
29
- const defaults = groups.get(row.tool_name) ?? {};
30
- setPath(defaults, row.param_path, parseStoredValue(row.value_json));
31
- groups.set(row.tool_name, defaults);
36
+ const groups = new Map<string, ToolDefaults>();
37
+ for (const row of rows) {
38
+ const defaults = groups.get(row.tool_name) ?? {};
39
+ setPath(defaults, row.param_path, parseStoredValue(row.value_json));
40
+ groups.set(row.tool_name, defaults);
41
+ }
42
+ return groups;
43
+ } finally {
44
+ if (ownedDb) connection.close();
32
45
  }
33
- return groups;
34
46
  }
35
47
 
36
48
  export function setDefault(
37
49
  toolName: string,
38
50
  paramPath: string,
39
51
  value: unknown,
40
- db: SqliteDb = initDefaultDatabase(),
52
+ db?: SqliteDb,
41
53
  ): void {
42
- db.prepare(
43
- `INSERT INTO tool_defaults (tool_name, param_path, value_json, set_at)
44
- VALUES (?, ?, ?, ?)
45
- ON CONFLICT(tool_name, param_path) DO UPDATE SET
46
- value_json = excluded.value_json,
47
- set_at = excluded.set_at`,
48
- ).run(toolName, paramPath, JSON.stringify(value), new Date().toISOString());
54
+ const ownedDb = db == null;
55
+ const connection = db ?? initDefaultDatabase();
56
+ try {
57
+ connection
58
+ .prepare(
59
+ `INSERT INTO tool_defaults (tool_name, param_path, value_json, set_at)
60
+ VALUES (?, ?, ?, ?)
61
+ ON CONFLICT(tool_name, param_path) DO UPDATE SET
62
+ value_json = excluded.value_json,
63
+ set_at = excluded.set_at`,
64
+ )
65
+ .run(toolName, paramPath, JSON.stringify(value), new Date().toISOString());
66
+ } finally {
67
+ if (ownedDb) connection.close();
68
+ }
49
69
  }
50
70
 
51
- export function clearDefault(
52
- toolName: string,
53
- paramPath: string,
54
- db: SqliteDb = initDefaultDatabase(),
55
- ): void {
56
- db.prepare(`DELETE FROM tool_defaults WHERE tool_name = ? AND param_path = ?`).run(
57
- toolName,
58
- paramPath,
59
- );
71
+ export function clearDefault(toolName: string, paramPath: string, db?: SqliteDb): void {
72
+ const ownedDb = db == null;
73
+ const connection = db ?? initDefaultDatabase();
74
+ try {
75
+ connection
76
+ .prepare(`DELETE FROM tool_defaults WHERE tool_name = ? AND param_path = ?`)
77
+ .run(toolName, paramPath);
78
+ } finally {
79
+ if (ownedDb) connection.close();
80
+ }
60
81
  }
61
82
 
62
83
  function parseStoredValue(valueJson: string): unknown {
@@ -17,10 +17,10 @@ export interface MemoryEntry {
17
17
 
18
18
  /** Staleness thresholds in milliseconds per category. */
19
19
  export const STALENESS_THRESHOLDS: Record<MemoryCategory, number> = {
20
- investor_profile: 90 * 24 * 60 * 60 * 1000, // 90 days
20
+ investor_profile: 90 * 24 * 60 * 60 * 1000, // 90 days
21
21
  interaction_feedback: 14 * 24 * 60 * 60 * 1000, // 14 days
22
- workflow_history: 7 * 24 * 60 * 60 * 1000, // 7 days
23
- references: 30 * 24 * 60 * 60 * 1000, // 30 days
22
+ workflow_history: 7 * 24 * 60 * 60 * 1000, // 7 days
23
+ references: 30 * 24 * 60 * 60 * 1000, // 30 days
24
24
  };
25
25
 
26
26
  /** Map preference keys to memory categories. */
package/src/monitor.ts ADDED
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ import "./infra/node-version.js";
3
+ import { parseArgs } from "node:util";
4
+ import { loadEnv } from "./config.js";
5
+ import { runLocalAutomationHeartbeat } from "./market-state/local-automation-service.js";
6
+ import { MarketStateService } from "./market-state/service.js";
7
+ import { initDefaultDatabase } from "./memory/sqlite.js";
8
+
9
+ const DEFAULT_MONITOR_INTERVAL_MS = 60_000;
10
+ const MIN_MONITOR_INTERVAL_MS = 5_000;
11
+ const MONITOR_OWNER_ID = `monitor:${process.pid}`;
12
+
13
+ interface MonitorOptions {
14
+ once: boolean;
15
+ intervalMs: number;
16
+ }
17
+
18
+ function parseMonitorOptions(argv = process.argv.slice(2)): MonitorOptions {
19
+ const parsed = parseArgs({
20
+ args: argv,
21
+ options: {
22
+ once: { type: "boolean", default: false },
23
+ "interval-ms": { type: "string" },
24
+ },
25
+ strict: false,
26
+ });
27
+ return {
28
+ once: parsed.values.once === true,
29
+ intervalMs: normalizeMonitorIntervalMs(parsed.values["interval-ms"]),
30
+ };
31
+ }
32
+
33
+ function normalizeMonitorIntervalMs(raw: string | boolean | undefined): number {
34
+ if (typeof raw !== "string" || raw.trim() === "") return DEFAULT_MONITOR_INTERVAL_MS;
35
+ const parsed = Number(raw);
36
+ if (!Number.isFinite(parsed) || parsed < MIN_MONITOR_INTERVAL_MS)
37
+ return DEFAULT_MONITOR_INTERVAL_MS;
38
+ return Math.floor(parsed);
39
+ }
40
+
41
+ async function runMonitorHeartbeat(
42
+ intervalMs: number,
43
+ releaseLeaseOnComplete = false,
44
+ ): Promise<void> {
45
+ const db = initDefaultDatabase();
46
+ const now = new Date().toISOString();
47
+ try {
48
+ const result = await runLocalAutomationHeartbeat(db, {
49
+ ownerId: MONITOR_OWNER_ID,
50
+ ownerKind: "monitor",
51
+ now,
52
+ ttlSeconds: Math.max(90, Math.ceil((intervalMs * 2) / 1000)),
53
+ checkAlerts: true,
54
+ checkReports: true,
55
+ releaseLeaseOnComplete,
56
+ });
57
+ if (!result.lease.acquired) {
58
+ console.log(
59
+ `OpenCandle monitor standby: runner owned by ${result.lease.ownerId} until ${result.lease.expiresAt}`,
60
+ );
61
+ return;
62
+ }
63
+ const alertSummary = result.alertCheck
64
+ ? `${result.alertCheck.checked} alert(s), ${result.alertCheck.triggered} triggered, ${result.alertCheck.unavailable} unavailable`
65
+ : "no due alerts";
66
+ const reportSummary = result.reportRuns.length
67
+ ? `${result.reportRuns.length} report run(s)`
68
+ : "no due reports";
69
+ console.log(`OpenCandle monitor heartbeat ${now}: ${alertSummary}; ${reportSummary}.`);
70
+ } finally {
71
+ db.close();
72
+ }
73
+ }
74
+
75
+ async function main(): Promise<void> {
76
+ loadEnv();
77
+ const options = parseMonitorOptions();
78
+ let inFlight = false;
79
+ const runOnce = async (): Promise<void> => {
80
+ if (inFlight) {
81
+ console.log("OpenCandle monitor heartbeat skipped: previous run is still active.");
82
+ return;
83
+ }
84
+ inFlight = true;
85
+ try {
86
+ await runMonitorHeartbeat(options.intervalMs, options.once);
87
+ } finally {
88
+ inFlight = false;
89
+ }
90
+ };
91
+
92
+ await runOnce();
93
+ if (options.once) return;
94
+
95
+ console.log(
96
+ `OpenCandle monitor running locally every ${options.intervalMs}ms. Keep this process open for local alerts and reports.`,
97
+ );
98
+ const timer = setInterval(() => {
99
+ void runOnce().catch((error) => {
100
+ console.error(
101
+ `OpenCandle monitor heartbeat failed: ${error instanceof Error ? error.message : String(error)}`,
102
+ );
103
+ });
104
+ }, options.intervalMs);
105
+
106
+ const stop = () => {
107
+ clearInterval(timer);
108
+ const db = initDefaultDatabase();
109
+ try {
110
+ new MarketStateService(db).releaseAutomationRunnerLease(MONITOR_OWNER_ID);
111
+ } finally {
112
+ db.close();
113
+ }
114
+ console.log("OpenCandle monitor stopped.");
115
+ process.exit(0);
116
+ };
117
+ process.once("SIGINT", stop);
118
+ process.once("SIGTERM", stop);
119
+ }
120
+
121
+ await main();
@@ -18,23 +18,15 @@
18
18
  // call will surface any lingering issue via the credential-required tag.
19
19
 
20
20
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
21
- import { openInBrowser } from "../infra/open-url.js";
22
21
  import {
23
- loadFileConfig,
24
- saveFileConfig,
25
22
  loadConfig,
23
+ loadFileConfig,
26
24
  type OpenCandleFileConfig,
25
+ saveFileConfig,
27
26
  } from "../config.js";
28
- import {
29
- getProvider,
30
- getCredentialSource,
31
- type ProviderId,
32
- } from "./providers.js";
33
- import {
34
- loadOnboardingState,
35
- markProviderCompleted,
36
- saveOnboardingState,
37
- } from "./state.js";
27
+ import { openInBrowser } from "../infra/open-url.js";
28
+ import { getCredentialSource, getProvider, type ProviderId } from "./providers.js";
29
+ import { loadOnboardingState, markProviderCompleted, saveOnboardingState } from "./state.js";
38
30
  import { validateCredential } from "./validation.js";
39
31
 
40
32
  export type ConnectResult =
@@ -55,9 +47,7 @@ function writeNested(
55
47
  next[head] = value;
56
48
  } else {
57
49
  const child =
58
- next[head] && typeof next[head] === "object"
59
- ? (next[head] as Record<string, unknown>)
60
- : {};
50
+ next[head] && typeof next[head] === "object" ? (next[head] as Record<string, unknown>) : {};
61
51
  next[head] = writeNested(child, rest, value);
62
52
  }
63
53
  return next;
@@ -74,17 +64,10 @@ function writeNested(
74
64
  * provider-setup form. Validation is the caller's responsibility — both
75
65
  * call sites run `validateCredential` before invoking this helper.
76
66
  */
77
- export function persistProviderCredential(
78
- providerId: ProviderId,
79
- key: string,
80
- ): void {
67
+ export function persistProviderCredential(providerId: ProviderId, key: string): void {
81
68
  const descriptor = getProvider(providerId);
82
69
  const existing = loadFileConfig() as unknown as Record<string, unknown>;
83
- const updated = writeNested(
84
- existing,
85
- descriptor.configPath,
86
- key,
87
- ) as OpenCandleFileConfig;
70
+ const updated = writeNested(existing, descriptor.configPath, key) as OpenCandleFileConfig;
88
71
  saveFileConfig(updated);
89
72
  loadConfig();
90
73
  const state = loadOnboardingState();
@@ -121,10 +104,7 @@ export async function runProviderConnect(
121
104
  // the browser can't be opened, the user can still paste a key they
122
105
  // already have, so we continue rather than bailing.
123
106
  await openInBrowser(descriptor.signupUrl).catch(() => {});
124
- ctx.ui.notify(
125
- `Opening ${descriptor.displayName} signup in your browser...`,
126
- "info",
127
- );
107
+ ctx.ui.notify(`Opening ${descriptor.displayName} signup in your browser...`, "info");
128
108
 
129
109
  // Prompt for the key. Uses the provider's instructionsHint as the
130
110
  // placeholder text to remind the user what they're pasting.
@@ -175,10 +155,7 @@ export async function runProviderConnect(
175
155
 
176
156
  persistProviderCredential(providerId, trimmed);
177
157
 
178
- ctx.ui.notify(
179
- `${descriptor.displayName} connected. Your key has been saved.`,
180
- "info",
181
- );
158
+ ctx.ui.notify(`${descriptor.displayName} connected. Your key has been saved.`, "info");
182
159
 
183
160
  return { status: "connected" };
184
161
  }
@@ -54,10 +54,7 @@ function buildRemediation(providerId: ProviderId): string {
54
54
  return `run /connect ${alias} to unlock`;
55
55
  }
56
56
 
57
- function skip(
58
- providerId: ProviderId,
59
- silenced: boolean,
60
- ): InterceptAction {
57
+ function skip(providerId: ProviderId, silenced: boolean): InterceptAction {
61
58
  return {
62
59
  action: "skip",
63
60
  provider: providerId,
@@ -74,17 +71,8 @@ function skip(
74
71
  *
75
72
  * No side effects. No Pi imports. Trivially unit-testable.
76
73
  */
77
- export function resolveCredentialRequired(
78
- input: InterceptInput,
79
- ): InterceptAction {
80
- const {
81
- provider,
82
- reason,
83
- state,
84
- sessionPromptedSet,
85
- hardPromptFiredInWorkflow,
86
- now,
87
- } = input;
74
+ export function resolveCredentialRequired(input: InterceptInput): InterceptAction {
75
+ const { provider, reason, state, sessionPromptedSet, hardPromptFiredInWorkflow, now } = input;
88
76
 
89
77
  const descriptor = getProvider(provider);
90
78
  const entry = state.providers[provider];
@@ -58,9 +58,7 @@ export function createDegradationAccumulator(): DegradationAccumulator {
58
58
  const silenced = entry?.status === "never_ask";
59
59
  const alias = descriptor.aliases[0] ?? descriptor.id;
60
60
  const baseRemediation = `run /connect ${alias} to unlock`;
61
- const remediation = silenced
62
- ? `${baseRemediation} (silenced)`
63
- : baseRemediation;
61
+ const remediation = silenced ? `${baseRemediation} (silenced)` : baseRemediation;
64
62
  lines.push(
65
63
  buildSkippedTag({
66
64
  provider,
@@ -10,18 +10,9 @@
10
10
 
11
11
  import { getConfig, loadFileConfig } from "../config.js";
12
12
 
13
- export type ProviderId =
14
- | "alpha_vantage"
15
- | "fred"
16
- | "finnhub"
17
- | "brave"
18
- | "exa";
13
+ export type ProviderId = "alpha_vantage" | "fred" | "finnhub" | "brave" | "exa";
19
14
 
20
- export type ProviderCategory =
21
- | "fundamentals"
22
- | "macro"
23
- | "news"
24
- | "web_search";
15
+ export type ProviderCategory = "fundamentals" | "macro" | "news" | "web_search";
25
16
 
26
17
  export type ProviderTier = "hard" | "soft";
27
18
 
@@ -87,12 +78,7 @@ export const PROVIDERS = [
87
78
  freeTier: true,
88
79
  envVar: "FRED_API_KEY",
89
80
  configPath: ["providers", "fred", "apiKey"],
90
- unlocks: [
91
- "interest rates",
92
- "inflation data",
93
- "yield curve",
94
- "economic indicators",
95
- ],
81
+ unlocks: ["interest rates", "inflation data", "yield curve", "economic indicators"],
96
82
  fallbackDescription: null,
97
83
  snoozeDurationDays: 7,
98
84
  instructionsHint: "Free, about 30 seconds, requires a St. Louis Fed account",
@@ -107,10 +93,7 @@ export const PROVIDERS = [
107
93
  freeTier: true,
108
94
  envVar: "FINNHUB_API_KEY",
109
95
  configPath: ["providers", "finnhub", "apiKey"],
110
- unlocks: [
111
- "ticker-tagged company news",
112
- "sentiment enrichment with a dedicated news source",
113
- ],
96
+ unlocks: ["ticker-tagged company news", "sentiment enrichment with a dedicated news source"],
114
97
  // Finnhub is a soft enrichment source — sentiment-summary continues to work
115
98
  // with Twitter/Reddit/web search when Finnhub is missing. The fallback is
116
99
  // "the other sentiment sources still run".
@@ -194,15 +177,11 @@ export function getProvider(id: ProviderId): ProviderDescriptor {
194
177
  return found;
195
178
  }
196
179
 
197
- export function getProvidersByCategory(
198
- category: ProviderCategory,
199
- ): readonly ProviderDescriptor[] {
180
+ export function getProvidersByCategory(category: ProviderCategory): readonly ProviderDescriptor[] {
200
181
  return PROVIDERS.filter((p) => p.category === category);
201
182
  }
202
183
 
203
- export function getProvidersByTier(
204
- tier: ProviderTier,
205
- ): readonly ProviderDescriptor[] {
184
+ export function getProvidersByTier(tier: ProviderTier): readonly ProviderDescriptor[] {
206
185
  return PROVIDERS.filter((p) => p.tier === tier);
207
186
  }
208
187
 
@@ -244,9 +223,7 @@ export function hasCredential(id: ProviderId): boolean {
244
223
  return typeof value === "string" && value.length > 0;
245
224
  }
246
225
 
247
- export function getCredentialSource(
248
- id: ProviderId,
249
- ): "env" | "file" | "absent" {
226
+ export function getCredentialSource(id: ProviderId): "env" | "file" | "absent" {
250
227
  return getCredential(id).source;
251
228
  }
252
229
 
@@ -271,10 +248,7 @@ export function getCredential(
271
248
 
272
249
  export function resolveProviderFromArgument(
273
250
  arg: string,
274
- ):
275
- | ProviderDescriptor
276
- | readonly ProviderDescriptor[]
277
- | undefined {
251
+ ): ProviderDescriptor | readonly ProviderDescriptor[] | undefined {
278
252
  const needle = arg.trim().toLowerCase();
279
253
  if (!needle) return undefined;
280
254
 
@@ -291,12 +265,7 @@ export function resolveProviderFromArgument(
291
265
  // 3. Category match: if the needle matches a category name, return the
292
266
  // providers in that category. One match → single descriptor. Multiple
293
267
  // matches → array (triggers the sub-picker in the /connect handler).
294
- const categories: readonly ProviderCategory[] = [
295
- "fundamentals",
296
- "macro",
297
- "news",
298
- "web_search",
299
- ];
268
+ const categories: readonly ProviderCategory[] = ["fundamentals", "macro", "news", "web_search"];
300
269
  const normalizedCategory = needle.replace("-", "_");
301
270
  if ((categories as readonly string[]).includes(normalizedCategory)) {
302
271
  const group = getProvidersByCategory(normalizedCategory as ProviderCategory);
@@ -53,9 +53,7 @@ function parseEntry(raw: unknown): ProviderOnboardingEntry | undefined {
53
53
  return { status: "never_ask", lastPromptAt };
54
54
  }
55
55
 
56
- function parseProvidersMap(
57
- raw: unknown,
58
- ): Partial<Record<ProviderId, ProviderOnboardingEntry>> {
56
+ function parseProvidersMap(raw: unknown): Partial<Record<ProviderId, ProviderOnboardingEntry>> {
59
57
  if (!raw || typeof raw !== "object") return {};
60
58
  const result: Partial<Record<ProviderId, ProviderOnboardingEntry>> = {};
61
59
  for (const [key, value] of Object.entries(raw as Record<string, unknown>)) {
@@ -96,10 +94,7 @@ export function loadOnboardingState(path = getOnboardingPath()): OnboardingState
96
94
  return { version, welcomeShownAt, providers };
97
95
  }
98
96
 
99
- export function saveOnboardingState(
100
- state: OnboardingState,
101
- path = getOnboardingPath(),
102
- ): void {
97
+ export function saveOnboardingState(state: OnboardingState, path = getOnboardingPath()): void {
103
98
  ensureParentDir(path);
104
99
  // Strip undefined fields for cleaner on-disk output.
105
100
  const serializable: Record<string, unknown> = {
@@ -120,10 +115,7 @@ function nowIso(): string {
120
115
  return new Date().toISOString();
121
116
  }
122
117
 
123
- export function markProviderCompleted(
124
- state: OnboardingState,
125
- id: ProviderId,
126
- ): OnboardingState {
118
+ export function markProviderCompleted(state: OnboardingState, id: ProviderId): OnboardingState {
127
119
  return {
128
120
  ...state,
129
121
  providers: {
@@ -153,10 +145,7 @@ export function markProviderSnoozed(
153
145
  };
154
146
  }
155
147
 
156
- export function markProviderNeverAsk(
157
- state: OnboardingState,
158
- id: ProviderId,
159
- ): OnboardingState {
148
+ export function markProviderNeverAsk(state: OnboardingState, id: ProviderId): OnboardingState {
160
149
  return {
161
150
  ...state,
162
151
  providers: {
@@ -14,10 +14,7 @@
14
14
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
15
15
  import { ProviderCredentialError } from "../providers/provider-credential-error.js";
16
16
  import { getProvider, hasCredential, type ProviderId } from "./providers.js";
17
- import {
18
- buildCredentialRequiredTag,
19
- type CredentialRequiredReason,
20
- } from "./tool-tags.js";
17
+ import { buildCredentialRequiredTag, type CredentialRequiredReason } from "./tool-tags.js";
21
18
 
22
19
  // Metadata carried on `details` for UI / test assertion. This is NOT the
23
20
  // LLM-facing contract — that lives in `content` as the tagged line. Pi
@@ -100,11 +97,7 @@ export async function withCredentialCheck<T>(
100
97
  return await fn();
101
98
  } catch (error) {
102
99
  if (error instanceof ProviderCredentialError) {
103
- return buildCredentialRequiredResult(
104
- error.provider,
105
- error.reason,
106
- error.httpStatus,
107
- );
100
+ return buildCredentialRequiredResult(error.provider, error.reason, error.httpStatus);
108
101
  }
109
102
  throw error;
110
103
  }
@@ -81,12 +81,12 @@ export function buildCredentialRequiredTag(fields: CredentialRequiredTagFields):
81
81
  }
82
82
 
83
83
  export function buildSoftDegradedTag(fields: SoftDegradedTagFields): string {
84
- return [
84
+ return `${[
85
85
  "[OPENCANDLE_SOFT_DEGRADED",
86
86
  formatField("provider", fields.provider),
87
87
  formatField("fallback", fields.fallback),
88
88
  `remediation=${quote(fields.remediation)}`,
89
- ].join(" ") + "]";
89
+ ].join(" ")}]`;
90
90
  }
91
91
 
92
92
  export function buildSkippedTag(fields: SkippedTagFields): string {
@@ -117,8 +117,9 @@ function parseFields(raw: string): Record<string, string> {
117
117
  // Split on `key=value` pairs, respecting quoted values.
118
118
  const fields: Record<string, string> = {};
119
119
  const pattern = /(\w+)=("((?:[^"\\]|\\.)*)"|([^\s\]]+))/g;
120
- let match: RegExpExecArray | null;
121
- while ((match = pattern.exec(raw)) !== null) {
120
+ while (true) {
121
+ const match = pattern.exec(raw);
122
+ if (!match) break;
122
123
  const key = match[1];
123
124
  const value = match[3] !== undefined ? match[3].replace(/\\"/g, '"') : match[4];
124
125
  fields[key] = value;
@@ -147,8 +148,7 @@ export function parseToolTag(text: string): ParsedTag | undefined {
147
148
  .split(",")
148
149
  .map((s) => s.trim())
149
150
  .filter((s) => s.length > 0);
150
- const fallback =
151
- fallbackRaw === undefined || fallbackRaw === "none" ? null : fallbackRaw;
151
+ const fallback = fallbackRaw === undefined || fallbackRaw === "none" ? null : fallbackRaw;
152
152
  const parsed: ParsedTag = {
153
153
  kind: "credential_required",
154
154
  provider: provider as ProviderId,
@@ -89,7 +89,7 @@ function classifyAlphaVantageBody(body: string): ValidationResult | undefined {
89
89
  if (typeof errorMessage === "string" && errorMessage.length > 0) {
90
90
  return { status: "invalid", message: errorMessage };
91
91
  }
92
- const information = parsed["Information"];
92
+ const information = parsed.Information;
93
93
  if (typeof information === "string" && /invalid api/i.test(information)) {
94
94
  return { status: "invalid", message: information };
95
95
  }
@@ -131,28 +131,22 @@ export async function validateCredential(
131
131
  );
132
132
 
133
133
  case "brave":
134
- return validateWithFetch(
135
- "https://api.search.brave.com/res/v1/web/search?q=test&count=1",
136
- {
137
- method: "GET",
138
- headers: {
139
- "X-Subscription-Token": key,
140
- Accept: "application/json",
141
- },
134
+ return validateWithFetch("https://api.search.brave.com/res/v1/web/search?q=test&count=1", {
135
+ method: "GET",
136
+ headers: {
137
+ "X-Subscription-Token": key,
138
+ Accept: "application/json",
142
139
  },
143
- );
140
+ });
144
141
 
145
142
  case "exa":
146
- return validateWithFetch(
147
- "https://api.exa.ai/search",
148
- {
149
- method: "POST",
150
- headers: {
151
- "Content-Type": "application/json",
152
- "x-api-key": key,
153
- },
154
- body: JSON.stringify({ query: "test", numResults: 1 }),
143
+ return validateWithFetch("https://api.exa.ai/search", {
144
+ method: "POST",
145
+ headers: {
146
+ "Content-Type": "application/json",
147
+ "x-api-key": key,
155
148
  },
156
- );
149
+ body: JSON.stringify({ query: "test", numResults: 1 }),
150
+ });
157
151
  }
158
152
  }