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,23 +1,29 @@
|
|
|
1
|
-
import { isAnalysisRequest, normalizeSymbol, } from "../analysts/orchestrator.js";
|
|
2
|
-
import { buildComprehensiveAnalysisDefinition } from "../analysts/orchestrator.js";
|
|
1
|
+
import { buildComprehensiveAnalysisDefinition, isAnalysisRequest, normalizeSymbol, } from "../analysts/orchestrator.js";
|
|
3
2
|
import { getConfig } from "../config.js";
|
|
4
|
-
import {
|
|
5
|
-
import { buildAssumptionsBlockFromRouter } from "../prompts/workflow-prompts.js";
|
|
6
|
-
import { buildPortfolioWorkflowDefinition, buildOptionsScreenerWorkflowDefinition, buildCompareAssetsWorkflowDefinition, } from "../workflows/index.js";
|
|
7
|
-
import { getOpenCandleToolDefinitions } from "./tool-adapter.js";
|
|
8
|
-
import { registerAskUserTool } from "../tools/interaction/ask-user.js";
|
|
9
|
-
import { registerTwitterLoginTool } from "../tools/interaction/twitter-login.js";
|
|
10
|
-
import { SessionCoordinator } from "../runtime/session-coordinator.js";
|
|
11
|
-
import { getProvider, } from "../onboarding/providers.js";
|
|
12
|
-
import { loadOnboardingState, saveOnboardingState, markProviderSnoozed, markProviderNeverAsk, markWelcomeShown, shouldShowWelcome, } from "../onboarding/state.js";
|
|
13
|
-
import { parseToolTag, buildSkippedTag, buildConnectedTag } from "../onboarding/tool-tags.js";
|
|
3
|
+
import { runProviderConnect } from "../onboarding/connect.js";
|
|
14
4
|
import { resolveCredentialRequired } from "../onboarding/credential-interceptor.js";
|
|
15
5
|
import { createDegradationAccumulator } from "../onboarding/degradation-accumulator.js";
|
|
16
6
|
import { promptUser } from "../onboarding/prompt-user.js";
|
|
17
|
-
import {
|
|
7
|
+
import { getProvider } from "../onboarding/providers.js";
|
|
8
|
+
import { loadOnboardingState, markProviderNeverAsk, markProviderSnoozed, markWelcomeShown, saveOnboardingState, shouldShowWelcome, } from "../onboarding/state.js";
|
|
9
|
+
import { buildConnectedTag, buildSkippedTag, parseToolTag } from "../onboarding/tool-tags.js";
|
|
18
10
|
import { DISCLAIMER_TEXT } from "../prompts/disclaimer.js";
|
|
11
|
+
import { formatPreflightDropAnnotation, preflightSymbols } from "../prompts/symbol-preflight.js";
|
|
12
|
+
import { buildAssumptionsBlockFromRouter } from "../prompts/workflow-prompts.js";
|
|
13
|
+
import { buildResolvedTurnContext, classifyWithLegacyRules, createPiAiRouterClient, hasFinanceSignals, resolveOptionsScreenerSlots, resolvePortfolioSlots, route as routeLlm, } from "../routing/index.js";
|
|
14
|
+
import { disambiguateSymbols } from "../routing/symbol-disambiguator.js";
|
|
15
|
+
import { SessionCoordinator } from "../runtime/session-coordinator.js";
|
|
16
|
+
import { generateSessionTitle } from "../runtime/session-title.js";
|
|
17
|
+
import { registerAskUserTool } from "../tools/interaction/ask-user.js";
|
|
18
|
+
import { buildCompareAssetsWorkflowDefinition, buildOptionsScreenerWorkflowDefinition, buildPortfolioWorkflowDefinition, } from "../workflows/index.js";
|
|
19
|
+
import { getOpenCandleToolDefinitions } from "./tool-adapter.js";
|
|
19
20
|
export default function openCandleExtension(pi, options) {
|
|
20
21
|
const coordinator = new SessionCoordinator();
|
|
22
|
+
// Workflow transforms replace the user's turn with the expanded prompt; this
|
|
23
|
+
// marker lets the GUI render the user's original words instead.
|
|
24
|
+
const markOriginalInput = (original) => {
|
|
25
|
+
pi.appendEntry("opencandle-user-input", { original });
|
|
26
|
+
};
|
|
21
27
|
// Credential-interception state. Lifetime:
|
|
22
28
|
// `sessionPromptedSet` — cleared on session_start, persists across turns
|
|
23
29
|
// within a session so users don't get re-prompted after picking
|
|
@@ -36,12 +42,15 @@ export default function openCandleExtension(pi, options) {
|
|
|
36
42
|
const degradationAccumulator = createDegradationAccumulator();
|
|
37
43
|
let activeToolSnapshot = null;
|
|
38
44
|
let currentRouteToolContext = null;
|
|
45
|
+
// LLM session-title state: one title attempt per session per process.
|
|
46
|
+
// Reset on session_start; set before the (async) title call fires so
|
|
47
|
+
// overlapping turn_end events cannot double-title.
|
|
48
|
+
let sessionTitleAttempted = false;
|
|
39
49
|
// Register tools
|
|
40
|
-
for (const tool of getOpenCandleToolDefinitions()) {
|
|
50
|
+
for (const tool of getOpenCandleToolDefinitions({ askUserHandler: options?.askUserHandler })) {
|
|
41
51
|
pi.registerTool(tool);
|
|
42
52
|
}
|
|
43
53
|
registerAskUserTool(pi, options?.askUserHandler);
|
|
44
|
-
registerTwitterLoginTool(pi);
|
|
45
54
|
// /analyze command
|
|
46
55
|
pi.registerCommand("analyze", {
|
|
47
56
|
description: "Run the multi-analyst OpenCandle workflow for a ticker symbol",
|
|
@@ -51,7 +60,9 @@ export default function openCandleExtension(pi, options) {
|
|
|
51
60
|
ctx.ui.notify("Usage: /analyze <ticker>", "warning");
|
|
52
61
|
return;
|
|
53
62
|
}
|
|
54
|
-
const definition = buildComprehensiveAnalysisDefinition(symbol, {
|
|
63
|
+
const definition = buildComprehensiveAnalysisDefinition(symbol, {
|
|
64
|
+
debate: getConfig().debate,
|
|
65
|
+
});
|
|
55
66
|
coordinator.executeWorkflow(pi, definition, ctx);
|
|
56
67
|
},
|
|
57
68
|
});
|
|
@@ -71,9 +82,9 @@ export default function openCandleExtension(pi, options) {
|
|
|
71
82
|
// `/connect <alias|id|category>` routes to a specific provider (or a
|
|
72
83
|
// sub-picker for multi-provider categories like "search").
|
|
73
84
|
pi.registerCommand("connect", {
|
|
74
|
-
description: "Connect
|
|
85
|
+
description: "Connect an API-key data provider (Alpha Vantage, FRED, Finnhub, Brave, Exa)",
|
|
75
86
|
handler: async (args, ctx) => {
|
|
76
|
-
const {
|
|
87
|
+
const { listApiKeyProviders, resolveProviderFromArgument, hasCredential, isApiKeyProvider } = await import("../onboarding/providers.js");
|
|
77
88
|
const formatState = (id) => {
|
|
78
89
|
const state = loadOnboardingState().providers[id];
|
|
79
90
|
if (state?.status === "completed")
|
|
@@ -99,12 +110,12 @@ export default function openCandleExtension(pi, options) {
|
|
|
99
110
|
let targetId;
|
|
100
111
|
if (trimmed === "") {
|
|
101
112
|
// Bare /connect → full picker.
|
|
102
|
-
targetId = await pickProvider(
|
|
113
|
+
targetId = await pickProvider(listApiKeyProviders());
|
|
103
114
|
}
|
|
104
115
|
else {
|
|
105
116
|
const resolved = resolveProviderFromArgument(trimmed);
|
|
106
117
|
if (!resolved) {
|
|
107
|
-
const all =
|
|
118
|
+
const all = listApiKeyProviders()
|
|
108
119
|
.map((p) => ` ${p.displayName} (${p.aliases.join(", ")})`)
|
|
109
120
|
.join("\n");
|
|
110
121
|
ctx.ui.notify(`Unknown provider: "${trimmed}". Available:\n${all}`, "warning");
|
|
@@ -112,10 +123,20 @@ export default function openCandleExtension(pi, options) {
|
|
|
112
123
|
}
|
|
113
124
|
if (Array.isArray(resolved)) {
|
|
114
125
|
// Multi-provider category — show a sub-picker.
|
|
115
|
-
|
|
126
|
+
const apiKeyProviders = resolved.filter(isApiKeyProvider);
|
|
127
|
+
if (apiKeyProviders.length === 0) {
|
|
128
|
+
ctx.ui.notify(`"${trimmed}" does not use API-key setup. Run opencandle doctor for setup status.`, "warning");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
targetId = await pickProvider(apiKeyProviders);
|
|
116
132
|
}
|
|
117
133
|
else {
|
|
118
|
-
|
|
134
|
+
const descriptor = resolved;
|
|
135
|
+
if (!isApiKeyProvider(descriptor)) {
|
|
136
|
+
ctx.ui.notify(`${descriptor.displayName} does not use API-key setup. Run opencandle doctor for setup status.`, "warning");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
targetId = descriptor.id;
|
|
119
140
|
}
|
|
120
141
|
}
|
|
121
142
|
if (!targetId) {
|
|
@@ -137,6 +158,7 @@ export default function openCandleExtension(pi, options) {
|
|
|
137
158
|
coordinator.initSession(ctx.sessionManager.getSessionId());
|
|
138
159
|
sessionPromptedSet.clear();
|
|
139
160
|
hardPromptFiredInWorkflow = false;
|
|
161
|
+
sessionTitleAttempted = false;
|
|
140
162
|
if (!ctx.hasUI)
|
|
141
163
|
return;
|
|
142
164
|
// Pin the user-facing disclaimer in the UI footer for the entire session.
|
|
@@ -210,6 +232,83 @@ export default function openCandleExtension(pi, options) {
|
|
|
210
232
|
}
|
|
211
233
|
degradationAccumulator.reset();
|
|
212
234
|
});
|
|
235
|
+
// LLM session titles. After the first completed user↔assistant exchange,
|
|
236
|
+
// replace the placeholder session name (the raw first prompt, set by the
|
|
237
|
+
// GUI server / Pi's firstMessage fallback) with a short model-written
|
|
238
|
+
// summary. Manual renames are left alone, and each session is attempted at
|
|
239
|
+
// most once per process. Model failures are swallowed (placeholder stays)
|
|
240
|
+
// but recorded as an `opencandle-title-error` entry for observability.
|
|
241
|
+
pi.on("turn_end", async (event, ctx) => {
|
|
242
|
+
if (sessionTitleAttempted)
|
|
243
|
+
return;
|
|
244
|
+
const msg = event.message;
|
|
245
|
+
if (msg?.role !== "assistant" || msg?.stopReason !== "stop")
|
|
246
|
+
return;
|
|
247
|
+
const sessionManager = ctx?.sessionManager;
|
|
248
|
+
if (typeof sessionManager?.getBranch !== "function")
|
|
249
|
+
return;
|
|
250
|
+
const branch = sessionManager.getBranch();
|
|
251
|
+
let firstUserText = null;
|
|
252
|
+
let firstAssistantText = null;
|
|
253
|
+
let originalInput = null;
|
|
254
|
+
for (const entry of branch) {
|
|
255
|
+
if (originalInput === null &&
|
|
256
|
+
entry.type === "custom" &&
|
|
257
|
+
entry.customType === "opencandle-user-input") {
|
|
258
|
+
const original = entry.data?.original;
|
|
259
|
+
if (typeof original === "string" && original.trim().length > 0) {
|
|
260
|
+
originalInput = original;
|
|
261
|
+
}
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (entry.type !== "message")
|
|
265
|
+
continue;
|
|
266
|
+
const message = entry.message;
|
|
267
|
+
const text = extractMessageText(message?.content);
|
|
268
|
+
if (text.trim().length === 0)
|
|
269
|
+
continue;
|
|
270
|
+
if (firstUserText === null && message?.role === "user")
|
|
271
|
+
firstUserText = text;
|
|
272
|
+
if (firstAssistantText === null && message?.role === "assistant") {
|
|
273
|
+
firstAssistantText = text;
|
|
274
|
+
}
|
|
275
|
+
if (firstUserText !== null && firstAssistantText !== null)
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
// Fall back to the just-finished assistant message when the branch has
|
|
279
|
+
// not surfaced it yet at turn_end time.
|
|
280
|
+
if (firstAssistantText === null) {
|
|
281
|
+
const eventText = extractMessageText(msg.content);
|
|
282
|
+
if (eventText.trim().length > 0)
|
|
283
|
+
firstAssistantText = eventText;
|
|
284
|
+
}
|
|
285
|
+
if (firstUserText === null || firstAssistantText === null)
|
|
286
|
+
return;
|
|
287
|
+
const userText = originalInput ?? firstUserText;
|
|
288
|
+
// A manual rename must be left alone — only replace the placeholder
|
|
289
|
+
// (unset, the raw first prompt, or the GUI's "first 77 chars + ..." form).
|
|
290
|
+
if (!isPlaceholderSessionName(pi.getSessionName(), [userText, firstUserText]))
|
|
291
|
+
return;
|
|
292
|
+
const completion = options?.titleCompletion ??
|
|
293
|
+
(() => {
|
|
294
|
+
const client = options?.routerLlmClient ?? resolveRouterLlmClient(ctx);
|
|
295
|
+
return client ? (prompt) => client.complete(prompt) : null;
|
|
296
|
+
})();
|
|
297
|
+
if (!completion)
|
|
298
|
+
return;
|
|
299
|
+
sessionTitleAttempted = true;
|
|
300
|
+
try {
|
|
301
|
+
const title = await generateSessionTitle({ userText, assistantText: firstAssistantText.slice(0, 500) }, completion);
|
|
302
|
+
if (title !== null) {
|
|
303
|
+
pi.setSessionName(title);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
pi.appendEntry("opencandle-title-error", {
|
|
308
|
+
message: err instanceof Error ? err.message : String(err),
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
});
|
|
213
312
|
// Intercept tool results for credential-required and soft-degraded tags.
|
|
214
313
|
pi.on("tool_result", async (event, ctx) => {
|
|
215
314
|
// First pass: record any soft-degradation tags in the per-turn accumulator
|
|
@@ -224,17 +323,85 @@ export default function openCandleExtension(pi, options) {
|
|
|
224
323
|
degradationAccumulator.record(parsed.provider);
|
|
225
324
|
}
|
|
226
325
|
}
|
|
227
|
-
// Second pass: look for
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
// is acted on — subsequent hard-tier prompts are silenced by the
|
|
231
|
-
// per-workflow cap at the decision-function level.
|
|
326
|
+
// Second pass: look for setup-required tags; on match, run the interception
|
|
327
|
+
// decision and either replace the tool result or prompt the user. Only the
|
|
328
|
+
// first setup tag in the content list is acted on.
|
|
232
329
|
for (const block of event.content) {
|
|
233
330
|
if (block.type !== "text")
|
|
234
331
|
continue;
|
|
235
332
|
const parsed = parseToolTag(block.text);
|
|
236
|
-
if (!parsed ||
|
|
333
|
+
if (!parsed ||
|
|
334
|
+
(parsed.kind !== "credential_required" && parsed.kind !== "external_tool_required")) {
|
|
237
335
|
continue;
|
|
336
|
+
}
|
|
337
|
+
if (parsed.kind === "external_tool_required") {
|
|
338
|
+
const state = loadOnboardingState();
|
|
339
|
+
const descriptor = getProvider(parsed.provider);
|
|
340
|
+
if (state.providers[parsed.provider]?.status === "never_ask") {
|
|
341
|
+
return {
|
|
342
|
+
content: [
|
|
343
|
+
{
|
|
344
|
+
type: "text",
|
|
345
|
+
text: `${buildSkippedTag({
|
|
346
|
+
provider: parsed.provider,
|
|
347
|
+
reason: "credential_not_provided",
|
|
348
|
+
remediation: `run ${parsed.installCmd} and ${parsed.loginCmd ?? "rdt login"} to enable ${descriptor.displayName} (silenced)`,
|
|
349
|
+
silenced: true,
|
|
350
|
+
})}\n\n` +
|
|
351
|
+
`${descriptor.displayName} data was not fetched because you previously asked not to be reminded about this provider.`,
|
|
352
|
+
},
|
|
353
|
+
],
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
const setupLabel = parsed.reason === "not_installed"
|
|
357
|
+
? `Continue after installing ${descriptor.displayName}`
|
|
358
|
+
: `Continue after logging in to ${descriptor.displayName}`;
|
|
359
|
+
const skipLabel = `Skip ${descriptor.displayName} once`;
|
|
360
|
+
const neverLabel = `Always skip ${descriptor.displayName}`;
|
|
361
|
+
const installOrLogin = parsed.reason === "not_installed"
|
|
362
|
+
? `Install command: ${parsed.installCmd}.`
|
|
363
|
+
: `Login command: ${parsed.loginCmd ?? "rdt login"}.`;
|
|
364
|
+
const questionBody = `${descriptor.displayName} requires a local external tool before this data can be fetched. ` +
|
|
365
|
+
`${installOrLogin} How would you like to proceed?`;
|
|
366
|
+
sessionPromptedSet.add(parsed.provider);
|
|
367
|
+
const promptResult = await promptUser(ctx, {
|
|
368
|
+
question: questionBody,
|
|
369
|
+
questionType: "select",
|
|
370
|
+
options: [setupLabel, skipLabel, neverLabel],
|
|
371
|
+
}, options?.askUserHandler);
|
|
372
|
+
const answer = promptResult.cancelled ? skipLabel : (promptResult.answer ?? skipLabel);
|
|
373
|
+
if (answer.startsWith("Always")) {
|
|
374
|
+
saveOnboardingState(markProviderNeverAsk(state, parsed.provider));
|
|
375
|
+
}
|
|
376
|
+
if (answer.startsWith("Continue")) {
|
|
377
|
+
return {
|
|
378
|
+
content: [
|
|
379
|
+
{
|
|
380
|
+
type: "text",
|
|
381
|
+
text: `${buildConnectedTag({ provider: parsed.provider })}\n\n` +
|
|
382
|
+
`${descriptor.displayName} setup was acknowledged. Re-run the original ${descriptor.displayName} request now; if setup is still unavailable, continue without ${descriptor.displayName} and disclose the source gap.`,
|
|
383
|
+
},
|
|
384
|
+
],
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
const silenced = answer.startsWith("Always");
|
|
388
|
+
const remediation = silenced
|
|
389
|
+
? `run ${parsed.installCmd} and ${parsed.loginCmd ?? "rdt login"} to enable ${descriptor.displayName} (silenced)`
|
|
390
|
+
: `run ${parsed.installCmd} and ${parsed.loginCmd ?? "rdt login"} to enable ${descriptor.displayName}`;
|
|
391
|
+
return {
|
|
392
|
+
content: [
|
|
393
|
+
{
|
|
394
|
+
type: "text",
|
|
395
|
+
text: `${buildSkippedTag({
|
|
396
|
+
provider: parsed.provider,
|
|
397
|
+
reason: "credential_not_provided",
|
|
398
|
+
remediation,
|
|
399
|
+
silenced,
|
|
400
|
+
})}\n\n${descriptor.displayName} data was omitted per your choice.`,
|
|
401
|
+
},
|
|
402
|
+
],
|
|
403
|
+
};
|
|
404
|
+
}
|
|
238
405
|
const state = loadOnboardingState();
|
|
239
406
|
const action = resolveCredentialRequired({
|
|
240
407
|
provider: parsed.provider,
|
|
@@ -396,11 +563,16 @@ export default function openCandleExtension(pi, options) {
|
|
|
396
563
|
pi.on("input", async (event, ctx) => {
|
|
397
564
|
if (event.source === "extension")
|
|
398
565
|
return;
|
|
566
|
+
coordinator.clearTickerValidationCache();
|
|
399
567
|
// Check for comprehensive analysis pattern — same in both modes.
|
|
400
568
|
const analysis = isAnalysisRequest(event.text);
|
|
401
569
|
if (analysis.match && analysis.symbol) {
|
|
402
|
-
const definition = buildComprehensiveAnalysisDefinition(analysis.symbol, {
|
|
570
|
+
const definition = buildComprehensiveAnalysisDefinition(analysis.symbol, {
|
|
571
|
+
debate: getConfig().debate,
|
|
572
|
+
});
|
|
403
573
|
const prompt = coordinator.transformWorkflowInput(pi, definition, ctx);
|
|
574
|
+
if (prompt)
|
|
575
|
+
markOriginalInput(event.text);
|
|
404
576
|
return prompt ? { action: "transform", text: prompt } : { action: "handled" };
|
|
405
577
|
}
|
|
406
578
|
const mode = getConfig().routerMode;
|
|
@@ -418,46 +590,177 @@ export default function openCandleExtension(pi, options) {
|
|
|
418
590
|
const storage = coordinator.getStorage();
|
|
419
591
|
const workflowPrefs = storage?.getWorkflowPreferences("global") ?? {};
|
|
420
592
|
// Classify intent
|
|
421
|
-
|
|
593
|
+
let classification = classifyWithLegacyRules(event.text);
|
|
594
|
+
const ruleModeDisambiguation = disambiguateRulesModeSymbols(event.text, classification.entities.symbols);
|
|
595
|
+
appendSymbolDropEntries(ruleModeDisambiguation.dropped, "rules");
|
|
596
|
+
classification = {
|
|
597
|
+
...classification,
|
|
598
|
+
entities: {
|
|
599
|
+
...classification.entities,
|
|
600
|
+
symbols: ruleModeDisambiguation.kept,
|
|
601
|
+
},
|
|
602
|
+
};
|
|
603
|
+
if (isComparePrompt(event.text) &&
|
|
604
|
+
ruleModeDisambiguation.dropped.length > 0 &&
|
|
605
|
+
classification.entities.symbols.length < 2) {
|
|
606
|
+
pi.appendEntry("opencandle-workflow-aborted", {
|
|
607
|
+
reason: "symbol-disambiguation-insufficient-symbols",
|
|
608
|
+
dropped: ruleModeDisambiguation.dropped,
|
|
609
|
+
validSymbols: classification.entities.symbols,
|
|
610
|
+
});
|
|
611
|
+
const base = coordinator.buildRouterContextBase(ctx.sessionManager);
|
|
612
|
+
const output = {
|
|
613
|
+
routeKind: "clarification",
|
|
614
|
+
route: "fallback",
|
|
615
|
+
workflow: "compare_assets",
|
|
616
|
+
entities: classification.entities,
|
|
617
|
+
slots: {},
|
|
618
|
+
preference_updates: [],
|
|
619
|
+
missing_required: ["symbols"],
|
|
620
|
+
tool_bundles: ["clarification"],
|
|
621
|
+
diagnostics: ruleModeDisambiguation.dropped.map((drop) => ({
|
|
622
|
+
code: "symbol_dropped",
|
|
623
|
+
message: `${drop.token} dropped: ${drop.reason}`,
|
|
624
|
+
details: {
|
|
625
|
+
token: drop.token,
|
|
626
|
+
reason: drop.reason,
|
|
627
|
+
signalsChecked: drop.signalsChecked,
|
|
628
|
+
source: "rules",
|
|
629
|
+
},
|
|
630
|
+
})),
|
|
631
|
+
reasoning: "rules-mode acronym disambiguation left fewer than two symbols for comparison",
|
|
632
|
+
};
|
|
633
|
+
const resolvedTurnContext = buildResolvedTurnContext({ text: event.text, ...base }, output, {
|
|
634
|
+
availableToolNames: safeGetAllToolNames(),
|
|
635
|
+
planning: {
|
|
636
|
+
migrationStatuses: getConfig().planningMigrationStatuses,
|
|
637
|
+
},
|
|
638
|
+
});
|
|
639
|
+
coordinator.setPendingResolvedTurnContext({
|
|
640
|
+
...resolvedTurnContext,
|
|
641
|
+
diagnostics: [
|
|
642
|
+
...resolvedTurnContext.diagnostics,
|
|
643
|
+
{
|
|
644
|
+
code: "compare_workflow_aborted",
|
|
645
|
+
message: "compare workflow needs at least two validated symbols after acronym disambiguation",
|
|
646
|
+
},
|
|
647
|
+
],
|
|
648
|
+
});
|
|
649
|
+
coordinator.setPendingFallbackContext({
|
|
650
|
+
assumptionsBlock: [
|
|
651
|
+
"Assumptions Context:",
|
|
652
|
+
classification.entities.symbols.length > 0
|
|
653
|
+
? ` valid symbols: ${classification.entities.symbols.join(", ")} (user)`
|
|
654
|
+
: " valid symbols: (none)",
|
|
655
|
+
` dropped ambiguous ticker-like tokens: ${ruleModeDisambiguation.dropped.map((d) => d.token).join(", ")} (no positive ticker signal)`,
|
|
656
|
+
].join("\n"),
|
|
657
|
+
missingRequired: ["symbols"],
|
|
658
|
+
extraContext: "Dropped ambiguous ticker-like tokens: " +
|
|
659
|
+
`${ruleModeDisambiguation.dropped.map((d) => d.token).join(", ")}. ` +
|
|
660
|
+
"Ask the user which ticker symbols they want compared before calling comparison tools.",
|
|
661
|
+
});
|
|
662
|
+
applyRouteToolScope(resolvedTurnContext);
|
|
663
|
+
return undefined;
|
|
664
|
+
}
|
|
422
665
|
if (classification.workflow === "portfolio_builder") {
|
|
423
666
|
const resolution = resolvePortfolioSlots(classification.entities, workflowPrefs);
|
|
424
667
|
coordinator.recordWorkflowRun("portfolio_builder", classification.entities, resolution.resolved, resolution.defaultsUsed);
|
|
425
|
-
pi.appendEntry("opencandle-workflow", {
|
|
668
|
+
pi.appendEntry("opencandle-workflow", {
|
|
669
|
+
workflow: "portfolio_builder",
|
|
670
|
+
entities: classification.entities,
|
|
671
|
+
resolved: resolution.resolved,
|
|
672
|
+
});
|
|
426
673
|
const definition = buildPortfolioWorkflowDefinition(resolution);
|
|
427
674
|
const prompt = coordinator.transformWorkflowInput(pi, definition, ctx);
|
|
675
|
+
if (prompt)
|
|
676
|
+
markOriginalInput(event.text);
|
|
428
677
|
return prompt ? { action: "transform", text: prompt } : { action: "handled" };
|
|
429
678
|
}
|
|
430
679
|
if (classification.workflow === "options_screener") {
|
|
431
680
|
const resolution = resolveOptionsScreenerSlots(classification.entities, workflowPrefs);
|
|
432
681
|
if (resolution.missingRequired.length === 0) {
|
|
433
682
|
coordinator.recordWorkflowRun("options_screener", classification.entities, resolution.resolved, resolution.defaultsUsed);
|
|
434
|
-
pi.appendEntry("opencandle-workflow", {
|
|
683
|
+
pi.appendEntry("opencandle-workflow", {
|
|
684
|
+
workflow: "options_screener",
|
|
685
|
+
entities: classification.entities,
|
|
686
|
+
resolved: resolution.resolved,
|
|
687
|
+
});
|
|
435
688
|
const definition = buildOptionsScreenerWorkflowDefinition(resolution);
|
|
436
689
|
const prompt = coordinator.transformWorkflowInput(pi, definition, ctx);
|
|
690
|
+
if (prompt)
|
|
691
|
+
markOriginalInput(event.text);
|
|
437
692
|
return prompt ? { action: "transform", text: prompt } : { action: "handled" };
|
|
438
693
|
}
|
|
439
694
|
}
|
|
440
|
-
if (classification.workflow === "compare_assets" &&
|
|
695
|
+
if (classification.workflow === "compare_assets" &&
|
|
696
|
+
classification.entities.symbols.length >= 2) {
|
|
441
697
|
const resolution = {
|
|
442
698
|
resolved: {
|
|
443
699
|
symbols: classification.entities.symbols,
|
|
444
700
|
metrics: classification.entities.compareMetrics,
|
|
445
701
|
timeHorizon: classification.entities.timeHorizon,
|
|
702
|
+
budget: classification.entities.budget,
|
|
703
|
+
assetScope: classification.entities.assetScope,
|
|
446
704
|
},
|
|
447
705
|
sources: {
|
|
448
706
|
symbols: "user",
|
|
449
707
|
...(classification.entities.timeHorizon ? { timeHorizon: "user" } : {}),
|
|
450
708
|
...(classification.entities.compareMetrics ? { metrics: "user" } : {}),
|
|
709
|
+
...(classification.entities.budget !== undefined ? { budget: "user" } : {}),
|
|
710
|
+
...(classification.entities.assetScope ? { assetScope: "user" } : {}),
|
|
451
711
|
},
|
|
452
712
|
defaultsUsed: [],
|
|
453
713
|
missingRequired: [],
|
|
454
714
|
};
|
|
455
715
|
coordinator.recordWorkflowRun("compare_assets", classification.entities, resolution.resolved, resolution.defaultsUsed);
|
|
456
|
-
pi.appendEntry("opencandle-workflow", {
|
|
457
|
-
|
|
716
|
+
pi.appendEntry("opencandle-workflow", {
|
|
717
|
+
workflow: "compare_assets",
|
|
718
|
+
symbols: classification.entities.symbols,
|
|
719
|
+
});
|
|
720
|
+
const preflight = await preflightCompareResolution(resolution);
|
|
721
|
+
if (!preflight) {
|
|
722
|
+
coordinator.recordWorkflowRun("fallback", classification.entities, resolution.resolved, [], "clarification");
|
|
723
|
+
coordinator.setPendingFallbackContext({
|
|
724
|
+
assumptionsBlock: [
|
|
725
|
+
"Assumptions Context:",
|
|
726
|
+
` original symbols: ${classification.entities.symbols.join(", ")} (user)`,
|
|
727
|
+
].join("\n"),
|
|
728
|
+
missingRequired: ["symbols"],
|
|
729
|
+
extraContext: "Compare workflow aborted because ticker preflight left fewer than two valid symbols. Ask the user to clarify the intended tickers before calling comparison tools.",
|
|
730
|
+
});
|
|
731
|
+
return undefined;
|
|
732
|
+
}
|
|
733
|
+
const definition = buildCompareAssetsWorkflowDefinition(preflight.resolution);
|
|
734
|
+
applyPreflightAnnotation(definition, preflight.dropped);
|
|
458
735
|
const prompt = coordinator.transformWorkflowInput(pi, definition, ctx);
|
|
736
|
+
if (prompt)
|
|
737
|
+
markOriginalInput(event.text);
|
|
459
738
|
return prompt ? { action: "transform", text: prompt } : { action: "handled" };
|
|
460
739
|
}
|
|
740
|
+
// Rules-mode finance fallback: no workflow dispatched, but the turn is
|
|
741
|
+
// finance-shaped (classified finance intent, extracted symbols, or finance
|
|
742
|
+
// vocabulary). Record the fallback turn and stash a fallback context so
|
|
743
|
+
// the system prompt carries saved market state for this turn; non-finance
|
|
744
|
+
// prompts stay untouched.
|
|
745
|
+
const isFinanceFallback = classification.workflow !== "unclassified" ||
|
|
746
|
+
classification.entities.symbols.length > 0 ||
|
|
747
|
+
hasFinanceSignals(event.text);
|
|
748
|
+
if (isFinanceFallback) {
|
|
749
|
+
coordinator.recordWorkflowRun("fallback", classification.entities, {}, [], "agent_task");
|
|
750
|
+
coordinator.setPendingFallbackContext({
|
|
751
|
+
assumptionsBlock: "",
|
|
752
|
+
missingRequired: [],
|
|
753
|
+
extraContext: classification.entities.symbols.length > 0
|
|
754
|
+
? `Rules-router extracted symbols: ${classification.entities.symbols.join(", ")}.`
|
|
755
|
+
: undefined,
|
|
756
|
+
});
|
|
757
|
+
pi.appendEntry("opencandle-fallback-context", {
|
|
758
|
+
mode: "rules",
|
|
759
|
+
classifiedWorkflow: classification.workflow,
|
|
760
|
+
symbols: classification.entities.symbols,
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
return undefined;
|
|
461
764
|
});
|
|
462
765
|
/**
|
|
463
766
|
* LLM-mode input handler. In this mode `classifyIntent` and
|
|
@@ -515,6 +818,7 @@ export default function openCandleExtension(pi, options) {
|
|
|
515
818
|
},
|
|
516
819
|
});
|
|
517
820
|
pi.appendEntry("opencandle-router", { output });
|
|
821
|
+
appendRouterSymbolDropEntries(output);
|
|
518
822
|
pi.appendEntry("opencandle-route-context", resolvedTurnContext);
|
|
519
823
|
coordinator.setPendingResolvedTurnContext(resolvedTurnContext);
|
|
520
824
|
applyRouteToolScope(resolvedTurnContext);
|
|
@@ -539,7 +843,7 @@ export default function openCandleExtension(pi, options) {
|
|
|
539
843
|
}
|
|
540
844
|
// Workflow dispatch for recognised workflows.
|
|
541
845
|
if (output.routeKind === "workflow_dispatch" && output.workflow) {
|
|
542
|
-
return dispatchRouterWorkflow(output, ctx);
|
|
846
|
+
return dispatchRouterWorkflow(output, ctx, text);
|
|
543
847
|
}
|
|
544
848
|
if (output.routeKind === "pass_through") {
|
|
545
849
|
return false;
|
|
@@ -552,13 +856,13 @@ export default function openCandleExtension(pi, options) {
|
|
|
552
856
|
assumptionsBlock,
|
|
553
857
|
missingRequired: output.missing_required,
|
|
554
858
|
extraContext: output.entities.symbols.length > 0
|
|
555
|
-
? `Router-extracted symbols: ${output.entities.symbols.join(", ")}.`
|
|
556
|
-
|
|
859
|
+
? `Router-extracted symbols: ${output.entities.symbols.join(", ")}.` +
|
|
860
|
+
` Route kind: ${output.routeKind}. Tool bundles: ${output.tool_bundles.join(", ") || "(none)"}.`
|
|
557
861
|
: undefined,
|
|
558
862
|
});
|
|
559
863
|
return false;
|
|
560
864
|
}
|
|
561
|
-
function dispatchRouterWorkflow(output, ctx) {
|
|
865
|
+
async function dispatchRouterWorkflow(output, ctx, originalText) {
|
|
562
866
|
const workflow = output.workflow;
|
|
563
867
|
const storage = coordinator.getStorage();
|
|
564
868
|
const workflowPrefs = storage?.getWorkflowPreferences("global") ?? {};
|
|
@@ -573,6 +877,8 @@ export default function openCandleExtension(pi, options) {
|
|
|
573
877
|
});
|
|
574
878
|
const definition = buildPortfolioWorkflowDefinition(resolution);
|
|
575
879
|
const prompt = coordinator.transformWorkflowInput(pi, definition, ctx);
|
|
880
|
+
if (prompt)
|
|
881
|
+
markOriginalInput(originalText);
|
|
576
882
|
return prompt ? { action: "transform", text: prompt } : false;
|
|
577
883
|
}
|
|
578
884
|
if (workflow === "options_screener") {
|
|
@@ -588,6 +894,8 @@ export default function openCandleExtension(pi, options) {
|
|
|
588
894
|
});
|
|
589
895
|
const definition = buildOptionsScreenerWorkflowDefinition(resolution);
|
|
590
896
|
const prompt = coordinator.transformWorkflowInput(pi, definition, ctx);
|
|
897
|
+
if (prompt)
|
|
898
|
+
markOriginalInput(originalText);
|
|
591
899
|
return prompt ? { action: "transform", text: prompt } : false;
|
|
592
900
|
}
|
|
593
901
|
// Missing required symbol — treat as fallback with ask_user directive.
|
|
@@ -598,11 +906,17 @@ export default function openCandleExtension(pi, options) {
|
|
|
598
906
|
symbols: entities.symbols,
|
|
599
907
|
metrics: entities.compareMetrics,
|
|
600
908
|
timeHorizon: entities.timeHorizon,
|
|
909
|
+
budget: entities.budget,
|
|
910
|
+
assetScope: entities.assetScope,
|
|
601
911
|
},
|
|
602
912
|
sources: {
|
|
603
913
|
symbols: sourceForRouterSlot(output, "symbols", "user"),
|
|
604
914
|
...(entities.timeHorizon ? { timeHorizon: "user" } : {}),
|
|
605
915
|
...(entities.compareMetrics ? { metrics: "user" } : {}),
|
|
916
|
+
...(entities.budget !== undefined
|
|
917
|
+
? { budget: sourceForRouterSlot(output, "budget", "user") }
|
|
918
|
+
: {}),
|
|
919
|
+
...(entities.assetScope ? { assetScope: "user" } : {}),
|
|
606
920
|
},
|
|
607
921
|
defaultsUsed: [],
|
|
608
922
|
missingRequired: [],
|
|
@@ -612,8 +926,22 @@ export default function openCandleExtension(pi, options) {
|
|
|
612
926
|
workflow: "compare_assets",
|
|
613
927
|
symbols: entities.symbols,
|
|
614
928
|
});
|
|
615
|
-
const
|
|
929
|
+
const preflight = await preflightCompareResolution(resolution);
|
|
930
|
+
if (!preflight) {
|
|
931
|
+
coordinator.recordWorkflowRun("fallback", output.entities, Object.fromEntries(Object.entries(output.slots).map(([k, v]) => [k, v.value])), [], output.routeKind);
|
|
932
|
+
coordinator.setPendingResolvedTurnContext(null);
|
|
933
|
+
coordinator.setPendingFallbackContext({
|
|
934
|
+
assumptionsBlock: buildAssumptionsBlockFromRouter(output.slots),
|
|
935
|
+
missingRequired: ["symbols"],
|
|
936
|
+
extraContext: "Compare workflow aborted because ticker preflight left fewer than two valid symbols. Ask the user to clarify the intended tickers.",
|
|
937
|
+
});
|
|
938
|
+
return false;
|
|
939
|
+
}
|
|
940
|
+
const definition = buildCompareAssetsWorkflowDefinition(preflight.resolution);
|
|
941
|
+
applyPreflightAnnotation(definition, preflight.dropped);
|
|
616
942
|
const prompt = coordinator.transformWorkflowInput(pi, definition, ctx);
|
|
943
|
+
if (prompt)
|
|
944
|
+
markOriginalInput(originalText);
|
|
617
945
|
return prompt ? { action: "transform", text: prompt } : false;
|
|
618
946
|
}
|
|
619
947
|
// single_asset_analysis / watchlist / general_qa + any workflow with
|
|
@@ -628,6 +956,89 @@ export default function openCandleExtension(pi, options) {
|
|
|
628
956
|
});
|
|
629
957
|
return false;
|
|
630
958
|
}
|
|
959
|
+
function appendRouterSymbolDropEntries(output) {
|
|
960
|
+
for (const diagnostic of output.diagnostics) {
|
|
961
|
+
if (diagnostic.code !== "symbol_dropped")
|
|
962
|
+
continue;
|
|
963
|
+
const details = diagnostic.details ?? {};
|
|
964
|
+
appendSymbolDropEntries([
|
|
965
|
+
{
|
|
966
|
+
token: String(details.token ?? ""),
|
|
967
|
+
reason: String(details.reason ?? ""),
|
|
968
|
+
signalsChecked: Array.isArray(details.signalsChecked)
|
|
969
|
+
? details.signalsChecked.map(String)
|
|
970
|
+
: [],
|
|
971
|
+
},
|
|
972
|
+
], String(details.source ?? "llm"));
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
function appendSymbolDropEntries(dropped, source) {
|
|
976
|
+
for (const drop of dropped) {
|
|
977
|
+
pi.appendEntry("opencandle-symbol-dropped", {
|
|
978
|
+
token: drop.token,
|
|
979
|
+
reason: drop.reason,
|
|
980
|
+
signalsChecked: drop.signalsChecked,
|
|
981
|
+
source,
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
function disambiguateRulesModeSymbols(text, extractedSymbols) {
|
|
986
|
+
const candidates = mergeSymbols(extractedSymbols, rawTickerLikeTokens(text));
|
|
987
|
+
const disambiguated = disambiguateSymbols(candidates, text);
|
|
988
|
+
return {
|
|
989
|
+
kept: disambiguated.kept.filter((symbol) => extractedSymbols.includes(symbol)),
|
|
990
|
+
dropped: disambiguated.dropped,
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
function rawTickerLikeTokens(text) {
|
|
994
|
+
const tokens = [];
|
|
995
|
+
for (const match of text.matchAll(/\$?([A-Za-z]{1,5})\b/g)) {
|
|
996
|
+
const raw = match[1];
|
|
997
|
+
if (raw !== raw.toUpperCase())
|
|
998
|
+
continue;
|
|
999
|
+
const token = raw.toUpperCase();
|
|
1000
|
+
if (!tokens.includes(token))
|
|
1001
|
+
tokens.push(token);
|
|
1002
|
+
}
|
|
1003
|
+
return tokens;
|
|
1004
|
+
}
|
|
1005
|
+
function isComparePrompt(text) {
|
|
1006
|
+
return /\b(?:compare|vs\.?|versus|which\s+is\s+better)\b/i.test(text);
|
|
1007
|
+
}
|
|
1008
|
+
async function preflightCompareResolution(resolution) {
|
|
1009
|
+
const result = await preflightSymbols(resolution.resolved.symbols, {
|
|
1010
|
+
cache: coordinator.getTickerValidationCache(),
|
|
1011
|
+
search: options?.symbolSearch,
|
|
1012
|
+
});
|
|
1013
|
+
for (const drop of result.dropped) {
|
|
1014
|
+
pi.appendEntry("opencandle-symbol-preflight-dropped", drop);
|
|
1015
|
+
}
|
|
1016
|
+
if (result.valid.length < 2) {
|
|
1017
|
+
pi.appendEntry("opencandle-workflow-aborted", {
|
|
1018
|
+
reason: "preflight-insufficient-symbols",
|
|
1019
|
+
dropped: result.dropped,
|
|
1020
|
+
});
|
|
1021
|
+
return null;
|
|
1022
|
+
}
|
|
1023
|
+
return {
|
|
1024
|
+
resolution: {
|
|
1025
|
+
...resolution,
|
|
1026
|
+
resolved: {
|
|
1027
|
+
...resolution.resolved,
|
|
1028
|
+
symbols: result.valid,
|
|
1029
|
+
},
|
|
1030
|
+
},
|
|
1031
|
+
dropped: result.dropped,
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
function applyPreflightAnnotation(definition, dropped) {
|
|
1035
|
+
if (dropped.length === 0 || definition.steps.length === 0)
|
|
1036
|
+
return;
|
|
1037
|
+
definition.steps[0] = {
|
|
1038
|
+
...definition.steps[0],
|
|
1039
|
+
prompt: `${formatPreflightDropAnnotation(dropped)}\n\n${definition.steps[0].prompt}`,
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
631
1042
|
function mergeRouterSlotsIntoEntities(output) {
|
|
632
1043
|
const entities = {
|
|
633
1044
|
...output.entities,
|
|
@@ -636,12 +1047,25 @@ export default function openCandleExtension(pi, options) {
|
|
|
636
1047
|
if (entities.budget === undefined && typeof output.slots.budget?.value === "number") {
|
|
637
1048
|
entities.budget = output.slots.budget.value;
|
|
638
1049
|
}
|
|
639
|
-
const
|
|
1050
|
+
const droppedSymbols = droppedSymbolsFromDiagnostics(output);
|
|
1051
|
+
const slotSymbols = symbolsFromRouterSlots(output).filter((symbol) => !droppedSymbols.has(symbol));
|
|
640
1052
|
if (slotSymbols.length > 0 && slotSymbols.length > entities.symbols.length) {
|
|
641
1053
|
entities.symbols = mergeSymbols(slotSymbols, entities.symbols);
|
|
642
1054
|
}
|
|
643
1055
|
return entities;
|
|
644
1056
|
}
|
|
1057
|
+
function droppedSymbolsFromDiagnostics(output) {
|
|
1058
|
+
const dropped = new Set();
|
|
1059
|
+
for (const diagnostic of output.diagnostics) {
|
|
1060
|
+
if (diagnostic.code !== "symbol_dropped")
|
|
1061
|
+
continue;
|
|
1062
|
+
const token = diagnostic.details?.token;
|
|
1063
|
+
if (typeof token === "string" && token.trim() !== "") {
|
|
1064
|
+
dropped.add(token.toUpperCase());
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
return dropped;
|
|
1068
|
+
}
|
|
645
1069
|
function withRouterSlotSources(resolution, output) {
|
|
646
1070
|
const sources = { ...resolution.sources };
|
|
647
1071
|
if (output.entities.budget === undefined && output.slots.budget) {
|
|
@@ -770,8 +1194,44 @@ export default function openCandleExtension(pi, options) {
|
|
|
770
1194
|
const fallbackContext = coordinator.consumePendingFallbackContext() ?? undefined;
|
|
771
1195
|
const resolvedTurnContext = coordinator.consumePendingResolvedTurnContext() ?? undefined;
|
|
772
1196
|
return {
|
|
773
|
-
systemPrompt: coordinator.buildSystemPrompt(event.systemPrompt,
|
|
1197
|
+
systemPrompt: coordinator.buildSystemPrompt(event.systemPrompt, coordinator.getActiveWorkflowType(), fallbackContext, resolvedTurnContext),
|
|
774
1198
|
};
|
|
775
1199
|
});
|
|
776
1200
|
}
|
|
1201
|
+
/** Concatenate text from a Pi message `content` (plain string or block array). */
|
|
1202
|
+
function extractMessageText(content) {
|
|
1203
|
+
if (typeof content === "string")
|
|
1204
|
+
return content;
|
|
1205
|
+
if (!Array.isArray(content))
|
|
1206
|
+
return "";
|
|
1207
|
+
let text = "";
|
|
1208
|
+
for (const block of content) {
|
|
1209
|
+
if (block &&
|
|
1210
|
+
typeof block === "object" &&
|
|
1211
|
+
block.type === "text" &&
|
|
1212
|
+
typeof block.text === "string") {
|
|
1213
|
+
text += block.text;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
return text;
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* True when the session name is still an auto-set placeholder that the LLM
|
|
1220
|
+
* title may replace: unset, exactly one of the candidate user texts, or the
|
|
1221
|
+
* GUI server's truncated "first 77 chars + ..." form of one of them.
|
|
1222
|
+
*/
|
|
1223
|
+
function isPlaceholderSessionName(name, candidates) {
|
|
1224
|
+
if (name === undefined || name.trim().length === 0)
|
|
1225
|
+
return true;
|
|
1226
|
+
const trimmed = name.trim();
|
|
1227
|
+
for (const candidate of candidates) {
|
|
1228
|
+
const candidateTrimmed = candidate.trim();
|
|
1229
|
+
if (trimmed === candidateTrimmed)
|
|
1230
|
+
return true;
|
|
1231
|
+
if (trimmed.endsWith("...") && candidateTrimmed.startsWith(trimmed.slice(0, -3))) {
|
|
1232
|
+
return true;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return false;
|
|
1236
|
+
}
|
|
777
1237
|
//# sourceMappingURL=opencandle-extension.js.map
|