opencandle 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +186 -117
- 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 +32 -8
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +19 -3
- package/dist/config.js +69 -3
- 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/browser.d.ts +1 -3
- package/dist/infra/browser.js +4 -2
- package/dist/infra/browser.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 +3 -3
- package/dist/infra/index.js +3 -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.d.ts +4 -0
- package/dist/infra/rate-limiter.js +17 -10
- 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.d.ts +9 -0
- package/dist/memory/manager.js +39 -22
- 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.d.ts +3 -2
- 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 +4 -0
- 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.js +4 -6
- 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/providers.js +3 -16
- package/dist/onboarding/providers.js.map +1 -1
- 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.js +6 -4
- package/dist/onboarding/tool-tags.js.map +1 -1
- 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 +637 -59
- 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 +17 -2
- package/dist/pi/setup.js.map +1 -1
- package/dist/pi/tool-adapter.js +5 -2
- package/dist/pi/tool-adapter.js.map +1 -1
- package/dist/prompts/context-builder.d.ts +18 -3
- package/dist/prompts/context-builder.js +117 -18
- package/dist/prompts/context-builder.js.map +1 -1
- package/dist/prompts/disclaimer.js +1 -1
- package/dist/prompts/disclaimer.js.map +1 -1
- package/dist/prompts/policy-cards.d.ts +13 -0
- package/dist/prompts/policy-cards.js +197 -0
- package/dist/prompts/policy-cards.js.map +1 -0
- package/dist/prompts/sections.d.ts +1 -1
- package/dist/prompts/sections.js +3 -3
- package/dist/prompts/sections.js.map +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 +209 -19
- package/dist/prompts/workflow-prompts.js.map +1 -1
- package/dist/providers/alpha-vantage.d.ts +1 -1
- package/dist/providers/alpha-vantage.js +49 -8
- 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/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.js +2 -2
- package/dist/providers/reddit.js.map +1 -1
- package/dist/providers/sec-edgar.d.ts +9 -1
- package/dist/providers/sec-edgar.js +181 -6
- 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.js +6 -8
- 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 +14 -8
- package/dist/providers/wrap-provider.js.map +1 -1
- package/dist/providers/yahoo-finance.d.ts +3 -1
- package/dist/providers/yahoo-finance.js +226 -11
- package/dist/providers/yahoo-finance.js.map +1 -1
- package/dist/routing/classify-intent.d.ts +9 -0
- package/dist/routing/classify-intent.js +153 -3
- package/dist/routing/classify-intent.js.map +1 -1
- package/dist/routing/defaults.d.ts +1 -1
- package/dist/routing/defaults.js +3 -3
- package/dist/routing/defaults.js.map +1 -1
- package/dist/routing/entity-extractor.d.ts +2 -0
- package/dist/routing/entity-extractor.js +377 -26
- 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 +12 -6
- package/dist/routing/index.js +8 -4
- package/dist/routing/index.js.map +1 -1
- package/dist/routing/legacy-rule-router.d.ts +9 -0
- package/dist/routing/legacy-rule-router.js +12 -0
- package/dist/routing/legacy-rule-router.js.map +1 -0
- package/dist/routing/planning.d.ts +54 -0
- package/dist/routing/planning.js +562 -0
- package/dist/routing/planning.js.map +1 -0
- package/dist/routing/route-manifest.d.ts +35 -0
- package/dist/routing/route-manifest.js +242 -0
- package/dist/routing/route-manifest.js.map +1 -0
- package/dist/routing/router-llm-client.js.map +1 -1
- package/dist/routing/router-prompt.js +46 -45
- package/dist/routing/router-prompt.js.map +1 -1
- package/dist/routing/router-types.d.ts +10 -0
- package/dist/routing/router.d.ts +1 -0
- package/dist/routing/router.js +572 -13
- package/dist/routing/router.js.map +1 -1
- package/dist/routing/slot-resolver.d.ts +1 -1
- package/dist/routing/slot-resolver.js +45 -7
- 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 +44 -0
- package/dist/routing/turn-context.js +45 -0
- package/dist/routing/turn-context.js.map +1 -0
- package/dist/routing/types.d.ts +15 -1
- package/dist/runtime/answer-contracts.d.ts +82 -0
- package/dist/runtime/answer-contracts.js +442 -0
- package/dist/runtime/answer-contracts.js.map +1 -0
- package/dist/runtime/artifact-contracts.d.ts +14 -0
- package/dist/runtime/artifact-contracts.js +57 -0
- package/dist/runtime/artifact-contracts.js.map +1 -0
- package/dist/runtime/planning-evidence.d.ts +99 -0
- package/dist/runtime/planning-evidence.js +466 -0
- package/dist/runtime/planning-evidence.js.map +1 -0
- 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 +29 -3
- package/dist/runtime/session-coordinator.js +204 -31
- 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 +1 -3
- 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 +9 -11
- package/dist/sentiment/index.js +9 -20
- package/dist/sentiment/index.js.map +1 -1
- 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 +1 -1
- package/dist/sentiment/pipeline.js.map +1 -1
- package/dist/sentiment/scorer.js +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.js.map +1 -1
- package/dist/system-prompt.js +7 -3
- 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 +12 -7
- package/dist/tools/fundamentals/company-overview.js.map +1 -1
- package/dist/tools/fundamentals/comps.js +19 -10
- package/dist/tools/fundamentals/comps.js.map +1 -1
- package/dist/tools/fundamentals/dcf.js +24 -12
- package/dist/tools/fundamentals/dcf.js.map +1 -1
- package/dist/tools/fundamentals/earnings.js +9 -4
- package/dist/tools/fundamentals/earnings.js.map +1 -1
- package/dist/tools/fundamentals/financials.js +9 -4
- package/dist/tools/fundamentals/financials.js.map +1 -1
- package/dist/tools/fundamentals/sec-filings.d.ts +1 -0
- package/dist/tools/fundamentals/sec-filings.js +36 -4
- package/dist/tools/fundamentals/sec-filings.js.map +1 -1
- package/dist/tools/index.d.ts +23 -18
- package/dist/tools/index.js +53 -38
- 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/interaction/twitter-login.js +13 -3
- package/dist/tools/interaction/twitter-login.js.map +1 -1
- package/dist/tools/macro/fear-greed.js +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 +44 -9
- package/dist/tools/macro/fred-data.js.map +1 -1
- package/dist/tools/market/crypto-history.js +21 -3
- package/dist/tools/market/crypto-history.js.map +1 -1
- package/dist/tools/market/crypto-price.js +4 -2
- 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 +161 -9
- 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 +27 -8
- package/dist/tools/market/stock-history.js.map +1 -1
- package/dist/tools/market/stock-quote.js +6 -4
- package/dist/tools/market/stock-quote.js.map +1 -1
- package/dist/tools/options/greeks.js +1 -2
- package/dist/tools/options/greeks.js.map +1 -1
- package/dist/tools/options/option-chain.js +27 -9
- 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 +34 -14
- 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.d.ts +8 -0
- package/dist/tools/portfolio/holdings-overlap.js +112 -0
- package/dist/tools/portfolio/holdings-overlap.js.map +1 -0
- 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 +338 -88
- 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 +46 -7
- 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 +247 -102
- package/dist/tools/portfolio/tracker.js.map +1 -1
- package/dist/tools/portfolio/watchlist.d.ts +6 -4
- package/dist/tools/portfolio/watchlist.js +209 -101
- package/dist/tools/portfolio/watchlist.js.map +1 -1
- package/dist/tools/sentiment/reddit-sentiment.js +24 -11
- package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
- package/dist/tools/sentiment/sentiment-summary.js +71 -14
- 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.js +13 -6
- 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 +37 -12
- package/dist/tools/sentiment/web-search.js.map +1 -1
- package/dist/tools/sentiment/web-sentiment.js +16 -4
- package/dist/tools/sentiment/web-sentiment.js.map +1 -1
- package/dist/tools/technical/backtest.d.ts +3 -3
- package/dist/tools/technical/backtest.js +65 -44
- package/dist/tools/technical/backtest.js.map +1 -1
- package/dist/tools/technical/indicators.js +24 -8
- 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/options.d.ts +10 -0
- package/dist/types/portfolio.d.ts +41 -4
- package/dist/workflows/compare-assets.d.ts +0 -3
- package/dist/workflows/compare-assets.js +55 -10
- 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 +88 -14
- package/dist/workflows/options-screener.js.map +1 -1
- package/dist/workflows/portfolio-builder.d.ts +0 -3
- package/dist/workflows/portfolio-builder.js +7 -11
- package/dist/workflows/portfolio-builder.js.map +1 -1
- package/gui/server/ask-user-bridge.ts +82 -0
- 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/gui-session-manager.ts +5 -0
- package/gui/server/invoke-tool.ts +144 -1
- 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 +149 -2
- package/gui/server/private-api-access.ts +62 -0
- package/gui/server/projector.ts +58 -11
- package/gui/server/prompt-observation.ts +58 -0
- package/gui/server/quote-snapshot-store.ts +50 -0
- package/gui/server/server.ts +236 -376
- package/gui/server/session-actions.ts +186 -1
- package/gui/server/session-entry-wait.ts +81 -0
- package/gui/server/shutdown.ts +47 -0
- package/gui/server/tool-invoke-ack.ts +49 -0
- package/gui/server/tool-metadata.ts +23 -10
- package/gui/server/websocket.ts +13 -3
- package/gui/server/writer-lock.ts +6 -2
- package/gui/server/ws-hub.ts +292 -0
- package/gui/shared/chat-events.ts +16 -1
- package/gui/shared/event-reducer.ts +24 -6
- package/gui/web/dist/assets/CatalogOverlay-eJ2cBk33.js +1 -0
- package/gui/web/dist/assets/index-2KZtKBmu.css +1 -0
- package/gui/web/dist/assets/index-CveNgtDg.js +69 -0
- package/gui/web/dist/index.html +2 -2
- package/package.json +22 -12
- package/src/analysts/contracts.ts +10 -23
- package/src/analysts/orchestrator.ts +8 -43
- package/src/cli.ts +37 -13
- package/src/config.ts +99 -7
- package/src/index.ts +1 -1
- package/src/infra/browser.ts +4 -2
- package/src/infra/cache.ts +41 -30
- package/src/infra/http-client.ts +72 -6
- package/src/infra/index.ts +7 -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 +32 -20
- 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 +57 -26
- package/src/memory/retrieval.ts +8 -7
- package/src/memory/sqlite.ts +407 -6
- package/src/memory/storage.ts +8 -17
- package/src/memory/tool-defaults.ts +60 -39
- package/src/memory/types.ts +7 -3
- package/src/monitor.ts +121 -0
- package/src/onboarding/connect.ts +10 -33
- package/src/onboarding/credential-interceptor.ts +3 -15
- package/src/onboarding/degradation-accumulator.ts +1 -3
- package/src/onboarding/providers.ts +9 -40
- package/src/onboarding/state.ts +4 -15
- package/src/onboarding/tool-helpers.ts +2 -9
- package/src/onboarding/tool-tags.ts +6 -6
- package/src/onboarding/validation.ts +14 -20
- package/src/pi/opencandle-extension.ts +795 -120
- package/src/pi/session.ts +7 -5
- package/src/pi/setup.ts +61 -33
- package/src/pi/tool-adapter.ts +5 -2
- package/src/prompts/context-builder.ts +143 -21
- package/src/prompts/disclaimer.ts +1 -1
- package/src/prompts/policy-cards.ts +220 -0
- package/src/prompts/sections.ts +4 -4
- package/src/prompts/symbol-preflight.ts +80 -0
- package/src/prompts/workflow-prompts.ts +231 -28
- package/src/providers/alpha-vantage.ts +82 -40
- 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/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.ts +17 -6
- package/src/providers/sec-edgar.ts +235 -5
- package/src/providers/tradingview.ts +399 -0
- package/src/providers/twitter.ts +6 -8
- package/src/providers/web-search.ts +30 -20
- package/src/providers/with-fallback.ts +8 -7
- package/src/providers/wrap-provider.ts +15 -10
- package/src/providers/yahoo-finance.ts +292 -20
- package/src/routing/classify-intent.ts +186 -4
- package/src/routing/defaults.ts +4 -4
- package/src/routing/entity-extractor.ts +428 -28
- package/src/routing/fund-symbols.ts +58 -0
- package/src/routing/horizon.ts +7 -0
- package/src/routing/index.ts +60 -16
- package/src/routing/legacy-rule-router.ts +13 -0
- package/src/routing/planning.ts +823 -0
- package/src/routing/route-manifest.ts +309 -0
- package/src/routing/router-llm-client.ts +4 -4
- package/src/routing/router-prompt.ts +52 -52
- package/src/routing/router-types.ts +18 -0
- package/src/routing/router.ts +717 -20
- package/src/routing/slot-resolver.ts +75 -14
- package/src/routing/symbol-disambiguator.ts +72 -0
- package/src/routing/turn-context.ts +108 -0
- package/src/routing/types.ts +15 -1
- package/src/runtime/answer-contracts.ts +672 -0
- package/src/runtime/artifact-contracts.ts +77 -0
- package/src/runtime/planning-evidence.ts +682 -0
- package/src/runtime/prompt-step.ts +1 -16
- package/src/runtime/run-context.ts +12 -2
- package/src/runtime/session-coordinator.ts +297 -56
- package/src/runtime/session-title.ts +60 -0
- package/src/runtime/tool-defaults-wrapper.ts +1 -3
- 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 +16 -26
- package/src/sentiment/keywords.ts +26 -4
- package/src/sentiment/pipeline.ts +15 -4
- package/src/sentiment/scorer.ts +1 -1
- package/src/sentiment/store.ts +2 -2
- package/src/sentiment/trends.ts +9 -3
- package/src/sentiment/types.ts +5 -4
- package/src/system-prompt.ts +7 -3
- package/src/tool-kit.ts +10 -9
- package/src/tools/fundamentals/company-overview.ts +20 -10
- package/src/tools/fundamentals/comps.ts +69 -56
- package/src/tools/fundamentals/dcf.ts +146 -96
- package/src/tools/fundamentals/earnings.ts +17 -7
- package/src/tools/fundamentals/financials.ts +17 -8
- package/src/tools/fundamentals/sec-filings.ts +52 -8
- package/src/tools/index.ts +53 -38
- package/src/tools/interaction/ask-user.ts +22 -10
- package/src/tools/interaction/twitter-login.ts +17 -5
- package/src/tools/macro/fear-greed.ts +2 -2
- package/src/tools/macro/fred-data.ts +80 -42
- package/src/tools/market/crypto-history.ts +25 -4
- package/src/tools/market/crypto-price.ts +7 -7
- package/src/tools/market/screen-stocks.ts +279 -0
- package/src/tools/market/search-ticker.ts +219 -18
- package/src/tools/market/stock-history.ts +38 -13
- package/src/tools/market/stock-quote.ts +11 -8
- package/src/tools/options/greeks.ts +5 -6
- package/src/tools/options/option-chain.ts +47 -18
- package/src/tools/portfolio/alerts.ts +457 -0
- package/src/tools/portfolio/correlation.ts +48 -21
- package/src/tools/portfolio/daily-report.ts +101 -0
- package/src/tools/portfolio/holdings-overlap.ts +139 -0
- package/src/tools/portfolio/notifications.ts +45 -0
- package/src/tools/portfolio/predictions.ts +407 -107
- package/src/tools/portfolio/risk-analysis.ts +47 -8
- package/src/tools/portfolio/tracker.ts +271 -110
- package/src/tools/portfolio/watchlist.ts +251 -116
- package/src/tools/sentiment/reddit-sentiment.ts +51 -25
- package/src/tools/sentiment/sentiment-summary.ts +116 -35
- package/src/tools/sentiment/sentiment-trend.ts +24 -7
- package/src/tools/sentiment/twitter-sentiment.ts +23 -16
- package/src/tools/sentiment/untrusted-text.ts +21 -0
- package/src/tools/sentiment/web-search.ts +52 -16
- package/src/tools/sentiment/web-sentiment.ts +27 -11
- package/src/tools/technical/backtest.ts +78 -47
- package/src/tools/technical/indicators.ts +40 -17
- package/src/types/index.ts +8 -3
- package/src/types/market.ts +1 -0
- package/src/types/options.ts +17 -0
- package/src/types/portfolio.ts +46 -4
- package/src/types/sentiment.ts +2 -2
- package/src/workflows/compare-assets.ts +67 -19
- package/src/workflows/index.ts +3 -4
- package/src/workflows/options-screener.ts +98 -22
- package/src/workflows/portfolio-builder.ts +40 -29
- package/dist/runtime/index.d.ts +0 -16
- package/dist/runtime/index.js +0 -10
- package/dist/runtime/index.js.map +0 -1
- package/dist/runtime/provider-ids.d.ts +0 -14
- package/dist/runtime/provider-ids.js +0 -14
- package/dist/runtime/provider-ids.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-D1ImSJTe.js +0 -1
- package/gui/web/dist/assets/index-DBrWq43L.css +0 -1
- package/gui/web/dist/assets/index-RflHaj0y.js +0 -67
- package/src/runtime/index.ts +0 -55
- package/src/runtime/provider-ids.ts +0 -15
- package/src/workflows/types.ts +0 -4
|
@@ -1,6 +1,3 @@
|
|
|
1
1
|
import type { OptionsScreenerSlots, SlotResolution } from "../routing/types.js";
|
|
2
|
-
import type { WorkflowPlan } from "./types.js";
|
|
3
2
|
import type { WorkflowDefinition } from "../runtime/prompt-step.js";
|
|
4
3
|
export declare function buildOptionsScreenerWorkflowDefinition(resolution: SlotResolution<OptionsScreenerSlots>): WorkflowDefinition;
|
|
5
|
-
/** @deprecated Use buildOptionsScreenerWorkflowDefinition instead */
|
|
6
|
-
export declare function buildOptionsScreenerWorkflow(resolution: SlotResolution<OptionsScreenerSlots>): WorkflowPlan;
|
|
@@ -2,7 +2,68 @@ import { buildOptionsScreenerPrompt } from "../prompts/workflow-prompts.js";
|
|
|
2
2
|
import { promptStep } from "../runtime/prompt-step.js";
|
|
3
3
|
export function buildOptionsScreenerWorkflowDefinition(resolution) {
|
|
4
4
|
const s = resolution.resolved;
|
|
5
|
-
const
|
|
5
|
+
const isProtectivePutContext = s.optionStrategy === "protective_put";
|
|
6
|
+
const contractType = s.direction === "bullish" && !isProtectivePutContext ? "calls" : "puts";
|
|
7
|
+
const horizonPhrase = describeDteTarget(s.dteTarget);
|
|
8
|
+
const isCoveredCallContext = !isProtectivePutContext &&
|
|
9
|
+
(s.optionStrategy === "covered_call" ||
|
|
10
|
+
s.costBasis !== undefined ||
|
|
11
|
+
(s.catalystSymbols?.length ?? 0) > 0);
|
|
12
|
+
const rankingInstruction = isProtectivePutContext
|
|
13
|
+
? "Rank by protection per dollar of premium, expiration fit, moneyness, hedge floor, live liquidity, and premium as a percent of the stock position."
|
|
14
|
+
: isCoveredCallContext
|
|
15
|
+
? "Rank by premium collected, strike above cost basis, assignment risk, event risk, live liquidity, and probability of expiring out of the money."
|
|
16
|
+
: `Rank by ${s.objective}: balance premium cost, delta exposure, and probability of profit. Only include contracts with |delta| >= 0.20.`;
|
|
17
|
+
const maxPremiumInstruction = s.maxPremium !== undefined
|
|
18
|
+
? ` Do not rank contracts above the user's max premium of $${s.maxPremium.toLocaleString("en-US")} unless no contracts under that cap are liquid; if so, say the cap could not be met.`
|
|
19
|
+
: "";
|
|
20
|
+
const riskInstruction = isProtectivePutContext
|
|
21
|
+
? "Include protective-put hedge risks: premium decay/cost, imperfect hedge before the strike, liquidity, and opportunity cost. Long protective puts do not have short-option assignment risk."
|
|
22
|
+
: isCoveredCallContext
|
|
23
|
+
? "Include covered-call sale risks: assignment risk, upside is capped at the strike plus premium, share-price downside in the owned stock less premium received, IV/event risk, and exit liquidity. Do not say max loss = premium or describe max loss as the option premium paid."
|
|
24
|
+
: "Include risk caveats: max loss = premium, IV crush risk, time decay (theta).";
|
|
25
|
+
const coveredCallFallback = isCoveredCallContext
|
|
26
|
+
? `
|
|
27
|
+
Covered-call fallback:
|
|
28
|
+
- Treat this as selling covered calls against an existing ${s.symbol} share position, not buying calls.
|
|
29
|
+
- Briefly state that you are treating ${s.symbol} as the held ticker because the user phrased it as an existing holding. If they meant memory exposure or a different ticker, tell them to clarify and do not silently switch to another underlying.
|
|
30
|
+
- If retrieved contracts have zero bid/ask, zero open interest, or otherwise unusable live quotes, do not stop at "cannot rank."
|
|
31
|
+
- This fallback overrides the normal ranking/table requirements and the delta >= 0.20 filter.
|
|
32
|
+
- Do NOT conclude with "I cannot provide a recommendation" in this fallback case.
|
|
33
|
+
- The final answer MUST include:
|
|
34
|
+
- "Best action:" no trade unless the user's broker shows a real bid.
|
|
35
|
+
- "Conditional candidate:" a conditional limit-order candidate using the user's cost basis and catalyst context.
|
|
36
|
+
- If you cannot compute an exact premium, say "premium: use live broker bid/ask" rather than omitting the candidate.
|
|
37
|
+
- Choose a conditional candidate strike above cost basis that the user would accept for assignment, prefer near-term expirations around the catalyst, and label it as conditional on live bid/ask and premium collected.
|
|
38
|
+
- For the top pick, include the effective assignment sale price (strike + premium collected) and compare it with the ${s.costBasis !== undefined ? `$${s.costBasis}` : "user's"} cost basis.
|
|
39
|
+
- Include return-if-assigned when cost basis is available: (strike - cost basis + premium received) / cost basis.
|
|
40
|
+
- Verify bid/ask and open interest in the user's broker before trading, even when OC shows live values.
|
|
41
|
+
- Mention the catalyst only as event/sympathy risk; do not switch the underlying away from ${s.symbol}.
|
|
42
|
+
- Do not describe max loss as the option premium paid; covered-call sale risk is assignment risk, upside is capped at strike plus premium, share-price downside in the owned stock less premium received, IV/event risk, and poor exit liquidity.
|
|
43
|
+
- If the tool reports closed_market_or_stale_quotes, do not treat zero bid/ask as confirmed live illiquidity; say the chain was checked outside regular options trading and recheck after regular options trading opens.
|
|
44
|
+
- Required fallback format:
|
|
45
|
+
Interpretation: Treating ${s.symbol} as the held ticker because you phrased it as an existing position. If you meant ${s.symbol} as memory exposure or another ticker, clarify before trading.
|
|
46
|
+
Best action: ...
|
|
47
|
+
Conditional candidate: ...
|
|
48
|
+
Why: ...
|
|
49
|
+
Risk: ...
|
|
50
|
+
`
|
|
51
|
+
: "";
|
|
52
|
+
const coveredCallNoDataGuidance = isCoveredCallContext
|
|
53
|
+
? "- For covered-call requests in that no-data fallback, explain how to evaluate covered calls: compare 1-week vs 2-week theta/gamma tradeoffs, use delta as an assignment-risk proxy, avoid strikes where assignment would violate the user's cost basis unless premium offsets it, calculate static premium yield and return-if-assigned, and flag catalyst/IV-crush risk."
|
|
54
|
+
: "";
|
|
55
|
+
const protectivePutFallback = isProtectivePutContext
|
|
56
|
+
? `
|
|
57
|
+
Protective-put requirements:
|
|
58
|
+
- Treat this as buying puts to hedge an existing long ${s.symbol} share position, not buying calls.
|
|
59
|
+
- The final answer MUST discuss hedge floor, premium as a percent of position value, expiration fit, moneyness, and liquidity.
|
|
60
|
+
- If share quantity is available, translate shares into approximate contract count using 1 put contract per 100 shares.
|
|
61
|
+
- If share quantity is available, state total premium for the required number of contracts.
|
|
62
|
+
- For cost-sensitive requests, explain the tradeoff between cheaper lower-strike puts and weaker protection.
|
|
63
|
+
- Mention lower-cost alternatives such as collars or put spreads when outright put premium is high.
|
|
64
|
+
- Do not frame assignment risk like a short option sale.
|
|
65
|
+
`
|
|
66
|
+
: "";
|
|
6
67
|
return {
|
|
7
68
|
workflowType: "options_screener",
|
|
8
69
|
steps: [
|
|
@@ -13,19 +74,23 @@ export function buildOptionsScreenerWorkflowDefinition(resolution) {
|
|
|
13
74
|
promptStep("rank_and_present", "Rank and present top contracts", `Now rank and present the top ${contractType} for ${s.symbol}. You MUST produce a final text response — never end this turn with only tool calls.
|
|
14
75
|
|
|
15
76
|
1. From the option chain data already fetched, select the top 3-5 contracts matching: ${s.moneynessPreference} strikes, DTE near ${s.dteTarget}, with ${s.liquidityMinimum}.
|
|
16
|
-
2.
|
|
17
|
-
3. Present a table: strike, expiry, premium, delta, IV, open interest, bid-ask spread.
|
|
77
|
+
2. ${rankingInstruction}${maxPremiumInstruction}
|
|
78
|
+
3. Present a table: strike, expiry, premium, delta, gamma, theta, vega, rho, IV, open interest, bid-ask spread.
|
|
18
79
|
4. Explain why the #1 pick is ranked highest.
|
|
19
|
-
5.
|
|
20
|
-
6.
|
|
80
|
+
5. Reproduce the exact Assumptions block from the initial prompt; keep the provenance label text intact and do not shorten it to "Assumptions:".
|
|
81
|
+
6. State the requested option horizon in plain language (${horizonPhrase}) in the bottom line or table intro; use the user's horizon wording or approximate DTE window without forcing a fixed sentence.
|
|
82
|
+
7. ${riskInstruction}
|
|
21
83
|
|
|
22
84
|
If some or all of the option chain fetches returned "⚠ Options chain unavailable" or similar gaps, do NOT abort. Instead:
|
|
23
85
|
- Rank and present whatever contracts you did retrieve from the successful fetches, even if fewer than 3.
|
|
24
|
-
- If no chain data is usable at all, still produce a text response: reproduce the Assumptions block, state which expirations failed, and
|
|
86
|
+
- If no chain data is usable at all, still produce a text response: reproduce the Assumptions block, state which expirations failed, and give actionable fallback guidance for the requested DTE instead of ranking nonexistent contracts. Do not promise to retry later. Never end the turn with only tool calls.
|
|
87
|
+
${coveredCallNoDataGuidance}
|
|
88
|
+
${coveredCallFallback}
|
|
89
|
+
${protectivePutFallback}
|
|
25
90
|
|
|
26
91
|
Length constraints:
|
|
27
92
|
- Max 1 sentence explaining the #1 pick.
|
|
28
|
-
- Risk
|
|
93
|
+
- Risk section: max 3 bullets.
|
|
29
94
|
- Keep total response under 30 lines.`, {
|
|
30
95
|
requiredInputs: ["option_chain"],
|
|
31
96
|
expectedOutputs: ["ranked_contracts"],
|
|
@@ -33,12 +98,21 @@ Length constraints:
|
|
|
33
98
|
],
|
|
34
99
|
};
|
|
35
100
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
101
|
+
function describeDteTarget(dteTarget) {
|
|
102
|
+
if (dteTarget === "25_to_45_days")
|
|
103
|
+
return "25-45 day target, roughly one month";
|
|
104
|
+
if (dteTarget === "7_to_14_days")
|
|
105
|
+
return "1-2 week horizon";
|
|
106
|
+
if (dteTarget === "0_to_7_days")
|
|
107
|
+
return "event-week or 0-7 day horizon";
|
|
108
|
+
if (dteTarget === "180_plus_days")
|
|
109
|
+
return "LEAPS or long-dated horizon";
|
|
110
|
+
const range = dteTarget.match(/^(\d+)_to_(\d+)_days$/);
|
|
111
|
+
if (range)
|
|
112
|
+
return `${range[1]}-${range[2]} day horizon`;
|
|
113
|
+
const plus = dteTarget.match(/^(\d+)_plus_days$/);
|
|
114
|
+
if (plus)
|
|
115
|
+
return `${plus[1]}+ day horizon`;
|
|
116
|
+
return `${dteTarget} horizon`;
|
|
43
117
|
}
|
|
44
118
|
//# sourceMappingURL=options-screener.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options-screener.js","sourceRoot":"","sources":["../../src/workflows/options-screener.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"options-screener.js","sourceRoot":"","sources":["../../src/workflows/options-screener.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
1
|
import type { PortfolioSlots, SlotResolution } from "../routing/types.js";
|
|
2
|
-
import type { WorkflowPlan } from "./types.js";
|
|
3
2
|
import type { WorkflowDefinition } from "../runtime/prompt-step.js";
|
|
4
3
|
export declare function buildPortfolioWorkflowDefinition(resolution: SlotResolution<PortfolioSlots>): WorkflowDefinition;
|
|
5
|
-
/** @deprecated Use buildPortfolioWorkflowDefinition instead */
|
|
6
|
-
export declare function buildPortfolioWorkflow(resolution: SlotResolution<PortfolioSlots>): WorkflowPlan;
|
|
@@ -13,7 +13,8 @@ export function buildPortfolioWorkflowDefinition(resolution) {
|
|
|
13
13
|
1. Use analyze_correlation across all ${s.positionCount} candidates to check for concentration risk.
|
|
14
14
|
2. Use analyze_risk on each position for volatility and max drawdown.
|
|
15
15
|
3. If correlation is too high (>0.7 between any pair), suggest a replacement to improve diversification.
|
|
16
|
-
4.
|
|
16
|
+
4. If any position's risk metrics undermine its intended role, lower its allocation, replace it with a role-equivalent candidate, or explicitly justify why it remains.
|
|
17
|
+
5. Confirm the portfolio fits a ${s.riskProfile} risk profile with ${s.timeHorizon} horizon.`, {
|
|
17
18
|
skippable: true,
|
|
18
19
|
requiredInputs: ["candidate_positions"],
|
|
19
20
|
expectedOutputs: ["risk_assessment"],
|
|
@@ -21,13 +22,16 @@ export function buildPortfolioWorkflowDefinition(resolution) {
|
|
|
21
22
|
promptStep("synthesize", "Present final portfolio draft", `Present the final portfolio draft as a structured summary:
|
|
22
23
|
- State all assumptions at the top (which parameters were defaults vs user-specified vs saved preferences).
|
|
23
24
|
- Commit to the allocation: concrete percentages per position, not ranges.
|
|
24
|
-
- Present an allocation table: symbol, allocation %, dollar amount ($${s.budget.toLocaleString("en-US")} total), and one-line analyst rationale per position.
|
|
25
|
+
- Present an allocation table: symbol, allocation %, dollar amount ($${s.budget.toLocaleString("en-US")} total), current price used, estimated shares, role, and one-line analyst rationale per position.
|
|
26
|
+
- Add a brief "Why this fits the horizon" summary explaining the growth/stability tradeoff for the ${s.timeHorizon} horizon.
|
|
25
27
|
- Include overall portfolio risk summary: estimated volatility, diversification quality, largest single risk. State an invalidation condition for the draft.
|
|
28
|
+
- Include implementation notes: rebalance cadence, low-cost/liquid implementation, and tax/account caveats where relevant.
|
|
26
29
|
- Suggest what to change for more growth or more safety.
|
|
27
30
|
|
|
28
31
|
Length constraints:
|
|
29
|
-
- Max 1 sentence of rationale per position in the allocation table.
|
|
32
|
+
- Max 1 sentence of rationale per position in the allocation table; do not paste company descriptions or issuer background.
|
|
30
33
|
- Risk summary: max 3 bullet points.
|
|
34
|
+
- Implementation notes: max 3 bullets.
|
|
31
35
|
- Growth/safety suggestions: max 2 bullet points each.
|
|
32
36
|
- Keep total response under 40 lines.`, {
|
|
33
37
|
requiredInputs: ["risk_assessment"],
|
|
@@ -36,12 +40,4 @@ Length constraints:
|
|
|
36
40
|
],
|
|
37
41
|
};
|
|
38
42
|
}
|
|
39
|
-
/** @deprecated Use buildPortfolioWorkflowDefinition instead */
|
|
40
|
-
export function buildPortfolioWorkflow(resolution) {
|
|
41
|
-
const def = buildPortfolioWorkflowDefinition(resolution);
|
|
42
|
-
return {
|
|
43
|
-
initialPrompt: def.steps[0].prompt,
|
|
44
|
-
followUps: def.steps.slice(1).map((s) => s.prompt),
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
43
|
//# sourceMappingURL=portfolio-builder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portfolio-builder.js","sourceRoot":"","sources":["../../src/workflows/portfolio-builder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"portfolio-builder.js","sourceRoot":"","sources":["../../src/workflows/portfolio-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAGtE,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEvD,MAAM,UAAU,gCAAgC,CAC9C,UAA0C;IAE1C,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC;IAE9B,OAAO;QACL,YAAY,EAAE,mBAAmB;QACjC,KAAK,EAAE;YACL,UAAU,CACR,kBAAkB,EAClB,yCAAyC,EACzC,oBAAoB,CAAC,UAAU,CAAC,EAChC;gBACE,cAAc,EAAE,CAAC,SAAS,CAAC;gBAC3B,eAAe,EAAE,CAAC,qBAAqB,CAAC;aACzC,CACF;YACD,UAAU,CACR,aAAa,EACb,iCAAiC,EACjC;wCACgC,CAAC,CAAC,aAAa;;;;kCAIrB,CAAC,CAAC,WAAW,sBAAsB,CAAC,CAAC,WAAW,WAAW,EACrF;gBACE,SAAS,EAAE,IAAI;gBACf,cAAc,EAAE,CAAC,qBAAqB,CAAC;gBACvC,eAAe,EAAE,CAAC,iBAAiB,CAAC;aACrC,CACF;YACD,UAAU,CACR,YAAY,EACZ,+BAA+B,EAC/B;;;uEAG+D,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC;qGACF,CAAC,CAAC,WAAW;;;;;;;;;;sCAU5E,EAC9B;gBACE,cAAc,EAAE,CAAC,iBAAiB,CAAC;gBACnC,eAAe,EAAE,CAAC,mBAAmB,CAAC;aACvC,CACF;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { AskUserHandler } from "../../src/types/index.js";
|
|
2
|
+
|
|
3
|
+
type AskUserParams = Parameters<AskUserHandler>[0];
|
|
4
|
+
type AskUserResult = Awaited<ReturnType<AskUserHandler>>;
|
|
5
|
+
|
|
6
|
+
export interface GuiAskUserPrompt extends AskUserParams {
|
|
7
|
+
id: string;
|
|
8
|
+
sessionId: string;
|
|
9
|
+
status: "pending" | "answered" | "cancelled";
|
|
10
|
+
answer: string | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface PendingPrompt {
|
|
14
|
+
prompt: GuiAskUserPrompt;
|
|
15
|
+
resolve: (result: AskUserResult) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function createAskUserBridge({
|
|
19
|
+
broadcast,
|
|
20
|
+
getSessionId,
|
|
21
|
+
}: {
|
|
22
|
+
broadcast: (message: unknown) => void;
|
|
23
|
+
getSessionId: () => string;
|
|
24
|
+
}): {
|
|
25
|
+
ask: AskUserHandler;
|
|
26
|
+
answer: (id: string, answer: string) => boolean;
|
|
27
|
+
cancel: (id: string) => boolean;
|
|
28
|
+
getPrompts: () => GuiAskUserPrompt[];
|
|
29
|
+
} {
|
|
30
|
+
let nextId = 1;
|
|
31
|
+
const prompts = new Map<string, GuiAskUserPrompt>();
|
|
32
|
+
const pending = new Map<string, PendingPrompt>();
|
|
33
|
+
|
|
34
|
+
const ask: AskUserHandler = async (params) => {
|
|
35
|
+
const id = `ask-user-${Date.now()}-${nextId++}`;
|
|
36
|
+
const prompt: GuiAskUserPrompt = {
|
|
37
|
+
id,
|
|
38
|
+
sessionId: getSessionId(),
|
|
39
|
+
question: params.question,
|
|
40
|
+
questionType: params.questionType,
|
|
41
|
+
options: params.options,
|
|
42
|
+
placeholder: params.placeholder,
|
|
43
|
+
reason: params.reason,
|
|
44
|
+
status: "pending",
|
|
45
|
+
answer: null,
|
|
46
|
+
};
|
|
47
|
+
prompts.set(id, prompt);
|
|
48
|
+
broadcast({ type: "ask_user.prompt", prompt });
|
|
49
|
+
|
|
50
|
+
return new Promise<AskUserResult>((resolve) => {
|
|
51
|
+
pending.set(id, { prompt, resolve });
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const resolvePrompt = (id: string, result: AskUserResult): boolean => {
|
|
56
|
+
const item = pending.get(id);
|
|
57
|
+
if (!item) return false;
|
|
58
|
+
pending.delete(id);
|
|
59
|
+
const prompt: GuiAskUserPrompt = {
|
|
60
|
+
...item.prompt,
|
|
61
|
+
status: result.cancelled ? "cancelled" : "answered",
|
|
62
|
+
answer: result.cancelled ? null : (result.answer ?? null),
|
|
63
|
+
};
|
|
64
|
+
prompts.set(id, prompt);
|
|
65
|
+
broadcast({ type: "ask_user.resolved", prompt });
|
|
66
|
+
item.resolve(result);
|
|
67
|
+
return true;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
ask,
|
|
72
|
+
answer(id, answer) {
|
|
73
|
+
return resolvePrompt(id, { answer, cancelled: false });
|
|
74
|
+
},
|
|
75
|
+
cancel(id) {
|
|
76
|
+
return resolvePrompt(id, { answer: null, cancelled: true });
|
|
77
|
+
},
|
|
78
|
+
getPrompts() {
|
|
79
|
+
return [...prompts.values()];
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { runLocalAutomationHeartbeat } from "../../src/market-state/local-automation-service.js";
|
|
2
|
+
import { initDefaultDatabase } from "../../src/memory/sqlite.js";
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_AUTOMATION_HEARTBEAT_MS = 60_000;
|
|
5
|
+
export const MIN_AUTOMATION_HEARTBEAT_MS = 5_000;
|
|
6
|
+
|
|
7
|
+
export function normalizeAutomationHeartbeatMs(raw: string | undefined): number {
|
|
8
|
+
if (raw == null || raw.trim() === "") return DEFAULT_AUTOMATION_HEARTBEAT_MS;
|
|
9
|
+
const parsed = Number(raw);
|
|
10
|
+
if (!Number.isFinite(parsed) || parsed < MIN_AUTOMATION_HEARTBEAT_MS)
|
|
11
|
+
return DEFAULT_AUTOMATION_HEARTBEAT_MS;
|
|
12
|
+
return Math.floor(parsed);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function createAutomationHeartbeatRunner(
|
|
16
|
+
run: (checkAlerts: boolean) => Promise<void>,
|
|
17
|
+
): (checkAlerts: boolean) => Promise<boolean> {
|
|
18
|
+
let inFlight = false;
|
|
19
|
+
return async (checkAlerts: boolean) => {
|
|
20
|
+
if (inFlight) return false;
|
|
21
|
+
inFlight = true;
|
|
22
|
+
try {
|
|
23
|
+
await run(checkAlerts);
|
|
24
|
+
return true;
|
|
25
|
+
} finally {
|
|
26
|
+
inFlight = false;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type IntervalHandle = ReturnType<typeof setInterval>;
|
|
32
|
+
type SetIntervalFn = (callback: () => void, ms: number) => IntervalHandle;
|
|
33
|
+
type ClearIntervalFn = (handle: IntervalHandle) => void;
|
|
34
|
+
|
|
35
|
+
export interface LocalAutomationHeartbeat {
|
|
36
|
+
start(): void;
|
|
37
|
+
stop(): void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface LocalAutomationHeartbeatOptions {
|
|
41
|
+
role: string;
|
|
42
|
+
getSessionId: () => string;
|
|
43
|
+
intervalMs: number;
|
|
44
|
+
initDatabase?: typeof initDefaultDatabase;
|
|
45
|
+
runHeartbeat?: typeof runLocalAutomationHeartbeat;
|
|
46
|
+
setIntervalFn?: SetIntervalFn;
|
|
47
|
+
clearIntervalFn?: ClearIntervalFn;
|
|
48
|
+
warn?: (message: string) => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createLocalAutomationHeartbeat({
|
|
52
|
+
role,
|
|
53
|
+
getSessionId,
|
|
54
|
+
intervalMs,
|
|
55
|
+
initDatabase = initDefaultDatabase,
|
|
56
|
+
runHeartbeat = runLocalAutomationHeartbeat,
|
|
57
|
+
setIntervalFn = setInterval,
|
|
58
|
+
clearIntervalFn = clearInterval,
|
|
59
|
+
warn = (message) => console.warn(message),
|
|
60
|
+
}: LocalAutomationHeartbeatOptions): LocalAutomationHeartbeat {
|
|
61
|
+
let automationHeartbeat: IntervalHandle | null = null;
|
|
62
|
+
|
|
63
|
+
async function executeGuiAutomationHeartbeat(checkAlerts: boolean): Promise<void> {
|
|
64
|
+
const db = initDatabase();
|
|
65
|
+
try {
|
|
66
|
+
await runHeartbeat(db, {
|
|
67
|
+
ownerId: `gui:${getSessionId()}`,
|
|
68
|
+
ownerKind: "writer",
|
|
69
|
+
ttlSeconds: Math.max(90, Math.ceil((intervalMs * 2) / 1000)),
|
|
70
|
+
checkAlerts,
|
|
71
|
+
});
|
|
72
|
+
} catch (error) {
|
|
73
|
+
warn(
|
|
74
|
+
`Local automation heartbeat failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
75
|
+
);
|
|
76
|
+
} finally {
|
|
77
|
+
db.close();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const runGuiAutomationHeartbeat = createAutomationHeartbeatRunner(executeGuiAutomationHeartbeat);
|
|
82
|
+
|
|
83
|
+
function start(): void {
|
|
84
|
+
if (role !== "writer") return;
|
|
85
|
+
void runGuiAutomationHeartbeat(true);
|
|
86
|
+
automationHeartbeat = setIntervalFn(() => void runGuiAutomationHeartbeat(true), intervalMs);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function stop(): void {
|
|
90
|
+
if (automationHeartbeat) {
|
|
91
|
+
clearIntervalFn(automationHeartbeat);
|
|
92
|
+
automationHeartbeat = null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { start, stop };
|
|
97
|
+
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import type { SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
2
1
|
import type { ToolResultMessage } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { SessionEntry, SessionManager } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { getAllTools } from "../../src/tools/index.js";
|
|
4
|
+
import { invokeToolFromUi } from "./invoke-tool.js";
|
|
5
|
+
import { projectDashboard } from "./projector.js";
|
|
6
|
+
|
|
7
|
+
export const BACKGROUND_QUOTE_POLL_INTERVAL_MS = 30_000;
|
|
3
8
|
|
|
4
9
|
export interface BackgroundQuoteRefresh {
|
|
5
10
|
symbol: string;
|
|
@@ -29,3 +34,94 @@ export class BackgroundQuoteRefreshes {
|
|
|
29
34
|
return [...entries, ...this.entriesBySymbol.values()];
|
|
30
35
|
}
|
|
31
36
|
}
|
|
37
|
+
|
|
38
|
+
type IntervalHandle = ReturnType<typeof setInterval>;
|
|
39
|
+
type SetIntervalFn = (callback: () => void, ms: number) => IntervalHandle;
|
|
40
|
+
type ClearIntervalFn = (handle: IntervalHandle) => void;
|
|
41
|
+
|
|
42
|
+
export interface BackgroundQuotePoller {
|
|
43
|
+
updatePoller(): void;
|
|
44
|
+
pollVisibleQuotes(): Promise<void>;
|
|
45
|
+
stop(): void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface BackgroundQuotePollerOptions {
|
|
49
|
+
getClientCount: () => number;
|
|
50
|
+
getSessionManager: () => SessionManager;
|
|
51
|
+
refreshes: BackgroundQuoteRefreshes;
|
|
52
|
+
broadcastState: () => void;
|
|
53
|
+
getTools?: typeof getAllTools;
|
|
54
|
+
invokeTool?: typeof invokeToolFromUi;
|
|
55
|
+
setIntervalFn?: SetIntervalFn;
|
|
56
|
+
clearIntervalFn?: ClearIntervalFn;
|
|
57
|
+
warn?: (message: string) => void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function createBackgroundQuotePoller({
|
|
61
|
+
getClientCount,
|
|
62
|
+
getSessionManager,
|
|
63
|
+
refreshes,
|
|
64
|
+
broadcastState,
|
|
65
|
+
getTools = getAllTools,
|
|
66
|
+
invokeTool = invokeToolFromUi,
|
|
67
|
+
setIntervalFn = setInterval,
|
|
68
|
+
clearIntervalFn = clearInterval,
|
|
69
|
+
warn = (message) => console.warn(message),
|
|
70
|
+
}: BackgroundQuotePollerOptions): BackgroundQuotePoller {
|
|
71
|
+
let poller: IntervalHandle | null = null;
|
|
72
|
+
let quotePollInFlight = false;
|
|
73
|
+
|
|
74
|
+
async function pollVisibleQuotes(): Promise<void> {
|
|
75
|
+
if (quotePollInFlight) return;
|
|
76
|
+
quotePollInFlight = true;
|
|
77
|
+
try {
|
|
78
|
+
const sessionManager = getSessionManager();
|
|
79
|
+
const state = projectDashboard(sessionManager.getEntries(), sessionManager.getSessionId());
|
|
80
|
+
const tool = getTools().find((candidate) => candidate.name === "get_stock_quote");
|
|
81
|
+
if (!tool) return;
|
|
82
|
+
for (const row of state.watchlist.filter((item) => item.pinned || item.quote)) {
|
|
83
|
+
const result = await invokeTool(
|
|
84
|
+
sessionManager,
|
|
85
|
+
tool,
|
|
86
|
+
{ symbol: row.symbol },
|
|
87
|
+
"background",
|
|
88
|
+
{ recordTranscript: false },
|
|
89
|
+
);
|
|
90
|
+
refreshes.upsert({
|
|
91
|
+
symbol: row.symbol,
|
|
92
|
+
toolName: tool.name,
|
|
93
|
+
args: { symbol: row.symbol },
|
|
94
|
+
value: result.result.details,
|
|
95
|
+
content: result.result.content,
|
|
96
|
+
isError: result.isError,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
broadcastState();
|
|
100
|
+
} catch (error) {
|
|
101
|
+
warn(
|
|
102
|
+
`Background quote refresh failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
103
|
+
);
|
|
104
|
+
} finally {
|
|
105
|
+
quotePollInFlight = false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function updatePoller(): void {
|
|
110
|
+
if (getClientCount() > 0 && !poller) {
|
|
111
|
+
poller = setIntervalFn(() => void pollVisibleQuotes(), BACKGROUND_QUOTE_POLL_INTERVAL_MS);
|
|
112
|
+
}
|
|
113
|
+
if (getClientCount() === 0 && poller) {
|
|
114
|
+
clearIntervalFn(poller);
|
|
115
|
+
poller = null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function stop(): void {
|
|
120
|
+
if (poller) {
|
|
121
|
+
clearIntervalFn(poller);
|
|
122
|
+
poller = null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { updatePoller, pollVisibleQuotes, stop };
|
|
127
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
2
1
|
import type { Message, ToolResultMessage } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import type { ChatEvent, MessageContent, ToolOutput } from "../shared/chat-events.js";
|
|
4
4
|
|
|
5
5
|
export interface SessionEventOptions {
|
|
@@ -16,6 +16,9 @@ export function sessionEntriesToChatEvents(
|
|
|
16
16
|
let seq = options.startSeq ?? 1;
|
|
17
17
|
const events: ChatEvent[] = [];
|
|
18
18
|
const seenToolCalls = new Set<string>();
|
|
19
|
+
// Set by an opencandle-user-input marker: the user's words before a workflow
|
|
20
|
+
// transform expanded the turn. The next user message renders this instead.
|
|
21
|
+
let pendingOriginalInput: string | null = null;
|
|
19
22
|
const updatedAt = options.updatedAt ?? entries.at(-1)?.timestamp ?? new Date().toISOString();
|
|
20
23
|
|
|
21
24
|
events.push({
|
|
@@ -27,12 +30,17 @@ export function sessionEntriesToChatEvents(
|
|
|
27
30
|
});
|
|
28
31
|
|
|
29
32
|
for (const entry of entries) {
|
|
33
|
+
if (isOriginalInputEntry(entry)) {
|
|
34
|
+
pendingOriginalInput = originalInputText(entry);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
|
|
30
38
|
if (entry.type === "custom_message") {
|
|
31
39
|
const messageId = entry.id;
|
|
32
|
-
events.push({ type: "message.created", messageId, role: "assistant", seq: seq++ });
|
|
33
40
|
events.push({
|
|
34
|
-
type: "message
|
|
41
|
+
type: "custom.message",
|
|
35
42
|
messageId,
|
|
43
|
+
customType: String((entry as { customType?: unknown }).customType || "custom"),
|
|
36
44
|
content: [{ type: "text", text: customMessageText(entry.content) }],
|
|
37
45
|
seq: seq++,
|
|
38
46
|
});
|
|
@@ -48,9 +56,10 @@ export function sessionEntriesToChatEvents(
|
|
|
48
56
|
events.push({
|
|
49
57
|
type: "message.completed",
|
|
50
58
|
messageId,
|
|
51
|
-
content: [{ type: "text", text: messageText(message.content) }],
|
|
59
|
+
content: [{ type: "text", text: pendingOriginalInput ?? messageText(message.content) }],
|
|
52
60
|
seq: seq++,
|
|
53
61
|
});
|
|
62
|
+
pendingOriginalInput = null;
|
|
54
63
|
continue;
|
|
55
64
|
}
|
|
56
65
|
|
|
@@ -109,6 +118,20 @@ export function sessionEntriesToChatEvents(
|
|
|
109
118
|
return events;
|
|
110
119
|
}
|
|
111
120
|
|
|
121
|
+
export function isOriginalInputEntry(entry: SessionEntry): boolean {
|
|
122
|
+
return (
|
|
123
|
+
entry.type === "custom" &&
|
|
124
|
+
(entry as { customType?: unknown }).customType === "opencandle-user-input"
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function originalInputText(entry: SessionEntry): string | null {
|
|
129
|
+
const data = (entry as { data?: { original?: unknown } }).data;
|
|
130
|
+
return typeof data?.original === "string" && data.original.trim().length > 0
|
|
131
|
+
? data.original
|
|
132
|
+
: null;
|
|
133
|
+
}
|
|
134
|
+
|
|
112
135
|
function customMessageText(content: unknown): string {
|
|
113
136
|
if (typeof content === "string") return content;
|
|
114
137
|
return messageText(content);
|
|
@@ -117,16 +140,15 @@ function customMessageText(content: unknown): string {
|
|
|
117
140
|
function messageText(content: unknown): string {
|
|
118
141
|
if (typeof content === "string") return content;
|
|
119
142
|
if (!Array.isArray(content)) return "";
|
|
120
|
-
return content
|
|
121
|
-
.map((part) => typeof part.text === "string" ? part.text : "")
|
|
122
|
-
.join("");
|
|
143
|
+
return content.map((part) => (typeof part.text === "string" ? part.text : "")).join("");
|
|
123
144
|
}
|
|
124
145
|
|
|
125
146
|
function toolOutput(message: ToolResultMessage): ToolOutput {
|
|
126
147
|
const details = message.details;
|
|
127
|
-
const source =
|
|
128
|
-
|
|
129
|
-
|
|
148
|
+
const source =
|
|
149
|
+
typeof details === "object" && details !== null && "source" in details
|
|
150
|
+
? String((details as { source?: unknown }).source ?? "")
|
|
151
|
+
: undefined;
|
|
130
152
|
return {
|
|
131
153
|
content: message.content,
|
|
132
154
|
details,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface ChatRunSessionConflict {
|
|
2
|
+
error: string;
|
|
3
|
+
code: "session_changed";
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function chatRunSessionConflict(
|
|
7
|
+
expectedSessionId: unknown,
|
|
8
|
+
activeSessionId: string,
|
|
9
|
+
): ChatRunSessionConflict | null {
|
|
10
|
+
const expected = typeof expectedSessionId === "string" ? expectedSessionId.trim() : "";
|
|
11
|
+
if (!expected || expected === activeSessionId) return null;
|
|
12
|
+
return {
|
|
13
|
+
error: "The active session changed before this message was sent.",
|
|
14
|
+
code: "session_changed",
|
|
15
|
+
};
|
|
16
|
+
}
|