@a1hvdy/cc-openclaw 0.26.4 → 0.26.6
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/dist/src/channels/adapter.js +0 -1
- package/dist/src/channels/telegram-mirror/askuser.js +16 -10
- package/dist/src/channels/telegram-mirror/burst-accumulator.js +0 -1
- package/dist/src/channels/telegram-mirror/callback-mapping.js +0 -1
- package/dist/src/channels/telegram-mirror/card-renderer.js +63 -21
- package/dist/src/channels/telegram-mirror/card-state.js +0 -1
- package/dist/src/channels/telegram-mirror/commands.js +0 -1
- package/dist/src/channels/telegram-mirror/compose-buffer.js +0 -1
- package/dist/src/channels/telegram-mirror/cost-views.js +0 -1
- package/dist/src/channels/telegram-mirror/failure/callback-data-overflow.js +0 -1
- package/dist/src/channels/telegram-mirror/failure/gateway-down.js +0 -1
- package/dist/src/channels/telegram-mirror/failure/in-flight-conflict.js +0 -1
- package/dist/src/channels/telegram-mirror/failure/index.js +0 -1
- package/dist/src/channels/telegram-mirror/failure/model-5xx.js +0 -1
- package/dist/src/channels/telegram-mirror/failure/network-blip.js +0 -1
- package/dist/src/channels/telegram-mirror/failure/pool-exhausted-fallback.js +0 -1
- package/dist/src/channels/telegram-mirror/failure/rate-limit.js +0 -1
- package/dist/src/channels/telegram-mirror/failure/returning-after-24h.js +0 -1
- package/dist/src/channels/telegram-mirror/failure/types.js +0 -1
- package/dist/src/channels/telegram-mirror/inbound-handler.js +0 -1
- package/dist/src/channels/telegram-mirror/index.js +0 -1
- package/dist/src/channels/telegram-mirror/plan-attachment.js +0 -1
- package/dist/src/channels/telegram-mirror/quota-reader.js +0 -1
- package/dist/src/channels/telegram-mirror/sessions-keyboard.js +0 -1
- package/dist/src/channels/telegram-mirror/soak-log.js +0 -1
- package/dist/src/channels/telegram-mirror/state-machine.js +21 -2
- package/dist/src/channels/telegram-mirror/status-line.js +0 -1
- package/dist/src/channels/telegram-mirror/sync-commands.js +0 -1
- package/dist/src/channels/telegram-mirror/threshold-watcher.js +0 -1
- package/dist/src/channels/telegram-mirror/turn-bridge.js +21 -2
- package/dist/src/cli/checks/bridge-wiring.js +0 -1
- package/dist/src/cli/checks/config-schema.js +0 -1
- package/dist/src/cli/checks/critical-openclaw-json-keys.js +0 -1
- package/dist/src/cli/checks/install-path.js +0 -1
- package/dist/src/cli/checks/patch-scaffold.js +0 -1
- package/dist/src/cli/doctor.js +0 -1
- package/dist/src/cli/index.js +0 -1
- package/dist/src/cli/migrate.js +0 -1
- package/dist/src/command-router/cc-handler.js +0 -1
- package/dist/src/command-router/index.js +0 -1
- package/dist/src/command-router/launch-policy.js +0 -1
- package/dist/src/command-router/resume-policy.js +0 -1
- package/dist/src/command-router/turn-formatter.js +0 -1
- package/dist/src/config/drift-detector.js +0 -1
- package/dist/src/config/loader.js +0 -1
- package/dist/src/config/schema.js +0 -1
- package/dist/src/constants.js +0 -1
- package/dist/src/council/build-agent-prompt.js +0 -1
- package/dist/src/council/cleanup-worktrees.js +0 -1
- package/dist/src/council/consensus.js +0 -1
- package/dist/src/council/council.js +0 -1
- package/dist/src/council/index.js +0 -1
- package/dist/src/council/system-prompt.js +0 -1
- package/dist/src/council/write-worktree-claude-md.js +0 -1
- package/dist/src/engines/base-oneshot-session.js +0 -1
- package/dist/src/engines/heartbeat-guard.js +0 -1
- package/dist/src/engines/index.js +0 -1
- package/dist/src/engines/persistent-codex-session.js +0 -1
- package/dist/src/engines/persistent-cursor-session.js +0 -1
- package/dist/src/engines/persistent-custom-session.js +0 -1
- package/dist/src/engines/persistent-gemini-session.js +0 -1
- package/dist/src/engines/persistent-session.js +30 -1
- package/dist/src/engines/resolve-bin.js +0 -1
- package/dist/src/engines/subprocess-pool.js +0 -1
- package/dist/src/health/handler.js +0 -1
- package/dist/src/health/index.js +0 -1
- package/dist/src/health/metrics.js +0 -1
- package/dist/src/index.js +0 -1
- package/dist/src/lib/auto-recovery.js +0 -1
- package/dist/src/lib/cache-parity.js +0 -1
- package/dist/src/lib/circuit-breaker.js +0 -1
- package/dist/src/lib/config-service.js +0 -1
- package/dist/src/lib/config.js +0 -1
- package/dist/src/lib/cost-rollup.js +0 -1
- package/dist/src/lib/debounce.js +0 -1
- package/dist/src/lib/debug-tap.js +0 -1
- package/dist/src/lib/domain-error.js +0 -1
- package/dist/src/lib/drift-detector.js +0 -1
- package/dist/src/lib/env-overrides.js +0 -1
- package/dist/src/lib/error-formatter.js +0 -1
- package/dist/src/lib/error-renderer.js +0 -1
- package/dist/src/lib/heartbeat-config.js +0 -1
- package/dist/src/lib/heartbeat-workaround.js +0 -1
- package/dist/src/lib/http-agent.js +0 -1
- package/dist/src/lib/index.js +0 -1
- package/dist/src/lib/json-array.js +0 -1
- package/dist/src/lib/markdown-to-mdv2.js +0 -1
- package/dist/src/lib/markdown-v2.js +0 -1
- package/dist/src/lib/perf/async-compact.js +0 -1
- package/dist/src/lib/perf/direct-sdk.js +0 -1
- package/dist/src/lib/perf/haiku-route.js +0 -1
- package/dist/src/lib/perf/predictive-continuation.js +0 -1
- package/dist/src/lib/perf/read-batch.js +0 -1
- package/dist/src/lib/perf/skill-list-collapse.js +0 -1
- package/dist/src/lib/perf/speculative-bubble.js +0 -1
- package/dist/src/lib/perf/typing-prefetch.js +0 -1
- package/dist/src/lib/register-guard.js +0 -1
- package/dist/src/lib/req-shape-log.js +0 -1
- package/dist/src/lib/safe-upstream-probes.js +0 -1
- package/dist/src/lib/session-registry.js +0 -1
- package/dist/src/lib/spawn-async.js +0 -1
- package/dist/src/lib/stale-pid-files.js +0 -1
- package/dist/src/lib/status-tee-reader.js +0 -1
- package/dist/src/lib/sysprompt-strip.js +0 -1
- package/dist/src/lib/telegram-bot-api.js +0 -1
- package/dist/src/lib/telemetry.js +0 -1
- package/dist/src/lib/test-mode.js +0 -1
- package/dist/src/lib/trajectory.js +0 -1
- package/dist/src/lib/vendor-paths.js +0 -1
- package/dist/src/lifecycle/boot.js +0 -1
- package/dist/src/lifecycle/patch-manifest.js +0 -1
- package/dist/src/lifecycle/phase-import-upstream.js +0 -1
- package/dist/src/lifecycle/phase-install-patches.js +0 -1
- package/dist/src/lifecycle/phase-schedule-jobs.js +0 -1
- package/dist/src/lifecycle/phase-start-server.js +0 -1
- package/dist/src/lifecycle/phase-validate-config.js +0 -1
- package/dist/src/lifecycle/phase-validate-upstream.js +0 -1
- package/dist/src/lifecycle/phase-wire-handlers.js +0 -1
- package/dist/src/lifecycle/safe-restart.js +0 -1
- package/dist/src/logger.js +0 -1
- package/dist/src/mcp/bridge.js +0 -1
- package/dist/src/mcp/index.js +0 -1
- package/dist/src/models.js +0 -1
- package/dist/src/observability/event-bus.js +0 -1
- package/dist/src/observability/get-event-bus.js +0 -1
- package/dist/src/observability/observability-service.js +0 -1
- package/dist/src/observability/perf-telemetry.js +0 -1
- package/dist/src/observability/subscribers/metrics.js +0 -1
- package/dist/src/observability/subscribers/session-capture.js +0 -1
- package/dist/src/openai-compat/bridges/allowlist.js +0 -1
- package/dist/src/openai-compat/bridges/factory.js +0 -1
- package/dist/src/openai-compat/bridges/media-bridge.js +0 -1
- package/dist/src/openai-compat/bridges/openclaw-api-shim.js +0 -1
- package/dist/src/openai-compat/bridges/openclaw-native-tools.js +0 -1
- package/dist/src/openai-compat/bridges/openclaw-tool-registry.js +0 -1
- package/dist/src/openai-compat/bridges/tts-media-bridge.js +0 -1
- package/dist/src/openai-compat/cli-stream-parser.js +0 -1
- package/dist/src/openai-compat/index.js +0 -1
- package/dist/src/openai-compat/message-extractor.js +0 -1
- package/dist/src/openai-compat/mode-flags.js +0 -1
- package/dist/src/openai-compat/non-streaming-handler.js +4 -2
- package/dist/src/openai-compat/openai-chunk-types.js +0 -1
- package/dist/src/openai-compat/openai-compat.js +0 -1
- package/dist/src/openai-compat/openai-types.js +0 -1
- package/dist/src/openai-compat/parse-route-body.js +0 -1
- package/dist/src/openai-compat/prompts.js +0 -1
- package/dist/src/openai-compat/response-formatter.js +0 -1
- package/dist/src/openai-compat/session-key-resolver.js +0 -1
- package/dist/src/openai-compat/skill-resolver.js +0 -1
- package/dist/src/openai-compat/sse-translator.js +0 -1
- package/dist/src/openai-compat/status-reporter.js +0 -1
- package/dist/src/openai-compat/streaming-handler.js +6 -2
- package/dist/src/openai-compat/tool-calls-parser.js +0 -1
- package/dist/src/openai-compat/tool-results-serializer.js +0 -1
- package/dist/src/openai-compat/tts-rule.js +0 -1
- package/dist/src/openai-compat/voice-recovery.js +0 -1
- package/dist/src/patches/cache-parity-registry.js +0 -1
- package/dist/src/patches/claude-md-injection.js +0 -1
- package/dist/src/patches/cwd-redirect.js +0 -1
- package/dist/src/patches/embedded-server-route.js +0 -1
- package/dist/src/patches/pricing-overrides.js +0 -1
- package/dist/src/patches/resume-registry-restore.js +0 -1
- package/dist/src/patches/session-pid-tracking.js +0 -1
- package/dist/src/patches/sysprompt-strip.js +0 -1
- package/dist/src/patches/tools-restoration.js +0 -1
- package/dist/src/persistence/migration-v0.js +0 -1
- package/dist/src/persistence/session-registry.js +0 -1
- package/dist/src/persistence/snapshot.js +0 -1
- package/dist/src/persistence/wal.js +0 -1
- package/dist/src/proxy/anthropic-adapter.js +0 -1
- package/dist/src/proxy/handler.js +0 -1
- package/dist/src/proxy/index.js +0 -1
- package/dist/src/proxy/schema-cleaner.js +0 -1
- package/dist/src/proxy/thought-cache.js +0 -1
- package/dist/src/session/embedded-server.js +0 -1
- package/dist/src/session/inbox-manager.js +0 -1
- package/dist/src/session/index.js +0 -1
- package/dist/src/session/persisted-sessions.js +0 -1
- package/dist/src/session/session-manager.js +0 -1
- package/dist/src/session/watchdogs.js +0 -1
- package/dist/src/session-bootstrap/boot-self-heal.js +0 -1
- package/dist/src/session-bootstrap/cwd-patch.js +0 -1
- package/dist/src/session-bootstrap/index.js +0 -1
- package/dist/src/session-bootstrap/resume-registry.js +0 -1
- package/dist/src/session-bootstrap/session-hygiene.js +0 -1
- package/dist/src/session-bootstrap/sysprompt-strip.js +0 -1
- package/dist/src/session-bootstrap/think-conflict-resolver.js +0 -1
- package/dist/src/types/index.js +0 -1
- package/dist/src/types/route.js +0 -1
- package/dist/src/types/runtime-config.js +0 -1
- package/dist/src/types/session.js +0 -1
- package/dist/src/types/sse.js +0 -1
- package/dist/src/types/tool-bridge.js +0 -1
- package/dist/src/types/upstream.js +0 -1
- package/dist/src/types.js +0 -1
- package/dist/src/validation.js +0 -1
- package/package.json +1 -1
- package/dist/src/channels/adapter.js.map +0 -1
- package/dist/src/channels/telegram/card-renderer.d.ts +0 -80
- package/dist/src/channels/telegram/card-renderer.js +0 -169
- package/dist/src/channels/telegram/card-renderer.js.map +0 -1
- package/dist/src/channels/telegram/completion-summary.d.ts +0 -21
- package/dist/src/channels/telegram/completion-summary.js +0 -218
- package/dist/src/channels/telegram/completion-summary.js.map +0 -1
- package/dist/src/channels/telegram/edit-cadence.d.ts +0 -37
- package/dist/src/channels/telegram/edit-cadence.js +0 -50
- package/dist/src/channels/telegram/edit-cadence.js.map +0 -1
- package/dist/src/channels/telegram/error-renderer.d.ts +0 -29
- package/dist/src/channels/telegram/error-renderer.js +0 -133
- package/dist/src/channels/telegram/error-renderer.js.map +0 -1
- package/dist/src/channels/telegram/event-reducer.d.ts +0 -33
- package/dist/src/channels/telegram/event-reducer.js +0 -647
- package/dist/src/channels/telegram/event-reducer.js.map +0 -1
- package/dist/src/channels/telegram/format-helpers.d.ts +0 -20
- package/dist/src/channels/telegram/format-helpers.js +0 -34
- package/dist/src/channels/telegram/format-helpers.js.map +0 -1
- package/dist/src/channels/telegram/index.d.ts +0 -13
- package/dist/src/channels/telegram/index.js +0 -14
- package/dist/src/channels/telegram/index.js.map +0 -1
- package/dist/src/channels/telegram/injector.d.ts +0 -53
- package/dist/src/channels/telegram/injector.js +0 -271
- package/dist/src/channels/telegram/injector.js.map +0 -1
- package/dist/src/channels/telegram/insight-formatter.d.ts +0 -36
- package/dist/src/channels/telegram/insight-formatter.js +0 -36
- package/dist/src/channels/telegram/insight-formatter.js.map +0 -1
- package/dist/src/channels/telegram/live-card.d.ts +0 -185
- package/dist/src/channels/telegram/live-card.js +0 -868
- package/dist/src/channels/telegram/live-card.js.map +0 -1
- package/dist/src/channels/telegram/logger.d.ts +0 -10
- package/dist/src/channels/telegram/logger.js +0 -13
- package/dist/src/channels/telegram/logger.js.map +0 -1
- package/dist/src/channels/telegram/result-preview.d.ts +0 -12
- package/dist/src/channels/telegram/result-preview.js +0 -48
- package/dist/src/channels/telegram/result-preview.js.map +0 -1
- package/dist/src/channels/telegram/speculative-bubble.d.ts +0 -33
- package/dist/src/channels/telegram/speculative-bubble.js +0 -42
- package/dist/src/channels/telegram/speculative-bubble.js.map +0 -1
- package/dist/src/channels/telegram/state-machine.d.ts +0 -22
- package/dist/src/channels/telegram/state-machine.js +0 -72
- package/dist/src/channels/telegram/state-machine.js.map +0 -1
- package/dist/src/channels/telegram/throttle-controller.d.ts +0 -88
- package/dist/src/channels/telegram/throttle-controller.js +0 -203
- package/dist/src/channels/telegram/throttle-controller.js.map +0 -1
- package/dist/src/channels/telegram/tool-tracker.d.ts +0 -149
- package/dist/src/channels/telegram/tool-tracker.js +0 -578
- package/dist/src/channels/telegram/tool-tracker.js.map +0 -1
- package/dist/src/channels/telegram-mirror/askuser.js.map +0 -1
- package/dist/src/channels/telegram-mirror/burst-accumulator.js.map +0 -1
- package/dist/src/channels/telegram-mirror/callback-mapping.js.map +0 -1
- package/dist/src/channels/telegram-mirror/card-renderer.js.map +0 -1
- package/dist/src/channels/telegram-mirror/card-state.js.map +0 -1
- package/dist/src/channels/telegram-mirror/commands.js.map +0 -1
- package/dist/src/channels/telegram-mirror/compose-buffer.js.map +0 -1
- package/dist/src/channels/telegram-mirror/cost-views.js.map +0 -1
- package/dist/src/channels/telegram-mirror/failure/callback-data-overflow.js.map +0 -1
- package/dist/src/channels/telegram-mirror/failure/gateway-down.js.map +0 -1
- package/dist/src/channels/telegram-mirror/failure/in-flight-conflict.js.map +0 -1
- package/dist/src/channels/telegram-mirror/failure/index.js.map +0 -1
- package/dist/src/channels/telegram-mirror/failure/model-5xx.js.map +0 -1
- package/dist/src/channels/telegram-mirror/failure/network-blip.js.map +0 -1
- package/dist/src/channels/telegram-mirror/failure/pool-exhausted-fallback.js.map +0 -1
- package/dist/src/channels/telegram-mirror/failure/rate-limit.js.map +0 -1
- package/dist/src/channels/telegram-mirror/failure/returning-after-24h.js.map +0 -1
- package/dist/src/channels/telegram-mirror/failure/types.js.map +0 -1
- package/dist/src/channels/telegram-mirror/inbound-handler.js.map +0 -1
- package/dist/src/channels/telegram-mirror/index.js.map +0 -1
- package/dist/src/channels/telegram-mirror/plan-attachment.js.map +0 -1
- package/dist/src/channels/telegram-mirror/quota-reader.js.map +0 -1
- package/dist/src/channels/telegram-mirror/sessions-keyboard.js.map +0 -1
- package/dist/src/channels/telegram-mirror/soak-log.js.map +0 -1
- package/dist/src/channels/telegram-mirror/state-machine.js.map +0 -1
- package/dist/src/channels/telegram-mirror/status-line.js.map +0 -1
- package/dist/src/channels/telegram-mirror/sync-commands.js.map +0 -1
- package/dist/src/channels/telegram-mirror/threshold-watcher.js.map +0 -1
- package/dist/src/channels/telegram-mirror/turn-bridge.js.map +0 -1
- package/dist/src/cli/checks/bridge-wiring.js.map +0 -1
- package/dist/src/cli/checks/config-schema.js.map +0 -1
- package/dist/src/cli/checks/critical-openclaw-json-keys.js.map +0 -1
- package/dist/src/cli/checks/install-path.js.map +0 -1
- package/dist/src/cli/checks/patch-scaffold.js.map +0 -1
- package/dist/src/cli/doctor.js.map +0 -1
- package/dist/src/cli/index.js.map +0 -1
- package/dist/src/cli/migrate.js.map +0 -1
- package/dist/src/command-router/cc-handler.js.map +0 -1
- package/dist/src/command-router/index.js.map +0 -1
- package/dist/src/command-router/launch-policy.js.map +0 -1
- package/dist/src/command-router/resume-policy.js.map +0 -1
- package/dist/src/command-router/turn-formatter.js.map +0 -1
- package/dist/src/config/drift-detector.js.map +0 -1
- package/dist/src/config/loader.js.map +0 -1
- package/dist/src/config/schema.js.map +0 -1
- package/dist/src/constants.js.map +0 -1
- package/dist/src/council/build-agent-prompt.js.map +0 -1
- package/dist/src/council/cleanup-worktrees.js.map +0 -1
- package/dist/src/council/consensus.js.map +0 -1
- package/dist/src/council/council.js.map +0 -1
- package/dist/src/council/index.js.map +0 -1
- package/dist/src/council/system-prompt.js.map +0 -1
- package/dist/src/council/write-worktree-claude-md.js.map +0 -1
- package/dist/src/engines/base-oneshot-session.js.map +0 -1
- package/dist/src/engines/heartbeat-guard.js.map +0 -1
- package/dist/src/engines/index.js.map +0 -1
- package/dist/src/engines/persistent-codex-session.js.map +0 -1
- package/dist/src/engines/persistent-cursor-session.js.map +0 -1
- package/dist/src/engines/persistent-custom-session.js.map +0 -1
- package/dist/src/engines/persistent-gemini-session.js.map +0 -1
- package/dist/src/engines/persistent-session.js.map +0 -1
- package/dist/src/engines/resolve-bin.js.map +0 -1
- package/dist/src/engines/subprocess-pool.js.map +0 -1
- package/dist/src/health/handler.js.map +0 -1
- package/dist/src/health/index.js.map +0 -1
- package/dist/src/health/metrics.js.map +0 -1
- package/dist/src/index.js.map +0 -1
- package/dist/src/lib/auto-recovery.js.map +0 -1
- package/dist/src/lib/cache-parity.js.map +0 -1
- package/dist/src/lib/circuit-breaker.js.map +0 -1
- package/dist/src/lib/config-service.js.map +0 -1
- package/dist/src/lib/config.js.map +0 -1
- package/dist/src/lib/cost-rollup.js.map +0 -1
- package/dist/src/lib/debounce.js.map +0 -1
- package/dist/src/lib/debug-tap.js.map +0 -1
- package/dist/src/lib/domain-error.js.map +0 -1
- package/dist/src/lib/drift-detector.js.map +0 -1
- package/dist/src/lib/env-overrides.js.map +0 -1
- package/dist/src/lib/error-formatter.js.map +0 -1
- package/dist/src/lib/error-renderer.js.map +0 -1
- package/dist/src/lib/heartbeat-config.js.map +0 -1
- package/dist/src/lib/heartbeat-workaround.js.map +0 -1
- package/dist/src/lib/http-agent.js.map +0 -1
- package/dist/src/lib/index.js.map +0 -1
- package/dist/src/lib/json-array.js.map +0 -1
- package/dist/src/lib/markdown-to-mdv2.js.map +0 -1
- package/dist/src/lib/markdown-v2.js.map +0 -1
- package/dist/src/lib/perf/async-compact.js.map +0 -1
- package/dist/src/lib/perf/direct-sdk.js.map +0 -1
- package/dist/src/lib/perf/haiku-route.js.map +0 -1
- package/dist/src/lib/perf/predictive-continuation.js.map +0 -1
- package/dist/src/lib/perf/read-batch.js.map +0 -1
- package/dist/src/lib/perf/skill-list-collapse.js.map +0 -1
- package/dist/src/lib/perf/speculative-bubble.js.map +0 -1
- package/dist/src/lib/perf/typing-prefetch.js.map +0 -1
- package/dist/src/lib/register-guard.js.map +0 -1
- package/dist/src/lib/req-shape-log.js.map +0 -1
- package/dist/src/lib/safe-upstream-probes.js.map +0 -1
- package/dist/src/lib/session-registry.js.map +0 -1
- package/dist/src/lib/spawn-async.js.map +0 -1
- package/dist/src/lib/stale-pid-files.js.map +0 -1
- package/dist/src/lib/status-tee-reader.js.map +0 -1
- package/dist/src/lib/sysprompt-strip.js.map +0 -1
- package/dist/src/lib/telegram-bot-api.js.map +0 -1
- package/dist/src/lib/telemetry.js.map +0 -1
- package/dist/src/lib/test-mode.js.map +0 -1
- package/dist/src/lib/trajectory.js.map +0 -1
- package/dist/src/lib/vendor-paths.js.map +0 -1
- package/dist/src/lifecycle/boot.js.map +0 -1
- package/dist/src/lifecycle/patch-manifest.js.map +0 -1
- package/dist/src/lifecycle/phase-import-upstream.js.map +0 -1
- package/dist/src/lifecycle/phase-install-patches.js.map +0 -1
- package/dist/src/lifecycle/phase-schedule-jobs.js.map +0 -1
- package/dist/src/lifecycle/phase-start-server.js.map +0 -1
- package/dist/src/lifecycle/phase-validate-config.js.map +0 -1
- package/dist/src/lifecycle/phase-validate-upstream.js.map +0 -1
- package/dist/src/lifecycle/phase-wire-handlers.js.map +0 -1
- package/dist/src/lifecycle/safe-restart.js.map +0 -1
- package/dist/src/logger.js.map +0 -1
- package/dist/src/mcp/bridge.js.map +0 -1
- package/dist/src/mcp/index.js.map +0 -1
- package/dist/src/models.js.map +0 -1
- package/dist/src/observability/event-bus.js.map +0 -1
- package/dist/src/observability/get-event-bus.js.map +0 -1
- package/dist/src/observability/observability-service.js.map +0 -1
- package/dist/src/observability/perf-telemetry.js.map +0 -1
- package/dist/src/observability/subscribers/metrics.js.map +0 -1
- package/dist/src/observability/subscribers/session-capture.js.map +0 -1
- package/dist/src/openai-compat/bridges/allowlist.js.map +0 -1
- package/dist/src/openai-compat/bridges/factory.js.map +0 -1
- package/dist/src/openai-compat/bridges/media-bridge.js.map +0 -1
- package/dist/src/openai-compat/bridges/openclaw-api-shim.js.map +0 -1
- package/dist/src/openai-compat/bridges/openclaw-native-tools.js.map +0 -1
- package/dist/src/openai-compat/bridges/openclaw-tool-registry.js.map +0 -1
- package/dist/src/openai-compat/bridges/tts-media-bridge.js.map +0 -1
- package/dist/src/openai-compat/cli-stream-parser.js.map +0 -1
- package/dist/src/openai-compat/index.js.map +0 -1
- package/dist/src/openai-compat/message-extractor.js.map +0 -1
- package/dist/src/openai-compat/mode-flags.js.map +0 -1
- package/dist/src/openai-compat/non-streaming-handler.js.map +0 -1
- package/dist/src/openai-compat/openai-chunk-types.js.map +0 -1
- package/dist/src/openai-compat/openai-compat.js.map +0 -1
- package/dist/src/openai-compat/openai-types.js.map +0 -1
- package/dist/src/openai-compat/parse-route-body.js.map +0 -1
- package/dist/src/openai-compat/prompts.js.map +0 -1
- package/dist/src/openai-compat/response-formatter.js.map +0 -1
- package/dist/src/openai-compat/session-key-resolver.js.map +0 -1
- package/dist/src/openai-compat/skill-resolver.js.map +0 -1
- package/dist/src/openai-compat/sse-translator.js.map +0 -1
- package/dist/src/openai-compat/status-reporter.js.map +0 -1
- package/dist/src/openai-compat/streaming-handler.js.map +0 -1
- package/dist/src/openai-compat/tool-calls-parser.js.map +0 -1
- package/dist/src/openai-compat/tool-results-serializer.js.map +0 -1
- package/dist/src/openai-compat/tts-rule.js.map +0 -1
- package/dist/src/openai-compat/voice-recovery.js.map +0 -1
- package/dist/src/patches/cache-parity-registry.js.map +0 -1
- package/dist/src/patches/claude-md-injection.js.map +0 -1
- package/dist/src/patches/cwd-redirect.js.map +0 -1
- package/dist/src/patches/embedded-server-route.js.map +0 -1
- package/dist/src/patches/pricing-overrides.js.map +0 -1
- package/dist/src/patches/resume-registry-restore.js.map +0 -1
- package/dist/src/patches/session-pid-tracking.js.map +0 -1
- package/dist/src/patches/sysprompt-strip.js.map +0 -1
- package/dist/src/patches/sysprompt-strip.spec.d.ts +0 -33
- package/dist/src/patches/sysprompt-strip.spec.js +0 -53
- package/dist/src/patches/sysprompt-strip.spec.js.map +0 -1
- package/dist/src/patches/tools-restoration.js.map +0 -1
- package/dist/src/persistence/migration-v0.js.map +0 -1
- package/dist/src/persistence/session-registry.js.map +0 -1
- package/dist/src/persistence/snapshot.js.map +0 -1
- package/dist/src/persistence/wal.js.map +0 -1
- package/dist/src/proxy/anthropic-adapter.js.map +0 -1
- package/dist/src/proxy/handler.js.map +0 -1
- package/dist/src/proxy/index.js.map +0 -1
- package/dist/src/proxy/schema-cleaner.js.map +0 -1
- package/dist/src/proxy/thought-cache.js.map +0 -1
- package/dist/src/session/embedded-server.js.map +0 -1
- package/dist/src/session/inbox-manager.js.map +0 -1
- package/dist/src/session/index.js.map +0 -1
- package/dist/src/session/persisted-sessions.js.map +0 -1
- package/dist/src/session/session-manager.js.map +0 -1
- package/dist/src/session/watchdogs.js.map +0 -1
- package/dist/src/session-bootstrap/boot-self-heal.js.map +0 -1
- package/dist/src/session-bootstrap/cwd-patch.js.map +0 -1
- package/dist/src/session-bootstrap/index.js.map +0 -1
- package/dist/src/session-bootstrap/resume-registry.js.map +0 -1
- package/dist/src/session-bootstrap/session-hygiene.js.map +0 -1
- package/dist/src/session-bootstrap/sysprompt-strip.js.map +0 -1
- package/dist/src/session-bootstrap/think-conflict-resolver.js.map +0 -1
- package/dist/src/types/index.js.map +0 -1
- package/dist/src/types/route.js.map +0 -1
- package/dist/src/types/runtime-config.js.map +0 -1
- package/dist/src/types/session.js.map +0 -1
- package/dist/src/types/sse.js.map +0 -1
- package/dist/src/types/tool-bridge.js.map +0 -1
- package/dist/src/types/upstream.js.map +0 -1
- package/dist/src/types.js.map +0 -1
- package/dist/src/validation.js.map +0 -1
|
@@ -1,868 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* telegram live-card — TypeScript port of savvy-claude-code/ux-bridge.js
|
|
3
|
-
*
|
|
4
|
-
* Handles:
|
|
5
|
-
* - Telegram Bot API HTTP calls (sendTg, editTg, telegramApi)
|
|
6
|
-
* - TelegramMessageQueue — serialises per-session card edits
|
|
7
|
-
* - bridgeEvent — maps raw stream events to TranscriptEntry shape
|
|
8
|
-
* - applyPatch — patches SessionManager.prototype.sendMessage to drive
|
|
9
|
-
* TelegramUXPipeline for every Telegram-originating CC session
|
|
10
|
-
*
|
|
11
|
-
* Source lines: 1–930 of ux-bridge.js
|
|
12
|
-
*/
|
|
13
|
-
import { readFileSync, unlinkSync, mkdirSync, appendFileSync } from 'node:fs';
|
|
14
|
-
import { cleanStalePidFiles } from '../../lib/stale-pid-files.js';
|
|
15
|
-
import { readFile as readFileAsync } from 'node:fs/promises';
|
|
16
|
-
import { createRequire } from 'node:module';
|
|
17
|
-
import { telegramApi, editTg, bridgeEvent } from './card-renderer.js';
|
|
18
|
-
import { defaultRegisterGuard } from '../../lib/register-guard.js';
|
|
19
|
-
import { isTestMode } from '../../lib/test-mode.js';
|
|
20
|
-
import { getQuotaPausePct } from '../../lib/config.js';
|
|
21
|
-
import { VENDOR_FILES } from '../../lib/vendor-paths.js';
|
|
22
|
-
import { _logger, setLogger } from './logger.js';
|
|
23
|
-
import { detectSlashCommand } from '../../openai-compat/skill-resolver.js';
|
|
24
|
-
import { TelegramMessageQueue, markDispatchedToTelegram, _sendMessageDedup, _hashPayload, DEDUP_WINDOW_MS, _inputFingerprint, } from './throttle-controller.js';
|
|
25
|
-
const __require = createRequire(import.meta.url);
|
|
26
|
-
// ── Constants ─────────────────────────────────────────────────────────────
|
|
27
|
-
const PLUGIN_TAG = '[cc-openclaw/telegram/live-card]';
|
|
28
|
-
const CC_PLUGIN_PATH = VENDOR_FILES.sessionManager;
|
|
29
|
-
export const UX_MODULE_PATH = '/home/a1xai/.openclaw/workspace/lib/telegram-ux';
|
|
30
|
-
export const VERBOSITY_STORE = '/home/a1xai/.openclaw/workspace/lib/telegram-ux/verbosity-prefs.json';
|
|
31
|
-
const OPENCLAW_CONFIG_PATH = '/home/a1xai/.openclaw/openclaw.json';
|
|
32
|
-
const STATUS_TEE_DIR = '/tmp/openclaw-ux-status';
|
|
33
|
-
const STATUS_POLL_MS = 30000;
|
|
34
|
-
const GPT_POLL_MS = 30000;
|
|
35
|
-
const QUOTA_POLL_MS = 30000;
|
|
36
|
-
// ── Shared mutable state (module-level singletons) ───────────────────────
|
|
37
|
-
export let BOT_TOKEN = '';
|
|
38
|
-
// eslint-disable-next-line prefer-const
|
|
39
|
-
export let minimaxTracker = null;
|
|
40
|
-
export let buttonHandler = null;
|
|
41
|
-
export let nsCommands = null;
|
|
42
|
-
// Active session registry for callback routing: sessionId → { pipeline, queue, name }
|
|
43
|
-
export const activeSessions = new Map();
|
|
44
|
-
// Finished-session grace cache — keeps a session discoverable for button
|
|
45
|
-
// callbacks (Continue / Transcript / Verbose / Model) that arrive AFTER
|
|
46
|
-
// the turn ends. Expires entries after FINISHED_SESSION_TTL_MS.
|
|
47
|
-
export const recentlyFinishedSessions = new Map();
|
|
48
|
-
export const FINISHED_SESSION_TTL_MS = 5 * 60 * 1000;
|
|
49
|
-
// Tier 6: Delegation chain store — tracks Savvy card IDs for CC session linking/cleanup
|
|
50
|
-
export const delegatedCardStore = new Map();
|
|
51
|
-
// F3 — Module-level mirror of the most recent quota fetch.
|
|
52
|
-
export let latestQuotaState = null;
|
|
53
|
-
// F3 — Auto-pause threshold.
|
|
54
|
-
const QUOTA_PAUSE_PCT = getQuotaPausePct();
|
|
55
|
-
// Per-chat cooldown on pause notifications
|
|
56
|
-
const _pauseNotifiedAt = new Map(); // chatId → timestamp
|
|
57
|
-
// TelegramMessageQueue, _sendMessageDedup, _hashPayload, DEDUP_WINDOW_MS,
|
|
58
|
-
// markDispatchedToTelegram, wasDispatchedToTelegram, _inputFingerprint were
|
|
59
|
-
// extracted to throttle-controller.ts in Step 3 of v0.11.0 decomposition.
|
|
60
|
-
// Re-exported below for backward compatibility with sibling modules that
|
|
61
|
-
// import them from live-card.
|
|
62
|
-
export { markDispatchedToTelegram, wasDispatchedToTelegram } from './throttle-controller.js';
|
|
63
|
-
const PAUSE_NOTIFY_COOLDOWN_MS = 60 * 1000;
|
|
64
|
-
// ── M1 helper — wraps options.onThinking so the live card receives a
|
|
65
|
-
// rolling 80-char preview alongside any pre-existing consumer (e.g. the
|
|
66
|
-
// streaming-handler SSE forwarder). Exported for unit tests; not part of
|
|
67
|
-
// the public API surface — callers go through patchedSendMessage.
|
|
68
|
-
export function wrapOnThinking(options, pipeline) {
|
|
69
|
-
const originalOnThinking = options.onThinking;
|
|
70
|
-
options.onThinking = (text) => {
|
|
71
|
-
if (text && typeof pipeline.setThinkingPreview === 'function') {
|
|
72
|
-
try {
|
|
73
|
-
pipeline.setThinkingPreview(text.slice(-80));
|
|
74
|
-
}
|
|
75
|
-
catch { /* ignore */ }
|
|
76
|
-
}
|
|
77
|
-
if (originalOnThinking) {
|
|
78
|
-
try {
|
|
79
|
-
originalOnThinking(text);
|
|
80
|
-
}
|
|
81
|
-
catch { /* ignore */ }
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
// ── v0.22.0 M1 helper — lifts v0.20.2 D2 deviation (settle-only refresh).
|
|
86
|
-
// Wraps options.callbacks.onText so per-delta assistant text reaches the
|
|
87
|
-
// pipeline's appendAssistantTextDelta sink (workspace forwards to
|
|
88
|
-
// savvy-card-state.setAssistantText(text,'streaming') — buffered in
|
|
89
|
-
// _pendingAssistantText until messageStop). Preserves any pre-existing
|
|
90
|
-
// onText consumer (SSE forwarder, streaming-handler) — same discipline as
|
|
91
|
-
// wrapOnThinking. Failure-open: if pipeline.appendAssistantTextDelta is
|
|
92
|
-
// absent (older workspace), this is a silent no-op aside from delegating
|
|
93
|
-
// to the original onText. Exported for unit tests.
|
|
94
|
-
export function wrapOnText(options, pipeline) {
|
|
95
|
-
if (!options.callbacks)
|
|
96
|
-
options.callbacks = {};
|
|
97
|
-
const originalOnText = options.callbacks.onText;
|
|
98
|
-
options.callbacks.onText = (text) => {
|
|
99
|
-
if (text && typeof pipeline.appendAssistantTextDelta === 'function') {
|
|
100
|
-
try {
|
|
101
|
-
pipeline.appendAssistantTextDelta(text);
|
|
102
|
-
}
|
|
103
|
-
catch { /* ignore */ }
|
|
104
|
-
}
|
|
105
|
-
if (originalOnText) {
|
|
106
|
-
try {
|
|
107
|
-
originalOnText(text);
|
|
108
|
-
}
|
|
109
|
-
catch { /* ignore */ }
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
// ── v0.22.0 M5 — dual-bubble layout-mode env gate ────────────────────────
|
|
114
|
-
//
|
|
115
|
-
// CC_OPENCLAW_LIVE_CARD_LAYOUT controls whether bubble #2 (terminal mirror)
|
|
116
|
-
// is emitted alongside the chrome card. Default `chrome_only` preserves
|
|
117
|
-
// exact v0.20.2 behavior with zero regression. Set to `dual_bubble` to
|
|
118
|
-
// enable the terminal mirror — a fenced code-block second message that
|
|
119
|
-
// streams tool-event log lines + rolling assistant text tail.
|
|
120
|
-
//
|
|
121
|
-
// Read once at module load (not per call) so the value is stable across
|
|
122
|
-
// the session — matches the discipline used by RICH_RENDER_ENABLED.
|
|
123
|
-
const DUAL_BUBBLE_LAYOUT_ENABLED = (process.env.CC_OPENCLAW_LIVE_CARD_LAYOUT || 'chrome_only').toLowerCase() === 'dual_bubble';
|
|
124
|
-
/** v0.22.0 M5 — testable getter for the layout-mode resolution.
|
|
125
|
-
* Returns the active layout: 'chrome_only' | 'dual_bubble'. Reads the
|
|
126
|
-
* env at call time (NOT cached) so test harnesses can flip the env and
|
|
127
|
-
* observe the change. The hot-path uses DUAL_BUBBLE_LAYOUT_ENABLED for
|
|
128
|
-
* one-time resolution; this getter is for tests + diagnostics. */
|
|
129
|
-
export function resolveLiveCardLayout() {
|
|
130
|
-
return (process.env.CC_OPENCLAW_LIVE_CARD_LAYOUT || 'chrome_only').toLowerCase() === 'dual_bubble'
|
|
131
|
-
? 'dual_bubble'
|
|
132
|
-
: 'chrome_only';
|
|
133
|
-
}
|
|
134
|
-
let _terminalRenderer = null;
|
|
135
|
-
let _terminalRendererLoaded = false;
|
|
136
|
-
function _loadTerminalRenderer() {
|
|
137
|
-
if (_terminalRendererLoaded)
|
|
138
|
-
return _terminalRenderer;
|
|
139
|
-
_terminalRendererLoaded = true;
|
|
140
|
-
try {
|
|
141
|
-
const mod = __require('/home/a1xai/.openclaw/workspace/lib/telegram-ux/telegram-renderer.js');
|
|
142
|
-
if (typeof mod._renderTerminalBubble === 'function') {
|
|
143
|
-
_terminalRenderer = mod._renderTerminalBubble;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
catch { /* fail open — bubble #2 stays off if workspace lacks renderer */ }
|
|
147
|
-
return _terminalRenderer;
|
|
148
|
-
}
|
|
149
|
-
/** v0.22.0 M5 — emit bubble #2 if the layout flag is set and the renderer
|
|
150
|
-
* is available. Fail-open: env unset / `chrome_only` / renderer missing
|
|
151
|
-
* all silently no-op, preserving zero-regression for the default path.
|
|
152
|
-
* Callers wire this AFTER updateCard within the same tick so the queue's
|
|
153
|
-
* shared _cardChain enforces the bubble-ordering invariant. */
|
|
154
|
-
export function maybeEmitDualBubble(state, queue) {
|
|
155
|
-
if (!DUAL_BUBBLE_LAYOUT_ENABLED)
|
|
156
|
-
return;
|
|
157
|
-
if (!queue || typeof queue.updateTerminalBubble !== 'function')
|
|
158
|
-
return;
|
|
159
|
-
const renderer = _loadTerminalRenderer();
|
|
160
|
-
if (!renderer)
|
|
161
|
-
return;
|
|
162
|
-
try {
|
|
163
|
-
const body = renderer(state, {});
|
|
164
|
-
if (body && body.trim())
|
|
165
|
-
queue.updateTerminalBubble(body);
|
|
166
|
-
}
|
|
167
|
-
catch { /* ignore — bubble #2 must never break bubble #1 */ }
|
|
168
|
-
}
|
|
169
|
-
// ── Telegram Bot API — re-exported from card-renderer.ts for caller compat ─
|
|
170
|
-
export { telegramApi, sendTg, editTg } from './card-renderer.js';
|
|
171
|
-
// ── Per-session Telegram message queue ────────────────────────────────────
|
|
172
|
-
// TelegramMessageQueue moved to throttle-controller.ts in Step 3 of v0.11.0
|
|
173
|
-
// decomposition. Re-exported for callers that import it from live-card.
|
|
174
|
-
export { TelegramMessageQueue } from './throttle-controller.js';
|
|
175
|
-
// ── Event bridge — re-exported from card-renderer.ts ─────────────────────
|
|
176
|
-
export { bridgeEvent } from './card-renderer.js';
|
|
177
|
-
// ── Module-level logger (set at register() time) ──────────────────────────
|
|
178
|
-
// Re-exported from shared logger.ts for backward-compat with siblings that import _logger from here.
|
|
179
|
-
export { _logger, setLogger } from './logger.js';
|
|
180
|
-
// ── UX module + patch state ───────────────────────────────────────────────
|
|
181
|
-
let uxModule = null;
|
|
182
|
-
let patchApplied = false;
|
|
183
|
-
const PATCH_MARKER = Symbol.for('telegram-ux-bridge:patched');
|
|
184
|
-
export function loadUxModule() {
|
|
185
|
-
if (!uxModule) {
|
|
186
|
-
uxModule = __require(UX_MODULE_PATH);
|
|
187
|
-
}
|
|
188
|
-
if (!buttonHandler) {
|
|
189
|
-
try {
|
|
190
|
-
const { ButtonHandler, NS_COMMANDS } = __require(`${UX_MODULE_PATH}/button-handler`);
|
|
191
|
-
buttonHandler = new ButtonHandler();
|
|
192
|
-
nsCommands = NS_COMMANDS || null;
|
|
193
|
-
}
|
|
194
|
-
catch { /* button-handler not available — buttons disabled */ }
|
|
195
|
-
}
|
|
196
|
-
return uxModule;
|
|
197
|
-
}
|
|
198
|
-
// ── input dedup fingerprint ───────────────────────────────────────────────
|
|
199
|
-
// _inputFingerprint moved to throttle-controller.ts. Imported above for use
|
|
200
|
-
// in applyPatch's tool-call dedup.
|
|
201
|
-
// ── applyPatch ────────────────────────────────────────────────────────────
|
|
202
|
-
export async function applyPatch() {
|
|
203
|
-
if (patchApplied)
|
|
204
|
-
return;
|
|
205
|
-
patchApplied = true;
|
|
206
|
-
let SessionManager;
|
|
207
|
-
try {
|
|
208
|
-
const mod = await import(CC_PLUGIN_PATH);
|
|
209
|
-
const Cls = (mod.SessionManager || mod.default?.SessionManager);
|
|
210
|
-
SessionManager = Cls;
|
|
211
|
-
}
|
|
212
|
-
catch (err) {
|
|
213
|
-
_logger.error(`${PLUGIN_TAG} Cannot import SessionManager: ${err.message}`);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
if (!SessionManager) {
|
|
217
|
-
_logger.error(`${PLUGIN_TAG} SessionManager class not found in module`);
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
try {
|
|
221
|
-
loadUxModule();
|
|
222
|
-
}
|
|
223
|
-
catch (err) {
|
|
224
|
-
_logger.error(`${PLUGIN_TAG} Cannot load telegram-ux module: ${err.message}`);
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
const original = SessionManager.prototype.sendMessage;
|
|
228
|
-
if (!original) {
|
|
229
|
-
_logger.error(`${PLUGIN_TAG} sendMessage not found on SessionManager.prototype`);
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
if (original[PATCH_MARKER]) {
|
|
233
|
-
_logger.warn(`${PLUGIN_TAG} sendMessage already patched — skipping to prevent message duplication`);
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
// TODO(P2): `this` context is the SessionManager instance at runtime — complex shape
|
|
237
|
-
SessionManager.prototype.sendMessage = async function patchedSendMessage(name, message, options = {}) {
|
|
238
|
-
const __PERF_KEY = Symbol.for('claude-local:perf-timing');
|
|
239
|
-
const __perfSendStart = performance.now();
|
|
240
|
-
let __perfFirstEventAbsMs = null;
|
|
241
|
-
const __perfStore = globalThis[__PERF_KEY]
|
|
242
|
-
= globalThis[__PERF_KEY] || new Map();
|
|
243
|
-
const __perfBareKey = name.startsWith('openai-') ? name.slice(7) : name;
|
|
244
|
-
{
|
|
245
|
-
const prev = __perfStore.get(name) || __perfStore.get(__perfBareKey) || {};
|
|
246
|
-
__perfStore.set(name, { ...prev, sendMessageStartAbsMs: __perfSendStart });
|
|
247
|
-
}
|
|
248
|
-
let chatId = options.telegramChatId || '';
|
|
249
|
-
let threadId = options.telegramThreadId || undefined;
|
|
250
|
-
if (!chatId) {
|
|
251
|
-
const UX_META = Symbol.for('telegram-ux-bridge:metadata');
|
|
252
|
-
const shared = globalThis[UX_META]?.get(name);
|
|
253
|
-
if (shared) {
|
|
254
|
-
chatId = shared.chatId || '';
|
|
255
|
-
threadId = shared.threadId || undefined;
|
|
256
|
-
globalThis[UX_META].delete(name);
|
|
257
|
-
if (chatId) {
|
|
258
|
-
_logger.info(`${PLUGIN_TAG} Resolved telegram metadata from global store for session "${name}": chat=${chatId} thread=${threadId || 'none'}`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
if (!BOT_TOKEN || !chatId) {
|
|
263
|
-
return original.call(this, name, message, options);
|
|
264
|
-
}
|
|
265
|
-
// ── In-flight dedup ─────────────────────────────────────────────────────
|
|
266
|
-
// Doubled-greeting defensive guard: if patchedSendMessage is invoked twice
|
|
267
|
-
// within DEDUP_WINDOW_MS for the same (chatId, payload), short-circuit the
|
|
268
|
-
// duplicate so it doesn't reach Telegram. Keyed on chatId because the
|
|
269
|
-
// upstream double-call uses two different session names for the same
|
|
270
|
-
// chat-bound output — sessionName is unstable, chatId is invariant.
|
|
271
|
-
// Logs the first duplicate at WARN to surface the upstream double-call.
|
|
272
|
-
{
|
|
273
|
-
const _payloadHash = _hashPayload(message);
|
|
274
|
-
const _existing = _sendMessageDedup.get(chatId);
|
|
275
|
-
const _now = Date.now();
|
|
276
|
-
if (_existing && _existing.hash === _payloadHash && _existing.expiresAt > _now) {
|
|
277
|
-
_logger.warn(`${PLUGIN_TAG} Duplicate patchedSendMessage detected within ${DEDUP_WINDOW_MS}ms for chat=${chatId} session=${name} payloadHash=${_payloadHash} — short-circuiting (upstream double-call evidence)`);
|
|
278
|
-
return; // no queue, no telegramApi call
|
|
279
|
-
}
|
|
280
|
-
_sendMessageDedup.set(chatId, { hash: _payloadHash, expiresAt: _now + DEDUP_WINDOW_MS });
|
|
281
|
-
// Cheap GC: prune entries whose window has elapsed (keeps map small).
|
|
282
|
-
if (_sendMessageDedup.size > 64) {
|
|
283
|
-
for (const [k, v] of _sendMessageDedup) {
|
|
284
|
-
if (v.expiresAt <= _now)
|
|
285
|
-
_sendMessageDedup.delete(k);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
// Mark this (chatId, message) so the state-machine's message_sending
|
|
290
|
-
// hook can cancel the native ACP dispatcher when it also fires for
|
|
291
|
-
// the same payload — that's the doubled-greeting cross-path bug.
|
|
292
|
-
markDispatchedToTelegram(chatId, message);
|
|
293
|
-
// F3 — Quota auto-pause
|
|
294
|
-
if (QUOTA_PAUSE_PCT > 0 && QUOTA_PAUSE_PCT <= 100) {
|
|
295
|
-
const pct7d = latestQuotaState?.sevenDay?.usedPercent;
|
|
296
|
-
if (typeof pct7d === 'number' && pct7d >= QUOTA_PAUSE_PCT) {
|
|
297
|
-
const lastNotif = _pauseNotifiedAt.get(chatId) || 0;
|
|
298
|
-
if (Date.now() - lastNotif > PAUSE_NOTIFY_COOLDOWN_MS) {
|
|
299
|
-
_pauseNotifiedAt.set(chatId, Date.now());
|
|
300
|
-
const note = `⏸ Weekly plan quota at ${pct7d}% — dispatch paused (threshold ${QUOTA_PAUSE_PCT}%). Set OPENCLAW_QUOTA_PAUSE_PCT=100 to disable.`;
|
|
301
|
-
telegramApi('sendMessage', {
|
|
302
|
-
chat_id: chatId,
|
|
303
|
-
text: note,
|
|
304
|
-
...(threadId ? { message_thread_id: Number(threadId) } : {}),
|
|
305
|
-
}).catch(() => { });
|
|
306
|
-
}
|
|
307
|
-
_logger.warn(`${PLUGIN_TAG} Quota pause: 7d=${pct7d}% threshold=${QUOTA_PAUSE_PCT}% name=${name}`);
|
|
308
|
-
const err = new Error(`quota-paused: 7d ${pct7d}% ≥ ${QUOTA_PAUSE_PCT}%`);
|
|
309
|
-
err.code = 'QUOTA_PAUSED';
|
|
310
|
-
throw err;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
const { TelegramUXPipeline } = loadUxModule();
|
|
314
|
-
const ackMessageId = options.telegramAckMessageId || null;
|
|
315
|
-
const queue = new TelegramMessageQueue(chatId, threadId, ackMessageId);
|
|
316
|
-
const delegatedInfo = delegatedCardStore.get(name);
|
|
317
|
-
if (delegatedInfo && !ackMessageId) {
|
|
318
|
-
queue._replyToMessageId = delegatedInfo.cardMessageId;
|
|
319
|
-
}
|
|
320
|
-
let model = 'claude';
|
|
321
|
-
let permissionMode = null;
|
|
322
|
-
try {
|
|
323
|
-
const managed = this._getSession?.(name);
|
|
324
|
-
model = (managed?.config?.model || managed?.session?.options?.model || 'claude');
|
|
325
|
-
permissionMode = managed?.config?.permissionMode || null;
|
|
326
|
-
}
|
|
327
|
-
catch { /* ignore */ }
|
|
328
|
-
const sessionType = name.startsWith('cc-') ? 'cc'
|
|
329
|
-
: name.startsWith('openai-') ? 'savvy'
|
|
330
|
-
: 'delegated';
|
|
331
|
-
let sessionRef = null;
|
|
332
|
-
try {
|
|
333
|
-
sessionRef = this._getSession?.(name)?.session || null;
|
|
334
|
-
}
|
|
335
|
-
catch { /* session may not exist yet */ }
|
|
336
|
-
const pipeline = new TelegramUXPipeline({
|
|
337
|
-
sessionName: name,
|
|
338
|
-
model,
|
|
339
|
-
chatKey: `telegram:${chatId}`,
|
|
340
|
-
verbosityStorePath: VERBOSITY_STORE,
|
|
341
|
-
editIntervalMs: 5000,
|
|
342
|
-
onCardUpdate: (text, state) => {
|
|
343
|
-
const markup = buttonHandler ? buttonHandler.buildButtons(state, name) : undefined;
|
|
344
|
-
queue.updateCard(text, markup);
|
|
345
|
-
// v0.22.0 M5 — emit bubble #2 (terminal mirror) in lockstep when
|
|
346
|
-
// CC_OPENCLAW_LIVE_CARD_LAYOUT=dual_bubble. No-op when env unset
|
|
347
|
-
// / 'chrome_only' / renderer module didn't load — preserves the
|
|
348
|
-
// v0.20.2 zero-regression default-path contract.
|
|
349
|
-
maybeEmitDualBubble(state, queue);
|
|
350
|
-
},
|
|
351
|
-
onNewMessage: (text) => {
|
|
352
|
-
// 2026-05-07: For cc-* sessions, cc-handler delivers the answer directly
|
|
353
|
-
// via sendDirectReply (see cc-handler.ts after sessionManager.sendMessage).
|
|
354
|
-
// Skipping pipeline-driven delivery here prevents the doubled answer the
|
|
355
|
-
// user observed (e.g. "15" appearing twice for /cc 7+8). The pipeline
|
|
356
|
-
// still owns metadata-card updates (timing, quota, tool count) — only the
|
|
357
|
-
// assistant-text delivery is suppressed for cc-* sessions.
|
|
358
|
-
if (sessionType === 'cc')
|
|
359
|
-
return;
|
|
360
|
-
// v0.20.1: escape:true — raw assistant text must be MarkdownV2-escaped
|
|
361
|
-
// before sending or content punctuation gets corrupted on fallback.
|
|
362
|
-
// See throttle-controller.ts sendMessage doc + card-renderer.ts
|
|
363
|
-
// fallback fix for the bug history.
|
|
364
|
-
queue.sendMessage(text, { escape: true });
|
|
365
|
-
},
|
|
366
|
-
livenessCheck: () => {
|
|
367
|
-
const pid = sessionRef?.pid || this._getSession?.(name)?.session?.pid;
|
|
368
|
-
if (!pid)
|
|
369
|
-
return true;
|
|
370
|
-
try {
|
|
371
|
-
process.kill(pid, 0);
|
|
372
|
-
return true;
|
|
373
|
-
}
|
|
374
|
-
catch {
|
|
375
|
-
return false;
|
|
376
|
-
}
|
|
377
|
-
},
|
|
378
|
-
});
|
|
379
|
-
pipeline.start();
|
|
380
|
-
// Stall auto-kill
|
|
381
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
382
|
-
const sessionMgr = this;
|
|
383
|
-
let consecutiveStalls = 0;
|
|
384
|
-
// v0.5.1: bumped 3 → 8. Long reasoning turns (54+ tools, 5+ min) accumulate
|
|
385
|
-
// 3 stalls trivially because the stall detector treats per-tool-execution
|
|
386
|
-
// gaps (60-100s while Bash runs) as model silence. Killing at 3 was killing
|
|
387
|
-
// turns mid-execution. 8 stalls (~800s of cumulative model-silence) gives
|
|
388
|
-
// multi-step plans time to complete; the pipeline's natural recovery on tool
|
|
389
|
-
// output still resets the counter, so most turns won't reach 8.
|
|
390
|
-
const MAX_STALLS_BEFORE_KILL = 8;
|
|
391
|
-
const origHandleStall = pipeline._handleStall.bind(pipeline);
|
|
392
|
-
const origHandleRecovery = pipeline._handleStallRecovery.bind(pipeline);
|
|
393
|
-
pipeline._handleStall = (idleMs) => {
|
|
394
|
-
consecutiveStalls++;
|
|
395
|
-
origHandleStall(idleMs);
|
|
396
|
-
const alive = pipeline._livenessCheck ? pipeline._livenessCheck() : true;
|
|
397
|
-
if (!alive || consecutiveStalls >= MAX_STALLS_BEFORE_KILL) {
|
|
398
|
-
_logger.warn(`${PLUGIN_TAG} Auto-killing stalled session "${name}" (alive=${alive}, stalls=${consecutiveStalls})`);
|
|
399
|
-
queue.sendMessage(`⚠️ Session auto-stopped: ${!alive ? 'process exited' : `${consecutiveStalls} consecutive stalls with no activity`}`);
|
|
400
|
-
try {
|
|
401
|
-
sessionMgr.stopSession(name);
|
|
402
|
-
}
|
|
403
|
-
catch (e) {
|
|
404
|
-
_logger.warn(`${PLUGIN_TAG} Auto-kill stopSession error: ${e.message}`);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
pipeline._handleStallRecovery = () => {
|
|
409
|
-
consecutiveStalls = 0;
|
|
410
|
-
origHandleRecovery();
|
|
411
|
-
};
|
|
412
|
-
activeSessions.set(name, { pipeline, queue, name });
|
|
413
|
-
if (permissionMode)
|
|
414
|
-
pipeline.setPermissionMode(permissionMode);
|
|
415
|
-
pipeline.setSessionType(sessionType);
|
|
416
|
-
// M3 (v0.19.1): surface the original /<slash> on the card pill.
|
|
417
|
-
// Savvy resolved-skill turns get it threaded via options.slashCommand
|
|
418
|
-
// (extractor → handler → patched sendMessage). CC turns + unresolved
|
|
419
|
-
// Savvy slashes fall back to detection on the raw message text.
|
|
420
|
-
try {
|
|
421
|
-
const optSlash = options.slashCommand;
|
|
422
|
-
const fallbackSlash = optSlash ? null
|
|
423
|
-
: (typeof message === 'string' ? detectSlashCommand(message) : null);
|
|
424
|
-
const slash = optSlash || fallbackSlash;
|
|
425
|
-
if (slash && typeof pipeline.setSlashCommand === 'function') {
|
|
426
|
-
pipeline.setSlashCommand(slash);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
catch { /* ignore — pill is cosmetic */ }
|
|
430
|
-
// Status file polling
|
|
431
|
-
let statusPollTimer = null;
|
|
432
|
-
let resolvedStatusPid = null;
|
|
433
|
-
const _pollStatus = async () => {
|
|
434
|
-
try {
|
|
435
|
-
if (!sessionRef) {
|
|
436
|
-
sessionRef = this._getSession?.(name)?.session || null;
|
|
437
|
-
}
|
|
438
|
-
const stats = sessionRef?.getStats?.() || sessionRef?.stats;
|
|
439
|
-
if (stats) {
|
|
440
|
-
if (typeof stats.tokensIn === 'number') {
|
|
441
|
-
pipeline.setTokenUsage(stats.tokensIn, stats.tokensOut || 0);
|
|
442
|
-
}
|
|
443
|
-
if (typeof stats.contextPercent === 'number' && stats.contextPercent > 0) {
|
|
444
|
-
pipeline.setContextPercent(Math.round(stats.contextPercent));
|
|
445
|
-
}
|
|
446
|
-
if (typeof stats.cachedTokens === 'number' && stats.cachedTokens > 0) {
|
|
447
|
-
const totalIn = (stats.tokensIn || 0) + (stats.cachedTokens || 0);
|
|
448
|
-
const pct = totalIn > 0 ? (stats.cachedTokens / totalIn) * 100 : null;
|
|
449
|
-
pipeline.setCachedTokens(stats.cachedTokens, pct);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
const sessionModel = sessionRef?.options?.model;
|
|
453
|
-
if (sessionModel)
|
|
454
|
-
pipeline.setModel(sessionModel);
|
|
455
|
-
if (minimaxTracker) {
|
|
456
|
-
const mmUsage = minimaxTracker.getUsage();
|
|
457
|
-
if (mmUsage.used > 0) {
|
|
458
|
-
pipeline.setMiniMaxUsage(mmUsage);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
catch { /* session not ready yet */ }
|
|
463
|
-
try {
|
|
464
|
-
if (!resolvedStatusPid) {
|
|
465
|
-
resolvedStatusPid = sessionRef?.pid || this._getSession?.(name)?.session?.pid || null;
|
|
466
|
-
}
|
|
467
|
-
if (!resolvedStatusPid) {
|
|
468
|
-
statusPollTimer = setTimeout(_pollStatus, STATUS_POLL_MS);
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
const statusFile = `${STATUS_TEE_DIR}/${resolvedStatusPid}.json`;
|
|
472
|
-
const raw = await readFileAsync(statusFile, 'utf8');
|
|
473
|
-
const data = JSON.parse(raw);
|
|
474
|
-
const ctxPct = data?.context_window?.used_percentage;
|
|
475
|
-
if (typeof ctxPct === 'number')
|
|
476
|
-
pipeline.setContextPercent(Math.round(ctxPct));
|
|
477
|
-
const rateLimits = data?.rate_limits;
|
|
478
|
-
const quotaPct = rateLimits?.five_hour?.used_percentage;
|
|
479
|
-
const quotaReset = rateLimits?.five_hour?.resets_at;
|
|
480
|
-
if (typeof quotaPct === 'number')
|
|
481
|
-
pipeline.setQuotaPercent(Math.round(quotaPct), quotaReset || null);
|
|
482
|
-
const quotaPct7d = rateLimits?.seven_day?.used_percentage;
|
|
483
|
-
const quotaReset7d = rateLimits?.seven_day?.resets_at;
|
|
484
|
-
if (typeof quotaPct7d === 'number')
|
|
485
|
-
pipeline.setQuota7d(Math.round(quotaPct7d), quotaReset7d || null);
|
|
486
|
-
const modelData = data?.model;
|
|
487
|
-
const modelName = modelData?.display_name || modelData?.model_id;
|
|
488
|
-
if (modelName)
|
|
489
|
-
pipeline.setModel(modelName);
|
|
490
|
-
const ctxSize = data?.context_window?.context_window_size;
|
|
491
|
-
if (typeof ctxSize === 'number' && ctxSize > 0)
|
|
492
|
-
pipeline.setContextWindowSize(ctxSize);
|
|
493
|
-
const usage = data?.context_window?.current_usage;
|
|
494
|
-
const inTok = usage?.input_tokens;
|
|
495
|
-
const outTok = usage?.output_tokens;
|
|
496
|
-
if (typeof inTok === 'number' && typeof outTok === 'number')
|
|
497
|
-
pipeline.setTokenUsage(inTok, outTok);
|
|
498
|
-
}
|
|
499
|
-
catch { /* status file doesn't exist or parse error — normal for pipe mode */ }
|
|
500
|
-
statusPollTimer = setTimeout(_pollStatus, STATUS_POLL_MS);
|
|
501
|
-
};
|
|
502
|
-
statusPollTimer = setTimeout(_pollStatus, STATUS_POLL_MS);
|
|
503
|
-
// GPT/Codex usage polling
|
|
504
|
-
let gptPollTimer = null;
|
|
505
|
-
try {
|
|
506
|
-
const _ux = loadUxModule();
|
|
507
|
-
const { getCodexUsage } = (_ux.getCodexUsage
|
|
508
|
-
? _ux
|
|
509
|
-
: __require(`${UX_MODULE_PATH}/codex-usage`));
|
|
510
|
-
const pollGpt = async () => {
|
|
511
|
-
try {
|
|
512
|
-
const usage = await getCodexUsage();
|
|
513
|
-
if (usage)
|
|
514
|
-
pipeline.setGptUsage(usage);
|
|
515
|
-
}
|
|
516
|
-
catch { /* ignore */ }
|
|
517
|
-
};
|
|
518
|
-
const initialUsage = await getCodexUsage().catch(() => null);
|
|
519
|
-
if (initialUsage)
|
|
520
|
-
pipeline.setGptUsage(initialUsage);
|
|
521
|
-
const lastUsedMs = initialUsage?.lastUsed || 0;
|
|
522
|
-
if (Date.now() - lastUsedMs < 24 * 60 * 60 * 1000) {
|
|
523
|
-
gptPollTimer = setInterval(pollGpt, GPT_POLL_MS);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
catch { /* codex-usage module not available — skip */ }
|
|
527
|
-
// Anthropic OAuth quota polling
|
|
528
|
-
let quotaPollTimer = null;
|
|
529
|
-
try {
|
|
530
|
-
const { QuotaFetcher } = __require(`${UX_MODULE_PATH}/quota-fetcher`);
|
|
531
|
-
const quotaFetcher = new QuotaFetcher();
|
|
532
|
-
quotaFetcher.startPolling(QUOTA_POLL_MS, (quota) => {
|
|
533
|
-
latestQuotaState = quota;
|
|
534
|
-
if (quota.fiveHour && quota.fiveHour.usedPercent != null) {
|
|
535
|
-
pipeline.setQuotaPercent(quota.fiveHour.usedPercent, quota.fiveHour.resetsAt);
|
|
536
|
-
}
|
|
537
|
-
if (quota.sevenDay && quota.sevenDay.usedPercent != null) {
|
|
538
|
-
pipeline.setQuota7d(quota.sevenDay.usedPercent, quota.sevenDay.resetsAt);
|
|
539
|
-
}
|
|
540
|
-
});
|
|
541
|
-
quotaPollTimer = quotaFetcher;
|
|
542
|
-
}
|
|
543
|
-
catch { /* quota-fetcher module not available — skip */ }
|
|
544
|
-
let capturedResult = null;
|
|
545
|
-
const resultListener = (event) => { capturedResult = event; };
|
|
546
|
-
try {
|
|
547
|
-
sessionRef = this._getSession?.(name)?.session || null;
|
|
548
|
-
if (sessionRef?.on) {
|
|
549
|
-
sessionRef.on('result', resultListener);
|
|
550
|
-
// M4 (v0.19.1): mid-stream quota refresh. Engine re-emits the
|
|
551
|
-
// CLI's `rate_limit_event` envelope; we forward 5h + 7d to the
|
|
552
|
-
// pipeline immediately so the header `Max N%` updates within
|
|
553
|
-
// the same tick (vs the existing 30s status-tee poll lag).
|
|
554
|
-
sessionRef.on('rate_limit_event', (evt) => {
|
|
555
|
-
try {
|
|
556
|
-
const rli = (evt?.rateLimitInfo || {});
|
|
557
|
-
if (rli.five_hour && typeof rli.five_hour.used_percentage === 'number') {
|
|
558
|
-
pipeline.setQuotaPercent(rli.five_hour.used_percentage, rli.five_hour.resets_at);
|
|
559
|
-
}
|
|
560
|
-
if (rli.seven_day && typeof rli.seven_day.used_percentage === 'number') {
|
|
561
|
-
pipeline.setQuota7d(rli.seven_day.used_percentage, rli.seven_day.resets_at);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
catch { /* ignore — quota refresh is best-effort */ }
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
catch { /* ignore */ }
|
|
569
|
-
// Bridge engine thinking-delta stream into the live card. Wraps any
|
|
570
|
-
// existing options.onThinking (e.g. streaming-handler's SSE forwarder)
|
|
571
|
-
// so both consumers fire — the SSE thinking-surface must not regress.
|
|
572
|
-
wrapOnThinking(options, pipeline);
|
|
573
|
-
// v0.22.0 M1 — bridge engine text_delta stream into the per-delta
|
|
574
|
-
// pipeline sink (workspace appendAssistantTextDelta → savvy-card-state
|
|
575
|
-
// setAssistantText(text,'streaming')). Lifts the v0.20.2 D2 deviation
|
|
576
|
-
// (settle-only refresh) so bubble #2 (terminal mirror, M3) can stream.
|
|
577
|
-
// Preserves any pre-existing options.callbacks.onText consumer.
|
|
578
|
-
wrapOnText(options, pipeline);
|
|
579
|
-
const originalOnEvent = options.onEvent;
|
|
580
|
-
const toolUseInputSize = new Map();
|
|
581
|
-
options.onEvent = (event) => {
|
|
582
|
-
if (event.type === 'tool_use') {
|
|
583
|
-
const id = event.id || event.tool_use_id || '';
|
|
584
|
-
if (id) {
|
|
585
|
-
const curr = _inputFingerprint(event.tool?.input);
|
|
586
|
-
const prev = toolUseInputSize.get(id);
|
|
587
|
-
if (prev && curr.size <= prev.size && curr.sig === prev.sig) {
|
|
588
|
-
if (originalOnEvent) {
|
|
589
|
-
try {
|
|
590
|
-
originalOnEvent(event);
|
|
591
|
-
}
|
|
592
|
-
catch { /* ignore */ }
|
|
593
|
-
}
|
|
594
|
-
return;
|
|
595
|
-
}
|
|
596
|
-
toolUseInputSize.set(id, curr);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
if (__perfFirstEventAbsMs == null) {
|
|
600
|
-
__perfFirstEventAbsMs = performance.now();
|
|
601
|
-
}
|
|
602
|
-
const entry = bridgeEvent(event);
|
|
603
|
-
if (entry) {
|
|
604
|
-
pipeline.processTranscriptEntry(entry);
|
|
605
|
-
}
|
|
606
|
-
if (originalOnEvent) {
|
|
607
|
-
try {
|
|
608
|
-
originalOnEvent(event);
|
|
609
|
-
}
|
|
610
|
-
catch { /* ignore */ }
|
|
611
|
-
}
|
|
612
|
-
};
|
|
613
|
-
try {
|
|
614
|
-
const result = await original.call(this, name, message, options);
|
|
615
|
-
try {
|
|
616
|
-
const stats = sessionRef?.stats || {};
|
|
617
|
-
const resultEntry = {
|
|
618
|
-
kind: 'result',
|
|
619
|
-
ts: new Date().toISOString(),
|
|
620
|
-
// Fallback chain (extended 2026-05-06): handle bare-string results too.
|
|
621
|
-
// session-manager.sendMessage() returns a STRING (the response text), not an
|
|
622
|
-
// object with .output. The original chain (.output → captured) failed when
|
|
623
|
-
// session-manager's post-stream coalesce path returns a plain string — so
|
|
624
|
-
// resultEntry.text was always empty for fast-turn /cc Q&A and the live card
|
|
625
|
-
// showed only metadata with no actual response text.
|
|
626
|
-
text: (typeof result === 'string' && result) ||
|
|
627
|
-
result?.output ||
|
|
628
|
-
result?.text ||
|
|
629
|
-
capturedResult?.result ||
|
|
630
|
-
'',
|
|
631
|
-
inputTokens: capturedResult?.usage?.input_tokens || stats.tokensIn || 0,
|
|
632
|
-
outputTokens: capturedResult?.usage?.output_tokens || stats.tokensOut || 0,
|
|
633
|
-
cachedTokens: capturedResult?.usage?.cache_read_input_tokens || stats.cachedTokens || 0,
|
|
634
|
-
costUsd: stats.costUsd || 0,
|
|
635
|
-
};
|
|
636
|
-
// M2 (v0.19.1): also flow cost through the dedicated setter so
|
|
637
|
-
// SessionCardState (CC) renders $X.XXXX in footer parity with Savvy.
|
|
638
|
-
try {
|
|
639
|
-
pipeline.setCostUsd?.(stats.costUsd || 0);
|
|
640
|
-
}
|
|
641
|
-
catch { /* ignore */ }
|
|
642
|
-
// M5 (v0.19.1): split cache reads vs new entries so the footer
|
|
643
|
-
// shows `12K cached · 3K new` instead of dropping cache_creation.
|
|
644
|
-
try {
|
|
645
|
-
const cacheRead = capturedResult?.usage?.cache_read_input_tokens
|
|
646
|
-
?? stats.cachedTokens
|
|
647
|
-
?? 0;
|
|
648
|
-
const cacheWrite = capturedResult?.usage?.cache_creation_input_tokens ?? 0;
|
|
649
|
-
if (cacheRead > 0 || cacheWrite > 0) {
|
|
650
|
-
pipeline.setCacheBreakdown?.(cacheRead, cacheWrite);
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
catch { /* ignore */ }
|
|
654
|
-
pipeline.finish(resultEntry);
|
|
655
|
-
// Fast-turn fallback: when the pipeline's gate suppresses card posting
|
|
656
|
-
// (no tools, <20s thinking, no files/errors), pipeline.finish() returns
|
|
657
|
-
// silently and the ack message is never edited. For plain Q&A turns
|
|
658
|
-
// (/cc how r u), this would silently drop the reply. If the card was
|
|
659
|
-
// never posted AND no message was sent through the queue AND we have
|
|
660
|
-
// an ack to edit AND a text result, deliver the result directly via
|
|
661
|
-
// queue.sendMessage() — which edits the ack in-place since
|
|
662
|
-
// cardMessageId is set. Closes a real bug surfaced in production
|
|
663
|
-
// 2026-04-27.
|
|
664
|
-
//
|
|
665
|
-
// Bug fix 2026-04-29: also gate on `queue.messageSent` to avoid
|
|
666
|
-
// double-firing when the pipeline's `onNewMessage` callback already
|
|
667
|
-
// delivered the result. Without this gate, fast turns like the
|
|
668
|
-
// /new greeting were dispatched twice (pipeline+fallback) — observed
|
|
669
|
-
// as two `[telegram] sendMessage ok` calls within ~350ms for one
|
|
670
|
-
// model output.
|
|
671
|
-
const _resultText = resultEntry.text || '';
|
|
672
|
-
// 2026-05-07 fix: dropped `_cardNeverPosted` check from the gate.
|
|
673
|
-
// The pipeline edits the card with METADATA (quota %, tool count, timing)
|
|
674
|
-
// even when the assistant text was never delivered to Telegram —
|
|
675
|
-
// _lastCardEditAt > 0 doesn't imply the answer reached the user.
|
|
676
|
-
// The original "doubled-greeting" concern is already prevented by
|
|
677
|
-
// `!queue.messageSent`: pipeline's onNewMessage callback (~line 482)
|
|
678
|
-
// sets queue.messageSent=true when it delivers via the queue.
|
|
679
|
-
// So:
|
|
680
|
-
// - pipeline delivered text → queue.messageSent=true → skip fallback
|
|
681
|
-
// - pipeline only posted metadata → queue.messageSent=false → deliver
|
|
682
|
-
// Diagnosed 2026-05-07 via stderr instrumentation: gate showed
|
|
683
|
-
// `cardNeverPosted=false, queueMessageSent=false, gateOpens=false`
|
|
684
|
-
// while resultText="10" was correctly extracted — proof the gate
|
|
685
|
-
// was the only blocker on /cc text delivery.
|
|
686
|
-
// 2026-05-07: cc-* sessions are delivered by cc-handler.ts directly via
|
|
687
|
-
// sendDirectReply. Fast-turn fallback here would double-fire — skip it
|
|
688
|
-
// for cc-* and let cc-handler be the sole authoritative delivery path.
|
|
689
|
-
// Other session types (openai-gw-*, delegated) keep the original path.
|
|
690
|
-
if (sessionType !== 'cc' && !queue.messageSent && ackMessageId && _resultText) {
|
|
691
|
-
// queue.sendMessage is fire-and-forget (returns void); errors are swallowed by the queue's own _msgChain.
|
|
692
|
-
queue.sendMessage(_resultText);
|
|
693
|
-
_logger.info(`${PLUGIN_TAG} fast-turn fallback: delivered result via queue.sendMessage (queue.messageSent gate, non-cc session) session=${name}`);
|
|
694
|
-
}
|
|
695
|
-
try {
|
|
696
|
-
const state = pipeline._cardState?.getState?.() || {};
|
|
697
|
-
const metricsDir = '/home/a1xai/.openclaw/workspace/memory';
|
|
698
|
-
mkdirSync(metricsDir, { recursive: true });
|
|
699
|
-
const __perfEnd = performance.now();
|
|
700
|
-
const __perfEntry = __perfStore.get(name) || __perfStore.get(__perfBareKey) || {};
|
|
701
|
-
const perfFields = {
|
|
702
|
-
perfRoutePatchMs: __perfEntry.routePatchMs ?? null,
|
|
703
|
-
perfRouteToSendMs: __perfEntry.routePatchEndAbsMs != null
|
|
704
|
-
? Math.round(__perfSendStart - __perfEntry.routePatchEndAbsMs)
|
|
705
|
-
: null,
|
|
706
|
-
perfTtftMs: __perfFirstEventAbsMs != null
|
|
707
|
-
? Math.round(__perfFirstEventAbsMs - __perfSendStart)
|
|
708
|
-
: null,
|
|
709
|
-
perfSendMessageMs: Math.round(__perfEnd - __perfSendStart),
|
|
710
|
-
perfTurnTotalMs: __perfEntry.routePatchEndAbsMs != null
|
|
711
|
-
? Math.round(__perfEnd - (__perfEntry.routePatchEndAbsMs - (__perfEntry.routePatchMs || 0)))
|
|
712
|
-
: null,
|
|
713
|
-
};
|
|
714
|
-
if (perfFields.perfRoutePatchMs != null) {
|
|
715
|
-
perfFields.perfRoutePatchMs = Math.round(perfFields.perfRoutePatchMs);
|
|
716
|
-
}
|
|
717
|
-
__perfStore.delete(name);
|
|
718
|
-
__perfStore.delete(__perfBareKey);
|
|
719
|
-
const metricsLine = JSON.stringify({
|
|
720
|
-
ts: new Date().toISOString(),
|
|
721
|
-
sessionName: name,
|
|
722
|
-
chatId: chatId || null,
|
|
723
|
-
durationMs: state.elapsedMs || 0,
|
|
724
|
-
toolCount: state.toolCount || 0,
|
|
725
|
-
toolsByCategory: state.toolSummary || {},
|
|
726
|
-
errorCount: state.errorCount || 0,
|
|
727
|
-
stallCount: state.stallCount || 0,
|
|
728
|
-
thinkingMs: state.thinkingMs || 0,
|
|
729
|
-
filesModifiedCount: (state.filesModified || []).length,
|
|
730
|
-
subagentCount: (state.subagents || []).length,
|
|
731
|
-
inputTokens: resultEntry.inputTokens,
|
|
732
|
-
outputTokens: resultEntry.outputTokens,
|
|
733
|
-
cachedTokens: resultEntry.cachedTokens,
|
|
734
|
-
status: state.status || 'done',
|
|
735
|
-
...perfFields,
|
|
736
|
-
}) + '\n';
|
|
737
|
-
setImmediate(() => {
|
|
738
|
-
try {
|
|
739
|
-
appendFileSync(`${metricsDir}/session-metrics.jsonl`, metricsLine);
|
|
740
|
-
}
|
|
741
|
-
catch { /* metrics advisory — non-fatal */ }
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
catch { /* metrics are advisory — never block on failure */ }
|
|
745
|
-
}
|
|
746
|
-
catch {
|
|
747
|
-
pipeline.finish(null);
|
|
748
|
-
}
|
|
749
|
-
return result;
|
|
750
|
-
}
|
|
751
|
-
catch (err) {
|
|
752
|
-
pipeline.abort();
|
|
753
|
-
throw err;
|
|
754
|
-
}
|
|
755
|
-
finally {
|
|
756
|
-
if (statusPollTimer)
|
|
757
|
-
clearTimeout(statusPollTimer);
|
|
758
|
-
if (gptPollTimer)
|
|
759
|
-
clearInterval(gptPollTimer);
|
|
760
|
-
if (quotaPollTimer)
|
|
761
|
-
quotaPollTimer.stopPolling();
|
|
762
|
-
if (resolvedStatusPid) {
|
|
763
|
-
try {
|
|
764
|
-
unlinkSync(`${STATUS_TEE_DIR}/${resolvedStatusPid}.json`);
|
|
765
|
-
}
|
|
766
|
-
catch { /* ignore */ }
|
|
767
|
-
}
|
|
768
|
-
if (sessionRef?.removeListener) {
|
|
769
|
-
sessionRef.removeListener('result', resultListener);
|
|
770
|
-
}
|
|
771
|
-
activeSessions.delete(name);
|
|
772
|
-
recentlyFinishedSessions.set(name, { pipeline, queue, name, finishedAt: Date.now() });
|
|
773
|
-
setTimeout(() => {
|
|
774
|
-
const e = recentlyFinishedSessions.get(name);
|
|
775
|
-
if (e && Date.now() - e.finishedAt >= FINISHED_SESSION_TTL_MS) {
|
|
776
|
-
recentlyFinishedSessions.delete(name);
|
|
777
|
-
}
|
|
778
|
-
}, FINISHED_SESSION_TTL_MS + 100);
|
|
779
|
-
const delegatedRef = delegatedCardStore.get(name);
|
|
780
|
-
if (delegatedRef) {
|
|
781
|
-
delegatedCardStore.delete(name);
|
|
782
|
-
const collapsedText = '➡️ *Savvy* — delegated \\(complete\\)';
|
|
783
|
-
editTg(delegatedRef.chatId, delegatedRef.cardMessageId, collapsedText, { inline_keyboard: [] })
|
|
784
|
-
.catch(e => _logger.warn(`${PLUGIN_TAG} delegated card collapse failed: ${e.message}`));
|
|
785
|
-
setTimeout(() => {
|
|
786
|
-
telegramApi('deleteMessage', {
|
|
787
|
-
chat_id: delegatedRef.chatId,
|
|
788
|
-
message_id: delegatedRef.cardMessageId,
|
|
789
|
-
}).catch(() => { });
|
|
790
|
-
}, 5 * 60 * 1000);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
};
|
|
794
|
-
SessionManager.prototype.sendMessage[PATCH_MARKER] = true;
|
|
795
|
-
_logger.info(`${PLUGIN_TAG} SessionManager.sendMessage patched — UX pipeline active`);
|
|
796
|
-
}
|
|
797
|
-
// ── Stale status file cleanup ─────────────────────────────────────────────
|
|
798
|
-
export function cleanStaleStatusFiles() {
|
|
799
|
-
cleanStalePidFiles(STATUS_TEE_DIR, '.json');
|
|
800
|
-
}
|
|
801
|
-
// ── register() ────────────────────────────────────────────────────────────
|
|
802
|
-
/**
|
|
803
|
-
* Register the live-card plugin hooks.
|
|
804
|
-
* Idempotent — subsequent calls are no-ops.
|
|
805
|
-
*/
|
|
806
|
-
export function register(api) {
|
|
807
|
-
setLogger(api.logger || console);
|
|
808
|
-
defaultRegisterGuard.guard('channels/telegram/live-card', api, () => {
|
|
809
|
-
// Read bot token from api.config first, then filesystem fallback
|
|
810
|
-
try {
|
|
811
|
-
const cfg = api.config || {};
|
|
812
|
-
const tgChannels = cfg.channels?.telegram;
|
|
813
|
-
if (tgChannels?.accounts) {
|
|
814
|
-
const acctKey = tgChannels.defaultAccount || 'default';
|
|
815
|
-
const acct = tgChannels.accounts[acctKey] || {};
|
|
816
|
-
BOT_TOKEN = acct.botToken || '';
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
catch { /* fall through to filesystem */ }
|
|
820
|
-
if (!BOT_TOKEN) {
|
|
821
|
-
try {
|
|
822
|
-
const raw = readFileSync(OPENCLAW_CONFIG_PATH, 'utf8');
|
|
823
|
-
const ocConfig = JSON.parse(raw);
|
|
824
|
-
const tgChannels = ocConfig.channels?.telegram;
|
|
825
|
-
if (tgChannels?.accounts) {
|
|
826
|
-
const acctKey = tgChannels.defaultAccount || 'default';
|
|
827
|
-
const acct = tgChannels.accounts[acctKey] || {};
|
|
828
|
-
BOT_TOKEN = acct.botToken || '';
|
|
829
|
-
}
|
|
830
|
-
if (BOT_TOKEN) {
|
|
831
|
-
_logger.info(`${PLUGIN_TAG} Telegram credentials loaded from ${OPENCLAW_CONFIG_PATH}`);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
catch (err) {
|
|
835
|
-
_logger.warn(`${PLUGIN_TAG} Filesystem config fallback failed: ${err.message}`);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
if (!BOT_TOKEN) {
|
|
839
|
-
_logger.warn(`${PLUGIN_TAG} No Telegram bot token found — plugin will be inactive`);
|
|
840
|
-
}
|
|
841
|
-
if (api.registerService) {
|
|
842
|
-
api.registerService({
|
|
843
|
-
id: 'telegram-ux-bridge',
|
|
844
|
-
start: async () => {
|
|
845
|
-
_logger.info(`${PLUGIN_TAG} Initialising via service lifecycle...`);
|
|
846
|
-
await applyPatch();
|
|
847
|
-
},
|
|
848
|
-
stop: () => {
|
|
849
|
-
_logger.info(`${PLUGIN_TAG} Stopped`);
|
|
850
|
-
},
|
|
851
|
-
});
|
|
852
|
-
}
|
|
853
|
-
if (BOT_TOKEN) {
|
|
854
|
-
if (isTestMode()) {
|
|
855
|
-
_logger.info(`${PLUGIN_TAG} TEST_MODE=1 — skipping eager SessionManager.sendMessage patch and stale status file cleanup`);
|
|
856
|
-
}
|
|
857
|
-
else {
|
|
858
|
-
_logger.info(`${PLUGIN_TAG} Eagerly applying SessionManager patch...`);
|
|
859
|
-
cleanStaleStatusFiles();
|
|
860
|
-
applyPatch().catch((err) => {
|
|
861
|
-
_logger.error(`${PLUGIN_TAG} Eager patch failed: ${err.message}`);
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
_logger.info(`${PLUGIN_TAG} live-card hooks registered`);
|
|
866
|
-
}); // end defaultRegisterGuard.guard
|
|
867
|
-
}
|
|
868
|
-
//# sourceMappingURL=live-card.js.map
|