opencandle 0.2.0 → 0.4.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 (401) hide show
  1. package/README.md +110 -87
  2. package/assets/logo.svg +187 -0
  3. package/dist/analysts/orchestrator.js +1 -2
  4. package/dist/analysts/orchestrator.js.map +1 -1
  5. package/dist/cli.d.ts +1 -1
  6. package/dist/cli.js +38 -2
  7. package/dist/cli.js.map +1 -1
  8. package/dist/config.d.ts +34 -5
  9. package/dist/config.js +29 -8
  10. package/dist/config.js.map +1 -1
  11. package/dist/infra/browser.d.ts +10 -0
  12. package/dist/infra/browser.js +1 -0
  13. package/dist/infra/browser.js.map +1 -1
  14. package/dist/infra/cache.d.ts +4 -0
  15. package/dist/infra/cache.js +4 -0
  16. package/dist/infra/cache.js.map +1 -1
  17. package/dist/infra/native-dependencies.d.ts +1 -0
  18. package/dist/infra/native-dependencies.js +10 -0
  19. package/dist/infra/native-dependencies.js.map +1 -0
  20. package/dist/infra/node-version.d.ts +2 -0
  21. package/dist/infra/node-version.js +23 -0
  22. package/dist/infra/node-version.js.map +1 -0
  23. package/dist/infra/rate-limiter.js +6 -0
  24. package/dist/infra/rate-limiter.js.map +1 -1
  25. package/dist/memory/index.d.ts +2 -0
  26. package/dist/memory/index.js +1 -0
  27. package/dist/memory/index.js.map +1 -1
  28. package/dist/memory/sqlite.js +42 -4
  29. package/dist/memory/sqlite.js.map +1 -1
  30. package/dist/memory/storage.d.ts +6 -0
  31. package/dist/memory/storage.js +3 -3
  32. package/dist/memory/storage.js.map +1 -1
  33. package/dist/memory/tool-defaults.d.ts +8 -0
  34. package/dist/memory/tool-defaults.js +59 -0
  35. package/dist/memory/tool-defaults.js.map +1 -0
  36. package/dist/onboarding/connect.d.ts +35 -0
  37. package/dist/onboarding/connect.js +118 -0
  38. package/dist/onboarding/connect.js.map +1 -0
  39. package/dist/onboarding/credential-interceptor.d.ts +44 -0
  40. package/dist/onboarding/credential-interceptor.js +72 -0
  41. package/dist/onboarding/credential-interceptor.js.map +1 -0
  42. package/dist/onboarding/degradation-accumulator.d.ts +21 -0
  43. package/dist/onboarding/degradation-accumulator.js +55 -0
  44. package/dist/onboarding/degradation-accumulator.js.map +1 -0
  45. package/dist/onboarding/prompt-user.d.ts +23 -0
  46. package/dist/onboarding/prompt-user.js +61 -0
  47. package/dist/onboarding/prompt-user.js.map +1 -0
  48. package/dist/onboarding/providers.d.ts +116 -0
  49. package/dist/onboarding/providers.js +239 -0
  50. package/dist/onboarding/providers.js.map +1 -0
  51. package/dist/onboarding/state.d.ts +31 -2
  52. package/dist/onboarding/state.js +141 -13
  53. package/dist/onboarding/state.js.map +1 -1
  54. package/dist/onboarding/tool-helpers.d.ts +34 -0
  55. package/dist/onboarding/tool-helpers.js +80 -0
  56. package/dist/onboarding/tool-helpers.js.map +1 -0
  57. package/dist/onboarding/tool-tags.d.ts +37 -0
  58. package/dist/onboarding/tool-tags.js +149 -0
  59. package/dist/onboarding/tool-tags.js.map +1 -0
  60. package/dist/onboarding/validation.d.ts +19 -0
  61. package/dist/onboarding/validation.js +117 -0
  62. package/dist/onboarding/validation.js.map +1 -0
  63. package/dist/pi/opencandle-extension.d.ts +7 -1
  64. package/dist/pi/opencandle-extension.js +488 -13
  65. package/dist/pi/opencandle-extension.js.map +1 -1
  66. package/dist/pi/session-storage.d.ts +2 -0
  67. package/dist/pi/session-storage.js +5 -0
  68. package/dist/pi/session-storage.js.map +1 -0
  69. package/dist/pi/session.d.ts +4 -1
  70. package/dist/pi/session.js +25 -3
  71. package/dist/pi/session.js.map +1 -1
  72. package/dist/pi/setup.d.ts +1 -2
  73. package/dist/pi/setup.js +67 -120
  74. package/dist/pi/setup.js.map +1 -1
  75. package/dist/pi/tool-adapter.d.ts +2 -2
  76. package/dist/pi/tool-adapter.js +14 -1
  77. package/dist/pi/tool-adapter.js.map +1 -1
  78. package/dist/prompts/context-builder.d.ts +22 -0
  79. package/dist/prompts/context-builder.js +47 -11
  80. package/dist/prompts/context-builder.js.map +1 -1
  81. package/dist/prompts/disclaimer.d.ts +6 -0
  82. package/dist/prompts/disclaimer.js +9 -0
  83. package/dist/prompts/disclaimer.js.map +1 -0
  84. package/dist/prompts/workflow-prompts.d.ts +8 -0
  85. package/dist/prompts/workflow-prompts.js +39 -5
  86. package/dist/prompts/workflow-prompts.js.map +1 -1
  87. package/dist/providers/alpha-vantage.js +20 -1
  88. package/dist/providers/alpha-vantage.js.map +1 -1
  89. package/dist/providers/exa-search.d.ts +39 -0
  90. package/dist/providers/exa-search.js +276 -0
  91. package/dist/providers/exa-search.js.map +1 -0
  92. package/dist/providers/finnhub.d.ts +17 -0
  93. package/dist/providers/finnhub.js +94 -0
  94. package/dist/providers/finnhub.js.map +1 -0
  95. package/dist/providers/fred.js +13 -1
  96. package/dist/providers/fred.js.map +1 -1
  97. package/dist/providers/index.d.ts +2 -0
  98. package/dist/providers/index.js +1 -0
  99. package/dist/providers/index.js.map +1 -1
  100. package/dist/providers/provider-credential-error.d.ts +8 -0
  101. package/dist/providers/provider-credential-error.js +22 -0
  102. package/dist/providers/provider-credential-error.js.map +1 -0
  103. package/dist/providers/reddit.d.ts +8 -0
  104. package/dist/providers/reddit.js +36 -9
  105. package/dist/providers/reddit.js.map +1 -1
  106. package/dist/providers/twitter.js +2 -8
  107. package/dist/providers/twitter.js.map +1 -1
  108. package/dist/providers/web-search.d.ts +17 -0
  109. package/dist/providers/web-search.js +224 -0
  110. package/dist/providers/web-search.js.map +1 -0
  111. package/dist/providers/wrap-provider.d.ts +7 -0
  112. package/dist/providers/wrap-provider.js +15 -0
  113. package/dist/providers/wrap-provider.js.map +1 -1
  114. package/dist/providers/yahoo-finance.js +70 -33
  115. package/dist/providers/yahoo-finance.js.map +1 -1
  116. package/dist/routing/classify-intent.js +22 -0
  117. package/dist/routing/classify-intent.js.map +1 -1
  118. package/dist/routing/defaults.js +1 -1
  119. package/dist/routing/defaults.js.map +1 -1
  120. package/dist/routing/index.d.ts +4 -0
  121. package/dist/routing/index.js +3 -0
  122. package/dist/routing/index.js.map +1 -1
  123. package/dist/routing/router-llm-client.d.ts +11 -0
  124. package/dist/routing/router-llm-client.js +42 -0
  125. package/dist/routing/router-llm-client.js.map +1 -0
  126. package/dist/routing/router-prompt.d.ts +2 -0
  127. package/dist/routing/router-prompt.js +138 -0
  128. package/dist/routing/router-prompt.js.map +1 -0
  129. package/dist/routing/router-types.d.ts +62 -0
  130. package/dist/routing/router-types.js +2 -0
  131. package/dist/routing/router-types.js.map +1 -0
  132. package/dist/routing/router.d.ts +10 -0
  133. package/dist/routing/router.js +194 -0
  134. package/dist/routing/router.js.map +1 -0
  135. package/dist/runtime/session-coordinator.d.ts +63 -4
  136. package/dist/runtime/session-coordinator.js +155 -4
  137. package/dist/runtime/session-coordinator.js.map +1 -1
  138. package/dist/runtime/tool-defaults-wrapper.d.ts +3 -0
  139. package/dist/runtime/tool-defaults-wrapper.js +25 -0
  140. package/dist/runtime/tool-defaults-wrapper.js.map +1 -0
  141. package/dist/sentiment/adapters/finnhub.d.ts +7 -0
  142. package/dist/sentiment/adapters/finnhub.js +39 -0
  143. package/dist/sentiment/adapters/finnhub.js.map +1 -0
  144. package/dist/sentiment/adapters/reddit.d.ts +11 -0
  145. package/dist/sentiment/adapters/reddit.js +54 -0
  146. package/dist/sentiment/adapters/reddit.js.map +1 -0
  147. package/dist/sentiment/adapters/twitter.d.ts +9 -0
  148. package/dist/sentiment/adapters/twitter.js +32 -0
  149. package/dist/sentiment/adapters/twitter.js.map +1 -0
  150. package/dist/sentiment/adapters/web.d.ts +9 -0
  151. package/dist/sentiment/adapters/web.js +40 -0
  152. package/dist/sentiment/adapters/web.js.map +1 -0
  153. package/dist/sentiment/index.d.ts +16 -0
  154. package/dist/sentiment/index.js +44 -0
  155. package/dist/sentiment/index.js.map +1 -0
  156. package/dist/sentiment/keywords.d.ts +2 -0
  157. package/dist/sentiment/keywords.js +9 -0
  158. package/dist/sentiment/keywords.js.map +1 -0
  159. package/dist/sentiment/pipeline.d.ts +9 -0
  160. package/dist/sentiment/pipeline.js +57 -0
  161. package/dist/sentiment/pipeline.js.map +1 -0
  162. package/dist/sentiment/scorer.d.ts +9 -0
  163. package/dist/sentiment/scorer.js +64 -0
  164. package/dist/sentiment/scorer.js.map +1 -0
  165. package/dist/sentiment/store.d.ts +24 -0
  166. package/dist/sentiment/store.js +182 -0
  167. package/dist/sentiment/store.js.map +1 -0
  168. package/dist/sentiment/trends.d.ts +13 -0
  169. package/dist/sentiment/trends.js +73 -0
  170. package/dist/sentiment/trends.js.map +1 -0
  171. package/dist/sentiment/types.d.ts +66 -0
  172. package/dist/sentiment/types.js +54 -0
  173. package/dist/sentiment/types.js.map +1 -0
  174. package/dist/system-prompt.js +29 -13
  175. package/dist/system-prompt.js.map +1 -1
  176. package/dist/tool-kit.d.ts +4 -4
  177. package/dist/tools/fundamentals/company-overview.d.ts +4 -2
  178. package/dist/tools/fundamentals/company-overview.js +27 -27
  179. package/dist/tools/fundamentals/company-overview.js.map +1 -1
  180. package/dist/tools/fundamentals/comps.d.ts +1 -1
  181. package/dist/tools/fundamentals/comps.js +45 -45
  182. package/dist/tools/fundamentals/comps.js.map +1 -1
  183. package/dist/tools/fundamentals/dcf.d.ts +1 -1
  184. package/dist/tools/fundamentals/dcf.js +82 -82
  185. package/dist/tools/fundamentals/dcf.js.map +1 -1
  186. package/dist/tools/fundamentals/earnings.d.ts +4 -2
  187. package/dist/tools/fundamentals/earnings.js +25 -25
  188. package/dist/tools/fundamentals/earnings.js.map +1 -1
  189. package/dist/tools/fundamentals/financials.d.ts +4 -2
  190. package/dist/tools/fundamentals/financials.js +23 -23
  191. package/dist/tools/fundamentals/financials.js.map +1 -1
  192. package/dist/tools/fundamentals/sec-filings.d.ts +1 -1
  193. package/dist/tools/index.d.ts +28 -1
  194. package/dist/tools/index.js +35 -2
  195. package/dist/tools/index.js.map +1 -1
  196. package/dist/tools/interaction/ask-user.d.ts +1 -1
  197. package/dist/tools/interaction/ask-user.js +28 -64
  198. package/dist/tools/interaction/ask-user.js.map +1 -1
  199. package/dist/tools/interaction/twitter-login.d.ts +1 -1
  200. package/dist/tools/macro/fear-greed.d.ts +1 -1
  201. package/dist/tools/macro/fred-data.d.ts +4 -2
  202. package/dist/tools/macro/fred-data.js +26 -26
  203. package/dist/tools/macro/fred-data.js.map +1 -1
  204. package/dist/tools/market/crypto-history.d.ts +1 -1
  205. package/dist/tools/market/crypto-price.d.ts +1 -1
  206. package/dist/tools/market/search-ticker.d.ts +1 -1
  207. package/dist/tools/market/stock-history.d.ts +1 -1
  208. package/dist/tools/market/stock-quote.d.ts +1 -1
  209. package/dist/tools/options/option-chain.d.ts +1 -1
  210. package/dist/tools/options/option-chain.js +4 -1
  211. package/dist/tools/options/option-chain.js.map +1 -1
  212. package/dist/tools/portfolio/correlation.d.ts +1 -1
  213. package/dist/tools/portfolio/predictions.d.ts +1 -1
  214. package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
  215. package/dist/tools/portfolio/tracker.d.ts +1 -1
  216. package/dist/tools/portfolio/watchlist.d.ts +1 -1
  217. package/dist/tools/sentiment/reddit-sentiment.d.ts +4 -2
  218. package/dist/tools/sentiment/reddit-sentiment.js +107 -22
  219. package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
  220. package/dist/tools/sentiment/sentiment-summary.d.ts +7 -0
  221. package/dist/tools/sentiment/sentiment-summary.js +230 -0
  222. package/dist/tools/sentiment/sentiment-summary.js.map +1 -0
  223. package/dist/tools/sentiment/sentiment-trend.d.ts +22 -0
  224. package/dist/tools/sentiment/sentiment-trend.js +39 -0
  225. package/dist/tools/sentiment/sentiment-trend.js.map +1 -0
  226. package/dist/tools/sentiment/twitter-sentiment.d.ts +1 -1
  227. package/dist/tools/sentiment/twitter-sentiment.js +17 -0
  228. package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
  229. package/dist/tools/sentiment/web-search.d.ts +11 -0
  230. package/dist/tools/sentiment/web-search.js +115 -0
  231. package/dist/tools/sentiment/web-search.js.map +1 -0
  232. package/dist/tools/sentiment/web-sentiment.d.ts +8 -0
  233. package/dist/tools/sentiment/web-sentiment.js +66 -0
  234. package/dist/tools/sentiment/web-sentiment.js.map +1 -0
  235. package/dist/tools/technical/backtest.d.ts +1 -1
  236. package/dist/tools/technical/indicators.d.ts +1 -1
  237. package/dist/tools/technical/indicators.js +7 -1
  238. package/dist/tools/technical/indicators.js.map +1 -1
  239. package/dist/types/index.d.ts +1 -1
  240. package/dist/types/sentiment.d.ts +21 -0
  241. package/dist/workflows/options-screener.js +7 -2
  242. package/dist/workflows/options-screener.js.map +1 -1
  243. package/dist/workflows/portfolio-builder.js +3 -3
  244. package/dist/workflows/portfolio-builder.js.map +1 -1
  245. package/gui/server/background-quotes.ts +31 -0
  246. package/gui/server/chat-event-adapter.ts +142 -0
  247. package/gui/server/invoke-tool.ts +89 -0
  248. package/gui/server/live-chat-event-adapter.ts +181 -0
  249. package/gui/server/model-setup.ts +100 -0
  250. package/gui/server/package.json +5 -0
  251. package/gui/server/projector.ts +212 -0
  252. package/gui/server/server.ts +592 -0
  253. package/gui/server/session-actions.ts +31 -0
  254. package/gui/server/tool-metadata.ts +88 -0
  255. package/gui/server/websocket.ts +128 -0
  256. package/gui/server/writer-lock.ts +118 -0
  257. package/gui/shared/chat-events.ts +118 -0
  258. package/gui/shared/event-reducer.ts +186 -0
  259. package/gui/web/dist/assets/CatalogOverlay-D1ImSJTe.js +1 -0
  260. package/gui/web/dist/assets/index-DBrWq43L.css +1 -0
  261. package/gui/web/dist/assets/index-RflHaj0y.js +67 -0
  262. package/gui/web/dist/assets/logo-CWpt6Y2a.svg +187 -0
  263. package/gui/web/dist/index.html +17 -0
  264. package/package.json +62 -20
  265. package/src/analysts/contracts.ts +189 -0
  266. package/src/analysts/orchestrator.ts +300 -0
  267. package/src/cli.ts +205 -0
  268. package/src/config.ts +161 -0
  269. package/src/index.ts +5 -0
  270. package/src/infra/browser.ts +111 -0
  271. package/src/infra/cache.ts +103 -0
  272. package/src/infra/http-client.ts +68 -0
  273. package/src/infra/index.ts +18 -0
  274. package/src/infra/native-dependencies.ts +12 -0
  275. package/src/infra/node-version.ts +24 -0
  276. package/src/infra/open-url.ts +28 -0
  277. package/src/infra/opencandle-paths.ts +64 -0
  278. package/src/infra/rate-limiter.ts +64 -0
  279. package/src/memory/index.ts +10 -0
  280. package/src/memory/manager.ts +159 -0
  281. package/src/memory/preference-extractor.ts +106 -0
  282. package/src/memory/retrieval.ts +70 -0
  283. package/src/memory/sqlite.ts +172 -0
  284. package/src/memory/storage.ts +204 -0
  285. package/src/memory/tool-defaults.ts +87 -0
  286. package/src/memory/types.ts +67 -0
  287. package/src/onboarding/connect.ts +184 -0
  288. package/src/onboarding/credential-interceptor.ts +134 -0
  289. package/src/onboarding/degradation-accumulator.ts +79 -0
  290. package/src/onboarding/prompt-user.ts +85 -0
  291. package/src/onboarding/providers.ts +315 -0
  292. package/src/onboarding/state.ts +218 -0
  293. package/src/onboarding/tool-helpers.ts +111 -0
  294. package/src/onboarding/tool-tags.ts +201 -0
  295. package/src/onboarding/validation.ts +158 -0
  296. package/src/pi/opencandle-extension.ts +724 -0
  297. package/src/pi/session-storage.ts +5 -0
  298. package/src/pi/session.ts +81 -0
  299. package/src/pi/setup.ts +371 -0
  300. package/src/pi/tool-adapter.ts +36 -0
  301. package/src/prompts/context-builder.ts +204 -0
  302. package/src/prompts/disclaimer.ts +9 -0
  303. package/src/prompts/sections.ts +46 -0
  304. package/src/prompts/workflow-prompts.ts +279 -0
  305. package/src/providers/alpha-vantage.ts +292 -0
  306. package/src/providers/coingecko.ts +96 -0
  307. package/src/providers/exa-search.ts +373 -0
  308. package/src/providers/fear-greed.ts +45 -0
  309. package/src/providers/finnhub.ts +124 -0
  310. package/src/providers/fred.ts +83 -0
  311. package/src/providers/index.ts +9 -0
  312. package/src/providers/provider-credential-error.ts +23 -0
  313. package/src/providers/reddit.ts +151 -0
  314. package/src/providers/sec-edgar.ts +96 -0
  315. package/src/providers/twitter.ts +173 -0
  316. package/src/providers/web-search.ts +293 -0
  317. package/src/providers/with-fallback.ts +41 -0
  318. package/src/providers/wrap-provider.ts +64 -0
  319. package/src/providers/yahoo-finance.ts +367 -0
  320. package/src/routing/classify-intent.ts +194 -0
  321. package/src/routing/defaults.ts +29 -0
  322. package/src/routing/entity-extractor.ts +140 -0
  323. package/src/routing/index.ts +26 -0
  324. package/src/routing/router-llm-client.ts +51 -0
  325. package/src/routing/router-prompt.ts +159 -0
  326. package/src/routing/router-types.ts +66 -0
  327. package/src/routing/router.ts +213 -0
  328. package/src/routing/slot-resolver.ts +152 -0
  329. package/src/routing/types.ts +63 -0
  330. package/src/runtime/evidence.ts +77 -0
  331. package/src/runtime/index.ts +55 -0
  332. package/src/runtime/prompt-step.ts +75 -0
  333. package/src/runtime/provider-ids.ts +15 -0
  334. package/src/runtime/provider-tracker.ts +40 -0
  335. package/src/runtime/run-context.ts +22 -0
  336. package/src/runtime/session-coordinator.ts +406 -0
  337. package/src/runtime/tool-defaults-wrapper.ts +35 -0
  338. package/src/runtime/validation.ts +214 -0
  339. package/src/runtime/workflow-events.ts +75 -0
  340. package/src/runtime/workflow-runner.ts +188 -0
  341. package/src/runtime/workflow-types.ts +102 -0
  342. package/src/sentiment/adapters/finnhub.ts +44 -0
  343. package/src/sentiment/adapters/reddit.ts +65 -0
  344. package/src/sentiment/adapters/twitter.ts +36 -0
  345. package/src/sentiment/adapters/web.ts +44 -0
  346. package/src/sentiment/index.ts +58 -0
  347. package/src/sentiment/keywords.ts +9 -0
  348. package/src/sentiment/pipeline.ts +68 -0
  349. package/src/sentiment/scorer.ts +78 -0
  350. package/src/sentiment/store.ts +260 -0
  351. package/src/sentiment/trends.ts +90 -0
  352. package/src/sentiment/types.ts +108 -0
  353. package/src/system-prompt.ts +115 -0
  354. package/src/tool-kit.ts +68 -0
  355. package/src/tools/AGENTS.md +36 -0
  356. package/src/tools/fundamentals/company-overview.ts +54 -0
  357. package/src/tools/fundamentals/comps.ts +156 -0
  358. package/src/tools/fundamentals/dcf.ts +267 -0
  359. package/src/tools/fundamentals/earnings.ts +47 -0
  360. package/src/tools/fundamentals/financials.ts +54 -0
  361. package/src/tools/fundamentals/sec-filings.ts +61 -0
  362. package/src/tools/index.ts +88 -0
  363. package/src/tools/interaction/ask-user.ts +81 -0
  364. package/src/tools/interaction/twitter-login.ts +93 -0
  365. package/src/tools/macro/fear-greed.ts +41 -0
  366. package/src/tools/macro/fred-data.ts +54 -0
  367. package/src/tools/market/crypto-history.ts +51 -0
  368. package/src/tools/market/crypto-price.ts +53 -0
  369. package/src/tools/market/search-ticker.ts +53 -0
  370. package/src/tools/market/stock-history.ts +79 -0
  371. package/src/tools/market/stock-quote.ts +64 -0
  372. package/src/tools/options/greeks.ts +82 -0
  373. package/src/tools/options/option-chain.ts +91 -0
  374. package/src/tools/portfolio/correlation.ts +162 -0
  375. package/src/tools/portfolio/predictions.ts +253 -0
  376. package/src/tools/portfolio/risk-analysis.ts +134 -0
  377. package/src/tools/portfolio/tracker.ts +147 -0
  378. package/src/tools/portfolio/watchlist.ts +153 -0
  379. package/src/tools/sentiment/reddit-sentiment.ts +164 -0
  380. package/src/tools/sentiment/sentiment-summary.ts +256 -0
  381. package/src/tools/sentiment/sentiment-trend.ts +58 -0
  382. package/src/tools/sentiment/twitter-sentiment.ts +96 -0
  383. package/src/tools/sentiment/web-search.ts +150 -0
  384. package/src/tools/sentiment/web-sentiment.ts +76 -0
  385. package/src/tools/technical/backtest.ts +246 -0
  386. package/src/tools/technical/indicators.ts +258 -0
  387. package/src/types/fundamentals.ts +46 -0
  388. package/src/types/index.ts +20 -0
  389. package/src/types/macro.ts +27 -0
  390. package/src/types/market.ts +43 -0
  391. package/src/types/options.ts +35 -0
  392. package/src/types/portfolio.ts +41 -0
  393. package/src/types/sentiment.ts +70 -0
  394. package/src/workflows/compare-assets.ts +39 -0
  395. package/src/workflows/index.ts +4 -0
  396. package/src/workflows/options-screener.ts +49 -0
  397. package/src/workflows/portfolio-builder.ts +52 -0
  398. package/src/workflows/types.ts +4 -0
  399. package/dist/tools/sentiment/news-sentiment.d.ts +0 -7
  400. package/dist/tools/sentiment/news-sentiment.js +0 -55
  401. package/dist/tools/sentiment/news-sentiment.js.map +0 -1
