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
@@ -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,21 @@
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";
27
+ import { openInBrowser } from "../infra/open-url.js";
28
28
  import {
29
- getProvider,
29
+ type ApiKeyProviderId,
30
30
  getCredentialSource,
31
+ getProvider,
32
+ isApiKeyProvider,
31
33
  type ProviderId,
32
34
  } from "./providers.js";
33
- import {
34
- loadOnboardingState,
35
- markProviderCompleted,
36
- saveOnboardingState,
37
- } from "./state.js";
35
+ import { loadOnboardingState, markProviderCompleted, saveOnboardingState } from "./state.js";
38
36
  import { validateCredential } from "./validation.js";
39
37
 
40
38
  export type ConnectResult =
@@ -55,9 +53,7 @@ function writeNested(
55
53
  next[head] = value;
56
54
  } else {
57
55
  const child =
58
- next[head] && typeof next[head] === "object"
59
- ? (next[head] as Record<string, unknown>)
60
- : {};
56
+ next[head] && typeof next[head] === "object" ? (next[head] as Record<string, unknown>) : {};
61
57
  next[head] = writeNested(child, rest, value);
62
58
  }
63
59
  return next;
@@ -74,17 +70,13 @@ function writeNested(
74
70
  * provider-setup form. Validation is the caller's responsibility — both
75
71
  * call sites run `validateCredential` before invoking this helper.
76
72
  */
77
- export function persistProviderCredential(
78
- providerId: ProviderId,
79
- key: string,
80
- ): void {
73
+ export function persistProviderCredential(providerId: ApiKeyProviderId, key: string): void {
81
74
  const descriptor = getProvider(providerId);
75
+ if (!isApiKeyProvider(descriptor)) {
76
+ throw new Error(`Provider ${providerId} does not accept API keys`);
77
+ }
82
78
  const existing = loadFileConfig() as unknown as Record<string, unknown>;
83
- const updated = writeNested(
84
- existing,
85
- descriptor.configPath,
86
- key,
87
- ) as OpenCandleFileConfig;
79
+ const updated = writeNested(existing, descriptor.configPath, key) as OpenCandleFileConfig;
88
80
  saveFileConfig(updated);
89
81
  loadConfig();
90
82
  const state = loadOnboardingState();
@@ -105,6 +97,13 @@ export async function runProviderConnect(
105
97
  providerId: ProviderId,
106
98
  ): Promise<ConnectResult> {
107
99
  const descriptor = getProvider(providerId);
100
+ if (!isApiKeyProvider(descriptor)) {
101
+ ctx.ui.notify(
102
+ `${descriptor.displayName} is not configured with an API key. Run opencandle doctor for setup status.`,
103
+ "warning",
104
+ );
105
+ return { status: "cancelled" };
106
+ }
108
107
 
109
108
  // Env-precedence check.
110
109
  if (getCredentialSource(providerId) === "env") {
@@ -121,10 +120,7 @@ export async function runProviderConnect(
121
120
  // the browser can't be opened, the user can still paste a key they
122
121
  // already have, so we continue rather than bailing.
123
122
  await openInBrowser(descriptor.signupUrl).catch(() => {});
124
- ctx.ui.notify(
125
- `Opening ${descriptor.displayName} signup in your browser...`,
126
- "info",
127
- );
123
+ ctx.ui.notify(`Opening ${descriptor.displayName} signup in your browser...`, "info");
128
124
 
129
125
  // Prompt for the key. Uses the provider's instructionsHint as the
130
126
  // placeholder text to remind the user what they're pasting.
@@ -144,7 +140,7 @@ export async function runProviderConnect(
144
140
  // short-circuit here without writing anything; transient failures warn
145
141
  // but proceed to persist so users don't get stuck on a provider outage.
146
142
  ctx.ui.notify(`Verifying your ${descriptor.displayName} key...`, "info");
147
- const validation = await validateCredential(providerId, trimmed);
143
+ const validation = await validateCredential(descriptor.id, trimmed);
148
144
 
149
145
  if (validation.status === "invalid") {
150
146
  const statusHint =
@@ -173,12 +169,9 @@ export async function runProviderConnect(
173
169
  // Fall through to persist.
174
170
  }
175
171
 
176
- persistProviderCredential(providerId, trimmed);
172
+ persistProviderCredential(descriptor.id, trimmed);
177
173
 
178
- ctx.ui.notify(
179
- `${descriptor.displayName} connected. Your key has been saved.`,
180
- "info",
181
- );
174
+ ctx.ui.notify(`${descriptor.displayName} connected. Your key has been saved.`, "info");
182
175
 
183
176
  return { status: "connected" };
184
177
  }
@@ -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,