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.
- package/README.md +170 -186
- package/dist/analysts/contracts.d.ts +1 -3
- package/dist/analysts/contracts.js +1 -11
- package/dist/analysts/contracts.js.map +1 -1
- package/dist/analysts/orchestrator.d.ts +1 -3
- package/dist/analysts/orchestrator.js +1 -26
- package/dist/analysts/orchestrator.js.map +1 -1
- package/dist/cli.js +66 -7
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +13 -3
- package/dist/config.js +25 -5
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/infra/cache.d.ts +8 -11
- package/dist/infra/cache.js +17 -15
- package/dist/infra/cache.js.map +1 -1
- package/dist/infra/http-client.d.ts +4 -1
- package/dist/infra/http-client.js +59 -6
- package/dist/infra/http-client.js.map +1 -1
- package/dist/infra/index.d.ts +2 -3
- package/dist/infra/index.js +2 -3
- package/dist/infra/index.js.map +1 -1
- package/dist/infra/native-dependencies.js +2 -2
- package/dist/infra/native-dependencies.js.map +1 -1
- package/dist/infra/node-version.js.map +1 -1
- package/dist/infra/opencandle-paths.d.ts +0 -3
- package/dist/infra/opencandle-paths.js +4 -11
- package/dist/infra/opencandle-paths.js.map +1 -1
- package/dist/infra/rate-limiter.js +12 -9
- package/dist/infra/rate-limiter.js.map +1 -1
- package/dist/market-state/alert-conditions.d.ts +34 -0
- package/dist/market-state/alert-conditions.js +23 -0
- package/dist/market-state/alert-conditions.js.map +1 -0
- package/dist/market-state/alert-runner.d.ts +55 -0
- package/dist/market-state/alert-runner.js +634 -0
- package/dist/market-state/alert-runner.js.map +1 -0
- package/dist/market-state/daily-report.d.ts +26 -0
- package/dist/market-state/daily-report.js +179 -0
- package/dist/market-state/daily-report.js.map +1 -0
- package/dist/market-state/local-automation-service.d.ts +25 -0
- package/dist/market-state/local-automation-service.js +119 -0
- package/dist/market-state/local-automation-service.js.map +1 -0
- package/dist/market-state/notification-delivery.d.ts +14 -0
- package/dist/market-state/notification-delivery.js +139 -0
- package/dist/market-state/notification-delivery.js.map +1 -0
- package/dist/market-state/resolve-for-mutation.d.ts +10 -0
- package/dist/market-state/resolve-for-mutation.js +15 -0
- package/dist/market-state/resolve-for-mutation.js.map +1 -0
- package/dist/market-state/resolve.d.ts +14 -0
- package/dist/market-state/resolve.js +89 -0
- package/dist/market-state/resolve.js.map +1 -0
- package/dist/market-state/service.d.ts +527 -0
- package/dist/market-state/service.js +1099 -0
- package/dist/market-state/service.js.map +1 -0
- package/dist/memory/index.d.ts +7 -7
- package/dist/memory/index.js +6 -6
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/manager.js +11 -11
- package/dist/memory/manager.js.map +1 -1
- package/dist/memory/retrieval.js +7 -4
- package/dist/memory/retrieval.js.map +1 -1
- package/dist/memory/sqlite.js +385 -3
- package/dist/memory/sqlite.js.map +1 -1
- package/dist/memory/storage.js +1 -2
- package/dist/memory/storage.js.map +1 -1
- package/dist/memory/tool-defaults.js +64 -28
- package/dist/memory/tool-defaults.js.map +1 -1
- package/dist/memory/types.js.map +1 -1
- package/dist/monitor.d.ts +2 -0
- package/dist/monitor.js +104 -0
- package/dist/monitor.js.map +1 -0
- package/dist/onboarding/connect.d.ts +2 -2
- package/dist/onboarding/connect.js +13 -8
- package/dist/onboarding/connect.js.map +1 -1
- package/dist/onboarding/credential-interceptor.js +1 -1
- package/dist/onboarding/credential-interceptor.js.map +1 -1
- package/dist/onboarding/degradation-accumulator.js +1 -3
- package/dist/onboarding/degradation-accumulator.js.map +1 -1
- package/dist/onboarding/provider-status.d.ts +48 -0
- package/dist/onboarding/provider-status.js +285 -0
- package/dist/onboarding/provider-status.js.map +1 -0
- package/dist/onboarding/providers.d.ts +85 -8
- package/dist/onboarding/providers.js +83 -18
- package/dist/onboarding/providers.js.map +1 -1
- package/dist/onboarding/state.d.ts +1 -0
- package/dist/onboarding/state.js +5 -0
- package/dist/onboarding/state.js.map +1 -1
- package/dist/onboarding/tool-helpers.js +1 -1
- package/dist/onboarding/tool-helpers.js.map +1 -1
- package/dist/onboarding/tool-tags.d.ts +12 -1
- package/dist/onboarding/tool-tags.js +37 -5
- package/dist/onboarding/tool-tags.js.map +1 -1
- package/dist/onboarding/validation.d.ts +2 -2
- package/dist/onboarding/validation.js +1 -1
- package/dist/onboarding/validation.js.map +1 -1
- package/dist/pi/opencandle-extension.d.ts +8 -0
- package/dist/pi/opencandle-extension.js +502 -42
- package/dist/pi/opencandle-extension.js.map +1 -1
- package/dist/pi/session.d.ts +1 -1
- package/dist/pi/session.js +3 -1
- package/dist/pi/session.js.map +1 -1
- package/dist/pi/setup.js +8 -3
- package/dist/pi/setup.js.map +1 -1
- package/dist/pi/tool-adapter.d.ts +4 -1
- package/dist/pi/tool-adapter.js +10 -6
- package/dist/pi/tool-adapter.js.map +1 -1
- package/dist/prompts/context-builder.d.ts +1 -1
- package/dist/prompts/context-builder.js +20 -7
- package/dist/prompts/context-builder.js.map +1 -1
- package/dist/prompts/policy-cards.d.ts +1 -1
- package/dist/prompts/policy-cards.js +2 -2
- package/dist/prompts/policy-cards.js.map +1 -1
- package/dist/prompts/sections.d.ts +1 -1
- package/dist/prompts/symbol-preflight.d.ts +20 -0
- package/dist/prompts/symbol-preflight.js +49 -0
- package/dist/prompts/symbol-preflight.js.map +1 -0
- package/dist/prompts/workflow-prompts.d.ts +1 -1
- package/dist/prompts/workflow-prompts.js +54 -16
- package/dist/prompts/workflow-prompts.js.map +1 -1
- package/dist/providers/alpha-vantage.d.ts +1 -1
- package/dist/providers/alpha-vantage.js +26 -7
- package/dist/providers/alpha-vantage.js.map +1 -1
- package/dist/providers/coingecko.js +1 -1
- package/dist/providers/coingecko.js.map +1 -1
- package/dist/providers/errors.d.ts +5 -0
- package/dist/providers/errors.js +11 -0
- package/dist/providers/errors.js.map +1 -0
- package/dist/providers/exa-search.d.ts +2 -2
- package/dist/providers/exa-search.js +19 -11
- package/dist/providers/exa-search.js.map +1 -1
- package/dist/providers/external-tool-error.d.ts +10 -0
- package/dist/providers/external-tool-error.js +21 -0
- package/dist/providers/external-tool-error.js.map +1 -0
- package/dist/providers/fear-greed.js +1 -1
- package/dist/providers/fear-greed.js.map +1 -1
- package/dist/providers/finnhub.js +3 -5
- package/dist/providers/finnhub.js.map +1 -1
- package/dist/providers/fred.js +2 -2
- package/dist/providers/fred.js.map +1 -1
- package/dist/providers/index.d.ts +7 -6
- package/dist/providers/index.js +6 -5
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/reddit-cli.d.ts +36 -0
- package/dist/providers/reddit-cli.js +201 -0
- package/dist/providers/reddit-cli.js.map +1 -0
- package/dist/providers/reddit.d.ts +1 -1
- package/dist/providers/reddit.js +9 -37
- package/dist/providers/reddit.js.map +1 -1
- package/dist/providers/sec-edgar.d.ts +1 -0
- package/dist/providers/sec-edgar.js +12 -4
- package/dist/providers/sec-edgar.js.map +1 -1
- package/dist/providers/tradingview.d.ts +47 -0
- package/dist/providers/tradingview.js +275 -0
- package/dist/providers/tradingview.js.map +1 -0
- package/dist/providers/twitter-cli.d.ts +40 -0
- package/dist/providers/twitter-cli.js +153 -0
- package/dist/providers/twitter-cli.js.map +1 -0
- package/dist/providers/twitter.d.ts +0 -8
- package/dist/providers/twitter.js +8 -60
- package/dist/providers/twitter.js.map +1 -1
- package/dist/providers/web-search.js +26 -12
- package/dist/providers/web-search.js.map +1 -1
- package/dist/providers/with-fallback.js +4 -2
- package/dist/providers/with-fallback.js.map +1 -1
- package/dist/providers/wrap-provider.d.ts +2 -3
- package/dist/providers/wrap-provider.js +44 -8
- package/dist/providers/wrap-provider.js.map +1 -1
- package/dist/providers/yahoo-finance.d.ts +1 -1
- package/dist/providers/yahoo-finance.js +153 -48
- package/dist/providers/yahoo-finance.js.map +1 -1
- package/dist/routing/classify-intent.d.ts +6 -0
- package/dist/routing/classify-intent.js +78 -7
- package/dist/routing/classify-intent.js.map +1 -1
- package/dist/routing/defaults.d.ts +1 -1
- package/dist/routing/entity-extractor.d.ts +1 -0
- package/dist/routing/entity-extractor.js +234 -29
- package/dist/routing/entity-extractor.js.map +1 -1
- package/dist/routing/fund-symbols.d.ts +2 -0
- package/dist/routing/fund-symbols.js +55 -0
- package/dist/routing/fund-symbols.js.map +1 -0
- package/dist/routing/horizon.d.ts +1 -0
- package/dist/routing/horizon.js +10 -0
- package/dist/routing/horizon.js.map +1 -0
- package/dist/routing/index.d.ts +10 -10
- package/dist/routing/index.js +6 -6
- package/dist/routing/index.js.map +1 -1
- package/dist/routing/planning.d.ts +2 -2
- package/dist/routing/planning.js +65 -34
- package/dist/routing/planning.js.map +1 -1
- package/dist/routing/route-manifest.d.ts +2 -2
- package/dist/routing/route-manifest.js +25 -4
- package/dist/routing/route-manifest.js.map +1 -1
- package/dist/routing/router-llm-client.js.map +1 -1
- package/dist/routing/router-prompt.js +7 -9
- package/dist/routing/router-prompt.js.map +1 -1
- package/dist/routing/router-types.d.ts +1 -0
- package/dist/routing/router.js +137 -22
- package/dist/routing/router.js.map +1 -1
- package/dist/routing/slot-resolver.d.ts +1 -1
- package/dist/routing/slot-resolver.js +2 -4
- package/dist/routing/slot-resolver.js.map +1 -1
- package/dist/routing/symbol-disambiguator.d.ts +11 -0
- package/dist/routing/symbol-disambiguator.js +52 -0
- package/dist/routing/symbol-disambiguator.js.map +1 -0
- package/dist/routing/turn-context.d.ts +1 -1
- package/dist/routing/turn-context.js +1 -1
- package/dist/routing/turn-context.js.map +1 -1
- package/dist/routing/types.d.ts +2 -0
- package/dist/runtime/answer-contracts.d.ts +1 -1
- package/dist/runtime/answer-contracts.js +48 -9
- package/dist/runtime/answer-contracts.js.map +1 -1
- package/dist/runtime/artifact-contracts.js.map +1 -1
- package/dist/runtime/planning-evidence.js +47 -26
- package/dist/runtime/planning-evidence.js.map +1 -1
- package/dist/runtime/prompt-step.d.ts +1 -9
- package/dist/runtime/prompt-step.js +0 -10
- package/dist/runtime/prompt-step.js.map +1 -1
- package/dist/runtime/run-context.d.ts +5 -2
- package/dist/runtime/run-context.js +8 -1
- package/dist/runtime/run-context.js.map +1 -1
- package/dist/runtime/session-coordinator.d.ts +13 -5
- package/dist/runtime/session-coordinator.js +160 -20
- package/dist/runtime/session-coordinator.js.map +1 -1
- package/dist/runtime/session-title.d.ts +14 -0
- package/dist/runtime/session-title.js +50 -0
- package/dist/runtime/session-title.js.map +1 -0
- package/dist/runtime/tool-defaults-wrapper.js +7 -5
- package/dist/runtime/tool-defaults-wrapper.js.map +1 -1
- package/dist/runtime/validation.js.map +1 -1
- package/dist/runtime/workflow-events.js.map +1 -1
- package/dist/runtime/workflow-runner.d.ts +3 -3
- package/dist/runtime/workflow-runner.js +1 -1
- package/dist/runtime/workflow-runner.js.map +1 -1
- package/dist/sentiment/adapters/finnhub.d.ts +1 -1
- package/dist/sentiment/adapters/finnhub.js +6 -1
- package/dist/sentiment/adapters/finnhub.js.map +1 -1
- package/dist/sentiment/adapters/reddit.d.ts +2 -2
- package/dist/sentiment/adapters/twitter.d.ts +1 -1
- package/dist/sentiment/adapters/web.d.ts +1 -1
- package/dist/sentiment/index.d.ts +10 -11
- package/dist/sentiment/index.js +10 -20
- package/dist/sentiment/index.js.map +1 -1
- package/dist/sentiment/insights.d.ts +17 -0
- package/dist/sentiment/insights.js +206 -0
- package/dist/sentiment/insights.js.map +1 -0
- package/dist/sentiment/keywords.js +26 -4
- package/dist/sentiment/keywords.js.map +1 -1
- package/dist/sentiment/pipeline.d.ts +2 -2
- package/dist/sentiment/pipeline.js +14 -2
- package/dist/sentiment/pipeline.js.map +1 -1
- package/dist/sentiment/scorer.d.ts +2 -0
- package/dist/sentiment/scorer.js +11 -2
- package/dist/sentiment/scorer.js.map +1 -1
- package/dist/sentiment/store.d.ts +1 -1
- package/dist/sentiment/store.js +1 -1
- package/dist/sentiment/store.js.map +1 -1
- package/dist/sentiment/trends.d.ts +1 -1
- package/dist/sentiment/trends.js.map +1 -1
- package/dist/sentiment/types.d.ts +2 -0
- package/dist/sentiment/types.js.map +1 -1
- package/dist/system-prompt.js +6 -9
- package/dist/system-prompt.js.map +1 -1
- package/dist/tool-kit.d.ts +7 -7
- package/dist/tool-kit.js +4 -4
- package/dist/tool-kit.js.map +1 -1
- package/dist/tools/fundamentals/company-overview.js +11 -6
- package/dist/tools/fundamentals/company-overview.js.map +1 -1
- package/dist/tools/fundamentals/comps.js +18 -9
- package/dist/tools/fundamentals/comps.js.map +1 -1
- package/dist/tools/fundamentals/dcf.js +23 -11
- package/dist/tools/fundamentals/dcf.js.map +1 -1
- package/dist/tools/fundamentals/earnings.js +8 -3
- package/dist/tools/fundamentals/earnings.js.map +1 -1
- package/dist/tools/fundamentals/financials.js +8 -3
- package/dist/tools/fundamentals/financials.js.map +1 -1
- package/dist/tools/fundamentals/sec-filings.js +21 -6
- package/dist/tools/fundamentals/sec-filings.js.map +1 -1
- package/dist/tools/index.d.ts +27 -20
- package/dist/tools/index.js +55 -43
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/interaction/ask-user.js +15 -3
- package/dist/tools/interaction/ask-user.js.map +1 -1
- package/dist/tools/macro/fear-greed.js.map +1 -1
- package/dist/tools/macro/fred-data.d.ts +1 -1
- package/dist/tools/macro/fred-data.js +17 -6
- package/dist/tools/macro/fred-data.js.map +1 -1
- package/dist/tools/market/crypto-history.js +3 -1
- package/dist/tools/market/crypto-history.js.map +1 -1
- package/dist/tools/market/crypto-price.js +3 -1
- package/dist/tools/market/crypto-price.js.map +1 -1
- package/dist/tools/market/screen-stocks.d.ts +18 -0
- package/dist/tools/market/screen-stocks.js +252 -0
- package/dist/tools/market/screen-stocks.js.map +1 -0
- package/dist/tools/market/search-ticker.js +160 -8
- package/dist/tools/market/search-ticker.js.map +1 -1
- package/dist/tools/market/stock-history.d.ts +2 -2
- package/dist/tools/market/stock-history.js +26 -7
- package/dist/tools/market/stock-history.js.map +1 -1
- package/dist/tools/market/stock-quote.js +5 -3
- package/dist/tools/market/stock-quote.js.map +1 -1
- package/dist/tools/options/greeks.js +1 -1
- package/dist/tools/options/greeks.js.map +1 -1
- package/dist/tools/options/option-chain.js +19 -6
- package/dist/tools/options/option-chain.js.map +1 -1
- package/dist/tools/portfolio/alerts.d.ts +15 -0
- package/dist/tools/portfolio/alerts.js +357 -0
- package/dist/tools/portfolio/alerts.js.map +1 -0
- package/dist/tools/portfolio/correlation.d.ts +1 -1
- package/dist/tools/portfolio/correlation.js +33 -13
- package/dist/tools/portfolio/correlation.js.map +1 -1
- package/dist/tools/portfolio/daily-report.d.ts +8 -0
- package/dist/tools/portfolio/daily-report.js +83 -0
- package/dist/tools/portfolio/daily-report.js.map +1 -0
- package/dist/tools/portfolio/holdings-overlap.js +10 -3
- package/dist/tools/portfolio/holdings-overlap.js.map +1 -1
- package/dist/tools/portfolio/notifications.d.ts +7 -0
- package/dist/tools/portfolio/notifications.js +43 -0
- package/dist/tools/portfolio/notifications.js.map +1 -0
- package/dist/tools/portfolio/predictions.d.ts +12 -6
- package/dist/tools/portfolio/predictions.js +337 -87
- package/dist/tools/portfolio/predictions.js.map +1 -1
- package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
- package/dist/tools/portfolio/risk-analysis.js +45 -6
- package/dist/tools/portfolio/risk-analysis.js.map +1 -1
- package/dist/tools/portfolio/tracker.d.ts +4 -3
- package/dist/tools/portfolio/tracker.js +246 -101
- package/dist/tools/portfolio/tracker.js.map +1 -1
- package/dist/tools/portfolio/watchlist.d.ts +6 -4
- package/dist/tools/portfolio/watchlist.js +208 -108
- package/dist/tools/portfolio/watchlist.js.map +1 -1
- package/dist/tools/sentiment/insight-format.d.ts +2 -0
- package/dist/tools/sentiment/insight-format.js +36 -0
- package/dist/tools/sentiment/insight-format.js.map +1 -0
- package/dist/tools/sentiment/query-match.d.ts +3 -0
- package/dist/tools/sentiment/query-match.js +113 -0
- package/dist/tools/sentiment/query-match.js.map +1 -0
- package/dist/tools/sentiment/reddit-sentiment.d.ts +12 -1
- package/dist/tools/sentiment/reddit-sentiment.js +266 -107
- package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
- package/dist/tools/sentiment/sentiment-summary.d.ts +9 -1
- package/dist/tools/sentiment/sentiment-summary.js +223 -205
- package/dist/tools/sentiment/sentiment-summary.js.map +1 -1
- package/dist/tools/sentiment/sentiment-trend.d.ts +1 -1
- package/dist/tools/sentiment/sentiment-trend.js +12 -2
- package/dist/tools/sentiment/sentiment-trend.js.map +1 -1
- package/dist/tools/sentiment/twitter-sentiment.d.ts +11 -1
- package/dist/tools/sentiment/twitter-sentiment.js +188 -58
- package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
- package/dist/tools/sentiment/untrusted-text.d.ts +2 -0
- package/dist/tools/sentiment/untrusted-text.js +17 -0
- package/dist/tools/sentiment/untrusted-text.js.map +1 -0
- package/dist/tools/sentiment/web-search.js +9 -13
- package/dist/tools/sentiment/web-search.js.map +1 -1
- package/dist/tools/sentiment/web-sentiment.js +19 -3
- package/dist/tools/sentiment/web-sentiment.js.map +1 -1
- package/dist/tools/technical/backtest.d.ts +1 -1
- package/dist/tools/technical/backtest.js +27 -20
- package/dist/tools/technical/backtest.js.map +1 -1
- package/dist/tools/technical/indicators.js +23 -5
- package/dist/tools/technical/indicators.js.map +1 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/market.d.ts +1 -0
- package/dist/types/portfolio.d.ts +14 -4
- package/dist/types/sentiment.d.ts +52 -0
- package/dist/workflows/compare-assets.d.ts +0 -3
- package/dist/workflows/compare-assets.js +20 -11
- package/dist/workflows/compare-assets.js.map +1 -1
- package/dist/workflows/index.d.ts +3 -4
- package/dist/workflows/index.js +3 -3
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/options-screener.d.ts +0 -3
- package/dist/workflows/options-screener.js +4 -11
- package/dist/workflows/options-screener.js.map +1 -1
- package/dist/workflows/portfolio-builder.d.ts +0 -3
- package/dist/workflows/portfolio-builder.js +0 -8
- package/dist/workflows/portfolio-builder.js.map +1 -1
- package/gui/server/ask-user-bridge.ts +1 -1
- package/gui/server/automation-heartbeat.ts +97 -0
- package/gui/server/background-quotes.ts +97 -1
- package/gui/server/chat-event-adapter.ts +32 -10
- package/gui/server/chat-run-session.ts +16 -0
- package/gui/server/invoke-tool.ts +160 -3
- package/gui/server/live-chat-event-adapter.ts +21 -6
- package/gui/server/market-state-api.ts +315 -0
- package/gui/server/model-setup.ts +156 -2
- package/gui/server/private-api-access.ts +62 -0
- package/gui/server/projector.ts +18 -9
- package/gui/server/prompt-observation.ts +4 -7
- package/gui/server/quote-snapshot-store.ts +50 -0
- package/gui/server/server.ts +218 -451
- package/gui/server/session-actions.ts +186 -1
- package/gui/server/shutdown.ts +47 -0
- package/gui/server/tool-invoke-ack.ts +49 -0
- package/gui/server/tool-metadata.ts +101 -24
- package/gui/server/websocket.ts +13 -3
- package/gui/server/writer-lock.ts +6 -2
- package/gui/server/ws-hub.ts +311 -0
- package/gui/shared/chat-events.ts +16 -1
- package/gui/shared/event-reducer.ts +24 -6
- package/gui/web/dist/assets/CatalogOverlay-CgeY5Pkp.js +1 -0
- package/gui/web/dist/assets/index-C6W_2eAn.js +69 -0
- package/gui/web/dist/assets/index-hwbx24a5.css +1 -0
- package/gui/web/dist/index.html +2 -2
- package/package.json +9 -6
- package/src/analysts/contracts.ts +10 -23
- package/src/analysts/orchestrator.ts +8 -43
- package/src/cli.ts +76 -12
- package/src/config.ts +44 -9
- package/src/index.ts +1 -1
- package/src/infra/cache.ts +41 -30
- package/src/infra/http-client.ts +72 -6
- package/src/infra/index.ts +6 -10
- package/src/infra/native-dependencies.ts +8 -3
- package/src/infra/node-version.ts +3 -1
- package/src/infra/opencandle-paths.ts +3 -14
- package/src/infra/rate-limiter.ts +22 -19
- package/src/market-state/alert-conditions.ts +82 -0
- package/src/market-state/alert-runner.ts +863 -0
- package/src/market-state/daily-report.ts +247 -0
- package/src/market-state/local-automation-service.ts +162 -0
- package/src/market-state/notification-delivery.ts +158 -0
- package/src/market-state/resolve-for-mutation.ts +24 -0
- package/src/market-state/resolve.ts +112 -0
- package/src/market-state/service.ts +2344 -0
- package/src/memory/index.ts +7 -7
- package/src/memory/manager.ts +14 -16
- package/src/memory/retrieval.ts +8 -7
- package/src/memory/sqlite.ts +407 -6
- package/src/memory/storage.ts +5 -15
- package/src/memory/tool-defaults.ts +60 -39
- package/src/memory/types.ts +3 -3
- package/src/monitor.ts +121 -0
- package/src/onboarding/connect.ts +24 -31
- package/src/onboarding/credential-interceptor.ts +3 -15
- package/src/onboarding/degradation-accumulator.ts +1 -3
- package/src/onboarding/provider-status.ts +410 -0
- package/src/onboarding/providers.ts +144 -45
- package/src/onboarding/state.ts +13 -15
- package/src/onboarding/tool-helpers.ts +2 -9
- package/src/onboarding/tool-tags.ts +51 -8
- package/src/onboarding/validation.ts +16 -22
- package/src/pi/opencandle-extension.ts +643 -101
- package/src/pi/session.ts +7 -5
- package/src/pi/setup.ts +61 -43
- package/src/pi/tool-adapter.ts +19 -6
- package/src/prompts/context-builder.ts +24 -13
- package/src/prompts/policy-cards.ts +3 -3
- package/src/prompts/sections.ts +1 -1
- package/src/prompts/symbol-preflight.ts +80 -0
- package/src/prompts/workflow-prompts.ts +77 -28
- package/src/providers/alpha-vantage.ts +58 -39
- package/src/providers/coingecko.ts +2 -5
- package/src/providers/errors.ts +9 -0
- package/src/providers/exa-search.ts +24 -22
- package/src/providers/external-tool-error.ts +20 -0
- package/src/providers/fear-greed.ts +1 -1
- package/src/providers/finnhub.ts +7 -6
- package/src/providers/fred.ts +3 -3
- package/src/providers/index.ts +14 -6
- package/src/providers/reddit-cli.ts +317 -0
- package/src/providers/reddit.ts +14 -59
- package/src/providers/sec-edgar.ts +20 -6
- package/src/providers/tradingview.ts +399 -0
- package/src/providers/twitter-cli.ts +233 -0
- package/src/providers/twitter.ts +8 -79
- package/src/providers/web-search.ts +30 -20
- package/src/providers/with-fallback.ts +8 -7
- package/src/providers/wrap-provider.ts +49 -10
- package/src/providers/yahoo-finance.ts +204 -66
- package/src/routing/classify-intent.ts +101 -10
- package/src/routing/defaults.ts +1 -1
- package/src/routing/entity-extractor.ts +287 -38
- package/src/routing/fund-symbols.ts +58 -0
- package/src/routing/horizon.ts +7 -0
- package/src/routing/index.ts +48 -48
- package/src/routing/planning.ts +145 -53
- package/src/routing/route-manifest.ts +37 -15
- package/src/routing/router-llm-client.ts +4 -4
- package/src/routing/router-prompt.ts +15 -19
- package/src/routing/router-types.ts +2 -5
- package/src/routing/router.ts +251 -53
- package/src/routing/slot-resolver.ts +34 -11
- package/src/routing/symbol-disambiguator.ts +72 -0
- package/src/routing/turn-context.ts +6 -9
- package/src/routing/types.ts +2 -0
- package/src/runtime/answer-contracts.ts +105 -45
- package/src/runtime/artifact-contracts.ts +2 -1
- package/src/runtime/planning-evidence.ts +157 -66
- package/src/runtime/prompt-step.ts +1 -16
- package/src/runtime/run-context.ts +12 -2
- package/src/runtime/session-coordinator.ts +238 -63
- package/src/runtime/session-title.ts +60 -0
- package/src/runtime/tool-defaults-wrapper.ts +13 -5
- package/src/runtime/validation.ts +1 -4
- package/src/runtime/workflow-events.ts +7 -7
- package/src/runtime/workflow-runner.ts +5 -11
- package/src/sentiment/adapters/finnhub.ts +7 -2
- package/src/sentiment/adapters/reddit.ts +2 -2
- package/src/sentiment/adapters/twitter.ts +1 -1
- package/src/sentiment/adapters/web.ts +1 -1
- package/src/sentiment/index.ts +17 -26
- package/src/sentiment/insights.ts +269 -0
- package/src/sentiment/keywords.ts +26 -4
- package/src/sentiment/pipeline.ts +28 -5
- package/src/sentiment/scorer.ts +13 -2
- package/src/sentiment/store.ts +2 -2
- package/src/sentiment/trends.ts +9 -3
- package/src/sentiment/types.ts +8 -4
- package/src/system-prompt.ts +6 -9
- package/src/tool-kit.ts +10 -9
- package/src/tools/fundamentals/company-overview.ts +19 -9
- package/src/tools/fundamentals/comps.ts +68 -55
- package/src/tools/fundamentals/dcf.ts +145 -95
- package/src/tools/fundamentals/earnings.ts +16 -6
- package/src/tools/fundamentals/financials.ts +16 -7
- package/src/tools/fundamentals/sec-filings.ts +37 -16
- package/src/tools/index.ts +56 -43
- package/src/tools/interaction/ask-user.ts +22 -10
- package/src/tools/macro/fear-greed.ts +1 -1
- package/src/tools/macro/fred-data.ts +58 -46
- package/src/tools/market/crypto-history.ts +8 -3
- package/src/tools/market/crypto-price.ts +6 -6
- package/src/tools/market/screen-stocks.ts +279 -0
- package/src/tools/market/search-ticker.ts +218 -17
- package/src/tools/market/stock-history.ts +37 -12
- package/src/tools/market/stock-quote.ts +10 -7
- package/src/tools/options/greeks.ts +5 -5
- package/src/tools/options/option-chain.ts +41 -17
- package/src/tools/portfolio/alerts.ts +457 -0
- package/src/tools/portfolio/correlation.ts +47 -20
- package/src/tools/portfolio/daily-report.ts +101 -0
- package/src/tools/portfolio/holdings-overlap.ts +31 -15
- package/src/tools/portfolio/notifications.ts +45 -0
- package/src/tools/portfolio/predictions.ts +406 -106
- package/src/tools/portfolio/risk-analysis.ts +46 -7
- package/src/tools/portfolio/tracker.ts +270 -109
- package/src/tools/portfolio/watchlist.ts +250 -121
- package/src/tools/sentiment/insight-format.ts +50 -0
- package/src/tools/sentiment/query-match.ts +117 -0
- package/src/tools/sentiment/reddit-sentiment.ts +360 -121
- package/src/tools/sentiment/sentiment-summary.ts +302 -235
- package/src/tools/sentiment/sentiment-trend.ts +24 -7
- package/src/tools/sentiment/twitter-sentiment.ts +264 -73
- package/src/tools/sentiment/untrusted-text.ts +21 -0
- package/src/tools/sentiment/web-search.ts +21 -18
- package/src/tools/sentiment/web-sentiment.ts +30 -10
- package/src/tools/technical/backtest.ts +32 -22
- package/src/tools/technical/indicators.ts +39 -14
- package/src/types/index.ts +8 -3
- package/src/types/market.ts +1 -0
- package/src/types/portfolio.ts +14 -4
- package/src/types/sentiment.ts +61 -2
- package/src/workflows/compare-assets.ts +33 -21
- package/src/workflows/index.ts +3 -4
- package/src/workflows/options-screener.ts +27 -29
- package/src/workflows/portfolio-builder.ts +34 -27
- package/dist/infra/browser.d.ts +0 -35
- package/dist/infra/browser.js +0 -103
- package/dist/infra/browser.js.map +0 -1
- package/dist/tools/interaction/twitter-login.d.ts +0 -8
- package/dist/tools/interaction/twitter-login.js +0 -77
- package/dist/tools/interaction/twitter-login.js.map +0 -1
- package/dist/workflows/types.d.ts +0 -4
- package/dist/workflows/types.js +0 -2
- package/dist/workflows/types.js.map +0 -1
- package/gui/web/dist/assets/CatalogOverlay-Bmp6Knu7.js +0 -1
- package/gui/web/dist/assets/index-Bxt9QpLX.css +0 -1
- package/gui/web/dist/assets/index-CZ9DHZYy.js +0 -67
- package/src/infra/browser.ts +0 -111
- package/src/tools/interaction/twitter-login.ts +0 -93
- package/src/workflows/types.ts +0 -4
|
@@ -1,32 +1,33 @@
|
|
|
1
|
-
// Provider registry — single source of truth for OpenCandle's
|
|
2
|
-
//
|
|
1
|
+
// Provider registry — single source of truth for OpenCandle's third-party
|
|
2
|
+
// data providers. Every setup/status pathway iterates this registry:
|
|
3
3
|
// first-run startup, the `/connect` command, the `tool_result` credential
|
|
4
|
-
// interception handler,
|
|
4
|
+
// interception handler, GUI provider rows, doctor checks, and gap-note
|
|
5
|
+
// generation all read from here.
|
|
5
6
|
//
|
|
6
|
-
// Adding a new
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// the array ever disagree.
|
|
7
|
+
// Adding a new provider is a two-step change: add its id to the relevant
|
|
8
|
+
// literal union below, and add its descriptor to the `PROVIDERS` array. The
|
|
9
|
+
// `satisfies` and exhaustiveness checks keep the registry and id unions aligned.
|
|
10
10
|
|
|
11
11
|
import { getConfig, loadFileConfig } from "../config.js";
|
|
12
12
|
|
|
13
|
-
export type
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
| "brave"
|
|
18
|
-
| "exa";
|
|
13
|
+
export type ApiKeyProviderId = "alpha_vantage" | "fred" | "finnhub" | "brave" | "exa";
|
|
14
|
+
export type ExternalToolProviderId = "twitter" | "reddit";
|
|
15
|
+
export type PublicHttpProviderId = "yahoo";
|
|
16
|
+
export type ProviderId = ApiKeyProviderId | ExternalToolProviderId | PublicHttpProviderId;
|
|
19
17
|
|
|
20
18
|
export type ProviderCategory =
|
|
21
19
|
| "fundamentals"
|
|
22
20
|
| "macro"
|
|
23
21
|
| "news"
|
|
24
|
-
| "web_search"
|
|
22
|
+
| "web_search"
|
|
23
|
+
| "sentiment"
|
|
24
|
+
| "market";
|
|
25
25
|
|
|
26
26
|
export type ProviderTier = "hard" | "soft";
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
interface BaseProviderDescriptor {
|
|
29
29
|
readonly id: ProviderId;
|
|
30
|
+
readonly kind: "api-key" | "external-tool" | "public-http";
|
|
30
31
|
readonly displayName: string;
|
|
31
32
|
readonly category: ProviderCategory;
|
|
32
33
|
/**
|
|
@@ -39,11 +40,6 @@ export interface ProviderDescriptor {
|
|
|
39
40
|
readonly tier: ProviderTier;
|
|
40
41
|
/** Lowercase friendly aliases accepted by `/connect` in addition to the id. */
|
|
41
42
|
readonly aliases: readonly string[];
|
|
42
|
-
readonly signupUrl: string;
|
|
43
|
-
readonly freeTier: boolean;
|
|
44
|
-
readonly envVar: string;
|
|
45
|
-
/** Nested key path into `OpenCandleFileConfig` where the key is persisted. */
|
|
46
|
-
readonly configPath: readonly string[];
|
|
47
43
|
readonly unlocks: readonly string[];
|
|
48
44
|
/**
|
|
49
45
|
* Human copy describing the degraded experience when missing, or `null`
|
|
@@ -54,11 +50,43 @@ export interface ProviderDescriptor {
|
|
|
54
50
|
readonly instructionsHint: string;
|
|
55
51
|
}
|
|
56
52
|
|
|
53
|
+
export interface ApiKeyProviderDescriptor extends BaseProviderDescriptor {
|
|
54
|
+
readonly id: ApiKeyProviderId;
|
|
55
|
+
readonly kind: "api-key";
|
|
56
|
+
readonly signupUrl: string;
|
|
57
|
+
readonly freeTier: boolean;
|
|
58
|
+
readonly envVar: string;
|
|
59
|
+
/** Nested key path into `OpenCandleFileConfig` where the key is persisted. */
|
|
60
|
+
readonly configPath: readonly string[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ExternalToolProviderDescriptor extends BaseProviderDescriptor {
|
|
64
|
+
readonly id: ExternalToolProviderId;
|
|
65
|
+
readonly kind: "external-tool";
|
|
66
|
+
readonly binary: string;
|
|
67
|
+
readonly installCmd: string;
|
|
68
|
+
readonly sessionSource: "browser-cookies";
|
|
69
|
+
readonly supportedBrowsers?: readonly string[];
|
|
70
|
+
readonly sessionProbeArgs?: readonly string[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface PublicHttpProviderDescriptor extends BaseProviderDescriptor {
|
|
74
|
+
readonly id: PublicHttpProviderId;
|
|
75
|
+
readonly kind: "public-http";
|
|
76
|
+
readonly probeUrl: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type ProviderDescriptor =
|
|
80
|
+
| ApiKeyProviderDescriptor
|
|
81
|
+
| ExternalToolProviderDescriptor
|
|
82
|
+
| PublicHttpProviderDescriptor;
|
|
83
|
+
|
|
57
84
|
// Declaration order matters: picker display order, per-workflow prompt priority,
|
|
58
85
|
// getProvidersByCategory/getProvidersByTier iteration order.
|
|
59
86
|
export const PROVIDERS = [
|
|
60
87
|
{
|
|
61
88
|
id: "alpha_vantage",
|
|
89
|
+
kind: "api-key",
|
|
62
90
|
displayName: "Alpha Vantage",
|
|
63
91
|
category: "fundamentals",
|
|
64
92
|
tier: "hard",
|
|
@@ -79,6 +107,7 @@ export const PROVIDERS = [
|
|
|
79
107
|
},
|
|
80
108
|
{
|
|
81
109
|
id: "fred",
|
|
110
|
+
kind: "api-key",
|
|
82
111
|
displayName: "FRED",
|
|
83
112
|
category: "macro",
|
|
84
113
|
tier: "hard",
|
|
@@ -87,18 +116,14 @@ export const PROVIDERS = [
|
|
|
87
116
|
freeTier: true,
|
|
88
117
|
envVar: "FRED_API_KEY",
|
|
89
118
|
configPath: ["providers", "fred", "apiKey"],
|
|
90
|
-
unlocks: [
|
|
91
|
-
"interest rates",
|
|
92
|
-
"inflation data",
|
|
93
|
-
"yield curve",
|
|
94
|
-
"economic indicators",
|
|
95
|
-
],
|
|
119
|
+
unlocks: ["interest rates", "inflation data", "yield curve", "economic indicators"],
|
|
96
120
|
fallbackDescription: null,
|
|
97
121
|
snoozeDurationDays: 7,
|
|
98
122
|
instructionsHint: "Free, about 30 seconds, requires a St. Louis Fed account",
|
|
99
123
|
},
|
|
100
124
|
{
|
|
101
125
|
id: "finnhub",
|
|
126
|
+
kind: "api-key",
|
|
102
127
|
displayName: "Finnhub",
|
|
103
128
|
category: "news",
|
|
104
129
|
tier: "soft",
|
|
@@ -107,10 +132,7 @@ export const PROVIDERS = [
|
|
|
107
132
|
freeTier: true,
|
|
108
133
|
envVar: "FINNHUB_API_KEY",
|
|
109
134
|
configPath: ["providers", "finnhub", "apiKey"],
|
|
110
|
-
unlocks: [
|
|
111
|
-
"ticker-tagged company news",
|
|
112
|
-
"sentiment enrichment with a dedicated news source",
|
|
113
|
-
],
|
|
135
|
+
unlocks: ["ticker-tagged company news", "sentiment enrichment with a dedicated news source"],
|
|
114
136
|
// Finnhub is a soft enrichment source — sentiment-summary continues to work
|
|
115
137
|
// with Twitter/Reddit/web search when Finnhub is missing. The fallback is
|
|
116
138
|
// "the other sentiment sources still run".
|
|
@@ -121,6 +143,7 @@ export const PROVIDERS = [
|
|
|
121
143
|
},
|
|
122
144
|
{
|
|
123
145
|
id: "brave",
|
|
146
|
+
kind: "api-key",
|
|
124
147
|
displayName: "Brave Search",
|
|
125
148
|
category: "web_search",
|
|
126
149
|
tier: "soft",
|
|
@@ -140,6 +163,7 @@ export const PROVIDERS = [
|
|
|
140
163
|
},
|
|
141
164
|
{
|
|
142
165
|
id: "exa",
|
|
166
|
+
kind: "api-key",
|
|
143
167
|
displayName: "Exa",
|
|
144
168
|
category: "web_search",
|
|
145
169
|
tier: "soft",
|
|
@@ -164,8 +188,64 @@ export const PROVIDERS = [
|
|
|
164
188
|
snoozeDurationDays: 7,
|
|
165
189
|
instructionsHint: "Paid with free tier, signup opens in your browser",
|
|
166
190
|
},
|
|
191
|
+
{
|
|
192
|
+
id: "yahoo",
|
|
193
|
+
kind: "public-http",
|
|
194
|
+
displayName: "Yahoo Finance",
|
|
195
|
+
category: "market",
|
|
196
|
+
tier: "hard",
|
|
197
|
+
aliases: ["yahoo", "yahoo-finance", "market-data", "options"],
|
|
198
|
+
probeUrl: "https://query1.finance.yahoo.com/v8/finance/chart/SPY?interval=1d&range=1d",
|
|
199
|
+
unlocks: ["quotes", "historical prices", "option chains", "market data"],
|
|
200
|
+
fallbackDescription: null,
|
|
201
|
+
snoozeDurationDays: 7,
|
|
202
|
+
instructionsHint: "No account needed; OpenCandle checks public Yahoo Finance reachability",
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: "twitter",
|
|
206
|
+
kind: "external-tool",
|
|
207
|
+
displayName: "X / Twitter",
|
|
208
|
+
category: "sentiment",
|
|
209
|
+
tier: "soft",
|
|
210
|
+
aliases: ["twitter", "x", "x-sentiment", "twitter-sentiment"],
|
|
211
|
+
binary: "twitter",
|
|
212
|
+
installCmd: "uv tool install twitter-cli",
|
|
213
|
+
sessionSource: "browser-cookies",
|
|
214
|
+
sessionProbeArgs: ["feed", "--max", "1", "--json"],
|
|
215
|
+
supportedBrowsers: ["Chrome", "Arc", "Edge", "Firefox", "Brave"],
|
|
216
|
+
unlocks: ["X/Twitter sentiment", "recent ticker mentions", "social engagement context"],
|
|
217
|
+
fallbackDescription:
|
|
218
|
+
"Sentiment summaries continue with Reddit, web search, and news when X is unavailable",
|
|
219
|
+
snoozeDurationDays: 7,
|
|
220
|
+
instructionsHint: "Install twitter-cli and stay logged into x.com in a supported browser",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
id: "reddit",
|
|
224
|
+
kind: "external-tool",
|
|
225
|
+
displayName: "Reddit",
|
|
226
|
+
category: "sentiment",
|
|
227
|
+
tier: "soft",
|
|
228
|
+
aliases: ["reddit", "reddit-sentiment", "subreddit"],
|
|
229
|
+
binary: "rdt",
|
|
230
|
+
installCmd: "uv tool install rdt-cli",
|
|
231
|
+
sessionSource: "browser-cookies",
|
|
232
|
+
supportedBrowsers: ["Chrome", "Arc", "Edge", "Firefox", "Brave"],
|
|
233
|
+
sessionProbeArgs: ["status", "--json"],
|
|
234
|
+
unlocks: ["Reddit sentiment", "ticker discussion context", "retail investor discussion"],
|
|
235
|
+
fallbackDescription:
|
|
236
|
+
"Sentiment summaries continue with X/Twitter, web search, and news when Reddit is unavailable",
|
|
237
|
+
snoozeDurationDays: 7,
|
|
238
|
+
instructionsHint: "Install rdt-cli and stay logged into reddit.com in a supported browser",
|
|
239
|
+
},
|
|
167
240
|
] as const satisfies readonly ProviderDescriptor[];
|
|
168
241
|
|
|
242
|
+
const _providerIdExhaustivenessCheck: Record<
|
|
243
|
+
| Exclude<ProviderId, (typeof PROVIDERS)[number]["id"]>
|
|
244
|
+
| Exclude<(typeof PROVIDERS)[number]["id"], ProviderId>,
|
|
245
|
+
never
|
|
246
|
+
> = {};
|
|
247
|
+
void _providerIdExhaustivenessCheck;
|
|
248
|
+
|
|
169
249
|
// -----------------------------------------------------------------------------
|
|
170
250
|
// Lookup helpers
|
|
171
251
|
// -----------------------------------------------------------------------------
|
|
@@ -186,6 +266,28 @@ export function listAllProviders(): readonly ProviderDescriptor[] {
|
|
|
186
266
|
return PROVIDERS;
|
|
187
267
|
}
|
|
188
268
|
|
|
269
|
+
export function isApiKeyProvider(
|
|
270
|
+
provider: ProviderDescriptor,
|
|
271
|
+
): provider is ApiKeyProviderDescriptor {
|
|
272
|
+
return provider.kind === "api-key";
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function isExternalToolProvider(
|
|
276
|
+
provider: ProviderDescriptor,
|
|
277
|
+
): provider is ExternalToolProviderDescriptor {
|
|
278
|
+
return provider.kind === "external-tool";
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function isPublicHttpProvider(
|
|
282
|
+
provider: ProviderDescriptor,
|
|
283
|
+
): provider is PublicHttpProviderDescriptor {
|
|
284
|
+
return provider.kind === "public-http";
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function listApiKeyProviders(): readonly ApiKeyProviderDescriptor[] {
|
|
288
|
+
return (PROVIDERS as readonly ProviderDescriptor[]).filter(isApiKeyProvider);
|
|
289
|
+
}
|
|
290
|
+
|
|
189
291
|
export function getProvider(id: ProviderId): ProviderDescriptor {
|
|
190
292
|
const found = byId().get(id);
|
|
191
293
|
if (!found) {
|
|
@@ -194,15 +296,11 @@ export function getProvider(id: ProviderId): ProviderDescriptor {
|
|
|
194
296
|
return found;
|
|
195
297
|
}
|
|
196
298
|
|
|
197
|
-
export function getProvidersByCategory(
|
|
198
|
-
category: ProviderCategory,
|
|
199
|
-
): readonly ProviderDescriptor[] {
|
|
299
|
+
export function getProvidersByCategory(category: ProviderCategory): readonly ProviderDescriptor[] {
|
|
200
300
|
return PROVIDERS.filter((p) => p.category === category);
|
|
201
301
|
}
|
|
202
302
|
|
|
203
|
-
export function getProvidersByTier(
|
|
204
|
-
tier: ProviderTier,
|
|
205
|
-
): readonly ProviderDescriptor[] {
|
|
303
|
+
export function getProvidersByTier(tier: ProviderTier): readonly ProviderDescriptor[] {
|
|
206
304
|
return PROVIDERS.filter((p) => p.tier === tier);
|
|
207
305
|
}
|
|
208
306
|
|
|
@@ -230,7 +328,7 @@ function readConfigValueByPath(
|
|
|
230
328
|
// `hasCredential` reads from `getConfig()` so that tests mocking `getConfig`
|
|
231
329
|
// see a consistent view; `getCredentialSource` reads `process.env` +
|
|
232
330
|
// `loadFileConfig` directly because it needs to distinguish env from file.
|
|
233
|
-
const CONFIG_FIELD_BY_ID: Record<
|
|
331
|
+
const CONFIG_FIELD_BY_ID: Record<ApiKeyProviderId, keyof ReturnType<typeof getConfig>> = {
|
|
234
332
|
alpha_vantage: "alphaVantageApiKey",
|
|
235
333
|
fred: "fredApiKey",
|
|
236
334
|
finnhub: "finnhubApiKey",
|
|
@@ -239,14 +337,14 @@ const CONFIG_FIELD_BY_ID: Record<ProviderId, keyof ReturnType<typeof getConfig>>
|
|
|
239
337
|
};
|
|
240
338
|
|
|
241
339
|
export function hasCredential(id: ProviderId): boolean {
|
|
242
|
-
const
|
|
340
|
+
const descriptor = getProvider(id);
|
|
341
|
+
if (!isApiKeyProvider(descriptor)) return false;
|
|
342
|
+
const field = CONFIG_FIELD_BY_ID[descriptor.id];
|
|
243
343
|
const value = getConfig()[field];
|
|
244
344
|
return typeof value === "string" && value.length > 0;
|
|
245
345
|
}
|
|
246
346
|
|
|
247
|
-
export function getCredentialSource(
|
|
248
|
-
id: ProviderId,
|
|
249
|
-
): "env" | "file" | "absent" {
|
|
347
|
+
export function getCredentialSource(id: ProviderId): "env" | "file" | "absent" {
|
|
250
348
|
return getCredential(id).source;
|
|
251
349
|
}
|
|
252
350
|
|
|
@@ -254,6 +352,8 @@ export function getCredential(
|
|
|
254
352
|
id: ProviderId,
|
|
255
353
|
): { source: "env" | "file"; value: string } | { source: "absent"; value?: undefined } {
|
|
256
354
|
const descriptor = getProvider(id);
|
|
355
|
+
if (!isApiKeyProvider(descriptor)) return { source: "absent" };
|
|
356
|
+
|
|
257
357
|
const envValue = process.env[descriptor.envVar];
|
|
258
358
|
if (envValue && envValue.length > 0) return { source: "env", value: envValue };
|
|
259
359
|
|
|
@@ -271,10 +371,7 @@ export function getCredential(
|
|
|
271
371
|
|
|
272
372
|
export function resolveProviderFromArgument(
|
|
273
373
|
arg: string,
|
|
274
|
-
):
|
|
275
|
-
| ProviderDescriptor
|
|
276
|
-
| readonly ProviderDescriptor[]
|
|
277
|
-
| undefined {
|
|
374
|
+
): ProviderDescriptor | readonly ProviderDescriptor[] | undefined {
|
|
278
375
|
const needle = arg.trim().toLowerCase();
|
|
279
376
|
if (!needle) return undefined;
|
|
280
377
|
|
|
@@ -296,6 +393,8 @@ export function resolveProviderFromArgument(
|
|
|
296
393
|
"macro",
|
|
297
394
|
"news",
|
|
298
395
|
"web_search",
|
|
396
|
+
"sentiment",
|
|
397
|
+
"market",
|
|
299
398
|
];
|
|
300
399
|
const normalizedCategory = needle.replace("-", "_");
|
|
301
400
|
if ((categories as readonly string[]).includes(normalizedCategory)) {
|
package/src/onboarding/state.ts
CHANGED
|
@@ -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: {
|
|
@@ -166,6 +155,15 @@ export function markProviderNeverAsk(
|
|
|
166
155
|
};
|
|
167
156
|
}
|
|
168
157
|
|
|
158
|
+
export function clearProviderOnboardingEntry(
|
|
159
|
+
state: OnboardingState,
|
|
160
|
+
id: ProviderId,
|
|
161
|
+
): OnboardingState {
|
|
162
|
+
const providers = { ...state.providers };
|
|
163
|
+
delete providers[id];
|
|
164
|
+
return { ...state, providers };
|
|
165
|
+
}
|
|
166
|
+
|
|
169
167
|
export function markWelcomeShown(state: OnboardingState): OnboardingState {
|
|
170
168
|
return { ...state, welcomeShownAt: nowIso() };
|
|
171
169
|
}
|
|
@@ -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
|
}
|
|
@@ -22,6 +22,7 @@ import type { ProviderId } from "./providers.js";
|
|
|
22
22
|
// -----------------------------------------------------------------------------
|
|
23
23
|
|
|
24
24
|
export type CredentialRequiredReason = "missing" | "stale";
|
|
25
|
+
export type ExternalToolRequiredReason = "not_installed" | "session_missing" | "session_stale";
|
|
25
26
|
|
|
26
27
|
export interface CredentialRequiredTagFields {
|
|
27
28
|
provider: ProviderId;
|
|
@@ -48,11 +49,20 @@ export interface ConnectedTagFields {
|
|
|
48
49
|
provider: ProviderId;
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
export interface ExternalToolRequiredTagFields {
|
|
53
|
+
provider: ProviderId;
|
|
54
|
+
reason: ExternalToolRequiredReason;
|
|
55
|
+
installCmd: string;
|
|
56
|
+
loginCmd?: string;
|
|
57
|
+
fallback: string | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
51
60
|
export type ParsedTag =
|
|
52
61
|
| ({ kind: "credential_required" } & CredentialRequiredTagFields)
|
|
53
62
|
| ({ kind: "soft_degraded" } & SoftDegradedTagFields)
|
|
54
63
|
| ({ kind: "skipped" } & SkippedTagFields)
|
|
55
|
-
| ({ kind: "connected" } & ConnectedTagFields)
|
|
64
|
+
| ({ kind: "connected" } & ConnectedTagFields)
|
|
65
|
+
| ({ kind: "external_tool_required" } & ExternalToolRequiredTagFields);
|
|
56
66
|
|
|
57
67
|
// -----------------------------------------------------------------------------
|
|
58
68
|
// Builders
|
|
@@ -81,12 +91,12 @@ export function buildCredentialRequiredTag(fields: CredentialRequiredTagFields):
|
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
export function buildSoftDegradedTag(fields: SoftDegradedTagFields): string {
|
|
84
|
-
return [
|
|
94
|
+
return `${[
|
|
85
95
|
"[OPENCANDLE_SOFT_DEGRADED",
|
|
86
96
|
formatField("provider", fields.provider),
|
|
87
97
|
formatField("fallback", fields.fallback),
|
|
88
98
|
`remediation=${quote(fields.remediation)}`,
|
|
89
|
-
].join(" ")
|
|
99
|
+
].join(" ")}]`;
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
export function buildSkippedTag(fields: SkippedTagFields): string {
|
|
@@ -106,19 +116,32 @@ export function buildConnectedTag(fields: ConnectedTagFields): string {
|
|
|
106
116
|
return `[OPENCANDLE_CONNECTED provider=${fields.provider}]`;
|
|
107
117
|
}
|
|
108
118
|
|
|
119
|
+
export function buildExternalToolRequiredTag(fields: ExternalToolRequiredTagFields): string {
|
|
120
|
+
const parts: string[] = [
|
|
121
|
+
"[OPENCANDLE_EXTERNAL_TOOL_REQUIRED",
|
|
122
|
+
formatField("provider", fields.provider),
|
|
123
|
+
formatField("reason", fields.reason),
|
|
124
|
+
`installCmd=${quote(fields.installCmd)}`,
|
|
125
|
+
formatField("fallback", fields.fallback ?? "none"),
|
|
126
|
+
];
|
|
127
|
+
if (fields.loginCmd) parts.push(`loginCmd=${quote(fields.loginCmd)}`);
|
|
128
|
+
return `${parts.join(" ")}]`;
|
|
129
|
+
}
|
|
130
|
+
|
|
109
131
|
// -----------------------------------------------------------------------------
|
|
110
132
|
// Parser
|
|
111
133
|
// -----------------------------------------------------------------------------
|
|
112
134
|
|
|
113
135
|
const TAG_LINE_REGEX =
|
|
114
|
-
/\[OPENCANDLE_(CREDENTIAL_REQUIRED|SOFT_DEGRADED|SKIPPED|CONNECTED)([^\]]*)\]/;
|
|
136
|
+
/\[OPENCANDLE_(CREDENTIAL_REQUIRED|SOFT_DEGRADED|SKIPPED|CONNECTED|EXTERNAL_TOOL_REQUIRED)([^\]]*)\]/;
|
|
115
137
|
|
|
116
138
|
function parseFields(raw: string): Record<string, string> {
|
|
117
139
|
// Split on `key=value` pairs, respecting quoted values.
|
|
118
140
|
const fields: Record<string, string> = {};
|
|
119
141
|
const pattern = /(\w+)=("((?:[^"\\]|\\.)*)"|([^\s\]]+))/g;
|
|
120
|
-
|
|
121
|
-
|
|
142
|
+
while (true) {
|
|
143
|
+
const match = pattern.exec(raw);
|
|
144
|
+
if (!match) break;
|
|
122
145
|
const key = match[1];
|
|
123
146
|
const value = match[3] !== undefined ? match[3].replace(/\\"/g, '"') : match[4];
|
|
124
147
|
fields[key] = value;
|
|
@@ -147,8 +170,7 @@ export function parseToolTag(text: string): ParsedTag | undefined {
|
|
|
147
170
|
.split(",")
|
|
148
171
|
.map((s) => s.trim())
|
|
149
172
|
.filter((s) => s.length > 0);
|
|
150
|
-
const fallback =
|
|
151
|
-
fallbackRaw === undefined || fallbackRaw === "none" ? null : fallbackRaw;
|
|
173
|
+
const fallback = fallbackRaw === undefined || fallbackRaw === "none" ? null : fallbackRaw;
|
|
152
174
|
const parsed: ParsedTag = {
|
|
153
175
|
kind: "credential_required",
|
|
154
176
|
provider: provider as ProviderId,
|
|
@@ -195,6 +217,27 @@ export function parseToolTag(text: string): ParsedTag | undefined {
|
|
|
195
217
|
return { kind: "connected", provider: provider as ProviderId };
|
|
196
218
|
}
|
|
197
219
|
|
|
220
|
+
case "EXTERNAL_TOOL_REQUIRED": {
|
|
221
|
+
const { provider, reason, installCmd, loginCmd, fallback } = fields;
|
|
222
|
+
if (
|
|
223
|
+
!provider ||
|
|
224
|
+
(reason !== "not_installed" &&
|
|
225
|
+
reason !== "session_missing" &&
|
|
226
|
+
reason !== "session_stale") ||
|
|
227
|
+
!installCmd
|
|
228
|
+
) {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
kind: "external_tool_required",
|
|
233
|
+
provider: provider as ProviderId,
|
|
234
|
+
reason,
|
|
235
|
+
installCmd,
|
|
236
|
+
loginCmd,
|
|
237
|
+
fallback: fallback === undefined || fallback === "none" ? null : fallback,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
198
241
|
default:
|
|
199
242
|
return undefined;
|
|
200
243
|
}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// All validators share a 5-second timeout via `AbortSignal.timeout` so the
|
|
21
21
|
// connect flow cannot hang if a provider is unreachable.
|
|
22
22
|
|
|
23
|
-
import type {
|
|
23
|
+
import type { ApiKeyProviderId } from "./providers.js";
|
|
24
24
|
|
|
25
25
|
const VALIDATION_TIMEOUT_MS = 5_000;
|
|
26
26
|
|
|
@@ -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
|
|
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
|
}
|
|
@@ -107,7 +107,7 @@ function classifyAlphaVantageBody(body: string): ValidationResult | undefined {
|
|
|
107
107
|
* touch the config file, onboarding state, or any in-memory cache.
|
|
108
108
|
*/
|
|
109
109
|
export async function validateCredential(
|
|
110
|
-
providerId:
|
|
110
|
+
providerId: ApiKeyProviderId,
|
|
111
111
|
key: string,
|
|
112
112
|
): Promise<ValidationResult> {
|
|
113
113
|
switch (providerId) {
|
|
@@ -131,28 +131,22 @@ export async function validateCredential(
|
|
|
131
131
|
);
|
|
132
132
|
|
|
133
133
|
case "brave":
|
|
134
|
-
return validateWithFetch(
|
|
135
|
-
"
|
|
136
|
-
{
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
"
|
|
148
|
-
{
|
|
149
|
-
|
|
150
|
-
|
|
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
|
}
|