principles-disciple 1.8.0 → 1.8.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/ADVANCED_CONFIG_ZH.md +97 -0
- package/AGENT_INSTALL.md +173 -0
- package/AGENT_INSTALL_EN.md +173 -0
- package/INSTALL.md +256 -0
- package/SKILL.md +63 -0
- package/docs/COMMAND_REFERENCE.md +76 -0
- package/docs/COMMAND_REFERENCE_EN.md +79 -0
- package/esbuild.config.js +75 -0
- package/openclaw.plugin.json +6 -1
- package/package.json +13 -15
- package/scripts/build-web.mjs +46 -0
- package/scripts/install-dependencies.cjs +47 -0
- package/scripts/sync-plugin.mjs +802 -0
- package/scripts/verify-build.mjs +109 -0
- package/src/agents/nocturnal-dreamer.md +152 -0
- package/src/agents/nocturnal-philosopher.md +138 -0
- package/src/agents/nocturnal-reflector.md +126 -0
- package/src/agents/nocturnal-scribe.md +164 -0
- package/src/commands/capabilities.ts +85 -0
- package/{dist/commands/context.js → src/commands/context.ts} +78 -38
- package/src/commands/evolution-status.ts +146 -0
- package/src/commands/export.ts +111 -0
- package/src/commands/focus.ts +533 -0
- package/src/commands/nocturnal-review.ts +311 -0
- package/src/commands/nocturnal-rollout.ts +763 -0
- package/src/commands/nocturnal-train.ts +1002 -0
- package/{dist/commands/pain.js → src/commands/pain.ts} +68 -49
- package/src/commands/principle-rollback.ts +27 -0
- package/{dist/commands/rollback.js → src/commands/rollback.ts} +44 -12
- package/src/commands/samples.ts +60 -0
- package/src/commands/strategy.ts +38 -0
- package/{dist/commands/thinking-os.js → src/commands/thinking-os.ts} +59 -36
- package/src/commands/workflow-debug.ts +128 -0
- package/{dist/config/defaults/runtime.js → src/config/defaults/runtime.ts} +12 -5
- package/src/config/errors.ts +163 -0
- package/{dist/config/index.d.ts → src/config/index.ts} +2 -1
- package/src/constants/diagnostician.ts +66 -0
- package/src/constants/tools.ts +62 -0
- package/src/core/adaptive-thresholds.ts +476 -0
- package/{dist/core/config-service.js → src/core/config-service.ts} +7 -4
- package/{dist/core/config.js → src/core/config.ts} +158 -46
- package/src/core/control-ui-db.ts +435 -0
- package/{dist/core/detection-funnel.js → src/core/detection-funnel.ts} +36 -21
- package/{dist/core/detection-service.js → src/core/detection-service.ts} +7 -4
- package/{dist/core/dictionary-service.js → src/core/dictionary-service.ts} +7 -4
- package/{dist/core/dictionary.js → src/core/dictionary.ts} +57 -34
- package/src/core/empathy-keyword-matcher.ts +327 -0
- package/src/core/empathy-types.ts +218 -0
- package/src/core/event-log.ts +544 -0
- package/src/core/evolution-engine.ts +612 -0
- package/src/core/evolution-logger.ts +353 -0
- package/src/core/evolution-migration.ts +77 -0
- package/src/core/evolution-reducer.ts +731 -0
- package/src/core/evolution-types.ts +456 -0
- package/src/core/external-training-contract.ts +527 -0
- package/src/core/focus-history.ts +1458 -0
- package/src/core/hygiene/tracker.ts +117 -0
- package/{dist/core/init.js → src/core/init.ts} +39 -26
- package/src/core/local-worker-routing.ts +617 -0
- package/{dist/core/migration.js → src/core/migration.ts} +18 -11
- package/src/core/model-deployment-registry.ts +722 -0
- package/src/core/model-training-registry.ts +813 -0
- package/src/core/nocturnal-arbiter.ts +706 -0
- package/src/core/nocturnal-candidate-scoring.ts +392 -0
- package/src/core/nocturnal-compliance.ts +1075 -0
- package/src/core/nocturnal-dataset.ts +668 -0
- package/src/core/nocturnal-executability.ts +428 -0
- package/src/core/nocturnal-export.ts +390 -0
- package/{dist/core/nocturnal-paths.js → src/core/nocturnal-paths.ts} +49 -23
- package/src/core/nocturnal-trajectory-extractor.ts +484 -0
- package/src/core/nocturnal-trinity.ts +1384 -0
- package/src/core/pain.ts +122 -0
- package/{dist/core/path-resolver.js → src/core/path-resolver.ts} +157 -36
- package/{dist/core/paths.js → src/core/paths.ts} +13 -4
- package/src/core/principle-training-state.ts +450 -0
- package/src/core/profile.ts +226 -0
- package/src/core/promotion-gate.ts +822 -0
- package/{dist/core/risk-calculator.js → src/core/risk-calculator.ts} +42 -16
- package/{dist/core/session-tracker.js → src/core/session-tracker.ts} +185 -63
- package/src/core/shadow-observation-registry.ts +534 -0
- package/{dist/core/system-logger.js → src/core/system-logger.ts} +9 -5
- package/src/core/thinking-models.ts +217 -0
- package/src/core/training-program.ts +630 -0
- package/src/core/trajectory-types.ts +243 -0
- package/src/core/trajectory.ts +1673 -0
- package/{dist/core/workspace-context.js → src/core/workspace-context.ts} +57 -32
- package/src/hooks/bash-risk.ts +171 -0
- package/src/hooks/edit-verification.ts +295 -0
- package/src/hooks/gate-block-helper.ts +160 -0
- package/src/hooks/gate.ts +210 -0
- package/src/hooks/gfi-gate.ts +177 -0
- package/src/hooks/lifecycle.ts +326 -0
- package/{dist/hooks/llm.js → src/hooks/llm.ts} +166 -139
- package/src/hooks/message-sanitize.ts +45 -0
- package/src/hooks/pain.ts +384 -0
- package/src/hooks/progressive-trust-gate.ts +174 -0
- package/src/hooks/prompt.ts +920 -0
- package/src/hooks/subagent.ts +207 -0
- package/src/hooks/thinking-checkpoint.ts +73 -0
- package/src/hooks/trajectory-collector.ts +290 -0
- package/src/http/principles-console-route.ts +716 -0
- package/src/i18n/commands.ts +117 -0
- package/src/index.ts +694 -0
- package/src/service/central-database.ts +831 -0
- package/src/service/control-ui-query-service.ts +888 -0
- package/src/service/evolution-query-service.ts +405 -0
- package/src/service/evolution-worker.ts +1646 -0
- package/src/service/health-query-service.ts +836 -0
- package/{dist/service/nocturnal-runtime.js → src/service/nocturnal-runtime.ts} +263 -36
- package/src/service/nocturnal-service.ts +1015 -0
- package/src/service/nocturnal-target-selector.ts +532 -0
- package/src/service/phase3-input-filter.ts +237 -0
- package/src/service/runtime-summary-service.ts +757 -0
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +513 -0
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +603 -0
- package/src/service/subagent-workflow/index.ts +51 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +856 -0
- package/src/service/subagent-workflow/runtime-direct-driver.ts +166 -0
- package/src/service/subagent-workflow/types.ts +378 -0
- package/src/service/subagent-workflow/workflow-store.ts +328 -0
- package/src/service/trajectory-service.ts +15 -0
- package/{dist/tools/critique-prompt.js → src/tools/critique-prompt.ts} +25 -8
- package/src/tools/deep-reflect.ts +349 -0
- package/{dist/tools/model-index.js → src/tools/model-index.ts} +33 -17
- package/src/types/event-types.ts +453 -0
- package/src/types/hygiene-types.ts +31 -0
- package/src/types/principle-tree-schema.ts +244 -0
- package/src/types/runtime-summary.ts +49 -0
- package/src/types.ts +74 -0
- package/src/utils/file-lock.ts +391 -0
- package/{dist/utils/glob-match.js → src/utils/glob-match.ts} +21 -20
- package/{dist/utils/hashing.js → src/utils/hashing.ts} +6 -4
- package/src/utils/io.ts +110 -0
- package/{dist/utils/nlp.js → src/utils/nlp.ts} +19 -12
- package/{dist/utils/plugin-logger.js → src/utils/plugin-logger.ts} +33 -8
- package/src/utils/subagent-probe.ts +94 -0
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +70 -1
- package/templates/pain_settings.json +2 -1
- package/tests/README.md +120 -0
- package/tests/build-artifacts.test.ts +111 -0
- package/tests/commands/evolution-status.test.ts +222 -0
- package/tests/commands/evolver.test.ts +22 -0
- package/tests/commands/export.test.ts +78 -0
- package/tests/commands/nocturnal-review.test.ts +448 -0
- package/tests/commands/nocturnal-train.test.ts +97 -0
- package/tests/commands/pain.test.ts +108 -0
- package/tests/commands/samples.test.ts +65 -0
- package/tests/commands/strategy.test.ts +34 -0
- package/tests/commands/thinking-os.test.ts +88 -0
- package/tests/core/adaptive-thresholds.test.ts +261 -0
- package/tests/core/config-service.test.ts +89 -0
- package/tests/core/config.test.ts +90 -0
- package/tests/core/control-ui-db.test.ts +75 -0
- package/tests/core/core-template-guidance.test.ts +21 -0
- package/tests/core/detection-funnel.test.ts +63 -0
- package/tests/core/detection-service.test.ts +50 -0
- package/tests/core/dictionary-service.test.ts +116 -0
- package/tests/core/dictionary.test.ts +168 -0
- package/tests/core/empathy-keyword-matcher.test.ts +209 -0
- package/tests/core/event-log.test.ts +181 -0
- package/tests/core/evolution-e2e.test.ts +58 -0
- package/tests/core/evolution-engine-gate-integration.test.ts +543 -0
- package/tests/core/evolution-engine.test.ts +562 -0
- package/tests/core/evolution-logger.test.ts +148 -0
- package/tests/core/evolution-migration.test.ts +50 -0
- package/tests/core/evolution-paths.test.ts +21 -0
- package/tests/core/evolution-reducer.detector-metadata.test.ts +602 -0
- package/tests/core/evolution-reducer.test.ts +180 -0
- package/tests/core/evolution-types-loop.test.ts +48 -0
- package/tests/core/evolution-user-stories.e2e.test.ts +249 -0
- package/tests/core/external-training-contract.test.ts +463 -0
- package/tests/core/focus-history.test.ts +682 -0
- package/tests/core/init-flatten.test.ts +69 -0
- package/tests/core/init-refactor.test.ts +87 -0
- package/tests/core/init-v1.3.test.ts +46 -0
- package/tests/core/init.test.ts +190 -0
- package/tests/core/local-worker-routing.test.ts +757 -0
- package/tests/core/migration.test.ts +84 -0
- package/tests/core/model-deployment-registry.test.ts +845 -0
- package/tests/core/model-training-registry.test.ts +889 -0
- package/tests/core/nocturnal-arbiter.test.ts +494 -0
- package/tests/core/nocturnal-candidate-scoring.test.ts +400 -0
- package/tests/core/nocturnal-compliance.test.ts +646 -0
- package/tests/core/nocturnal-dataset.test.ts +892 -0
- package/tests/core/nocturnal-executability.test.ts +357 -0
- package/tests/core/nocturnal-export.test.ts +462 -0
- package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +428 -0
- package/tests/core/nocturnal-trajectory-extractor.test.ts +634 -0
- package/tests/core/nocturnal-trinity.test.ts +953 -0
- package/tests/core/pain.test.ts +33 -0
- package/tests/core/path-resolver.test.ts +57 -0
- package/tests/core/paths-refactor.test.ts +42 -0
- package/tests/core/phase7-rollout-integration.test.ts +477 -0
- package/tests/core/principle-training-state.test.ts +712 -0
- package/tests/core/profile.test.ts +56 -0
- package/tests/core/promotion-gate.test.ts +556 -0
- package/tests/core/risk-calculator.test.ts +168 -0
- package/tests/core/session-tracker.test.ts +191 -0
- package/tests/core/training-program.test.ts +472 -0
- package/tests/core/trajectory.test.ts +265 -0
- package/tests/core/workspace-context-factory.test.ts +18 -0
- package/tests/core/workspace-context.test.ts +134 -0
- package/tests/fixtures/nocturnal-reviewed-subset.json +183 -0
- package/tests/fixtures/production-compatibility.test.ts +147 -0
- package/tests/fixtures/production-mock-generator.ts +282 -0
- package/tests/hooks/bash-risk-integration.test.ts +137 -0
- package/tests/hooks/bash-risk.test.ts +81 -0
- package/tests/hooks/edit-verification.test.ts +678 -0
- package/tests/hooks/gate-edit-verification-p1.test.ts +632 -0
- package/tests/hooks/gate-edit-verification.test.ts +435 -0
- package/tests/hooks/gate-pipeline-integration.test.ts +404 -0
- package/tests/hooks/gate.test.ts +271 -0
- package/tests/hooks/gfi-gate-unit.test.ts +422 -0
- package/tests/hooks/gfi-gate.test.ts +669 -0
- package/tests/hooks/lifecycle.test.ts +248 -0
- package/tests/hooks/llm.test.ts +308 -0
- package/tests/hooks/message-sanitize.test.ts +36 -0
- package/tests/hooks/pain.test.ts +141 -0
- package/tests/hooks/progressive-trust-gate.test.ts +277 -0
- package/tests/hooks/prompt.test.ts +1411 -0
- package/tests/hooks/subagent.test.ts +467 -0
- package/tests/hooks/thinking-gate.test.ts +313 -0
- package/tests/http/principles-console-route.test.ts +140 -0
- package/tests/hygiene-tracker.test.ts +77 -0
- package/tests/index.integration.test.ts +179 -0
- package/tests/index.shadow-routing.integration.test.ts +140 -0
- package/tests/index.test.ts +9 -0
- package/tests/integration/empathy-workflow-integration.test.ts +627 -0
- package/tests/service/control-ui-query-service.test.ts +121 -0
- package/tests/service/empathy-observer-workflow-manager.test.ts +176 -0
- package/tests/service/evolution-worker.test.ts +585 -0
- package/tests/service/nocturnal-runtime.test.ts +470 -0
- package/tests/service/nocturnal-service.test.ts +577 -0
- package/tests/service/nocturnal-target-selector.test.ts +615 -0
- package/tests/service/nocturnal-workflow-manager.test.ts +439 -0
- package/tests/service/phase3-input-filter.test.ts +289 -0
- package/tests/service/runtime-summary-service.test.ts +919 -0
- package/tests/task-compliance.test.ts +166 -0
- package/tests/test-utils.ts +48 -0
- package/tests/tools/critique-prompt.test.ts +260 -0
- package/tests/tools/deep-reflect.test.ts +232 -0
- package/tests/tools/model-index.test.ts +246 -0
- package/tests/ui/app.test.tsx +114 -0
- package/tests/utils/file-lock.test.ts +407 -0
- package/tests/utils/hashing.test.ts +32 -0
- package/tests/utils/io.test.ts +39 -0
- package/tests/utils/nlp.test.ts +53 -0
- package/tests/utils/plugin-logger.test.ts +156 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/ui/src/App.tsx +45 -0
- package/ui/src/api.ts +216 -0
- package/ui/src/charts.tsx +586 -0
- package/ui/src/components/ErrorState.tsx +6 -0
- package/ui/src/components/Loading.tsx +13 -0
- package/ui/src/components/ProtectedRoute.tsx +12 -0
- package/ui/src/components/Shell.tsx +91 -0
- package/ui/src/components/WorkspaceConfig.tsx +146 -0
- package/ui/src/components/index.ts +5 -0
- package/ui/src/context/auth.tsx +80 -0
- package/ui/src/context/theme.tsx +66 -0
- package/ui/src/hooks/useAutoRefresh.ts +39 -0
- package/ui/src/i18n/ui.ts +363 -0
- package/ui/src/main.tsx +16 -0
- package/ui/src/pages/EvolutionPage.tsx +352 -0
- package/ui/src/pages/FeedbackPage.tsx +140 -0
- package/ui/src/pages/GateMonitorPage.tsx +136 -0
- package/ui/src/pages/LoginPage.tsx +88 -0
- package/ui/src/pages/OverviewPage.tsx +238 -0
- package/ui/src/pages/SamplesPage.tsx +174 -0
- package/ui/src/pages/ThinkingModelsPage.tsx +127 -0
- package/ui/src/styles.css +1661 -0
- package/ui/src/types.ts +368 -0
- package/ui/src/utils/format.ts +15 -0
- package/vitest.config.ts +23 -0
- package/dist/commands/capabilities.d.ts +0 -3
- package/dist/commands/capabilities.js +0 -73
- package/dist/commands/context.d.ts +0 -5
- package/dist/commands/evolution-status.d.ts +0 -4
- package/dist/commands/evolution-status.js +0 -117
- package/dist/commands/evolver.d.ts +0 -9
- package/dist/commands/evolver.js +0 -26
- package/dist/commands/export.d.ts +0 -2
- package/dist/commands/export.js +0 -98
- package/dist/commands/focus.d.ts +0 -14
- package/dist/commands/focus.js +0 -457
- package/dist/commands/nocturnal-review.d.ts +0 -24
- package/dist/commands/nocturnal-review.js +0 -265
- package/dist/commands/nocturnal-rollout.d.ts +0 -27
- package/dist/commands/nocturnal-rollout.js +0 -671
- package/dist/commands/nocturnal-train.d.ts +0 -25
- package/dist/commands/nocturnal-train.js +0 -919
- package/dist/commands/pain.d.ts +0 -5
- package/dist/commands/principle-rollback.d.ts +0 -4
- package/dist/commands/principle-rollback.js +0 -22
- package/dist/commands/rollback.d.ts +0 -19
- package/dist/commands/samples.d.ts +0 -2
- package/dist/commands/samples.js +0 -55
- package/dist/commands/strategy.d.ts +0 -3
- package/dist/commands/strategy.js +0 -29
- package/dist/commands/thinking-os.d.ts +0 -2
- package/dist/config/defaults/runtime.d.ts +0 -40
- package/dist/config/errors.d.ts +0 -84
- package/dist/config/errors.js +0 -94
- package/dist/config/index.js +0 -7
- package/dist/constants/diagnostician.d.ts +0 -12
- package/dist/constants/diagnostician.js +0 -56
- package/dist/constants/tools.d.ts +0 -17
- package/dist/constants/tools.js +0 -54
- package/dist/core/adaptive-thresholds.d.ts +0 -186
- package/dist/core/adaptive-thresholds.js +0 -300
- package/dist/core/config-service.d.ts +0 -15
- package/dist/core/config.d.ts +0 -127
- package/dist/core/control-ui-db.d.ts +0 -95
- package/dist/core/control-ui-db.js +0 -292
- package/dist/core/detection-funnel.d.ts +0 -33
- package/dist/core/detection-service.d.ts +0 -15
- package/dist/core/dictionary-service.d.ts +0 -15
- package/dist/core/dictionary.d.ts +0 -38
- package/dist/core/event-log.d.ts +0 -82
- package/dist/core/event-log.js +0 -463
- package/dist/core/evolution-engine.d.ts +0 -118
- package/dist/core/evolution-engine.js +0 -464
- package/dist/core/evolution-logger.d.ts +0 -137
- package/dist/core/evolution-logger.js +0 -256
- package/dist/core/evolution-migration.d.ts +0 -5
- package/dist/core/evolution-migration.js +0 -65
- package/dist/core/evolution-reducer.d.ts +0 -98
- package/dist/core/evolution-reducer.js +0 -465
- package/dist/core/evolution-types.d.ts +0 -287
- package/dist/core/evolution-types.js +0 -78
- package/dist/core/external-training-contract.d.ts +0 -276
- package/dist/core/external-training-contract.js +0 -269
- package/dist/core/focus-history.d.ts +0 -210
- package/dist/core/focus-history.js +0 -1185
- package/dist/core/hygiene/tracker.d.ts +0 -22
- package/dist/core/hygiene/tracker.js +0 -106
- package/dist/core/init.d.ts +0 -12
- package/dist/core/local-worker-routing.d.ts +0 -175
- package/dist/core/local-worker-routing.js +0 -525
- package/dist/core/migration.d.ts +0 -6
- package/dist/core/model-deployment-registry.d.ts +0 -218
- package/dist/core/model-deployment-registry.js +0 -503
- package/dist/core/model-training-registry.d.ts +0 -295
- package/dist/core/model-training-registry.js +0 -475
- package/dist/core/nocturnal-arbiter.d.ts +0 -159
- package/dist/core/nocturnal-arbiter.js +0 -534
- package/dist/core/nocturnal-candidate-scoring.d.ts +0 -137
- package/dist/core/nocturnal-candidate-scoring.js +0 -266
- package/dist/core/nocturnal-compliance.d.ts +0 -175
- package/dist/core/nocturnal-compliance.js +0 -824
- package/dist/core/nocturnal-dataset.d.ts +0 -224
- package/dist/core/nocturnal-dataset.js +0 -443
- package/dist/core/nocturnal-executability.d.ts +0 -85
- package/dist/core/nocturnal-executability.js +0 -331
- package/dist/core/nocturnal-export.d.ts +0 -124
- package/dist/core/nocturnal-export.js +0 -275
- package/dist/core/nocturnal-paths.d.ts +0 -124
- package/dist/core/nocturnal-trajectory-extractor.d.ts +0 -242
- package/dist/core/nocturnal-trajectory-extractor.js +0 -307
- package/dist/core/nocturnal-trinity.d.ts +0 -311
- package/dist/core/nocturnal-trinity.js +0 -880
- package/dist/core/pain.d.ts +0 -4
- package/dist/core/pain.js +0 -70
- package/dist/core/path-resolver.d.ts +0 -46
- package/dist/core/paths.d.ts +0 -65
- package/dist/core/principle-training-state.d.ts +0 -121
- package/dist/core/principle-training-state.js +0 -321
- package/dist/core/profile.d.ts +0 -62
- package/dist/core/profile.js +0 -210
- package/dist/core/promotion-gate.d.ts +0 -238
- package/dist/core/promotion-gate.js +0 -529
- package/dist/core/risk-calculator.d.ts +0 -22
- package/dist/core/session-tracker.d.ts +0 -99
- package/dist/core/shadow-observation-registry.d.ts +0 -217
- package/dist/core/shadow-observation-registry.js +0 -308
- package/dist/core/system-logger.d.ts +0 -8
- package/dist/core/thinking-models.d.ts +0 -38
- package/dist/core/thinking-models.js +0 -170
- package/dist/core/training-program.d.ts +0 -233
- package/dist/core/training-program.js +0 -433
- package/dist/core/trajectory.d.ts +0 -411
- package/dist/core/trajectory.js +0 -1307
- package/dist/core/workspace-context.d.ts +0 -71
- package/dist/hooks/bash-risk.d.ts +0 -57
- package/dist/hooks/bash-risk.js +0 -137
- package/dist/hooks/edit-verification.d.ts +0 -62
- package/dist/hooks/edit-verification.js +0 -256
- package/dist/hooks/gate-block-helper.d.ts +0 -44
- package/dist/hooks/gate-block-helper.js +0 -119
- package/dist/hooks/gate.d.ts +0 -24
- package/dist/hooks/gate.js +0 -173
- package/dist/hooks/gfi-gate.d.ts +0 -40
- package/dist/hooks/gfi-gate.js +0 -113
- package/dist/hooks/lifecycle.d.ts +0 -5
- package/dist/hooks/lifecycle.js +0 -284
- package/dist/hooks/llm.d.ts +0 -12
- package/dist/hooks/message-sanitize.d.ts +0 -3
- package/dist/hooks/message-sanitize.js +0 -37
- package/dist/hooks/pain.d.ts +0 -5
- package/dist/hooks/pain.js +0 -301
- package/dist/hooks/progressive-trust-gate.d.ts +0 -51
- package/dist/hooks/progressive-trust-gate.js +0 -89
- package/dist/hooks/prompt.d.ts +0 -47
- package/dist/hooks/prompt.js +0 -884
- package/dist/hooks/subagent.d.ts +0 -10
- package/dist/hooks/subagent.js +0 -387
- package/dist/hooks/thinking-checkpoint.d.ts +0 -37
- package/dist/hooks/thinking-checkpoint.js +0 -51
- package/dist/hooks/trajectory-collector.d.ts +0 -32
- package/dist/hooks/trajectory-collector.js +0 -256
- package/dist/http/principles-console-route.d.ts +0 -9
- package/dist/http/principles-console-route.js +0 -567
- package/dist/i18n/commands.d.ts +0 -26
- package/dist/i18n/commands.js +0 -116
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -581
- package/dist/service/central-database.d.ts +0 -104
- package/dist/service/central-database.js +0 -649
- package/dist/service/control-ui-query-service.d.ts +0 -221
- package/dist/service/control-ui-query-service.js +0 -543
- package/dist/service/empathy-observer-manager.d.ts +0 -52
- package/dist/service/empathy-observer-manager.js +0 -229
- package/dist/service/evolution-query-service.d.ts +0 -155
- package/dist/service/evolution-query-service.js +0 -258
- package/dist/service/evolution-worker.d.ts +0 -101
- package/dist/service/evolution-worker.js +0 -974
- package/dist/service/nocturnal-runtime.d.ts +0 -183
- package/dist/service/nocturnal-service.d.ts +0 -163
- package/dist/service/nocturnal-service.js +0 -787
- package/dist/service/nocturnal-target-selector.d.ts +0 -145
- package/dist/service/nocturnal-target-selector.js +0 -315
- package/dist/service/phase3-input-filter.d.ts +0 -73
- package/dist/service/phase3-input-filter.js +0 -172
- package/dist/service/runtime-summary-service.d.ts +0 -122
- package/dist/service/runtime-summary-service.js +0 -485
- package/dist/service/trajectory-service.d.ts +0 -2
- package/dist/service/trajectory-service.js +0 -15
- package/dist/tools/critique-prompt.d.ts +0 -14
- package/dist/tools/deep-reflect.d.ts +0 -39
- package/dist/tools/deep-reflect.js +0 -350
- package/dist/tools/model-index.d.ts +0 -9
- package/dist/types/event-types.d.ts +0 -306
- package/dist/types/event-types.js +0 -106
- package/dist/types/hygiene-types.d.ts +0 -20
- package/dist/types/hygiene-types.js +0 -12
- package/dist/types/runtime-summary.d.ts +0 -47
- package/dist/types/runtime-summary.js +0 -1
- package/dist/types.d.ts +0 -50
- package/dist/types.js +0 -22
- package/dist/utils/file-lock.d.ts +0 -71
- package/dist/utils/file-lock.js +0 -309
- package/dist/utils/glob-match.d.ts +0 -28
- package/dist/utils/hashing.d.ts +0 -9
- package/dist/utils/io.d.ts +0 -6
- package/dist/utils/io.js +0 -106
- package/dist/utils/nlp.d.ts +0 -9
- package/dist/utils/plugin-logger.d.ts +0 -39
- package/dist/utils/subagent-probe.d.ts +0 -34
- package/dist/utils/subagent-probe.js +0 -81
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production Compatibility Tests
|
|
3
|
+
*
|
|
4
|
+
* These tests validate that new code works correctly with production data patterns.
|
|
5
|
+
* Run with: npm test -- tests/fixtures/production-compatibility.test.ts
|
|
6
|
+
*
|
|
7
|
+
* NOTE: These tests require access to ~/.openclaw directory (production data)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as fs from 'fs';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import { describe, expect, it, beforeAll } from 'vitest';
|
|
13
|
+
import {
|
|
14
|
+
loadProductionPainFlag,
|
|
15
|
+
loadProductionEvolutionQueue,
|
|
16
|
+
loadProductionPainCandidates,
|
|
17
|
+
generateTestFixtureFromProduction,
|
|
18
|
+
validateProductionCompatibility,
|
|
19
|
+
createMockQueueItem,
|
|
20
|
+
createMockPainFlag,
|
|
21
|
+
PRODUCTION_FIXTURES,
|
|
22
|
+
} from './production-mock-generator.js';
|
|
23
|
+
|
|
24
|
+
// Skip tests if production data not available
|
|
25
|
+
const hasProductionData = fs.existsSync(PRODUCTION_FIXTURES.STATE_DIR);
|
|
26
|
+
|
|
27
|
+
describe.skipIf(!hasProductionData)('Production Data Compatibility', () => {
|
|
28
|
+
describe('Data Loading', () => {
|
|
29
|
+
it('should load production pain_flag', () => {
|
|
30
|
+
const painFlag = loadProductionPainFlag();
|
|
31
|
+
// May be null if no active pain
|
|
32
|
+
if (painFlag) {
|
|
33
|
+
expect(painFlag).toHaveProperty('score');
|
|
34
|
+
expect(painFlag).toHaveProperty('source');
|
|
35
|
+
expect(painFlag).toHaveProperty('reason');
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should load production evolution_queue', () => {
|
|
40
|
+
const queue = loadProductionEvolutionQueue();
|
|
41
|
+
expect(Array.isArray(queue)).toBe(true);
|
|
42
|
+
|
|
43
|
+
if (queue.length > 0) {
|
|
44
|
+
const item = queue[0];
|
|
45
|
+
expect(item).toHaveProperty('id');
|
|
46
|
+
expect(item).toHaveProperty('score');
|
|
47
|
+
expect(item).toHaveProperty('status');
|
|
48
|
+
expect(['pending', 'in_progress', 'completed', 'resolved']).toContain(item.status);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should load production pain_candidates', () => {
|
|
53
|
+
const candidates = loadProductionPainCandidates();
|
|
54
|
+
expect(typeof candidates).toBe('object');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('New Field Compatibility', () => {
|
|
59
|
+
it('should detect missing session_id in pain_flag (expected: fails until production updated)', () => {
|
|
60
|
+
const result = validateProductionCompatibility();
|
|
61
|
+
|
|
62
|
+
// This test documents the current state - not a failure
|
|
63
|
+
console.log('Compatibility issues:', result.issues);
|
|
64
|
+
console.log('Production data:', result.productionData);
|
|
65
|
+
|
|
66
|
+
// New fields are expected to be missing in production
|
|
67
|
+
if (!result.compatible) {
|
|
68
|
+
console.log('⚠️ Production data does not have new fields (session_id, agent_id)');
|
|
69
|
+
console.log(' This is expected after code changes. Production will be updated on next pain event.');
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should handle queue items without session_id gracefully', () => {
|
|
74
|
+
const queue = loadProductionEvolutionQueue();
|
|
75
|
+
|
|
76
|
+
for (const item of queue.slice(0, 5)) {
|
|
77
|
+
// Code should handle missing session_id/agent_id
|
|
78
|
+
expect(() => {
|
|
79
|
+
const normalized = {
|
|
80
|
+
...item,
|
|
81
|
+
session_id: item.session_id || undefined,
|
|
82
|
+
agent_id: item.agent_id || undefined,
|
|
83
|
+
};
|
|
84
|
+
return normalized;
|
|
85
|
+
}).not.toThrow();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('Pattern Extraction', () => {
|
|
91
|
+
it('should extract patterns from production data', () => {
|
|
92
|
+
const fixture = generateTestFixtureFromProduction();
|
|
93
|
+
|
|
94
|
+
expect(fixture.patterns).toBeDefined();
|
|
95
|
+
expect(fixture.patterns.painSources.length).toBeGreaterThanOrEqual(0);
|
|
96
|
+
|
|
97
|
+
console.log('Pain sources:', fixture.patterns.painSources);
|
|
98
|
+
console.log('Score distribution:', fixture.patterns.scoreDistribution);
|
|
99
|
+
console.log('Status distribution:', fixture.patterns.statusDistribution);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('Mock Generation', () => {
|
|
104
|
+
it('should create realistic mock queue items', () => {
|
|
105
|
+
const item = createMockQueueItem({
|
|
106
|
+
session_id: 'test-session-123',
|
|
107
|
+
agent_id: 'main',
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(item.session_id).toBe('test-session-123');
|
|
111
|
+
expect(item.agent_id).toBe('main');
|
|
112
|
+
expect(item.id).toBeDefined();
|
|
113
|
+
expect(item.timestamp).toBeDefined();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should create realistic mock pain flags', () => {
|
|
117
|
+
const flag = createMockPainFlag({
|
|
118
|
+
session_id: 'test-session-456',
|
|
119
|
+
agent_id: 'builder',
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(flag.session_id).toBe('test-session-456');
|
|
123
|
+
expect(flag.agent_id).toBe('builder');
|
|
124
|
+
expect(flag.time).toBeDefined();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('Edge Cases from Production', () => {
|
|
130
|
+
it('should handle empty evolution queue', () => {
|
|
131
|
+
const item = createMockQueueItem(); // Uses fallback template
|
|
132
|
+
expect(item).toBeDefined();
|
|
133
|
+
expect(item.id).toBeDefined();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should handle very long reason strings', () => {
|
|
137
|
+
const longReason = 'A'.repeat(10000);
|
|
138
|
+
const item = createMockQueueItem({ reason: longReason });
|
|
139
|
+
expect(item.reason).toBe(longReason);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should handle special characters in reason', () => {
|
|
143
|
+
const specialReason = 'Error: "quotes" and \n newlines \t tabs';
|
|
144
|
+
const item = createMockQueueItem({ reason: specialReason });
|
|
145
|
+
expect(item.reason).toBe(specialReason);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production Mock Data Generator
|
|
3
|
+
*
|
|
4
|
+
* Extracts patterns from production data to create realistic test fixtures.
|
|
5
|
+
* This ensures tests match real-world scenarios and catches edge cases.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
|
|
11
|
+
// Production data paths
|
|
12
|
+
const OPENCLAW_HOME = process.env.HOME + '/.openclaw';
|
|
13
|
+
const WORKSPACE_MAIN = OPENCLAW_HOME + '/workspace-main';
|
|
14
|
+
const STATE_DIR = WORKSPACE_MAIN + '/.state';
|
|
15
|
+
|
|
16
|
+
// Types extracted from production
|
|
17
|
+
export interface ProductionPainFlag {
|
|
18
|
+
is_risky: string;
|
|
19
|
+
reason: string;
|
|
20
|
+
score: string;
|
|
21
|
+
source: string;
|
|
22
|
+
time: string;
|
|
23
|
+
status?: string;
|
|
24
|
+
task_id?: string;
|
|
25
|
+
session_id?: string; // New field
|
|
26
|
+
agent_id?: string; // New field
|
|
27
|
+
trace_id?: string; // From recent changes
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ProductionEvolutionQueueItem {
|
|
31
|
+
id: string;
|
|
32
|
+
score: number;
|
|
33
|
+
source: string;
|
|
34
|
+
reason: string;
|
|
35
|
+
trigger_text_preview: string;
|
|
36
|
+
timestamp: string;
|
|
37
|
+
enqueued_at: string;
|
|
38
|
+
status: 'pending' | 'in_progress' | 'completed' | 'resolved';
|
|
39
|
+
task?: string;
|
|
40
|
+
started_at?: string;
|
|
41
|
+
completed_at?: string;
|
|
42
|
+
resolved_at?: string;
|
|
43
|
+
assigned_session_key?: string;
|
|
44
|
+
resolution?: string;
|
|
45
|
+
session_id?: string; // New field
|
|
46
|
+
agent_id?: string; // New field
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ProductionPainCandidate {
|
|
50
|
+
count: number;
|
|
51
|
+
status?: string;
|
|
52
|
+
firstSeen: string;
|
|
53
|
+
lastSeen?: string;
|
|
54
|
+
samples: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Load real pain_flag from production
|
|
59
|
+
*/
|
|
60
|
+
export function loadProductionPainFlag(): ProductionPainFlag | null {
|
|
61
|
+
const painFlagPath = path.join(STATE_DIR, '.pain_flag');
|
|
62
|
+
if (!fs.existsSync(painFlagPath)) return null;
|
|
63
|
+
|
|
64
|
+
const content = fs.readFileSync(painFlagPath, 'utf8');
|
|
65
|
+
const result: ProductionPainFlag = {} as ProductionPainFlag;
|
|
66
|
+
|
|
67
|
+
for (const line of content.split('\n')) {
|
|
68
|
+
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
69
|
+
if (match) {
|
|
70
|
+
(result as any)[match[1]] = match[2];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Load real evolution queue from production
|
|
79
|
+
*/
|
|
80
|
+
export function loadProductionEvolutionQueue(): ProductionEvolutionQueueItem[] {
|
|
81
|
+
const queuePath = path.join(STATE_DIR, 'evolution_queue.json');
|
|
82
|
+
if (!fs.existsSync(queuePath)) return [];
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(fs.readFileSync(queuePath, 'utf8'));
|
|
86
|
+
} catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Load real pain candidates from production
|
|
93
|
+
*/
|
|
94
|
+
export function loadProductionPainCandidates(): Record<string, ProductionPainCandidate> {
|
|
95
|
+
const candidatesPath = path.join(STATE_DIR, 'pain_candidates.json');
|
|
96
|
+
if (!fs.existsSync(candidatesPath)) return {};
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const data = JSON.parse(fs.readFileSync(candidatesPath, 'utf8'));
|
|
100
|
+
return data.candidates || {};
|
|
101
|
+
} catch {
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Sample a JSONL session file for message patterns
|
|
108
|
+
*/
|
|
109
|
+
export function sampleSessionMessages(
|
|
110
|
+
sessionId: string,
|
|
111
|
+
agentId: string = 'main',
|
|
112
|
+
limit: number = 10
|
|
113
|
+
): Array<{ role: string; content: string; timestamp: number }> {
|
|
114
|
+
const sessionPath = path.join(OPENCLAW_HOME, 'agents', agentId, 'sessions', `${sessionId}.jsonl`);
|
|
115
|
+
if (!fs.existsSync(sessionPath)) return [];
|
|
116
|
+
|
|
117
|
+
const messages: Array<{ role: string; content: string; timestamp: number }> = [];
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const lines = fs.readFileSync(sessionPath, 'utf8').split('\n').filter(Boolean);
|
|
121
|
+
for (const line of lines.slice(0, limit * 3)) { // Read more lines to get enough messages
|
|
122
|
+
try {
|
|
123
|
+
const entry = JSON.parse(line);
|
|
124
|
+
if (entry.type === 'message' && entry.message) {
|
|
125
|
+
const msg = entry.message;
|
|
126
|
+
let content = '';
|
|
127
|
+
if (typeof msg.content === 'string') {
|
|
128
|
+
content = msg.content;
|
|
129
|
+
} else if (Array.isArray(msg.content)) {
|
|
130
|
+
content = msg.content
|
|
131
|
+
.filter((c: any) => c.type === 'text')
|
|
132
|
+
.map((c: any) => c.text)
|
|
133
|
+
.join('\n');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Truncate to reasonable size
|
|
137
|
+
if (content.length > 500) {
|
|
138
|
+
content = content.slice(0, 500) + '...';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
messages.push({
|
|
142
|
+
role: msg.role,
|
|
143
|
+
content,
|
|
144
|
+
timestamp: entry.timestamp || Date.now(),
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (messages.length >= limit) break;
|
|
148
|
+
}
|
|
149
|
+
} catch {
|
|
150
|
+
// Skip malformed lines
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return messages;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Generate realistic test fixtures from production patterns
|
|
162
|
+
*/
|
|
163
|
+
export function generateTestFixtureFromProduction() {
|
|
164
|
+
const painFlag = loadProductionPainFlag();
|
|
165
|
+
const queue = loadProductionEvolutionQueue();
|
|
166
|
+
const candidates = loadProductionPainCandidates();
|
|
167
|
+
|
|
168
|
+
// Extract patterns
|
|
169
|
+
const patterns = {
|
|
170
|
+
// Pain sources seen in production
|
|
171
|
+
painSources: [...new Set(queue.map(q => q.source))],
|
|
172
|
+
|
|
173
|
+
// Common error patterns
|
|
174
|
+
errorPatterns: queue.map(q => ({
|
|
175
|
+
type: q.source,
|
|
176
|
+
reasonPreview: q.reason.slice(0, 100),
|
|
177
|
+
})),
|
|
178
|
+
|
|
179
|
+
// Score distribution
|
|
180
|
+
scoreDistribution: queue.reduce((acc, q) => {
|
|
181
|
+
acc[q.score] = (acc[q.score] || 0) + 1;
|
|
182
|
+
return acc;
|
|
183
|
+
}, {} as Record<number, number>),
|
|
184
|
+
|
|
185
|
+
// Status distribution
|
|
186
|
+
statusDistribution: queue.reduce((acc, q) => {
|
|
187
|
+
acc[q.status] = (acc[q.status] || 0) + 1;
|
|
188
|
+
return acc;
|
|
189
|
+
}, {} as Record<string, number>),
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
painFlag,
|
|
194
|
+
queue,
|
|
195
|
+
candidates,
|
|
196
|
+
patterns,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Create a mock evolution queue item based on production patterns
|
|
202
|
+
*/
|
|
203
|
+
export function createMockQueueItem(overrides: Partial<ProductionEvolutionQueueItem> = {}): ProductionEvolutionQueueItem {
|
|
204
|
+
const queue = loadProductionEvolutionQueue();
|
|
205
|
+
const template = queue[0] || {
|
|
206
|
+
id: 'test-001',
|
|
207
|
+
score: 50,
|
|
208
|
+
source: 'tool_failure',
|
|
209
|
+
reason: 'Tool write failed on test.md. Error: Test error.',
|
|
210
|
+
trigger_text_preview: '',
|
|
211
|
+
timestamp: new Date().toISOString(),
|
|
212
|
+
enqueued_at: new Date().toISOString(),
|
|
213
|
+
status: 'pending',
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
...template,
|
|
218
|
+
...overrides,
|
|
219
|
+
id: overrides.id || `test-${Date.now().toString(36)}`,
|
|
220
|
+
timestamp: overrides.timestamp || new Date().toISOString(),
|
|
221
|
+
enqueued_at: overrides.enqueued_at || new Date().toISOString(),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Create a mock pain flag based on production patterns
|
|
227
|
+
*/
|
|
228
|
+
export function createMockPainFlag(overrides: Partial<ProductionPainFlag> = {}): ProductionPainFlag {
|
|
229
|
+
const realFlag = loadProductionPainFlag();
|
|
230
|
+
const template = realFlag || {
|
|
231
|
+
is_risky: 'false',
|
|
232
|
+
reason: 'Test pain signal',
|
|
233
|
+
score: '50',
|
|
234
|
+
source: 'tool_failure',
|
|
235
|
+
time: new Date().toISOString(),
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
...template,
|
|
240
|
+
...overrides,
|
|
241
|
+
time: overrides.time || new Date().toISOString(),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Validate that new code handles production data correctly
|
|
247
|
+
*/
|
|
248
|
+
export function validateProductionCompatibility() {
|
|
249
|
+
const issues: string[] = [];
|
|
250
|
+
|
|
251
|
+
// Check if pain_flag has session_id/agent_id (new fields)
|
|
252
|
+
const painFlag = loadProductionPainFlag();
|
|
253
|
+
if (painFlag && !painFlag.session_id) {
|
|
254
|
+
issues.push('pain_flag missing session_id (new field not yet in production)');
|
|
255
|
+
}
|
|
256
|
+
if (painFlag && !painFlag.agent_id) {
|
|
257
|
+
issues.push('pain_flag missing agent_id (new field not yet in production)');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Check if queue items have session_id/agent_id
|
|
261
|
+
const queue = loadProductionEvolutionQueue();
|
|
262
|
+
const missingSessionId = queue.filter(q => !q.session_id);
|
|
263
|
+
if (missingSessionId.length > 0) {
|
|
264
|
+
issues.push(`evolution_queue: ${missingSessionId.length}/${queue.length} items missing session_id`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
compatible: issues.length === 0,
|
|
269
|
+
issues,
|
|
270
|
+
productionData: {
|
|
271
|
+
painFlag,
|
|
272
|
+
queueCount: queue.length,
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Export for use in tests
|
|
278
|
+
export const PRODUCTION_FIXTURES = {
|
|
279
|
+
OPENCLAW_HOME,
|
|
280
|
+
WORKSPACE_MAIN,
|
|
281
|
+
STATE_DIR,
|
|
282
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { analyzeBashCommand } from '../../src/hooks/bash-risk.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Integration tests for bash-risk module
|
|
6
|
+
* Tests zero-width character detection and command chain analysis
|
|
7
|
+
*/
|
|
8
|
+
describe('Bash Risk Analysis - Integration', () => {
|
|
9
|
+
describe('Zero-width character detection', () => {
|
|
10
|
+
it('should block commands with zero-width space (U+200B)', () => {
|
|
11
|
+
// Zero-width space injected between characters
|
|
12
|
+
const maliciousCmd = 'rm\u200B -rf /';
|
|
13
|
+
const result = analyzeBashCommand(maliciousCmd, [], [], { warn: vi.fn() });
|
|
14
|
+
expect(result).toBe('dangerous');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should block commands with zero-width non-joiner (U+200C)', () => {
|
|
18
|
+
const maliciousCmd = 'rm\u200C -rf /';
|
|
19
|
+
const result = analyzeBashCommand(maliciousCmd, [], [], { warn: vi.fn() });
|
|
20
|
+
expect(result).toBe('dangerous');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should block commands with zero-width joiner (U+200D)', () => {
|
|
24
|
+
const maliciousCmd = 'rm\u200D -rf /';
|
|
25
|
+
const result = analyzeBashCommand(maliciousCmd, [], [], { warn: vi.fn() });
|
|
26
|
+
expect(result).toBe('dangerous');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should block commands with word joiner (U+2060)', () => {
|
|
30
|
+
const maliciousCmd = 'rm\u2060 -rf /';
|
|
31
|
+
const result = analyzeBashCommand(maliciousCmd, [], [], { warn: vi.fn() });
|
|
32
|
+
expect(result).toBe('dangerous');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should block commands with zero-width invisible separator (U+FEFF)', () => {
|
|
36
|
+
const maliciousCmd = 'rm\uFEFF -rf /';
|
|
37
|
+
const result = analyzeBashCommand(maliciousCmd, [], [], { warn: vi.fn() });
|
|
38
|
+
expect(result).toBe('dangerous');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should block commands with multiple zero-width characters', () => {
|
|
42
|
+
const maliciousCmd = 'rm\u200B\u200C\u200D -rf /';
|
|
43
|
+
const result = analyzeBashCommand(maliciousCmd, [], [], { warn: vi.fn() });
|
|
44
|
+
expect(result).toBe('dangerous');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should allow normal commands without zero-width characters', () => {
|
|
48
|
+
const normalCmd = 'rm -rf /tmp/test';
|
|
49
|
+
const result = analyzeBashCommand(normalCmd, ['^rm\\s'], [], { warn: vi.fn() });
|
|
50
|
+
expect(result).toBe('safe');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should detect zero-width characters hidden in normal-looking commands', () => {
|
|
54
|
+
// Command looks like 'git status' but contains hidden chars
|
|
55
|
+
const hiddenCmd = 'git\u200B status';
|
|
56
|
+
const result = analyzeBashCommand(hiddenCmd, [], [], { warn: vi.fn() });
|
|
57
|
+
expect(result).toBe('dangerous');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('Cyrillic homograph attack detection', () => {
|
|
62
|
+
it('should de-obfuscate Cyrillic characters and match dangerous patterns', () => {
|
|
63
|
+
// Cyrillic 'р' (U+0440) looks like Latin 'p' - 'g\u0440ush' → 'gpush' then 'push'
|
|
64
|
+
// After toLowerCase + deobfuscation: 'gpush' won't match dangerous, but 'push' alone might
|
|
65
|
+
// Actually: '\u0440' maps to 'p' so 'g\u0440ush' → 'gp' + 'ush' = 'gpush'
|
|
66
|
+
// Let's use Cyrillic 'е' which maps to 'e': '\u0435' → 'e'
|
|
67
|
+
const cyrillicCmd = 'r\u0435m -rf /tmp';
|
|
68
|
+
const result = analyzeBashCommand(cyrillicCmd, [], ['^rem'], { warn: vi.fn() });
|
|
69
|
+
expect(result).toBe('dangerous');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should handle Cyrillic а being converted to Latin a', () => {
|
|
73
|
+
// Cyrillic 'а' (U+0430) maps to Latin 'a' - 's\u0430do' → 'sado'
|
|
74
|
+
// After de-obfuscation: 'sado' - doesn't match dangerous patterns
|
|
75
|
+
const mixedCmd = 's\u0430do apt update';
|
|
76
|
+
const result = analyzeBashCommand(mixedCmd, [], ['^sudo'], { warn: vi.fn() });
|
|
77
|
+
expect(result).toBe('normal');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('Command chain tokenization', () => {
|
|
82
|
+
it('should detect dangerous commands in chains using &&', () => {
|
|
83
|
+
const chainCmd = 'echo "hello" && rm -rf /tmp/test';
|
|
84
|
+
const result = analyzeBashCommand(chainCmd, [], ['rm\\s+-rf'], { warn: vi.fn() });
|
|
85
|
+
expect(result).toBe('dangerous');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should detect dangerous commands in chains using ||', () => {
|
|
89
|
+
const chainCmd = 'ls || rm -rf /tmp/test';
|
|
90
|
+
const result = analyzeBashCommand(chainCmd, [], ['rm\\s+-rf'], { warn: vi.fn() });
|
|
91
|
+
expect(result).toBe('dangerous');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should detect dangerous commands in chains using ;', () => {
|
|
95
|
+
const chainCmd = 'ls ; rm -rf /tmp/test';
|
|
96
|
+
const result = analyzeBashCommand(chainCmd, [], ['rm\\s+-rf'], { warn: vi.fn() });
|
|
97
|
+
expect(result).toBe('dangerous');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should return safe if no segment matches dangerous patterns', () => {
|
|
101
|
+
const safeChain = 'echo "hello" && echo "world"';
|
|
102
|
+
const result = analyzeBashCommand(safeChain, [], ['rm\\s+-rf'], { warn: vi.fn() });
|
|
103
|
+
expect(result).toBe('normal');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe('Fail-closed behavior', () => {
|
|
108
|
+
it('should return dangerous for invalid regex in dangerousPatterns', () => {
|
|
109
|
+
const cmd = 'ls';
|
|
110
|
+
const result = analyzeBashCommand(cmd, [], ['[invalid'], { warn: vi.fn() });
|
|
111
|
+
expect(result).toBe('dangerous'); // Fail-closed
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should ignore invalid regex in safePatterns', () => {
|
|
115
|
+
const cmd = 'echo hello';
|
|
116
|
+
const warnFn = vi.fn();
|
|
117
|
+
const result = analyzeBashCommand(cmd, ['[invalid'], [], { warn: warnFn });
|
|
118
|
+
// Should not crash, should return normal (not all segments safe)
|
|
119
|
+
expect(result).toBe('normal');
|
|
120
|
+
expect(warnFn).toHaveBeenCalled();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('Safe pattern override', () => {
|
|
125
|
+
it('should return safe when all segments match safe patterns', () => {
|
|
126
|
+
const safeCmd = 'git status';
|
|
127
|
+
const result = analyzeBashCommand(safeCmd, ['^git\\s+status', '^ls'], []);
|
|
128
|
+
expect(result).toBe('safe');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should return normal when not all segments are safe', () => {
|
|
132
|
+
const mixedCmd = 'git status && rm -rf /tmp';
|
|
133
|
+
const result = analyzeBashCommand(mixedCmd, ['^git\\s+status'], ['rm\\s+-rf']);
|
|
134
|
+
expect(result).toBe('dangerous');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { analyzeBashCommand } from '../../src/hooks/bash-risk.js';
|
|
3
|
+
|
|
4
|
+
describe('analyzeBashCommand', () => {
|
|
5
|
+
it('should return safe for commands matching safe patterns', () => {
|
|
6
|
+
const result = analyzeBashCommand('npm install lodash', ['^npm\\s+install'], []);
|
|
7
|
+
expect(result).toBe('safe');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should return dangerous for commands matching dangerous patterns', () => {
|
|
11
|
+
const result = analyzeBashCommand('rm -rf /', [], ['rm\\s+.*-rf']);
|
|
12
|
+
expect(result).toBe('dangerous');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should return normal for commands not in safe/dangerous lists', () => {
|
|
16
|
+
const result = analyzeBashCommand('npm install lodash', [], []);
|
|
17
|
+
expect(result).toBe('normal');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should de-obfuscate Cyrillic lookalikes', () => {
|
|
21
|
+
// Using Cyrillic 'rеset' (with Cyrillic 'е' U+0435) instead of 'reset'
|
|
22
|
+
// This should de-obfuscate to 'git reset --hard' and match the dangerous pattern
|
|
23
|
+
const result = analyzeBashCommand('git rеset --hard', [], ['git\\s+(push\\s+.*--force|reset\\s+--hard|clean\\s+-fd)']);
|
|
24
|
+
expect(result).toBe('dangerous');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should tokenize command chains', () => {
|
|
28
|
+
const result = analyzeBashCommand('npm install && npm test', ['^npm\\s+install'], ['npm\\s+publish']);
|
|
29
|
+
expect(result).toBe('normal');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should fail-closed on invalid dangerous regex', () => {
|
|
33
|
+
const mockLogger = { warn: vi.fn() };
|
|
34
|
+
const result = analyzeBashCommand('echo test', ['^echo'], ['invalid('], mockLogger);
|
|
35
|
+
expect(result).toBe('dangerous'); // Fail-closed behavior
|
|
36
|
+
expect(mockLogger.warn).toHaveBeenCalledWith(
|
|
37
|
+
expect.stringContaining('Invalid dangerous bash regex')
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should ignore safe pattern on invalid safe regex', () => {
|
|
42
|
+
const mockLogger = { warn: vi.fn() };
|
|
43
|
+
const result = analyzeBashCommand('echo test', ['invalid('], [], mockLogger);
|
|
44
|
+
expect(result).toBe('normal'); // Not safe because safe pattern is invalid and ignored
|
|
45
|
+
expect(mockLogger.warn).toHaveBeenCalledWith(
|
|
46
|
+
expect.stringContaining('Invalid safe bash regex')
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should strip $() from commands before pattern matching', () => {
|
|
51
|
+
const result = analyzeBashCommand('$(npm install)', ['^npm\\s+install'], []);
|
|
52
|
+
expect(result).toBe('safe');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should strip backticks from commands before pattern matching', () => {
|
|
56
|
+
const result = analyzeBashCommand('`npm install`', ['^npm\\s+install'], []);
|
|
57
|
+
expect(result).toBe('safe');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should handle command chains with semicolon separator', () => {
|
|
61
|
+
const result = analyzeBashCommand('npm install ; npm test', ['^npm\\s+install'], ['rm\\s+']);
|
|
62
|
+
expect(result).toBe('normal'); // Second command not safe
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should handle command chains with OR separator', () => {
|
|
66
|
+
const result = analyzeBashCommand('npm install || npm test', ['^npm\\s+install'], ['rm\\s+']);
|
|
67
|
+
expect(result).toBe('normal');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should convert uppercase Cyrillic to lowercase Latin', () => {
|
|
71
|
+
// Using uppercase Cyrillic 'Е' (U+0415) which should convert to 'e'
|
|
72
|
+
// 'git REsET --hard' should become 'git reset --hard' and match the dangerous pattern
|
|
73
|
+
const result = analyzeBashCommand('git rEsET --hard', [], ['git\\s+(push\\s+.*--force|reset\\s+--hard|clean\\s+-fd)']);
|
|
74
|
+
expect(result).toBe('dangerous');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should handle additional confusable Unicode characters', () => {
|
|
78
|
+
const result = analyzeBashCommand('del node_modules', [], ['rm\\s+']);
|
|
79
|
+
expect(result).toBe('normal'); // 'del' is not in dangerous patterns
|
|
80
|
+
});
|
|
81
|
+
});
|