@@ -0,0 +1,201 @@
1
+ // Tool-result text tags used by OpenCandle's conversational-provider-setup
2
+ // flow. These tagged lines are the LLM-facing contract — Pi provider adapters
3
+ // serialize the `content` field of tool results into the model's conversation,
4
+ // so any signal we want the model to reason over must live in text, not
5
+ // `details`.
6
+ //
7
+ // Tag format (single line, parseable):
8
+ // [OPENCANDLE_<KIND> field=value field2=value ... ]
9
+ //
10
+ // Values may be:
11
+ // - bare: provider=alpha_vantage reason=missing silenced=true httpStatus=401
12
+ // - quoted (for spaces or commas): remediation="run /connect economy"
13
+ // - lists (comma-separated inside quotes): unlocks="fundamentals, DCF"
14
+ // - null/absent: fallback=none
15
+ //
16
+ // Parser is tolerant of unknown fields and extra whitespace.
17
+
18
+ import type { ProviderId } from "./providers.js";
19
+
20
+ // -----------------------------------------------------------------------------
21
+ // Public types
22
+ // -----------------------------------------------------------------------------
23
+
24
+ export type CredentialRequiredReason = "missing" | "stale";
25
+
26
+ export interface CredentialRequiredTagFields {
27
+ provider: ProviderId;
28
+ reason: CredentialRequiredReason;
29
+ unlocks: readonly string[];
30
+ fallback: string | null;
31
+ httpStatus?: number;
32
+ }
33
+
34
+ export interface SoftDegradedTagFields {
35
+ provider: ProviderId;
36
+ fallback: string;
37
+ remediation: string;
38
+ }
39
+
40
+ export interface SkippedTagFields {
41
+ provider: ProviderId;
42
+ reason: "credential_not_provided";
43
+ remediation: string;
44
+ silenced?: boolean;
45
+ }
46
+
47
+ export interface ConnectedTagFields {
48
+ provider: ProviderId;
49
+ }
50
+
51
+ export type ParsedTag =
52
+ | ({ kind: "credential_required" } & CredentialRequiredTagFields)
53
+ | ({ kind: "soft_degraded" } & SoftDegradedTagFields)
54
+ | ({ kind: "skipped" } & SkippedTagFields)
55
+ | ({ kind: "connected" } & ConnectedTagFields);
56
+
57
+ // -----------------------------------------------------------------------------
58
+ // Builders
59
+ // -----------------------------------------------------------------------------
60
+
61
+ function quote(value: string): string {
62
+ return `"${value.replace(/"/g, '\\"')}"`;
63
+ }
64
+
65
+ function formatField(key: string, value: string | number | boolean): string {
66
+ return `${key}=${value}`;
67
+ }
68
+
69
+ export function buildCredentialRequiredTag(fields: CredentialRequiredTagFields): string {
70
+ const parts: string[] = [
71
+ "[OPENCANDLE_CREDENTIAL_REQUIRED",
72
+ formatField("provider", fields.provider),
73
+ formatField("reason", fields.reason),
74
+ `unlocks=${quote(fields.unlocks.join(", "))}`,
75
+ formatField("fallback", fields.fallback ?? "none"),
76
+ ];
77
+ if (fields.httpStatus !== undefined) {
78
+ parts.push(formatField("httpStatus", fields.httpStatus));
79
+ }
80
+ return `${parts.join(" ")}]`;
81
+ }
82
+
83
+ export function buildSoftDegradedTag(fields: SoftDegradedTagFields): string {
84
+ return [
85
+ "[OPENCANDLE_SOFT_DEGRADED",
86
+ formatField("provider", fields.provider),
87
+ formatField("fallback", fields.fallback),
88
+ `remediation=${quote(fields.remediation)}`,
89
+ ].join(" ") + "]";
90
+ }
91
+
92
+ export function buildSkippedTag(fields: SkippedTagFields): string {
93
+ const parts: string[] = [
94
+ "[OPENCANDLE_SKIPPED",
95
+ formatField("provider", fields.provider),
96
+ formatField("reason", fields.reason),
97
+ `remediation=${quote(fields.remediation)}`,
98
+ ];
99
+ if (fields.silenced) {
100
+ parts.push(formatField("silenced", "true"));
101
+ }
102
+ return `${parts.join(" ")}]`;
103
+ }
104
+
105
+ export function buildConnectedTag(fields: ConnectedTagFields): string {
106
+ return `[OPENCANDLE_CONNECTED provider=${fields.provider}]`;
107
+ }
108
+
109
+ // -----------------------------------------------------------------------------
110
+ // Parser
111
+ // -----------------------------------------------------------------------------
112
+
113
+ const TAG_LINE_REGEX =
114
+ /\[OPENCANDLE_(CREDENTIAL_REQUIRED|SOFT_DEGRADED|SKIPPED|CONNECTED)([^\]]*)\]/;
115
+
116
+ function parseFields(raw: string): Record<string, string> {
117
+ // Split on `key=value` pairs, respecting quoted values.
118
+ const fields: Record<string, string> = {};
119
+ const pattern = /(\w+)=("((?:[^"\\]|\\.)*)"|([^\s\]]+))/g;
120
+ let match: RegExpExecArray | null;
121
+ while ((match = pattern.exec(raw)) !== null) {
122
+ const key = match[1];
123
+ const value = match[3] !== undefined ? match[3].replace(/\\"/g, '"') : match[4];
124
+ fields[key] = value;
125
+ }
126
+ return fields;
127
+ }
128
+
129
+ export function parseToolTag(text: string): ParsedTag | undefined {
130
+ const match = TAG_LINE_REGEX.exec(text);
131
+ if (!match) return undefined;
132
+
133
+ const kind = match[1];
134
+ const rest = match[2];
135
+ const fields = parseFields(rest);
136
+
137
+ switch (kind) {
138
+ case "CREDENTIAL_REQUIRED": {
139
+ const provider = fields.provider;
140
+ const reason = fields.reason;
141
+ const unlocksRaw = fields.unlocks ?? "";
142
+ const fallbackRaw = fields.fallback;
143
+ if (!provider || (reason !== "missing" && reason !== "stale")) {
144
+ return undefined;
145
+ }
146
+ const unlocks = unlocksRaw
147
+ .split(",")
148
+ .map((s) => s.trim())
149
+ .filter((s) => s.length > 0);
150
+ const fallback =
151
+ fallbackRaw === undefined || fallbackRaw === "none" ? null : fallbackRaw;
152
+ const parsed: ParsedTag = {
153
+ kind: "credential_required",
154
+ provider: provider as ProviderId,
155
+ reason: reason as CredentialRequiredReason,
156
+ unlocks,
157
+ fallback,
158
+ };
159
+ if (fields.httpStatus !== undefined) {
160
+ const n = Number(fields.httpStatus);
161
+ if (!Number.isNaN(n)) parsed.httpStatus = n;
162
+ }
163
+ return parsed;
164
+ }
165
+
166
+ case "SOFT_DEGRADED": {
167
+ const { provider, fallback, remediation } = fields;
168
+ if (!provider || !fallback || !remediation) return undefined;
169
+ return {
170
+ kind: "soft_degraded",
171
+ provider: provider as ProviderId,
172
+ fallback,
173
+ remediation,
174
+ };
175
+ }
176
+
177
+ case "SKIPPED": {
178
+ const { provider, reason, remediation } = fields;
179
+ if (!provider || reason !== "credential_not_provided" || !remediation) {
180
+ return undefined;
181
+ }
182
+ const parsed: ParsedTag = {
183
+ kind: "skipped",
184
+ provider: provider as ProviderId,
185
+ reason: "credential_not_provided",
186
+ remediation,
187
+ };
188
+ if (fields.silenced === "true") parsed.silenced = true;
189
+ return parsed;
190
+ }
191
+
192
+ case "CONNECTED": {
193
+ const { provider } = fields;
194
+ if (!provider) return undefined;
195
+ return { kind: "connected", provider: provider as ProviderId };
196
+ }
197
+
198
+ default:
199
+ return undefined;
200
+ }
201
+ }
@@ -0,0 +1,158 @@
1
+ // Pre-persist credential validation for OpenCandle data providers.
2
+ //
3
+ // Each `validateCredential` call makes a single cheap request to the
4
+ // provider's canonical API with the pasted key and classifies the response
5
+ // into one of three states:
6
+ //
7
+ // - `valid` — provider accepted the credential (persist it)
8
+ // - `invalid` — provider rejected the credential (do NOT persist;
9
+ // re-prompt or return a classified failure)
10
+ // - `transient` — network/timeout/server error (persist anyway; the next
11
+ // tool call will surface any real issue and re-open the
12
+ // prompt via the credential-required tag)
13
+ //
14
+ // The endpoint chosen for each provider is the cheapest one available that
15
+ // also exercises the auth header/query param — we don't pay for meaningful
16
+ // data, just "did the server like the key". Alpha Vantage is a special case:
17
+ // it returns HTTP 200 even for bad keys, with an `Error Message` or
18
+ // `Information` field in the JSON body, so the body must be inspected.
19
+ //
20
+ // All validators share a 5-second timeout via `AbortSignal.timeout` so the
21
+ // connect flow cannot hang if a provider is unreachable.
22
+
23
+ import type { ProviderId } from "./providers.js";
24
+
25
+ const VALIDATION_TIMEOUT_MS = 5_000;
26
+
27
+ export type ValidationResult =
28
+ | { status: "valid" }
29
+ | { status: "invalid"; httpStatus?: number; message?: string }
30
+ | { status: "transient"; reason: string };
31
+
32
+ type BodyClassifier = (body: string) => ValidationResult | undefined;
33
+
34
+ async function validateWithFetch(
35
+ url: string,
36
+ init: RequestInit,
37
+ classifyBody?: BodyClassifier,
38
+ ): Promise<ValidationResult> {
39
+ try {
40
+ const response = await fetch(url, {
41
+ ...init,
42
+ signal: AbortSignal.timeout(VALIDATION_TIMEOUT_MS),
43
+ });
44
+
45
+ // Hard auth failures — the key is definitively bad.
46
+ if (response.status === 401 || response.status === 403) {
47
+ return { status: "invalid", httpStatus: response.status };
48
+ }
49
+
50
+ // FRED returns 400 with an `error_message` for bad keys.
51
+ if (response.status === 400) {
52
+ return { status: "invalid", httpStatus: 400 };
53
+ }
54
+
55
+ // 5xx and other non-2xx — treat as transient. Don't block the user on a
56
+ // provider outage; the next real tool call will surface a fresh error.
57
+ if (!response.ok) {
58
+ return { status: "transient", reason: `HTTP ${response.status}` };
59
+ }
60
+
61
+ // Body-level classification for providers that return 200 with an error
62
+ // shape (Alpha Vantage).
63
+ if (classifyBody) {
64
+ const body = await response.text();
65
+ const classified = classifyBody(body);
66
+ if (classified) return classified;
67
+ }
68
+
69
+ return { status: "valid" };
70
+ } catch (err) {
71
+ const reason = err instanceof Error ? err.message : String(err);
72
+ return { status: "transient", reason };
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Alpha Vantage returns HTTP 200 with an error shape in the JSON body when
78
+ * the key is bad. Two fields to watch for:
79
+ * - `Error Message`: indicates a malformed query OR an invalid key
80
+ * - `Information`: used for rate-limit messages AND for "Invalid API key"
81
+ *
82
+ * The rate-limit form of `Information` is NOT an invalid-key signal, so we
83
+ * only match when the string mentions "invalid api".
84
+ */
85
+ function classifyAlphaVantageBody(body: string): ValidationResult | undefined {
86
+ try {
87
+ const parsed = JSON.parse(body) as Record<string, unknown>;
88
+ const errorMessage = parsed["Error Message"];
89
+ if (typeof errorMessage === "string" && errorMessage.length > 0) {
90
+ return { status: "invalid", message: errorMessage };
91
+ }
92
+ const information = parsed["Information"];
93
+ if (typeof information === "string" && /invalid api/i.test(information)) {
94
+ return { status: "invalid", message: information };
95
+ }
96
+ } catch {
97
+ // Non-JSON body — fall through and trust the HTTP status.
98
+ }
99
+ return undefined;
100
+ }
101
+
102
+ /**
103
+ * Validate a pasted credential against the provider's API. Returns a
104
+ * ValidationResult describing whether the key should be persisted.
105
+ *
106
+ * This function is side-effect-free beyond the network call: it does not
107
+ * touch the config file, onboarding state, or any in-memory cache.
108
+ */
109
+ export async function validateCredential(
110
+ providerId: ProviderId,
111
+ key: string,
112
+ ): Promise<ValidationResult> {
113
+ switch (providerId) {
114
+ case "alpha_vantage":
115
+ return validateWithFetch(
116
+ `https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=IBM&apikey=${encodeURIComponent(key)}`,
117
+ { method: "GET" },
118
+ classifyAlphaVantageBody,
119
+ );
120
+
121
+ case "fred":
122
+ return validateWithFetch(
123
+ `https://api.stlouisfed.org/fred/series?series_id=GDP&api_key=${encodeURIComponent(key)}&file_type=json`,
124
+ { method: "GET" },
125
+ );
126
+
127
+ case "finnhub":
128
+ return validateWithFetch(
129
+ `https://finnhub.io/api/v1/quote?symbol=AAPL&token=${encodeURIComponent(key)}`,
130
+ { method: "GET" },
131
+ );
132
+
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
+ },
142
+ },
143
+ );
144
+
145
+ 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 }),
155
+ },
156
+ );
157
+ }
158
+ }