@a1hvdy/cc-openclaw 0.3.2
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 +22 -0
- package/README.md +207 -0
- package/configs/.gitkeep +0 -0
- package/configs/council-reviewer-prompt.md +82 -0
- package/configs/council-system-prompt.md +141 -0
- package/dist/scripts/bench/ab-harness.d.ts +58 -0
- package/dist/scripts/bench/ab-harness.d.ts.map +1 -0
- package/dist/scripts/bench/ab-harness.js +78 -0
- package/dist/scripts/bench/ab-harness.js.map +1 -0
- package/dist/src/channels/adapter.d.ts +103 -0
- package/dist/src/channels/adapter.d.ts.map +1 -0
- package/dist/src/channels/adapter.js +38 -0
- package/dist/src/channels/adapter.js.map +1 -0
- package/dist/src/channels/telegram/completion-summary.d.ts +22 -0
- package/dist/src/channels/telegram/completion-summary.d.ts.map +1 -0
- package/dist/src/channels/telegram/completion-summary.js +186 -0
- package/dist/src/channels/telegram/completion-summary.js.map +1 -0
- package/dist/src/channels/telegram/error-renderer.d.ts +30 -0
- package/dist/src/channels/telegram/error-renderer.d.ts.map +1 -0
- package/dist/src/channels/telegram/error-renderer.js +133 -0
- package/dist/src/channels/telegram/error-renderer.js.map +1 -0
- package/dist/src/channels/telegram/event-reducer.d.ts +34 -0
- package/dist/src/channels/telegram/event-reducer.d.ts.map +1 -0
- package/dist/src/channels/telegram/event-reducer.js +579 -0
- package/dist/src/channels/telegram/event-reducer.js.map +1 -0
- package/dist/src/channels/telegram/index.d.ts +14 -0
- package/dist/src/channels/telegram/index.d.ts.map +1 -0
- package/dist/src/channels/telegram/index.js +14 -0
- package/dist/src/channels/telegram/index.js.map +1 -0
- package/dist/src/channels/telegram/injector.d.ts +54 -0
- package/dist/src/channels/telegram/injector.d.ts.map +1 -0
- package/dist/src/channels/telegram/injector.js +200 -0
- package/dist/src/channels/telegram/injector.js.map +1 -0
- package/dist/src/channels/telegram/live-card.d.ts +230 -0
- package/dist/src/channels/telegram/live-card.d.ts.map +1 -0
- package/dist/src/channels/telegram/live-card.js +916 -0
- package/dist/src/channels/telegram/live-card.js.map +1 -0
- package/dist/src/channels/telegram/state-machine.d.ts +23 -0
- package/dist/src/channels/telegram/state-machine.d.ts.map +1 -0
- package/dist/src/channels/telegram/state-machine.js +72 -0
- package/dist/src/channels/telegram/state-machine.js.map +1 -0
- package/dist/src/channels/telegram/tool-tracker.d.ts +147 -0
- package/dist/src/channels/telegram/tool-tracker.d.ts.map +1 -0
- package/dist/src/channels/telegram/tool-tracker.js +520 -0
- package/dist/src/channels/telegram/tool-tracker.js.map +1 -0
- package/dist/src/circuit-breaker.d.ts +22 -0
- package/dist/src/circuit-breaker.d.ts.map +1 -0
- package/dist/src/circuit-breaker.js +47 -0
- package/dist/src/circuit-breaker.js.map +1 -0
- package/dist/src/command-router/cc-handler.d.ts +67 -0
- package/dist/src/command-router/cc-handler.d.ts.map +1 -0
- package/dist/src/command-router/cc-handler.js +980 -0
- package/dist/src/command-router/cc-handler.js.map +1 -0
- package/dist/src/command-router/index.d.ts +3 -0
- package/dist/src/command-router/index.d.ts.map +1 -0
- package/dist/src/command-router/index.js +2 -0
- package/dist/src/command-router/index.js.map +1 -0
- package/dist/src/constants.d.ts +132 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +140 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/council/consensus.d.ts +21 -0
- package/dist/src/council/consensus.d.ts.map +1 -0
- package/dist/src/council/consensus.js +52 -0
- package/dist/src/council/consensus.js.map +1 -0
- package/dist/src/council/council.d.ts +68 -0
- package/dist/src/council/council.d.ts.map +1 -0
- package/dist/src/council/council.js +914 -0
- package/dist/src/council/council.js.map +1 -0
- package/dist/src/council/index.d.ts +3 -0
- package/dist/src/council/index.d.ts.map +1 -0
- package/dist/src/council/index.js +3 -0
- package/dist/src/council/index.js.map +1 -0
- package/dist/src/engines/base-oneshot-session.d.ts +88 -0
- package/dist/src/engines/base-oneshot-session.d.ts.map +1 -0
- package/dist/src/engines/base-oneshot-session.js +228 -0
- package/dist/src/engines/base-oneshot-session.js.map +1 -0
- package/dist/src/engines/index.d.ts +7 -0
- package/dist/src/engines/index.d.ts.map +1 -0
- package/dist/src/engines/index.js +7 -0
- package/dist/src/engines/index.js.map +1 -0
- package/dist/src/engines/persistent-codex-session.d.ts +17 -0
- package/dist/src/engines/persistent-codex-session.d.ts.map +1 -0
- package/dist/src/engines/persistent-codex-session.js +106 -0
- package/dist/src/engines/persistent-codex-session.js.map +1 -0
- package/dist/src/engines/persistent-cursor-session.d.ts +22 -0
- package/dist/src/engines/persistent-cursor-session.d.ts.map +1 -0
- package/dist/src/engines/persistent-cursor-session.js +242 -0
- package/dist/src/engines/persistent-cursor-session.js.map +1 -0
- package/dist/src/engines/persistent-custom-session.d.ts +79 -0
- package/dist/src/engines/persistent-custom-session.d.ts.map +1 -0
- package/dist/src/engines/persistent-custom-session.js +939 -0
- package/dist/src/engines/persistent-custom-session.js.map +1 -0
- package/dist/src/engines/persistent-gemini-session.d.ts +22 -0
- package/dist/src/engines/persistent-gemini-session.d.ts.map +1 -0
- package/dist/src/engines/persistent-gemini-session.js +217 -0
- package/dist/src/engines/persistent-gemini-session.js.map +1 -0
- package/dist/src/engines/persistent-session.d.ts +77 -0
- package/dist/src/engines/persistent-session.d.ts.map +1 -0
- package/dist/src/engines/persistent-session.js +730 -0
- package/dist/src/engines/persistent-session.js.map +1 -0
- package/dist/src/health/handler.d.ts +40 -0
- package/dist/src/health/handler.d.ts.map +1 -0
- package/dist/src/health/handler.js +70 -0
- package/dist/src/health/handler.js.map +1 -0
- package/dist/src/health/index.d.ts +2 -0
- package/dist/src/health/index.d.ts.map +1 -0
- package/dist/src/health/index.js +2 -0
- package/dist/src/health/index.js.map +1 -0
- package/dist/src/index.d.ts +49 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +84 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib/auto-recovery.d.ts +45 -0
- package/dist/src/lib/auto-recovery.d.ts.map +1 -0
- package/dist/src/lib/auto-recovery.js +217 -0
- package/dist/src/lib/auto-recovery.js.map +1 -0
- package/dist/src/lib/cache-parity.d.ts +39 -0
- package/dist/src/lib/cache-parity.d.ts.map +1 -0
- package/dist/src/lib/cache-parity.js +92 -0
- package/dist/src/lib/cache-parity.js.map +1 -0
- package/dist/src/lib/circuit-breaker.d.ts +22 -0
- package/dist/src/lib/circuit-breaker.d.ts.map +1 -0
- package/dist/src/lib/circuit-breaker.js +47 -0
- package/dist/src/lib/circuit-breaker.js.map +1 -0
- package/dist/src/lib/config.d.ts +74 -0
- package/dist/src/lib/config.d.ts.map +1 -0
- package/dist/src/lib/config.js +244 -0
- package/dist/src/lib/config.js.map +1 -0
- package/dist/src/lib/drift-detector.d.ts +47 -0
- package/dist/src/lib/drift-detector.d.ts.map +1 -0
- package/dist/src/lib/drift-detector.js +192 -0
- package/dist/src/lib/drift-detector.js.map +1 -0
- package/dist/src/lib/error-formatter.d.ts +78 -0
- package/dist/src/lib/error-formatter.d.ts.map +1 -0
- package/dist/src/lib/error-formatter.js +149 -0
- package/dist/src/lib/error-formatter.js.map +1 -0
- package/dist/src/lib/heartbeat-workaround.d.ts +45 -0
- package/dist/src/lib/heartbeat-workaround.d.ts.map +1 -0
- package/dist/src/lib/heartbeat-workaround.js +61 -0
- package/dist/src/lib/heartbeat-workaround.js.map +1 -0
- package/dist/src/lib/index.d.ts +8 -0
- package/dist/src/lib/index.d.ts.map +1 -0
- package/dist/src/lib/index.js +8 -0
- package/dist/src/lib/index.js.map +1 -0
- package/dist/src/lib/register-guard.d.ts +49 -0
- package/dist/src/lib/register-guard.d.ts.map +1 -0
- package/dist/src/lib/register-guard.js +73 -0
- package/dist/src/lib/register-guard.js.map +1 -0
- package/dist/src/lib/route-flag.d.ts +50 -0
- package/dist/src/lib/route-flag.d.ts.map +1 -0
- package/dist/src/lib/route-flag.js +52 -0
- package/dist/src/lib/route-flag.js.map +1 -0
- package/dist/src/lib/sysprompt-strip.d.ts +54 -0
- package/dist/src/lib/sysprompt-strip.d.ts.map +1 -0
- package/dist/src/lib/sysprompt-strip.js +75 -0
- package/dist/src/lib/sysprompt-strip.js.map +1 -0
- package/dist/src/lib/telemetry.d.ts +39 -0
- package/dist/src/lib/telemetry.d.ts.map +1 -0
- package/dist/src/lib/telemetry.js +73 -0
- package/dist/src/lib/telemetry.js.map +1 -0
- package/dist/src/lib/test-mode.d.ts +27 -0
- package/dist/src/lib/test-mode.d.ts.map +1 -0
- package/dist/src/lib/test-mode.js +38 -0
- package/dist/src/lib/test-mode.js.map +1 -0
- package/dist/src/lib/vendor-paths.d.ts +15 -0
- package/dist/src/lib/vendor-paths.d.ts.map +1 -0
- package/dist/src/lib/vendor-paths.js +32 -0
- package/dist/src/lib/vendor-paths.js.map +1 -0
- package/dist/src/logger.d.ts +17 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +46 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/mcp/bridge.d.ts +22 -0
- package/dist/src/mcp/bridge.d.ts.map +1 -0
- package/dist/src/mcp/bridge.js +78 -0
- package/dist/src/mcp/bridge.js.map +1 -0
- package/dist/src/mcp/index.d.ts +3 -0
- package/dist/src/mcp/index.d.ts.map +1 -0
- package/dist/src/mcp/index.js +2 -0
- package/dist/src/mcp/index.js.map +1 -0
- package/dist/src/models.d.ts +70 -0
- package/dist/src/models.d.ts.map +1 -0
- package/dist/src/models.js +289 -0
- package/dist/src/models.js.map +1 -0
- package/dist/src/openai-compat/cli-stream-parser.d.ts +135 -0
- package/dist/src/openai-compat/cli-stream-parser.d.ts.map +1 -0
- package/dist/src/openai-compat/cli-stream-parser.js +195 -0
- package/dist/src/openai-compat/cli-stream-parser.js.map +1 -0
- package/dist/src/openai-compat/index.d.ts +2 -0
- package/dist/src/openai-compat/index.d.ts.map +1 -0
- package/dist/src/openai-compat/index.js +2 -0
- package/dist/src/openai-compat/index.js.map +1 -0
- package/dist/src/openai-compat/openai-compat.d.ts +281 -0
- package/dist/src/openai-compat/openai-compat.d.ts.map +1 -0
- package/dist/src/openai-compat/openai-compat.js +939 -0
- package/dist/src/openai-compat/openai-compat.js.map +1 -0
- package/dist/src/openai-compat/skill-resolver.d.ts +36 -0
- package/dist/src/openai-compat/skill-resolver.d.ts.map +1 -0
- package/dist/src/openai-compat/skill-resolver.js +134 -0
- package/dist/src/openai-compat/skill-resolver.js.map +1 -0
- package/dist/src/openai-compat/sse-translator.d.ts +32 -0
- package/dist/src/openai-compat/sse-translator.d.ts.map +1 -0
- package/dist/src/openai-compat/sse-translator.js +155 -0
- package/dist/src/openai-compat/sse-translator.js.map +1 -0
- package/dist/src/proxy/anthropic-adapter.d.ts +137 -0
- package/dist/src/proxy/anthropic-adapter.d.ts.map +1 -0
- package/dist/src/proxy/anthropic-adapter.js +392 -0
- package/dist/src/proxy/anthropic-adapter.js.map +1 -0
- package/dist/src/proxy/handler.d.ts +40 -0
- package/dist/src/proxy/handler.d.ts.map +1 -0
- package/dist/src/proxy/handler.js +378 -0
- package/dist/src/proxy/handler.js.map +1 -0
- package/dist/src/proxy/index.d.ts +5 -0
- package/dist/src/proxy/index.d.ts.map +1 -0
- package/dist/src/proxy/index.js +5 -0
- package/dist/src/proxy/index.js.map +1 -0
- package/dist/src/proxy/schema-cleaner.d.ts +12 -0
- package/dist/src/proxy/schema-cleaner.d.ts.map +1 -0
- package/dist/src/proxy/schema-cleaner.js +34 -0
- package/dist/src/proxy/schema-cleaner.js.map +1 -0
- package/dist/src/proxy/thought-cache.d.ts +20 -0
- package/dist/src/proxy/thought-cache.d.ts.map +1 -0
- package/dist/src/proxy/thought-cache.js +53 -0
- package/dist/src/proxy/thought-cache.js.map +1 -0
- package/dist/src/session/embedded-server.d.ts +26 -0
- package/dist/src/session/embedded-server.d.ts.map +1 -0
- package/dist/src/session/embedded-server.js +367 -0
- package/dist/src/session/embedded-server.js.map +1 -0
- package/dist/src/session/inbox-manager.d.ts +39 -0
- package/dist/src/session/inbox-manager.d.ts.map +1 -0
- package/dist/src/session/inbox-manager.js +111 -0
- package/dist/src/session/inbox-manager.js.map +1 -0
- package/dist/src/session/index.d.ts +4 -0
- package/dist/src/session/index.d.ts.map +1 -0
- package/dist/src/session/index.js +4 -0
- package/dist/src/session/index.js.map +1 -0
- package/dist/src/session/session-manager.d.ts +212 -0
- package/dist/src/session/session-manager.d.ts.map +1 -0
- package/dist/src/session/session-manager.js +1351 -0
- package/dist/src/session/session-manager.js.map +1 -0
- package/dist/src/session-bootstrap/cwd-patch.d.ts +51 -0
- package/dist/src/session-bootstrap/cwd-patch.d.ts.map +1 -0
- package/dist/src/session-bootstrap/cwd-patch.js +955 -0
- package/dist/src/session-bootstrap/cwd-patch.js.map +1 -0
- package/dist/src/session-bootstrap/index.d.ts +4 -0
- package/dist/src/session-bootstrap/index.d.ts.map +1 -0
- package/dist/src/session-bootstrap/index.js +4 -0
- package/dist/src/session-bootstrap/index.js.map +1 -0
- package/dist/src/session-bootstrap/sysprompt-strip.d.ts +26 -0
- package/dist/src/session-bootstrap/sysprompt-strip.d.ts.map +1 -0
- package/dist/src/session-bootstrap/sysprompt-strip.js +57 -0
- package/dist/src/session-bootstrap/sysprompt-strip.js.map +1 -0
- package/dist/src/session-bootstrap/think-conflict-resolver.d.ts +33 -0
- package/dist/src/session-bootstrap/think-conflict-resolver.d.ts.map +1 -0
- package/dist/src/session-bootstrap/think-conflict-resolver.js +234 -0
- package/dist/src/session-bootstrap/think-conflict-resolver.js.map +1 -0
- package/dist/src/types.d.ts +489 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +8 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/validation.d.ts +32 -0
- package/dist/src/validation.d.ts.map +1 -0
- package/dist/src/validation.js +104 -0
- package/dist/src/validation.js.map +1 -0
- package/dist/tests/_helpers/subprocess-mock.d.ts +35 -0
- package/dist/tests/_helpers/subprocess-mock.d.ts.map +1 -0
- package/dist/tests/_helpers/subprocess-mock.js +136 -0
- package/dist/tests/_helpers/subprocess-mock.js.map +1 -0
- package/dist/tests/auto-recovery.test.d.ts +2 -0
- package/dist/tests/auto-recovery.test.d.ts.map +1 -0
- package/dist/tests/auto-recovery.test.js +189 -0
- package/dist/tests/auto-recovery.test.js.map +1 -0
- package/dist/tests/bench-harness.test.d.ts +2 -0
- package/dist/tests/bench-harness.test.d.ts.map +1 -0
- package/dist/tests/bench-harness.test.js +21 -0
- package/dist/tests/bench-harness.test.js.map +1 -0
- package/dist/tests/cache-parity.test.d.ts +2 -0
- package/dist/tests/cache-parity.test.d.ts.map +1 -0
- package/dist/tests/cache-parity.test.js +401 -0
- package/dist/tests/cache-parity.test.js.map +1 -0
- package/dist/tests/command-router.test.d.ts +2 -0
- package/dist/tests/command-router.test.d.ts.map +1 -0
- package/dist/tests/command-router.test.js +60 -0
- package/dist/tests/command-router.test.js.map +1 -0
- package/dist/tests/council.test.d.ts +2 -0
- package/dist/tests/council.test.d.ts.map +1 -0
- package/dist/tests/council.test.js +20 -0
- package/dist/tests/council.test.js.map +1 -0
- package/dist/tests/drift-detector.test.d.ts +2 -0
- package/dist/tests/drift-detector.test.d.ts.map +1 -0
- package/dist/tests/drift-detector.test.js +268 -0
- package/dist/tests/drift-detector.test.js.map +1 -0
- package/dist/tests/eager-bootstrap-gating.test.d.ts +9 -0
- package/dist/tests/eager-bootstrap-gating.test.d.ts.map +1 -0
- package/dist/tests/eager-bootstrap-gating.test.js +97 -0
- package/dist/tests/eager-bootstrap-gating.test.js.map +1 -0
- package/dist/tests/engines.test.d.ts +2 -0
- package/dist/tests/engines.test.d.ts.map +1 -0
- package/dist/tests/engines.test.js +8 -0
- package/dist/tests/engines.test.js.map +1 -0
- package/dist/tests/error-formatter.test.d.ts +2 -0
- package/dist/tests/error-formatter.test.d.ts.map +1 -0
- package/dist/tests/error-formatter.test.js +220 -0
- package/dist/tests/error-formatter.test.js.map +1 -0
- package/dist/tests/health.test.d.ts +2 -0
- package/dist/tests/health.test.d.ts.map +1 -0
- package/dist/tests/health.test.js +110 -0
- package/dist/tests/health.test.js.map +1 -0
- package/dist/tests/heartbeat-workaround.test.d.ts +2 -0
- package/dist/tests/heartbeat-workaround.test.d.ts.map +1 -0
- package/dist/tests/heartbeat-workaround.test.js +90 -0
- package/dist/tests/heartbeat-workaround.test.js.map +1 -0
- package/dist/tests/index.test.d.ts +2 -0
- package/dist/tests/index.test.d.ts.map +1 -0
- package/dist/tests/index.test.js +7 -0
- package/dist/tests/index.test.js.map +1 -0
- package/dist/tests/lib-sysprompt-strip.test.d.ts +2 -0
- package/dist/tests/lib-sysprompt-strip.test.d.ts.map +1 -0
- package/dist/tests/lib-sysprompt-strip.test.js +145 -0
- package/dist/tests/lib-sysprompt-strip.test.js.map +1 -0
- package/dist/tests/listener-activation.test.d.ts +2 -0
- package/dist/tests/listener-activation.test.d.ts.map +1 -0
- package/dist/tests/listener-activation.test.js +87 -0
- package/dist/tests/listener-activation.test.js.map +1 -0
- package/dist/tests/mcp-bridge.test.d.ts +2 -0
- package/dist/tests/mcp-bridge.test.d.ts.map +1 -0
- package/dist/tests/mcp-bridge.test.js +137 -0
- package/dist/tests/mcp-bridge.test.js.map +1 -0
- package/dist/tests/openai-compat.test.d.ts +2 -0
- package/dist/tests/openai-compat.test.d.ts.map +1 -0
- package/dist/tests/openai-compat.test.js +8 -0
- package/dist/tests/openai-compat.test.js.map +1 -0
- package/dist/tests/proxy-heartbeat-integration.test.d.ts +15 -0
- package/dist/tests/proxy-heartbeat-integration.test.d.ts.map +1 -0
- package/dist/tests/proxy-heartbeat-integration.test.js +122 -0
- package/dist/tests/proxy-heartbeat-integration.test.js.map +1 -0
- package/dist/tests/proxy.test.d.ts +2 -0
- package/dist/tests/proxy.test.d.ts.map +1 -0
- package/dist/tests/proxy.test.js +8 -0
- package/dist/tests/proxy.test.js.map +1 -0
- package/dist/tests/register-guard-stacking.test.d.ts +2 -0
- package/dist/tests/register-guard-stacking.test.d.ts.map +1 -0
- package/dist/tests/register-guard-stacking.test.js +61 -0
- package/dist/tests/register-guard-stacking.test.js.map +1 -0
- package/dist/tests/register-guard.test.d.ts +2 -0
- package/dist/tests/register-guard.test.d.ts.map +1 -0
- package/dist/tests/register-guard.test.js +129 -0
- package/dist/tests/register-guard.test.js.map +1 -0
- package/dist/tests/route-flag-rollback.test.d.ts +2 -0
- package/dist/tests/route-flag-rollback.test.d.ts.map +1 -0
- package/dist/tests/route-flag-rollback.test.js +70 -0
- package/dist/tests/route-flag-rollback.test.js.map +1 -0
- package/dist/tests/route-flag.test.d.ts +2 -0
- package/dist/tests/route-flag.test.d.ts.map +1 -0
- package/dist/tests/route-flag.test.js +101 -0
- package/dist/tests/route-flag.test.js.map +1 -0
- package/dist/tests/session-bootstrap.test.d.ts +2 -0
- package/dist/tests/session-bootstrap.test.d.ts.map +1 -0
- package/dist/tests/session-bootstrap.test.js +183 -0
- package/dist/tests/session-bootstrap.test.js.map +1 -0
- package/dist/tests/session.test.d.ts +2 -0
- package/dist/tests/session.test.d.ts.map +1 -0
- package/dist/tests/session.test.js +17 -0
- package/dist/tests/session.test.js.map +1 -0
- package/dist/tests/state-machine.test.d.ts +2 -0
- package/dist/tests/state-machine.test.d.ts.map +1 -0
- package/dist/tests/state-machine.test.js +133 -0
- package/dist/tests/state-machine.test.js.map +1 -0
- package/dist/tests/streaming/cli-stream-parser.test.d.ts +2 -0
- package/dist/tests/streaming/cli-stream-parser.test.d.ts.map +1 -0
- package/dist/tests/streaming/cli-stream-parser.test.js +233 -0
- package/dist/tests/streaming/cli-stream-parser.test.js.map +1 -0
- package/dist/tests/streaming/feature-flag.test.d.ts +14 -0
- package/dist/tests/streaming/feature-flag.test.d.ts.map +1 -0
- package/dist/tests/streaming/feature-flag.test.js +163 -0
- package/dist/tests/streaming/feature-flag.test.js.map +1 -0
- package/dist/tests/streaming/no-tools-prompt.test.d.ts +17 -0
- package/dist/tests/streaming/no-tools-prompt.test.d.ts.map +1 -0
- package/dist/tests/streaming/no-tools-prompt.test.js +229 -0
- package/dist/tests/streaming/no-tools-prompt.test.js.map +1 -0
- package/dist/tests/streaming/skill-plus-tools.test.d.ts +14 -0
- package/dist/tests/streaming/skill-plus-tools.test.d.ts.map +1 -0
- package/dist/tests/streaming/skill-plus-tools.test.js +234 -0
- package/dist/tests/streaming/skill-plus-tools.test.js.map +1 -0
- package/dist/tests/streaming/sse-translator.test.d.ts +2 -0
- package/dist/tests/streaming/sse-translator.test.d.ts.map +1 -0
- package/dist/tests/streaming/sse-translator.test.js +227 -0
- package/dist/tests/streaming/sse-translator.test.js.map +1 -0
- package/dist/tests/streaming/tool-result-roundtrip.test.d.ts +11 -0
- package/dist/tests/streaming/tool-result-roundtrip.test.d.ts.map +1 -0
- package/dist/tests/streaming/tool-result-roundtrip.test.js +215 -0
- package/dist/tests/streaming/tool-result-roundtrip.test.js.map +1 -0
- package/dist/tests/streaming/tool-use-translation.test.d.ts +10 -0
- package/dist/tests/streaming/tool-use-translation.test.d.ts.map +1 -0
- package/dist/tests/streaming/tool-use-translation.test.js +251 -0
- package/dist/tests/streaming/tool-use-translation.test.js.map +1 -0
- package/dist/tests/telegram-bridge.test.d.ts +2 -0
- package/dist/tests/telegram-bridge.test.d.ts.map +1 -0
- package/dist/tests/telegram-bridge.test.js +17 -0
- package/dist/tests/telegram-bridge.test.js.map +1 -0
- package/dist/tests/telegram-injector.test.d.ts +2 -0
- package/dist/tests/telegram-injector.test.d.ts.map +1 -0
- package/dist/tests/telegram-injector.test.js +74 -0
- package/dist/tests/telegram-injector.test.js.map +1 -0
- package/dist/tests/telemetry.test.d.ts +2 -0
- package/dist/tests/telemetry.test.d.ts.map +1 -0
- package/dist/tests/telemetry.test.js +405 -0
- package/dist/tests/telemetry.test.js.map +1 -0
- package/dist/tests/test-mode.test.d.ts +2 -0
- package/dist/tests/test-mode.test.d.ts.map +1 -0
- package/dist/tests/test-mode.test.js +39 -0
- package/dist/tests/test-mode.test.js.map +1 -0
- package/mcp-config.template.json +13 -0
- package/mcp-tools.json +1 -0
- package/openclaw-mcp-bridge.cjs +152 -0
- package/openclaw.plugin.json +30 -0
- package/package.json +45 -0
- package/skills/.gitkeep +0 -0
- package/stubs/commands-status-deps.runtime.js +10 -0
- package/stubs/status.runtime.js +149 -0
- package/vendor/base-oneshot-session.d.ts +87 -0
- package/vendor/base-oneshot-session.js +227 -0
- package/vendor/base-oneshot-session.js.map +1 -0
- package/vendor/circuit-breaker.d.ts +21 -0
- package/vendor/circuit-breaker.js +47 -0
- package/vendor/circuit-breaker.js.map +1 -0
- package/vendor/consensus.d.ts +20 -0
- package/vendor/consensus.js +52 -0
- package/vendor/consensus.js.map +1 -0
- package/vendor/constants.d.ts +130 -0
- package/vendor/constants.js +139 -0
- package/vendor/constants.js.map +1 -0
- package/vendor/council.d.ts +67 -0
- package/vendor/council.js +913 -0
- package/vendor/council.js.map +1 -0
- package/vendor/embedded-server.d.ts +25 -0
- package/vendor/embedded-server.js +360 -0
- package/vendor/embedded-server.js.map +1 -0
- package/vendor/inbox-manager.d.ts +38 -0
- package/vendor/inbox-manager.js +111 -0
- package/vendor/inbox-manager.js.map +1 -0
- package/vendor/index.d.ts +63 -0
- package/vendor/index.js +705 -0
- package/vendor/index.js.map +1 -0
- package/vendor/logger.d.ts +16 -0
- package/vendor/logger.js +44 -0
- package/vendor/logger.js.map +1 -0
- package/vendor/models.d.ts +69 -0
- package/vendor/models.js +289 -0
- package/vendor/models.js.map +1 -0
- package/vendor/openai-compat.d.ts +197 -0
- package/vendor/openai-compat.js +721 -0
- package/vendor/openai-compat.js.map +1 -0
- package/vendor/persistent-codex-session.d.ts +16 -0
- package/vendor/persistent-codex-session.js +105 -0
- package/vendor/persistent-codex-session.js.map +1 -0
- package/vendor/persistent-cursor-session.d.ts +21 -0
- package/vendor/persistent-cursor-session.js +241 -0
- package/vendor/persistent-cursor-session.js.map +1 -0
- package/vendor/persistent-custom-session.d.ts +78 -0
- package/vendor/persistent-custom-session.js +937 -0
- package/vendor/persistent-custom-session.js.map +1 -0
- package/vendor/persistent-gemini-session.d.ts +21 -0
- package/vendor/persistent-gemini-session.js +216 -0
- package/vendor/persistent-gemini-session.js.map +1 -0
- package/vendor/persistent-session.d.ts +74 -0
- package/vendor/persistent-session.js +684 -0
- package/vendor/persistent-session.js.map +1 -0
- package/vendor/proxy/anthropic-adapter.d.ts +136 -0
- package/vendor/proxy/anthropic-adapter.js +392 -0
- package/vendor/proxy/anthropic-adapter.js.map +1 -0
- package/vendor/proxy/handler.d.ts +39 -0
- package/vendor/proxy/handler.js +323 -0
- package/vendor/proxy/handler.js.map +1 -0
- package/vendor/proxy/schema-cleaner.d.ts +11 -0
- package/vendor/proxy/schema-cleaner.js +34 -0
- package/vendor/proxy/schema-cleaner.js.map +1 -0
- package/vendor/proxy/thought-cache.d.ts +19 -0
- package/vendor/proxy/thought-cache.js +53 -0
- package/vendor/proxy/thought-cache.js.map +1 -0
- package/vendor/session-manager.d.ts +211 -0
- package/vendor/session-manager.js +1345 -0
- package/vendor/session-manager.js.map +1 -0
- package/vendor/skill-resolver.js +107 -0
- package/vendor/types.d.ts +466 -0
- package/vendor/types.js +8 -0
- package/vendor/types.js.map +1 -0
- package/vendor/validation.d.ts +31 -0
- package/vendor/validation.js +104 -0
- package/vendor/validation.js.map +1 -0
|
@@ -0,0 +1,955 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cwd-patch — OpenClaw plugin module
|
|
3
|
+
*
|
|
4
|
+
* Ports the core CWD-patching and compat-bridge infrastructure from
|
|
5
|
+
* savvy-claude-code/cwd-enhancer.js.
|
|
6
|
+
*
|
|
7
|
+
* Two jobs:
|
|
8
|
+
*
|
|
9
|
+
* 1. EAGER SERVER START — Creates a SessionManager + EmbeddedServer eagerly
|
|
10
|
+
* at boot so :18796 is available immediately (before the first tool call).
|
|
11
|
+
* When the main plugin's lazy init eventually fires, it sees EADDRINUSE
|
|
12
|
+
* and gracefully skips.
|
|
13
|
+
*
|
|
14
|
+
* 2. CWD REDIRECT — Patches SessionManager.prototype.startSession so that
|
|
15
|
+
* openai-compat-* sessions use the workspace directory instead of /tmp.
|
|
16
|
+
* Gives Claude Code full project context when acting as Savvy's brain.
|
|
17
|
+
*
|
|
18
|
+
* Also includes:
|
|
19
|
+
* - Resume registry mirror (parallel write-ahead for claude-sessions.json)
|
|
20
|
+
* - Cache parity registry (Track B: OPENCLAW_CACHE_PARITY=1)
|
|
21
|
+
* - CoS session naming helpers (cosSessionStore used by think-conflict-resolver)
|
|
22
|
+
* - Runtime stub injection (ensureStatusRuntimeStubs)
|
|
23
|
+
* - Sandbox binary permission self-healing (ensureSandboxPermissions)
|
|
24
|
+
* - Route patch (applyRoutePatch: stable session keys, tool strip, sysprompt inline)
|
|
25
|
+
* - Metrics endpoint (/enhancer/metrics)
|
|
26
|
+
*
|
|
27
|
+
* Source: savvy-claude-code/cwd-enhancer.js lines 1–1357 (primary concern)
|
|
28
|
+
*/
|
|
29
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync, chmodSync, statSync, } from 'node:fs';
|
|
30
|
+
import { createHash } from 'node:crypto';
|
|
31
|
+
import { dirname, join } from 'node:path';
|
|
32
|
+
import { homedir } from 'node:os';
|
|
33
|
+
import { stripSysprompt } from './sysprompt-strip.js';
|
|
34
|
+
import { defaultRegisterGuard } from '../lib/register-guard.js';
|
|
35
|
+
import { isTestMode } from '../lib/test-mode.js';
|
|
36
|
+
import { isCacheParityTrackB, isTokenTelemetryEnabled, isSyspromptDumpEnabled, getMaxConcurrentSessions, getSessionTtlMinutes, ensureUxBridgeAllSessionsDefault, } from '../lib/config.js';
|
|
37
|
+
import { VENDOR_FILES } from '../lib/vendor-paths.js';
|
|
38
|
+
// ── Constants ─────────────────────────────────────────────────────────────
|
|
39
|
+
const TAG = '[cc-openclaw/cwd-patch]';
|
|
40
|
+
const DEFAULT_PORT = 18796;
|
|
41
|
+
const HOME = homedir();
|
|
42
|
+
// PLUGIN_DIR: the plugin install root, where stubs/ and mcp-config.json live.
|
|
43
|
+
// At runtime this file is at <plugin-root>/dist/src/session-bootstrap/cwd-patch.js,
|
|
44
|
+
// so we walk up looking for openclaw.plugin.json — a stable marker that survives
|
|
45
|
+
// any future build-output restructuring.
|
|
46
|
+
const PLUGIN_DIR = (() => {
|
|
47
|
+
try {
|
|
48
|
+
let dir = dirname(new URL(import.meta.url).pathname);
|
|
49
|
+
for (let i = 0; i < 6; i++) {
|
|
50
|
+
if (existsSync(join(dir, 'openclaw.plugin.json')))
|
|
51
|
+
return dir;
|
|
52
|
+
const parent = dirname(dir);
|
|
53
|
+
if (parent === dir)
|
|
54
|
+
break;
|
|
55
|
+
dir = parent;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// fall through
|
|
60
|
+
}
|
|
61
|
+
return join(HOME, '.openclaw/extensions/cc-openclaw');
|
|
62
|
+
})();
|
|
63
|
+
const PATHS = {
|
|
64
|
+
workspace: join(HOME, '.openclaw'),
|
|
65
|
+
ccPlugin: VENDOR_FILES.sessionManager,
|
|
66
|
+
ccServer: VENDOR_FILES.embeddedServer,
|
|
67
|
+
ccModels: VENDOR_FILES.models,
|
|
68
|
+
mcpConfig: join(PLUGIN_DIR, 'mcp-config.json'),
|
|
69
|
+
stubsDir: join(PLUGIN_DIR, 'stubs'),
|
|
70
|
+
thinkSkill: join(HOME, '.openclaw/workspace/skills/think/SKILL.md'),
|
|
71
|
+
openclawDist: join(HOME, '.npm-global/lib/node_modules/openclaw/dist'),
|
|
72
|
+
openclawRoot: join(HOME, '.npm-global/lib/node_modules/openclaw'),
|
|
73
|
+
autoReplyDir: join(HOME, '.npm-global/lib/node_modules/auto-reply/reply'),
|
|
74
|
+
seccompBinaries: [
|
|
75
|
+
join(HOME, '.npm-global/lib/node_modules/@anthropic-ai/claude-code/vendor/seccomp/x64/apply-seccomp'),
|
|
76
|
+
join(HOME, '.npm-global/lib/node_modules/@anthropic-ai/claude-code/vendor/seccomp/arm64/apply-seccomp'),
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
const DEPS_STUB_PATH = join(PATHS.openclawDist, 'commands-status-deps.runtime.js');
|
|
80
|
+
const STATUS_STUB_PATH = join(PATHS.openclawRoot, 'status.runtime.js');
|
|
81
|
+
const AUTO_REPLY_STATUS_PATH = join(PATHS.autoReplyDir, 'commands-status.runtime.js');
|
|
82
|
+
const SAVVY_REGISTRY_PATH = join(HOME, '.openclaw/savvy-resume-registry.json');
|
|
83
|
+
const CLAUDE_SESSIONS_PATH = join(HOME, '.openclaw/claude-sessions.json');
|
|
84
|
+
const CACHE_PARITY_REGISTRY_PATH = join(HOME, '.openclaw/openclaw-cache-parity-registry.json');
|
|
85
|
+
// Patch identity symbols (module-scoped, stable across re-imports within a process)
|
|
86
|
+
const PATCH_MARKER = Symbol.for('claude-local-enhancer:patched');
|
|
87
|
+
const ROUTE_PATCH_MARKER = Symbol.for('claude-local-enhancer:route-patched');
|
|
88
|
+
// ── Metrics ───────────────────────────────────────────────────────────────
|
|
89
|
+
const METRICS = {
|
|
90
|
+
cwdRedirects: 0,
|
|
91
|
+
toolStrips: 0,
|
|
92
|
+
sessionKeyInjections: 0,
|
|
93
|
+
staleSessionsKilled: 0,
|
|
94
|
+
cosNamesGenerated: 0,
|
|
95
|
+
claudeMdInjections: 0,
|
|
96
|
+
systemPromptInlined: 0,
|
|
97
|
+
uxMetaSeeded: 0,
|
|
98
|
+
cacheParityHits: 0,
|
|
99
|
+
cacheParityMisses: 0,
|
|
100
|
+
cacheParityRegistryWrites: 0,
|
|
101
|
+
cacheParityAppendInjections: 0,
|
|
102
|
+
startedAt: Date.now(),
|
|
103
|
+
};
|
|
104
|
+
// ── Module state ──────────────────────────────────────────────────────────
|
|
105
|
+
let logger = console;
|
|
106
|
+
let patchApplied = false;
|
|
107
|
+
let serverStarted = false;
|
|
108
|
+
// ── UX_META sweeper ───────────────────────────────────────────────────────
|
|
109
|
+
// B5 — sweep stale telegram-ux-bridge metadata entries every 5 min.
|
|
110
|
+
// Entries older than 10 min that were never consumed (error paths, name
|
|
111
|
+
// mismatches, early session termination) leak indefinitely without this.
|
|
112
|
+
// .unref() so the timer doesn't keep the process alive at shutdown.
|
|
113
|
+
const _UX_META_KEY = Symbol.for('telegram-ux-bridge:metadata');
|
|
114
|
+
const UX_META_MAX_AGE_MS = 10 * 60 * 1000;
|
|
115
|
+
const UX_META_SWEEP_INTERVAL_MS = 5 * 60 * 1000;
|
|
116
|
+
setInterval(() => {
|
|
117
|
+
// TODO(P2): refine type when full plugin types land
|
|
118
|
+
const meta = globalThis[_UX_META_KEY];
|
|
119
|
+
if (!(meta instanceof Map) || meta.size === 0)
|
|
120
|
+
return;
|
|
121
|
+
const cutoff = Date.now() - UX_META_MAX_AGE_MS;
|
|
122
|
+
let swept = 0;
|
|
123
|
+
for (const [key, val] of meta) {
|
|
124
|
+
if (val?.storedAt && val.storedAt < cutoff) {
|
|
125
|
+
meta.delete(key);
|
|
126
|
+
swept++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (swept > 0) {
|
|
130
|
+
logger.info(`${TAG} UX_META sweeper evicted ${swept} stale entries (size=${meta.size})`);
|
|
131
|
+
}
|
|
132
|
+
}, UX_META_SWEEP_INTERVAL_MS).unref();
|
|
133
|
+
const _systemInlineCache = new Map();
|
|
134
|
+
const SYSTEM_INLINE_CACHE_MAX = 500;
|
|
135
|
+
function _setSystemInlineCache(key, val) {
|
|
136
|
+
if (_systemInlineCache.size >= SYSTEM_INLINE_CACHE_MAX && !_systemInlineCache.has(key)) {
|
|
137
|
+
const oldest = _systemInlineCache.keys().next().value;
|
|
138
|
+
if (oldest !== undefined)
|
|
139
|
+
_systemInlineCache.delete(oldest);
|
|
140
|
+
}
|
|
141
|
+
_systemInlineCache.set(key, val);
|
|
142
|
+
}
|
|
143
|
+
// ── Tool dump hash guard ──────────────────────────────────────────────────
|
|
144
|
+
let _lastToolDumpHash = null;
|
|
145
|
+
// ── sessionId capture state ───────────────────────────────────────────────
|
|
146
|
+
let _lastCapturedJson = '';
|
|
147
|
+
// ── Resume registry helpers ───────────────────────────────────────────────
|
|
148
|
+
function _readJSONArraySafe(filePath) {
|
|
149
|
+
if (!existsSync(filePath))
|
|
150
|
+
return null;
|
|
151
|
+
try {
|
|
152
|
+
const raw = readFileSync(filePath, 'utf8');
|
|
153
|
+
const arr = JSON.parse(raw);
|
|
154
|
+
return Array.isArray(arr) ? arr : null;
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function restoreClaudeSessionsFromBackup() {
|
|
161
|
+
try {
|
|
162
|
+
const backup = _readJSONArraySafe(SAVVY_REGISTRY_PATH);
|
|
163
|
+
if (!backup || backup.length === 0)
|
|
164
|
+
return;
|
|
165
|
+
const current = _readJSONArraySafe(CLAUDE_SESSIONS_PATH);
|
|
166
|
+
if (current && current.length > 0) {
|
|
167
|
+
logger.info(`${TAG} claude-sessions.json already has ${current.length} entries — skipping restore`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const tmp = CLAUDE_SESSIONS_PATH + '.savvy-restore.tmp';
|
|
171
|
+
writeFileSync(tmp, JSON.stringify(backup, null, 2));
|
|
172
|
+
renameSync(tmp, CLAUDE_SESSIONS_PATH);
|
|
173
|
+
logger.info(`${TAG} Restored ${backup.length} entries from savvy-resume-registry.json → claude-sessions.json`);
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
logger.warn(`${TAG} Resume registry restore failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function writeBackupRegistry(entriesArray) {
|
|
180
|
+
try {
|
|
181
|
+
const tmp = SAVVY_REGISTRY_PATH + '.tmp';
|
|
182
|
+
writeFileSync(tmp, JSON.stringify(entriesArray, null, 2));
|
|
183
|
+
renameSync(tmp, SAVVY_REGISTRY_PATH);
|
|
184
|
+
}
|
|
185
|
+
catch { /* mirror is advisory; dist plugin still runs its own debounced save */ }
|
|
186
|
+
}
|
|
187
|
+
function _readCacheParityRegistry() {
|
|
188
|
+
if (!existsSync(CACHE_PARITY_REGISTRY_PATH))
|
|
189
|
+
return {};
|
|
190
|
+
try {
|
|
191
|
+
const raw = readFileSync(CACHE_PARITY_REGISTRY_PATH, 'utf8');
|
|
192
|
+
const obj = JSON.parse(raw);
|
|
193
|
+
return (obj && typeof obj === 'object' && !Array.isArray(obj))
|
|
194
|
+
? obj
|
|
195
|
+
: {};
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return {};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function _writeCacheParityEntry(sessionKey, hash, sysContent) {
|
|
202
|
+
if (!sessionKey || sessionKey === 'unknown')
|
|
203
|
+
return;
|
|
204
|
+
try {
|
|
205
|
+
const registry = _readCacheParityRegistry();
|
|
206
|
+
registry[sessionKey] = { hash, sysContent, updatedAt: Date.now() };
|
|
207
|
+
const tmp = CACHE_PARITY_REGISTRY_PATH + '.tmp';
|
|
208
|
+
writeFileSync(tmp, JSON.stringify(registry, null, 2));
|
|
209
|
+
renameSync(tmp, CACHE_PARITY_REGISTRY_PATH);
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
logger.warn(`${TAG} cache-parity registry write failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// ── Resume registry mirror patch ──────────────────────────────────────────
|
|
216
|
+
// Source: cwd-enhancer.js lines 163–181
|
|
217
|
+
function applyResumeRegistryMirror(SessionManager) {
|
|
218
|
+
// TODO(P2): refine type when full plugin types land
|
|
219
|
+
const proto = SessionManager.prototype;
|
|
220
|
+
if (typeof proto['_persistSession'] !== 'function') {
|
|
221
|
+
logger.warn(`${TAG} _persistSession not on SessionManager.prototype — mirror skipped`);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const orig = proto['_persistSession'];
|
|
225
|
+
if (orig.__savvyMirrored)
|
|
226
|
+
return;
|
|
227
|
+
const patched = function (...args) {
|
|
228
|
+
const ret = orig.apply(this, args);
|
|
229
|
+
try {
|
|
230
|
+
writeBackupRegistry(Array.from(this.persistedSessions.values()));
|
|
231
|
+
}
|
|
232
|
+
catch { /* swallow — mirror is advisory */ }
|
|
233
|
+
return ret;
|
|
234
|
+
};
|
|
235
|
+
patched.__savvyMirrored = true;
|
|
236
|
+
proto['_persistSession'] = patched;
|
|
237
|
+
logger.info(`${TAG} Resume registry mirror patch applied to SessionManager.prototype._persistSession`);
|
|
238
|
+
}
|
|
239
|
+
// ── Bootstrap resume registry ─────────────────────────────────────────────
|
|
240
|
+
function bootstrapResumeRegistry(eagerManager) {
|
|
241
|
+
try {
|
|
242
|
+
const sessions = eagerManager?.persistedSessions;
|
|
243
|
+
if (!(sessions instanceof Map) || sessions.size === 0) {
|
|
244
|
+
logger.info(`${TAG} Bootstrap: persistedSessions is empty — nothing to dump`);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
writeBackupRegistry(Array.from(sessions.values()));
|
|
248
|
+
logger.info(`${TAG} Bootstrap: dumped ${sessions.size} active session(s) to savvy-resume-registry.json`);
|
|
249
|
+
}
|
|
250
|
+
catch (err) {
|
|
251
|
+
logger.warn(`${TAG} Bootstrap dump failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// ── sessionId capture poller ──────────────────────────────────────────────
|
|
255
|
+
// Stage 5b — bypasses the dist plugin's broken _persistSession gate.
|
|
256
|
+
// Source: cwd-enhancer.js lines 197–256
|
|
257
|
+
function capturePersistedSessionsFromManager(manager) {
|
|
258
|
+
try {
|
|
259
|
+
const mgr = manager;
|
|
260
|
+
if (!mgr?.sessions)
|
|
261
|
+
return;
|
|
262
|
+
const captured = [];
|
|
263
|
+
for (const [name, managedSession] of mgr.sessions) {
|
|
264
|
+
const sessionId = managedSession?.claudeSessionId ?? managedSession?.session?.sessionId;
|
|
265
|
+
if (!sessionId)
|
|
266
|
+
continue;
|
|
267
|
+
const entry = {
|
|
268
|
+
name,
|
|
269
|
+
claudeSessionId: sessionId,
|
|
270
|
+
cwd: managedSession?.config?.cwd
|
|
271
|
+
|| managedSession?.session?.cwd
|
|
272
|
+
|| managedSession?.session?.options?.cwd,
|
|
273
|
+
model: managedSession?.config?.model
|
|
274
|
+
|| managedSession?.session?.options?.model,
|
|
275
|
+
lastActivity: managedSession?.lastActivity || Date.now(),
|
|
276
|
+
};
|
|
277
|
+
captured.push(entry);
|
|
278
|
+
if (mgr.persistedSessions instanceof Map && !mgr.persistedSessions.has(name)) {
|
|
279
|
+
mgr.persistedSessions.set(name, entry);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (captured.length === 0)
|
|
283
|
+
return;
|
|
284
|
+
const json = JSON.stringify(captured, null, 2);
|
|
285
|
+
if (json === _lastCapturedJson)
|
|
286
|
+
return;
|
|
287
|
+
_lastCapturedJson = json;
|
|
288
|
+
writeBackupRegistry(captured);
|
|
289
|
+
logger.info(`${TAG} sessionId capture: persisted ${captured.length} session(s) (forced-populate: ${captured.length} entries into dist Map)`);
|
|
290
|
+
}
|
|
291
|
+
catch (err) {
|
|
292
|
+
logger.warn(`${TAG} sessionId capture failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
// ── Path validation ───────────────────────────────────────────────────────
|
|
296
|
+
function validatePaths() {
|
|
297
|
+
const critical = [
|
|
298
|
+
['CC plugin (SessionManager)', PATHS.ccPlugin],
|
|
299
|
+
['CC server (EmbeddedServer)', PATHS.ccServer],
|
|
300
|
+
];
|
|
301
|
+
const missing = critical.filter(([, p]) => !existsSync(p));
|
|
302
|
+
if (missing.length) {
|
|
303
|
+
logger.warn(`${TAG} Missing critical files: ${missing.map(([label]) => label).join(', ')}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// ── Runtime stub injection ────────────────────────────────────────────────
|
|
307
|
+
// Source: cwd-enhancer.js lines 415–437
|
|
308
|
+
export function ensureStatusRuntimeStubs() {
|
|
309
|
+
let created = 0;
|
|
310
|
+
const stubs = [
|
|
311
|
+
{ target: DEPS_STUB_PATH, source: join(PATHS.stubsDir, 'commands-status-deps.runtime.js'), mkdirTarget: false },
|
|
312
|
+
{ target: STATUS_STUB_PATH, source: join(PATHS.stubsDir, 'status.runtime.js'), mkdirTarget: false },
|
|
313
|
+
{ target: AUTO_REPLY_STATUS_PATH, source: join(PATHS.stubsDir, 'status.runtime.js'), mkdirTarget: true },
|
|
314
|
+
];
|
|
315
|
+
for (const { target, source, mkdirTarget } of stubs) {
|
|
316
|
+
if (existsSync(target))
|
|
317
|
+
continue;
|
|
318
|
+
try {
|
|
319
|
+
const content = readFileSync(source, 'utf8');
|
|
320
|
+
if (mkdirTarget)
|
|
321
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
322
|
+
writeFileSync(target, content, 'utf8');
|
|
323
|
+
logger.info(`${TAG} Created missing ${target}`);
|
|
324
|
+
created++;
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
logger.error(`${TAG} Failed to create stub ${target}: ${err instanceof Error ? err.message : String(err)}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (created === 0) {
|
|
331
|
+
logger.info(`${TAG} Status runtime files already present — no stubs needed`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
// ── Sandbox binary permission self-healing ────────────────────────────────
|
|
335
|
+
// Source: cwd-enhancer.js lines 439–464
|
|
336
|
+
function ensureSandboxPermissions() {
|
|
337
|
+
let fixed = 0;
|
|
338
|
+
for (const binPath of PATHS.seccompBinaries) {
|
|
339
|
+
if (!existsSync(binPath))
|
|
340
|
+
continue;
|
|
341
|
+
try {
|
|
342
|
+
const st = statSync(binPath);
|
|
343
|
+
if ((st.mode & 0o111) === 0) {
|
|
344
|
+
chmodSync(binPath, 0o755);
|
|
345
|
+
logger.info(`${TAG} Fixed sandbox binary permissions: ${binPath} (was ${(st.mode & 0o777).toString(8)}, now 755)`);
|
|
346
|
+
fixed++;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
logger.error(`${TAG} Failed to fix sandbox permissions for ${binPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (fixed === 0) {
|
|
354
|
+
logger.info(`${TAG} Sandbox binaries already have execute permissions`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
let _ccModules = null;
|
|
358
|
+
async function getCCModules() {
|
|
359
|
+
if (_ccModules)
|
|
360
|
+
return _ccModules;
|
|
361
|
+
const [smMod, esMod] = await Promise.all([
|
|
362
|
+
import(PATHS.ccPlugin),
|
|
363
|
+
import(PATHS.ccServer),
|
|
364
|
+
]);
|
|
365
|
+
_ccModules = {
|
|
366
|
+
SessionManager: (smMod['SessionManager'] ?? smMod['default']?.['SessionManager']),
|
|
367
|
+
EmbeddedServer: (esMod['EmbeddedServer'] ?? esMod['default']?.['EmbeddedServer']),
|
|
368
|
+
};
|
|
369
|
+
return _ccModules;
|
|
370
|
+
}
|
|
371
|
+
// ── applyPatch ────────────────────────────────────────────────────────────
|
|
372
|
+
// Patches SessionManager.prototype.startSession for CWD redirect + extras.
|
|
373
|
+
// Source: cwd-enhancer.js lines 480–622
|
|
374
|
+
async function applyPatch(workspaceCwd) {
|
|
375
|
+
if (patchApplied)
|
|
376
|
+
return;
|
|
377
|
+
patchApplied = true;
|
|
378
|
+
let SessionManager;
|
|
379
|
+
try {
|
|
380
|
+
({ SessionManager } = await getCCModules());
|
|
381
|
+
}
|
|
382
|
+
catch (err) {
|
|
383
|
+
logger.error(`${TAG} Cannot import CC modules: ${err instanceof Error ? err.message : String(err)}`);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (!SessionManager) {
|
|
387
|
+
logger.error(`${TAG} SessionManager class not found in module`);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
applyResumeRegistryMirror(SessionManager);
|
|
391
|
+
// TODO(P2): refine type when full plugin types land
|
|
392
|
+
const proto = SessionManager.prototype;
|
|
393
|
+
const original = proto['startSession'];
|
|
394
|
+
if (!original) {
|
|
395
|
+
logger.error(`${TAG} startSession not found on SessionManager.prototype`);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (original[PATCH_MARKER]) {
|
|
399
|
+
logger.info(`${TAG} startSession already patched — skipping`);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const patched = async function (config) {
|
|
403
|
+
if (typeof config.name === 'string' && config.name.startsWith('openai-')) {
|
|
404
|
+
// Fix 1: CWD redirect
|
|
405
|
+
if (typeof config.cwd === 'string' && config.cwd.includes('/tmp')) {
|
|
406
|
+
const originalCwd = config.cwd;
|
|
407
|
+
config.cwd = workspaceCwd;
|
|
408
|
+
METRICS.cwdRedirects++;
|
|
409
|
+
logger.info(`${TAG} Redirected CWD: ${config.name} | ${originalCwd} → ${workspaceCwd}`);
|
|
410
|
+
}
|
|
411
|
+
// Fix 2: Restore CLI built-in tools
|
|
412
|
+
if (config.tools === '') {
|
|
413
|
+
delete config.tools;
|
|
414
|
+
logger.info(`${TAG} Restored CLI tools for ${config.name} (bridge XML mode bypassed)`);
|
|
415
|
+
}
|
|
416
|
+
// Fix 4: Inject MCP config
|
|
417
|
+
const mcpJsonPath = PATHS.mcpConfig;
|
|
418
|
+
if (existsSync(mcpJsonPath) && !config.mcpConfig) {
|
|
419
|
+
config.mcpConfig = [mcpJsonPath];
|
|
420
|
+
logger.info(`${TAG} Injected MCP config: ${mcpJsonPath}`);
|
|
421
|
+
}
|
|
422
|
+
// Fix 5: Inject workspace CLAUDE.md as system prompt
|
|
423
|
+
if (!config.appendSystemPrompt) {
|
|
424
|
+
const claudeMdPath = join(typeof config.cwd === 'string' ? config.cwd : workspaceCwd, '.claude', 'CLAUDE.md');
|
|
425
|
+
if (existsSync(claudeMdPath)) {
|
|
426
|
+
try {
|
|
427
|
+
config.appendSystemPrompt = readFileSync(claudeMdPath, 'utf8');
|
|
428
|
+
METRICS.claudeMdInjections++;
|
|
429
|
+
logger.info(`${TAG} Injected CLAUDE.md for ${config.name}`);
|
|
430
|
+
}
|
|
431
|
+
catch (err) {
|
|
432
|
+
logger.warn(`${TAG} Failed to read CLAUDE.md: ${err instanceof Error ? err.message : String(err)}`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
// Track B — Cache parity append injection (OPENCLAW_CACHE_PARITY=1)
|
|
437
|
+
if (isCacheParityTrackB()) {
|
|
438
|
+
try {
|
|
439
|
+
const reg = _readCacheParityRegistry();
|
|
440
|
+
const entry = reg[config.name];
|
|
441
|
+
if (entry?.sysContent) {
|
|
442
|
+
const prev = config.appendSystemPrompt || '';
|
|
443
|
+
config.appendSystemPrompt = prev ? `${prev}\n\n${entry.sysContent}` : entry.sysContent;
|
|
444
|
+
METRICS.cacheParityAppendInjections++;
|
|
445
|
+
logger.info(`${TAG} cache-parity: appended ${entry.sysContent.length} chars to ${config.name} appendSystemPrompt (hash=${entry.hash})`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
catch (err) {
|
|
449
|
+
logger.warn(`${TAG} cache-parity startSession injection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
// Stage 5d / Fix 6 — Force resume injection past skipPersistence gate
|
|
453
|
+
if (!config.resumeSessionId) {
|
|
454
|
+
const persisted = this.persistedSessions?.get?.(config.name);
|
|
455
|
+
const resumeId = persisted?.claudeSessionId;
|
|
456
|
+
if (resumeId) {
|
|
457
|
+
config.resumeSessionId = resumeId;
|
|
458
|
+
METRICS['resumeInjections'] = (METRICS['resumeInjections'] || 0) + 1;
|
|
459
|
+
logger.info(`${TAG} Resume injection for ${config.name}: ${resumeId.slice(0, 8)}...`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return original.call(this, config);
|
|
464
|
+
};
|
|
465
|
+
patched[PATCH_MARKER] = true;
|
|
466
|
+
proto['startSession'] = patched;
|
|
467
|
+
logger.info(`${TAG} SessionManager.startSession patched — compat sessions use workspace CWD`);
|
|
468
|
+
}
|
|
469
|
+
// ── applyRoutePatch ───────────────────────────────────────────────────────
|
|
470
|
+
// Patches EmbeddedServer.prototype.route for session keys, tool strip,
|
|
471
|
+
// sysprompt inline, cache parity, stale session cleanup, UX meta seeding.
|
|
472
|
+
// Source: cwd-enhancer.js lines 624–1013
|
|
473
|
+
function applyRoutePatch(EmbeddedServer) {
|
|
474
|
+
const proto = EmbeddedServer.prototype;
|
|
475
|
+
const origRoute = proto['route'];
|
|
476
|
+
if (!origRoute || origRoute[ROUTE_PATCH_MARKER])
|
|
477
|
+
return;
|
|
478
|
+
// TODO(P2): refine type when full plugin types land
|
|
479
|
+
proto['route'] = async function patchedRoute(path, body, query, res, headers) {
|
|
480
|
+
const __perfRouteStart = performance.now();
|
|
481
|
+
// Metrics endpoint
|
|
482
|
+
if (path === '/enhancer/metrics') {
|
|
483
|
+
const payload = {
|
|
484
|
+
...METRICS,
|
|
485
|
+
uptimeSeconds: Math.round((Date.now() - METRICS.startedAt) / 1000),
|
|
486
|
+
};
|
|
487
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
488
|
+
res.end(JSON.stringify(payload));
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
// Session key injection
|
|
492
|
+
if (path === '/v1/chat/completions' && Array.isArray(body.messages)) {
|
|
493
|
+
const headerKey = headers?.['x-session-id'];
|
|
494
|
+
if (typeof headerKey === 'string' && headerKey.trim()) {
|
|
495
|
+
body.user = headerKey.trim();
|
|
496
|
+
METRICS.sessionKeyInjections++;
|
|
497
|
+
logger.info(`${TAG} Session key from X-Session-Id header: ${body.user}`);
|
|
498
|
+
}
|
|
499
|
+
else if (!body.user) {
|
|
500
|
+
const userMsgs = body.messages
|
|
501
|
+
.filter(m => m?.role === 'user');
|
|
502
|
+
const anchor = userMsgs[1] || userMsgs[0];
|
|
503
|
+
const extractText = (c) => typeof c === 'string'
|
|
504
|
+
? c
|
|
505
|
+
: Array.isArray(c)
|
|
506
|
+
? c
|
|
507
|
+
.map(p => (p && (p.text ?? p.content)) || '')
|
|
508
|
+
.filter(Boolean)
|
|
509
|
+
.join('\n')
|
|
510
|
+
: '';
|
|
511
|
+
const anchorText = extractText(anchor?.content);
|
|
512
|
+
body.user = 'gw-' + createHash('sha1').update(anchorText).digest('hex').slice(0, 12);
|
|
513
|
+
METRICS.sessionKeyInjections++;
|
|
514
|
+
logger.info(`${TAG} Stable session key: ${body.user} (userMsgs=${userMsgs.length}, anchorCh=${anchorText.length}, model=${body.model || '?'})`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
// UX meta seeding
|
|
518
|
+
if (path === '/v1/chat/completions' && body.user) {
|
|
519
|
+
const PENDING_CHAT_QUEUE = Symbol.for('claude-local-ux:pending-chat');
|
|
520
|
+
const UX_META_KEY = Symbol.for('telegram-ux-bridge:metadata');
|
|
521
|
+
const queue = globalThis[PENDING_CHAT_QUEUE];
|
|
522
|
+
if (Array.isArray(queue) && queue.length > 0) {
|
|
523
|
+
const meta = queue.shift();
|
|
524
|
+
if (meta?.chatId) {
|
|
525
|
+
const g = globalThis;
|
|
526
|
+
if (!g[UX_META_KEY])
|
|
527
|
+
g[UX_META_KEY] = new Map();
|
|
528
|
+
const sessionName = 'openai-' + body.user;
|
|
529
|
+
g[UX_META_KEY].set(sessionName, {
|
|
530
|
+
chatId: meta.chatId,
|
|
531
|
+
threadId: meta.threadId,
|
|
532
|
+
origMessageId: meta.origMessageId,
|
|
533
|
+
storedAt: Date.now(),
|
|
534
|
+
});
|
|
535
|
+
METRICS.uxMetaSeeded++;
|
|
536
|
+
logger.info(`${TAG} Seeded UX_META for ${sessionName} — chat=${meta.chatId} thread=${meta.threadId || 'none'}`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
// Tool strip
|
|
541
|
+
if (path === '/v1/chat/completions' && Array.isArray(body.tools) && body.tools.length > 0) {
|
|
542
|
+
const toolCount = body.tools.length;
|
|
543
|
+
try {
|
|
544
|
+
const dumpPath = join(PLUGIN_DIR, 'mcp-tools.json');
|
|
545
|
+
const toolsJson = JSON.stringify(body.tools);
|
|
546
|
+
const toolsHash = createHash('sha1').update(toolsJson).digest('hex').slice(0, 16);
|
|
547
|
+
if (_lastToolDumpHash !== toolsHash) {
|
|
548
|
+
const tmpPath = dumpPath + '.tmp';
|
|
549
|
+
writeFileSync(tmpPath, toolsJson);
|
|
550
|
+
renameSync(tmpPath, dumpPath);
|
|
551
|
+
_lastToolDumpHash = toolsHash;
|
|
552
|
+
logger.info(`${TAG} Dumped ${toolCount} tool definitions for MCP bridge (hash ${toolsHash})`);
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
METRICS['toolDumpSkipped'] = (METRICS['toolDumpSkipped'] || 0) + 1;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
catch (e) {
|
|
559
|
+
logger.warn(`${TAG} Tool dump failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
560
|
+
}
|
|
561
|
+
delete body.tools;
|
|
562
|
+
delete body.tool_choice;
|
|
563
|
+
METRICS.toolStrips++;
|
|
564
|
+
}
|
|
565
|
+
// Stale session auto-recovery
|
|
566
|
+
if (path === '/v1/chat/completions' && body.user && this.manager) {
|
|
567
|
+
const sessionName = 'openai-' + body.user;
|
|
568
|
+
try {
|
|
569
|
+
const mgr = this.manager;
|
|
570
|
+
const existing = mgr?.sessions?.get?.(sessionName);
|
|
571
|
+
const ready = existing?.session?.stats?.isReady ?? existing?.stats?.isReady;
|
|
572
|
+
if (existing && ready === false) {
|
|
573
|
+
METRICS.staleSessionsKilled++;
|
|
574
|
+
logger.info(`${TAG} Stale session detected: ${sessionName} — stopping for auto-recovery`);
|
|
575
|
+
const stopFn = this.manager['stopSession'];
|
|
576
|
+
if (stopFn)
|
|
577
|
+
await stopFn.call(this.manager, sessionName).catch(() => { });
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
catch { /* best effort */ }
|
|
581
|
+
}
|
|
582
|
+
// System-prompt inline (Fix 6) + cache parity (Track B) + sysprompt strip
|
|
583
|
+
if (path === '/v1/chat/completions' && Array.isArray(body?.messages)) {
|
|
584
|
+
const extractText = (c) => typeof c === 'string'
|
|
585
|
+
? c
|
|
586
|
+
: Array.isArray(c)
|
|
587
|
+
? c
|
|
588
|
+
.map(p => (p && (p.text ?? p.content)) || '')
|
|
589
|
+
.filter(Boolean)
|
|
590
|
+
.join('\n')
|
|
591
|
+
: '';
|
|
592
|
+
const messages = body.messages;
|
|
593
|
+
const sysMsgs = messages.filter(m => m?.role === 'system');
|
|
594
|
+
if (sysMsgs.length > 0) {
|
|
595
|
+
let sysContent = sysMsgs.map(m => extractText(m.content)).filter(Boolean).join('\n\n');
|
|
596
|
+
// Always-on strips (Path B + location strip)
|
|
597
|
+
const stripResult = stripSysprompt(sysContent);
|
|
598
|
+
sysContent = stripResult.content;
|
|
599
|
+
if (stripResult.changed) {
|
|
600
|
+
METRICS['sysPromptVolatileStripped'] = (METRICS['sysPromptVolatileStripped'] || 0) + 1;
|
|
601
|
+
}
|
|
602
|
+
// Aggressive strip (F2.1) — default-on in cc-openclaw (P2 flip).
|
|
603
|
+
// Opt-out via OPENCLAW_AGGRESSIVE_STRIP=0/false/off.
|
|
604
|
+
// Legacy explicit OPENCLAW_AGGRESSIVE_STRIP=1 also enables (backward compat).
|
|
605
|
+
// stripSysprompt() already ran always-on strips above; call again with
|
|
606
|
+
// forceEnabled to ensure F2.1 runs regardless of env-parse in lib.
|
|
607
|
+
// Skip only when opt-out env is set (isStripEnabled() returns false).
|
|
608
|
+
{
|
|
609
|
+
const aggrResult = stripSysprompt(sysContent);
|
|
610
|
+
if (aggrResult.aggressiveStripped) {
|
|
611
|
+
sysContent = aggrResult.content;
|
|
612
|
+
METRICS['sysPromptAggressiveStripped'] = (METRICS['sysPromptAggressiveStripped'] || 0) + 1;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
let lastUserIdx = -1;
|
|
616
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
617
|
+
if (messages[i]?.role === 'user') {
|
|
618
|
+
lastUserIdx = i;
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
// F2.5 — Per-turn token-cost telemetry (OPENCLAW_TOKEN_TELEMETRY=1)
|
|
623
|
+
if (isTokenTelemetryEnabled()) {
|
|
624
|
+
try {
|
|
625
|
+
const tlmSessionKey = body.user || 'unknown';
|
|
626
|
+
const lastUserText = lastUserIdx >= 0 ? extractText(messages[lastUserIdx].content) : '';
|
|
627
|
+
const sysChars = sysContent.length;
|
|
628
|
+
const userChars = lastUserText.length;
|
|
629
|
+
const totalChars = sysChars + userChars;
|
|
630
|
+
const line = JSON.stringify({
|
|
631
|
+
ts: new Date().toISOString(),
|
|
632
|
+
sessionKey: tlmSessionKey,
|
|
633
|
+
sysChars,
|
|
634
|
+
sysTokensEst: Math.round(sysChars / 4),
|
|
635
|
+
userChars,
|
|
636
|
+
userTokensEst: Math.round(userChars / 4),
|
|
637
|
+
totalChars,
|
|
638
|
+
totalTokensEst: Math.round(totalChars / 4),
|
|
639
|
+
}) + '\n';
|
|
640
|
+
const tlmPath = join(HOME, '.openclaw/workspace/memory/sysprompt-cost.jsonl');
|
|
641
|
+
try {
|
|
642
|
+
writeFileSync(tlmPath, line, { flag: 'a' });
|
|
643
|
+
}
|
|
644
|
+
catch (e) {
|
|
645
|
+
logger.warn(`${TAG} token telemetry write failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
catch (e) {
|
|
649
|
+
logger.warn(`${TAG} token telemetry exception: ${e instanceof Error ? e.message : String(e)}`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (sysContent && lastUserIdx >= 0) {
|
|
653
|
+
const lu = messages[lastUserIdx];
|
|
654
|
+
const origText = extractText(lu.content);
|
|
655
|
+
const sysHash = createHash('sha1').update(sysContent).digest('hex').slice(0, 16);
|
|
656
|
+
const sessionKey = body.user || 'unknown';
|
|
657
|
+
// Track B — Cache parity check
|
|
658
|
+
let cacheParityHandled = false;
|
|
659
|
+
if (isCacheParityTrackB()) {
|
|
660
|
+
try {
|
|
661
|
+
const reg = _readCacheParityRegistry();
|
|
662
|
+
const entry = reg[sessionKey];
|
|
663
|
+
if (entry && entry.hash === sysHash) {
|
|
664
|
+
body.messages = messages.filter(m => m?.role !== 'system');
|
|
665
|
+
METRICS.cacheParityHits++;
|
|
666
|
+
METRICS.systemPromptInlined++;
|
|
667
|
+
cacheParityHandled = true;
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
_writeCacheParityEntry(sessionKey, sysHash, sysContent);
|
|
671
|
+
METRICS.cacheParityMisses++;
|
|
672
|
+
METRICS.cacheParityRegistryWrites++;
|
|
673
|
+
logger.info(`${TAG} cache-parity miss: session=${sessionKey} oldHash=${entry?.hash || 'none'} newHash=${sysHash} sysLen=${sysContent.length} (registry updated; next session start will append)`);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
catch (err) {
|
|
677
|
+
logger.warn(`${TAG} cache-parity branch failed: ${err instanceof Error ? err.message : String(err)} — falling back to legacy inlining`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
// P1 — System-prompt inline cache (legacy path)
|
|
681
|
+
if (!cacheParityHandled) {
|
|
682
|
+
const cached = _systemInlineCache.get(sessionKey);
|
|
683
|
+
let inlinedPrefix;
|
|
684
|
+
if (cached && cached.hash === sysHash) {
|
|
685
|
+
inlinedPrefix = cached.prefix;
|
|
686
|
+
METRICS['systemPromptInlineCacheHits'] = (METRICS['systemPromptInlineCacheHits'] || 0) + 1;
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
inlinedPrefix = `<system>\n${sysContent}\n</system>\n\n`;
|
|
690
|
+
_setSystemInlineCache(sessionKey, { hash: sysHash, prefix: inlinedPrefix });
|
|
691
|
+
METRICS['systemPromptInlineCacheMisses'] = (METRICS['systemPromptInlineCacheMisses'] || 0) + 1;
|
|
692
|
+
logger.info(`${TAG} system-prompt hash churn: session=${sessionKey} oldHash=${cached?.hash || 'none'} newHash=${sysHash} sysLen=${sysContent.length}`);
|
|
693
|
+
// Diagnostic dump (OPENCLAW_SYSPROMPT_DUMP=1)
|
|
694
|
+
if (isSyspromptDumpEnabled()) {
|
|
695
|
+
try {
|
|
696
|
+
const debugDir = join(HOME, '.openclaw/workspace/debug/sysprompt');
|
|
697
|
+
mkdirSync(debugDir, { recursive: true });
|
|
698
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
699
|
+
writeFileSync(`${debugDir}/${ts}_${sessionKey}_${sysHash}.txt`, sysContent);
|
|
700
|
+
}
|
|
701
|
+
catch (e) {
|
|
702
|
+
logger.warn(`${TAG} sysprompt debug dump failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
lu.content = `${inlinedPrefix}${origText}`;
|
|
707
|
+
body.messages = messages.filter(m => m?.role !== 'system');
|
|
708
|
+
METRICS.systemPromptInlined++;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
// Stage 0 — record route-patch duration for ux-bridge correlation
|
|
714
|
+
try {
|
|
715
|
+
const sessionName = body?.user;
|
|
716
|
+
if (sessionName) {
|
|
717
|
+
const PERF_KEY = Symbol.for('claude-local:perf-timing');
|
|
718
|
+
const g = globalThis;
|
|
719
|
+
g[PERF_KEY] = g[PERF_KEY] || new Map();
|
|
720
|
+
const perfMap = g[PERF_KEY];
|
|
721
|
+
const prev = perfMap.get(sessionName) || {};
|
|
722
|
+
perfMap.set(sessionName, {
|
|
723
|
+
...prev,
|
|
724
|
+
routePatchMs: performance.now() - __perfRouteStart,
|
|
725
|
+
routePatchEndAbsMs: performance.now(),
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
catch { /* perf instrumentation must never block requests */ }
|
|
730
|
+
return origRoute.call(this, path, body, query, res, headers);
|
|
731
|
+
};
|
|
732
|
+
proto['route'][ROUTE_PATCH_MARKER] = true;
|
|
733
|
+
logger.info(`${TAG} EmbeddedServer.route patched — stable session keys active`);
|
|
734
|
+
}
|
|
735
|
+
// ── startEagerServer ──────────────────────────────────────────────────────
|
|
736
|
+
// Source: cwd-enhancer.js lines 1015–1127
|
|
737
|
+
async function startEagerServer(port) {
|
|
738
|
+
if (serverStarted)
|
|
739
|
+
return null;
|
|
740
|
+
serverStarted = true;
|
|
741
|
+
let SessionManager;
|
|
742
|
+
let EmbeddedServer;
|
|
743
|
+
try {
|
|
744
|
+
({ SessionManager, EmbeddedServer } = await getCCModules());
|
|
745
|
+
}
|
|
746
|
+
catch (err) {
|
|
747
|
+
logger.error(`${TAG} Cannot import CC modules for eager server: ${err instanceof Error ? err.message : String(err)}`);
|
|
748
|
+
return null;
|
|
749
|
+
}
|
|
750
|
+
if (!SessionManager || !EmbeddedServer) {
|
|
751
|
+
logger.error(`${TAG} Missing SessionManager or EmbeddedServer class`);
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
754
|
+
applyRoutePatch(EmbeddedServer);
|
|
755
|
+
let eagerManager = null;
|
|
756
|
+
const maxRetries = 3;
|
|
757
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
758
|
+
try {
|
|
759
|
+
eagerManager = new SessionManager({
|
|
760
|
+
maxConcurrentSessions: getMaxConcurrentSessions(),
|
|
761
|
+
sessionTtlMinutes: getSessionTtlMinutes(),
|
|
762
|
+
});
|
|
763
|
+
const mgr = eagerManager;
|
|
764
|
+
logger.info(`${TAG} SessionManager idle TTL: ${mgr.pluginConfig?.sessionTtlMinutes ?? '?'} min (max sessions: ${mgr.pluginConfig?.maxConcurrentSessions ?? '?'})`);
|
|
765
|
+
const server = new EmbeddedServer(eagerManager, port);
|
|
766
|
+
await server.start();
|
|
767
|
+
logger.info(`${TAG} Eager EmbeddedServer started on :${port} — compat bridge ready`);
|
|
768
|
+
const SM_KEY = Symbol.for('claude-local-enhancer:session-manager');
|
|
769
|
+
globalThis[SM_KEY] = eagerManager;
|
|
770
|
+
logger.info(`${TAG} SessionManager exposed via globalThis for bridge access`);
|
|
771
|
+
// Model guard: verify main agent routes through claude-local/*
|
|
772
|
+
try {
|
|
773
|
+
const cfgPath = join(PATHS.workspace, 'openclaw.json');
|
|
774
|
+
const cfg = JSON.parse(readFileSync(cfgPath, 'utf8'));
|
|
775
|
+
const agentList = Array.isArray(cfg?.agents)
|
|
776
|
+
? cfg.agents
|
|
777
|
+
: Array.isArray(cfg?.agents?.list)
|
|
778
|
+
? cfg.agents.list
|
|
779
|
+
: [];
|
|
780
|
+
const mainAgent = agentList.find(a => a?.id === 'main');
|
|
781
|
+
const readPrimary = (m) => typeof m === 'string' ? m : m?.primary;
|
|
782
|
+
const mainPrimary = readPrimary(mainAgent?.model);
|
|
783
|
+
const defaultsPrimary = readPrimary(cfg?.agents?.defaults?.model);
|
|
784
|
+
const effective = mainPrimary || defaultsPrimary;
|
|
785
|
+
if (effective && !effective.startsWith('claude-local/') && !effective.startsWith('cc-openclaw/')) {
|
|
786
|
+
logger.warn([
|
|
787
|
+
'',
|
|
788
|
+
'════════════════════════════════════════════════════════════════════',
|
|
789
|
+
`${TAG} ⚠ SAVVY MAIN-AGENT EFFECTIVE PRIMARY MODEL IS '${effective}'`,
|
|
790
|
+
` (main override: ${mainPrimary ?? 'none'} | defaults: ${defaultsPrimary ?? 'none'})`,
|
|
791
|
+
' This is NOT a claude-local/* or cc-openclaw/* model → persistent sessions disabled.',
|
|
792
|
+
' Consequences:',
|
|
793
|
+
' • Savvy will forget context across every message',
|
|
794
|
+
' • Telegram live card will not populate (no TOOL_USE events)',
|
|
795
|
+
' Fix: set agents.list[id=main].model.primary (or agents.defaults.model.primary)',
|
|
796
|
+
` to cc-openclaw/claude-opus-4-7 in ${cfgPath}`,
|
|
797
|
+
'════════════════════════════════════════════════════════════════════',
|
|
798
|
+
'',
|
|
799
|
+
].join('\n'));
|
|
800
|
+
}
|
|
801
|
+
else if (effective) {
|
|
802
|
+
logger.info(`${TAG} Savvy main-agent primary OK — ${effective} (source: ${mainPrimary ? 'main-override' : 'defaults'})`);
|
|
803
|
+
}
|
|
804
|
+
if (defaultsPrimary && !defaultsPrimary.startsWith('claude-local/') && !defaultsPrimary.startsWith('cc-openclaw/')) {
|
|
805
|
+
logger.warn(`${TAG} ⚠ agents.defaults.model.primary is '${defaultsPrimary}' — subagents without a model override will be stateless`);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
catch (err) {
|
|
809
|
+
logger.warn(`${TAG} model-guard skipped: ${err instanceof Error ? err.message : String(err)}`);
|
|
810
|
+
}
|
|
811
|
+
// Boot health verification
|
|
812
|
+
try {
|
|
813
|
+
const healthRes = await fetch(`http://127.0.0.1:${port}/health`);
|
|
814
|
+
if (healthRes.ok) {
|
|
815
|
+
const health = (await healthRes.json());
|
|
816
|
+
logger.info(`${TAG} Health check OK — sessions: ${health.sessions ?? '?'}, version: ${health.version ?? '?'}`);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
catch { /* non-critical */ }
|
|
820
|
+
// ── Prewarm Claude CLI subprocess ─────────────────────────────────────
|
|
821
|
+
// The Claude CLI subprocess (vendor/persistent-session.js spawn) is
|
|
822
|
+
// normally deferred to the first incoming request — which adds 30-60s
|
|
823
|
+
// of cold-start latency on the first chat completion after a gateway
|
|
824
|
+
// restart. Pre-spawning a session at boot moves that cost off the
|
|
825
|
+
// user-visible critical path. Non-blocking: failures here don't block
|
|
826
|
+
// gateway boot. The session is created with skipPersistence so it
|
|
827
|
+
// doesn't pollute the persisted-sessions map.
|
|
828
|
+
const _prewarmStartMs = Date.now();
|
|
829
|
+
eagerManager
|
|
830
|
+
.startSession({
|
|
831
|
+
name: 'cc-openclaw-prewarm',
|
|
832
|
+
model: 'claude-haiku-4-5',
|
|
833
|
+
skipPersistence: true,
|
|
834
|
+
})
|
|
835
|
+
.then(() => {
|
|
836
|
+
const elapsedMs = Date.now() - _prewarmStartMs;
|
|
837
|
+
logger.info(`${TAG} Prewarm session ready — Claude CLI subprocess hot (${elapsedMs}ms; first request will be fast)`);
|
|
838
|
+
})
|
|
839
|
+
.catch((err) => {
|
|
840
|
+
logger.warn(`${TAG} Prewarm session failed (cold-start optimization disabled this boot): ${err instanceof Error ? err.message : String(err)}`);
|
|
841
|
+
});
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
844
|
+
catch (err) {
|
|
845
|
+
const e = err;
|
|
846
|
+
if (e.code === 'EADDRINUSE') {
|
|
847
|
+
logger.info(`${TAG} Port :${port} already in use — main plugin owns it, skipping`);
|
|
848
|
+
eagerManager = null;
|
|
849
|
+
break;
|
|
850
|
+
}
|
|
851
|
+
logger.error(`${TAG} Eager server start failed (attempt ${attempt + 1}/${maxRetries + 1}): ${e.message}`);
|
|
852
|
+
eagerManager = null;
|
|
853
|
+
if (attempt < maxRetries) {
|
|
854
|
+
const delay = 2000 * Math.pow(2, attempt);
|
|
855
|
+
await new Promise(r => setTimeout(r, delay));
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return eagerManager;
|
|
860
|
+
}
|
|
861
|
+
// ── Stale session cleanup ─────────────────────────────────────────────────
|
|
862
|
+
// Source: cwd-enhancer.js lines 1129–1146
|
|
863
|
+
function cleanStaleSessions(manager) {
|
|
864
|
+
if (!manager)
|
|
865
|
+
return;
|
|
866
|
+
try {
|
|
867
|
+
const mgr = manager;
|
|
868
|
+
const sessions = mgr.listSessions();
|
|
869
|
+
if (!Array.isArray(sessions) || !sessions.length)
|
|
870
|
+
return;
|
|
871
|
+
const stale = sessions.filter(s => s.stats?.isReady === false);
|
|
872
|
+
if (!stale.length) {
|
|
873
|
+
logger.info(`${TAG} All ${sessions.length} bridge session(s) healthy`);
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
logger.info(`${TAG} Cleaning ${stale.length} stale session(s)...`);
|
|
877
|
+
for (const s of stale) {
|
|
878
|
+
mgr.stopSession(s.name).catch(() => { });
|
|
879
|
+
logger.info(`${TAG} Stopped stale: ${s.name}`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
catch (e) {
|
|
883
|
+
logger.warn(`${TAG} Stale cleanup failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
// ── Register ──────────────────────────────────────────────────────────────
|
|
887
|
+
/**
|
|
888
|
+
* Register the CWD-patch plugin.
|
|
889
|
+
* Idempotent: subsequent calls on the same api are no-ops.
|
|
890
|
+
*/
|
|
891
|
+
export function register(api) {
|
|
892
|
+
logger = api.logger ?? console;
|
|
893
|
+
defaultRegisterGuard.guard('session-bootstrap/cwd-patch', api, () => {
|
|
894
|
+
const pluginConfig = api.config?.plugins?.entries?.['claude-local-enhancer']?.config ?? {};
|
|
895
|
+
const workspaceCwd = pluginConfig['workspaceCwd'] || PATHS.workspace;
|
|
896
|
+
const port = pluginConfig['port'] || DEFAULT_PORT;
|
|
897
|
+
// registerService stub for API compliance — always wired, even in test mode
|
|
898
|
+
api.registerService({
|
|
899
|
+
id: 'claude-local-enhancer',
|
|
900
|
+
start: async () => { },
|
|
901
|
+
stop: async () => { },
|
|
902
|
+
});
|
|
903
|
+
if (isTestMode()) {
|
|
904
|
+
logger.info(`${TAG} TEST_MODE=1 — skipping eager bootstrap (validatePaths, UX_BRIDGE_ALL_SESSIONS mutation, pricing overrides, resume registry restore, self-healing stubs, applyPatch+startEagerServer)`);
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
validatePaths();
|
|
908
|
+
logger.info(`${TAG} Registered — workspace CWD: ${workspaceCwd}, port: ${port}`);
|
|
909
|
+
// Enable UX bridge for ALL Claude Code sessions
|
|
910
|
+
// Changed 2026-04-26: explicit UX_BRIDGE_ALL_SESSIONS=false is respected.
|
|
911
|
+
// The mutation is centralized in config.ensureUxBridgeAllSessionsDefault().
|
|
912
|
+
const uxBridge = ensureUxBridgeAllSessionsDefault();
|
|
913
|
+
if (uxBridge.mutated) {
|
|
914
|
+
logger.info(`${TAG} UX_BRIDGE_ALL_SESSIONS enabled (set programmatically — was unset)`);
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
logger.info(`${TAG} UX_BRIDGE_ALL_SESSIONS=${uxBridge.value} (env-file value, respected)`);
|
|
918
|
+
}
|
|
919
|
+
// Patch openclaw-claude-code model pricing table for opus-4-7 / haiku-4-5
|
|
920
|
+
import(PATHS.ccModels).then((mod) => {
|
|
921
|
+
const m = mod;
|
|
922
|
+
if (typeof m['overrideModelPricing'] !== 'function') {
|
|
923
|
+
logger.warn(`${TAG} overrideModelPricing not exported by models.js — skipping pricing patch`);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
m['overrideModelPricing']({
|
|
927
|
+
'claude-opus-4-7': { input: 5, output: 25, cached: 0.5 },
|
|
928
|
+
'claude-haiku-4-5': { input: 1, output: 5, cached: 0.1 },
|
|
929
|
+
});
|
|
930
|
+
logger.info(`${TAG} Pricing overrides applied for claude-opus-4-7 + claude-haiku-4-5`);
|
|
931
|
+
}).catch((err) => {
|
|
932
|
+
logger.warn(`${TAG} Pricing override import failed: ${err.message}`);
|
|
933
|
+
});
|
|
934
|
+
// Restore parallel resume registry BEFORE applyPatch + eagerServer
|
|
935
|
+
restoreClaudeSessionsFromBackup();
|
|
936
|
+
// Self-healing
|
|
937
|
+
ensureStatusRuntimeStubs();
|
|
938
|
+
ensureSandboxPermissions();
|
|
939
|
+
logger.info(`${TAG} Eagerly applying patch + starting server...`);
|
|
940
|
+
applyPatch(workspaceCwd)
|
|
941
|
+
.then(() => startEagerServer(port))
|
|
942
|
+
.then((manager) => {
|
|
943
|
+
bootstrapResumeRegistry(manager);
|
|
944
|
+
const captureInterval = setInterval(() => capturePersistedSessionsFromManager(manager), 5_000);
|
|
945
|
+
captureInterval.unref();
|
|
946
|
+
cleanStaleSessions(manager);
|
|
947
|
+
const cleanupInterval = setInterval(() => cleanStaleSessions(manager), 120_000);
|
|
948
|
+
cleanupInterval.unref();
|
|
949
|
+
})
|
|
950
|
+
.catch((err) => {
|
|
951
|
+
logger.error(`${TAG} Eager init failed: ${err.message}`);
|
|
952
|
+
});
|
|
953
|
+
}); // end defaultRegisterGuard.guard
|
|
954
|
+
}
|
|
955
|
+
//# sourceMappingURL=cwd-patch.js.map
|