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,577 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
import {
|
|
6
|
+
executeNocturnalReflection,
|
|
7
|
+
executeNocturnalReflectionAsync,
|
|
8
|
+
listApprovedNocturnalArtifacts,
|
|
9
|
+
} from '../../src/service/nocturnal-service.js';
|
|
10
|
+
import { createNocturnalTrajectoryExtractor } from '../../src/core/nocturnal-trajectory-extractor.js';
|
|
11
|
+
import { TrajectoryDatabase, TrajectoryRegistry } from '../../src/core/trajectory.js';
|
|
12
|
+
import {
|
|
13
|
+
checkWorkspaceIdle,
|
|
14
|
+
clearAllCooldowns,
|
|
15
|
+
recordRunStart,
|
|
16
|
+
recordRunEnd,
|
|
17
|
+
} from '../../src/service/nocturnal-runtime.js';
|
|
18
|
+
import { NocturnalPathResolver } from '../../src/core/nocturnal-paths.js';
|
|
19
|
+
import { seedSessionForTest, clearSession, listSessions } from '../../src/core/session-tracker.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* NocturnalService Integration Tests
|
|
23
|
+
*
|
|
24
|
+
* NOTE: These tests have complex setup due to:
|
|
25
|
+
* 1. TrajectoryRegistry singleton - must use same instance as service
|
|
26
|
+
* 2. Async cleanup on Windows - wrapped in try-catch
|
|
27
|
+
* 3. Fire-and-forget cooldowns - use awaitClearAllCooldowns helper
|
|
28
|
+
*/
|
|
29
|
+
describe('NocturnalService', () => {
|
|
30
|
+
let tmpDir: string;
|
|
31
|
+
let workspaceDir: string;
|
|
32
|
+
let stateDir: string;
|
|
33
|
+
let trajectory: TrajectoryDatabase;
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
// Create a clean temp directory structure:
|
|
37
|
+
// tmpDir/
|
|
38
|
+
// workspace/ ← workspaceDir
|
|
39
|
+
// state/ ← stateDir
|
|
40
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-nocturnal-service-test-'));
|
|
41
|
+
workspaceDir = path.join(tmpDir, 'workspace');
|
|
42
|
+
stateDir = path.join(tmpDir, 'state');
|
|
43
|
+
fs.mkdirSync(workspaceDir, { recursive: true });
|
|
44
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
45
|
+
|
|
46
|
+
// Initialize trajectory DB and prime the TrajectoryRegistry singleton.
|
|
47
|
+
// The service uses createNocturnalTrajectoryExtractor which internally
|
|
48
|
+
// calls TrajectoryRegistry.get(workspaceDir). We call it here so that
|
|
49
|
+
// the service sees the SAME instance we seed with test data.
|
|
50
|
+
trajectory = new TrajectoryDatabase({ workspaceDir });
|
|
51
|
+
|
|
52
|
+
// Prime the singleton so the service gets our seeded instance
|
|
53
|
+
const extractor = createNocturnalTrajectoryExtractor(workspaceDir);
|
|
54
|
+
// extractor holds the same TrajectoryDatabase instance via singleton
|
|
55
|
+
|
|
56
|
+
// Also clear any residual SessionTracker sessions from previous tests
|
|
57
|
+
// (SessionTracker uses an in-memory Map that persists across tests)
|
|
58
|
+
for (const session of listSessions()) {
|
|
59
|
+
clearSession(session.sessionId);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
// Dispose in correct order: trajectory first, then registry.
|
|
65
|
+
// On Windows, file handles may not be released immediately, so wrap in try-catch.
|
|
66
|
+
try {
|
|
67
|
+
trajectory.dispose();
|
|
68
|
+
} catch {
|
|
69
|
+
// Best effort
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
TrajectoryRegistry.dispose(workspaceDir);
|
|
73
|
+
} catch {
|
|
74
|
+
// Best effort
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
78
|
+
} catch {
|
|
79
|
+
// On Windows, some file handles may still be open
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// -------------------------------------------------------------------------
|
|
84
|
+
// Helper: awaitable clear cooldowns (sync version writes directly)
|
|
85
|
+
// -------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
function clearCooldownsSync(): void {
|
|
88
|
+
// Write empty cooldown state directly without async
|
|
89
|
+
const runtimePath = path.join(stateDir, 'nocturnal-runtime.json');
|
|
90
|
+
const defaultState = {
|
|
91
|
+
principleCooldowns: {},
|
|
92
|
+
recentRunTimestamps: [],
|
|
93
|
+
};
|
|
94
|
+
fs.writeFileSync(runtimePath, JSON.stringify(defaultState, null, 2), 'utf-8');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// -------------------------------------------------------------------------
|
|
98
|
+
// Helper: create a properly idle override (bypasses real idle check)
|
|
99
|
+
// -------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
function makeIdleResult(): ReturnType<typeof checkWorkspaceIdle> {
|
|
102
|
+
return {
|
|
103
|
+
isIdle: true,
|
|
104
|
+
mostRecentActivityAt: Date.now() - 2 * 60 * 60 * 1000, // 2 hours ago
|
|
105
|
+
idleForMs: 2 * 60 * 60 * 1000,
|
|
106
|
+
userActiveSessions: 0,
|
|
107
|
+
abandonedSessionIds: [],
|
|
108
|
+
trajectoryGuardrailConfirmsIdle: true,
|
|
109
|
+
reason: 'test override — workspace considered idle',
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// -------------------------------------------------------------------------
|
|
114
|
+
// Helper: seed a minimal trajectory
|
|
115
|
+
// -------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
function seedSession(
|
|
118
|
+
sessionId: string,
|
|
119
|
+
startedAt: string,
|
|
120
|
+
opts: {
|
|
121
|
+
withToolCalls?: number;
|
|
122
|
+
withPain?: boolean;
|
|
123
|
+
withGateBlock?: boolean;
|
|
124
|
+
outcome?: 'success' | 'failure';
|
|
125
|
+
} = {}
|
|
126
|
+
): void {
|
|
127
|
+
trajectory.recordSession({ sessionId, startedAt });
|
|
128
|
+
const { withToolCalls = 1, withPain = false, withGateBlock = false, outcome = 'failure' } = opts;
|
|
129
|
+
|
|
130
|
+
for (let i = 0; i < withToolCalls; i++) {
|
|
131
|
+
trajectory.recordToolCall({
|
|
132
|
+
sessionId,
|
|
133
|
+
toolName: 'Bash',
|
|
134
|
+
outcome,
|
|
135
|
+
errorMessage: outcome === 'failure' ? 'Command failed: exit code 1' : null,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (withPain) {
|
|
140
|
+
trajectory.recordPainEvent({
|
|
141
|
+
sessionId,
|
|
142
|
+
source: 'test',
|
|
143
|
+
score: 50,
|
|
144
|
+
reason: 'Test pain event',
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (withGateBlock) {
|
|
149
|
+
trajectory.recordGateBlock({
|
|
150
|
+
sessionId,
|
|
151
|
+
toolName: 'Edit',
|
|
152
|
+
reason: 'Safety check: RISK_PATH modification requires PLAN.md',
|
|
153
|
+
riskLevel: 'medium',
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Also seed SessionTracker.sessions so checkWorkspaceIdle can see the session
|
|
158
|
+
// (SessionTracker is separate from TrajectoryDatabase)
|
|
159
|
+
const lastActivityAt = new Date(startedAt).getTime();
|
|
160
|
+
seedSessionForTest(sessionId, workspaceDir, lastActivityAt);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// -------------------------------------------------------------------------
|
|
164
|
+
// Helper: seed evaluable principles (T-08 for most tests)
|
|
165
|
+
// -------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
function seedPrinciples(): void {
|
|
168
|
+
const trainingStatesPath = path.join(stateDir, 'principle_training_state.json');
|
|
169
|
+
// T-01 has low compliance so T-08 wins selection (higher compliance = lower score priority)
|
|
170
|
+
const data = {
|
|
171
|
+
'T-01': {
|
|
172
|
+
principleId: 'T-01',
|
|
173
|
+
principleName: 'Map Before Territory',
|
|
174
|
+
evaluability: 'deterministic',
|
|
175
|
+
complianceRate: 0.3,
|
|
176
|
+
violationTrend: 1,
|
|
177
|
+
observedViolationCount: 3,
|
|
178
|
+
applicableOpportunityCount: 10,
|
|
179
|
+
generatedSampleCount: 1,
|
|
180
|
+
cooldownUntil: null,
|
|
181
|
+
internalizationStatus: 'internalized',
|
|
182
|
+
},
|
|
183
|
+
'T-08': {
|
|
184
|
+
principleId: 'T-08',
|
|
185
|
+
principleName: 'Pain as Signal',
|
|
186
|
+
evaluability: 'deterministic',
|
|
187
|
+
complianceRate: 0.8,
|
|
188
|
+
violationTrend: 1,
|
|
189
|
+
observedViolationCount: 5,
|
|
190
|
+
applicableOpportunityCount: 8,
|
|
191
|
+
generatedSampleCount: 0,
|
|
192
|
+
cooldownUntil: null,
|
|
193
|
+
internalizationStatus: 'internalized',
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
fs.writeFileSync(trainingStatesPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// -------------------------------------------------------------------------
|
|
200
|
+
// Helper: force workspace to be "idle"
|
|
201
|
+
// -------------------------------------------------------------------------
|
|
202
|
+
|
|
203
|
+
function forceIdleWorkspace(): void {
|
|
204
|
+
// Workspace is idle when there are no recent sessions or sessions are old
|
|
205
|
+
// A session from 2+ hours ago will be considered abandoned
|
|
206
|
+
const oldSessionId = 'session-old-abandoned';
|
|
207
|
+
const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000 - 1000).toISOString();
|
|
208
|
+
seedSession(oldSessionId, twoHoursAgo, { withToolCalls: 0 });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// -------------------------------------------------------------------------
|
|
212
|
+
// Tests: executeNocturnalReflection — successful run
|
|
213
|
+
// -------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
describe('executeNocturnalReflection — successful run', () => {
|
|
216
|
+
it('produces an approved artifact when all conditions are met', () => {
|
|
217
|
+
// Setup: idle workspace + evaluable principles + clear cooldowns + violating session
|
|
218
|
+
forceIdleWorkspace();
|
|
219
|
+
seedPrinciples();
|
|
220
|
+
clearCooldownsSync();
|
|
221
|
+
|
|
222
|
+
const recentSessionId = 'session-recent-violation';
|
|
223
|
+
const now = new Date().toISOString();
|
|
224
|
+
seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
|
|
225
|
+
|
|
226
|
+
const idleResult = makeIdleResult();
|
|
227
|
+
const result = executeNocturnalReflection(workspaceDir, stateDir, { idleCheckOverride: idleResult });
|
|
228
|
+
|
|
229
|
+
expect(result.success).toBe(true);
|
|
230
|
+
expect(result.artifact).toBeDefined();
|
|
231
|
+
expect(result.artifact?.principleId).toBe('T-08');
|
|
232
|
+
expect(result.artifact?.sessionId).toBe(recentSessionId);
|
|
233
|
+
expect(result.artifact?.badDecision).toBeTruthy();
|
|
234
|
+
expect(result.artifact?.betterDecision).toBeTruthy();
|
|
235
|
+
expect(result.artifact?.rationale).toBeTruthy();
|
|
236
|
+
expect(result.diagnostics.persisted).toBe(true);
|
|
237
|
+
expect(result.diagnostics.persistedPath).toBeDefined();
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('persists artifact to the samples directory', () => {
|
|
241
|
+
forceIdleWorkspace();
|
|
242
|
+
seedPrinciples();
|
|
243
|
+
clearCooldownsSync();
|
|
244
|
+
|
|
245
|
+
const recentSessionId = 'session-recent-2';
|
|
246
|
+
const now = new Date().toISOString();
|
|
247
|
+
seedSession(recentSessionId, now, { withToolCalls: 2, withPain: true, outcome: 'failure' });
|
|
248
|
+
|
|
249
|
+
const idleResult = makeIdleResult();
|
|
250
|
+
const result = executeNocturnalReflection(workspaceDir, stateDir, { idleCheckOverride: idleResult });
|
|
251
|
+
expect(result.success).toBe(true);
|
|
252
|
+
expect(result.diagnostics.persistedPath).toBeDefined();
|
|
253
|
+
|
|
254
|
+
// Verify file exists
|
|
255
|
+
const persistedPath = result.diagnostics.persistedPath!;
|
|
256
|
+
expect(fs.existsSync(persistedPath)).toBe(true);
|
|
257
|
+
|
|
258
|
+
// Verify content
|
|
259
|
+
const content = JSON.parse(fs.readFileSync(persistedPath, 'utf-8'));
|
|
260
|
+
expect(content.status).toBe('approved');
|
|
261
|
+
expect(content.artifactId).toBe(result.artifact?.artifactId);
|
|
262
|
+
expect(content.boundedAction).toBeDefined();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('returns a boundedAction in the artifact', () => {
|
|
266
|
+
forceIdleWorkspace();
|
|
267
|
+
seedPrinciples();
|
|
268
|
+
clearCooldownsSync();
|
|
269
|
+
|
|
270
|
+
const recentSessionId = 'session-recent-3';
|
|
271
|
+
const now = new Date().toISOString();
|
|
272
|
+
// Seed with pain=true so T-08 is violated (pain+failure needed for T-08)
|
|
273
|
+
seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
|
|
274
|
+
|
|
275
|
+
const idleResult = makeIdleResult();
|
|
276
|
+
const result = executeNocturnalReflection(workspaceDir, stateDir, { idleCheckOverride: idleResult });
|
|
277
|
+
expect(result.success).toBe(true);
|
|
278
|
+
expect(result.artifact?.boundedAction).toBeDefined();
|
|
279
|
+
expect(result.artifact?.boundedAction?.verb).toBeTruthy();
|
|
280
|
+
expect(result.artifact?.boundedAction?.target).toBeTruthy();
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// -------------------------------------------------------------------------
|
|
285
|
+
// Tests: executeNocturnalReflection — skip conditions
|
|
286
|
+
// -------------------------------------------------------------------------
|
|
287
|
+
|
|
288
|
+
describe('executeNocturnalReflection — skip conditions', () => {
|
|
289
|
+
it('skips when workspace is not idle', () => {
|
|
290
|
+
// Setup: NO forceIdleWorkspace - workspace has a very recent session
|
|
291
|
+
seedPrinciples();
|
|
292
|
+
clearCooldownsSync();
|
|
293
|
+
|
|
294
|
+
// Create a session that started JUST NOW (non-idle)
|
|
295
|
+
const activeSessionId = 'session-active';
|
|
296
|
+
const justNow = new Date().toISOString();
|
|
297
|
+
seedSession(activeSessionId, justNow, { withToolCalls: 1 });
|
|
298
|
+
|
|
299
|
+
const result = executeNocturnalReflection(workspaceDir, stateDir);
|
|
300
|
+
|
|
301
|
+
// Workspace is NOT idle, so preflight should block
|
|
302
|
+
expect(result.success).toBe(false);
|
|
303
|
+
expect(result.noTargetSelected).toBe(false); // preflight blocked
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it('skips when no evaluable principles exist', () => {
|
|
307
|
+
forceIdleWorkspace();
|
|
308
|
+
clearCooldownsSync();
|
|
309
|
+
// Don't seed any principles
|
|
310
|
+
|
|
311
|
+
const recentSessionId = 'session-recent';
|
|
312
|
+
const now = new Date().toISOString();
|
|
313
|
+
seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
|
|
314
|
+
|
|
315
|
+
const idleResult = makeIdleResult();
|
|
316
|
+
const result = executeNocturnalReflection(workspaceDir, stateDir, { idleCheckOverride: idleResult });
|
|
317
|
+
expect(result.success).toBe(false);
|
|
318
|
+
expect(result.noTargetSelected).toBe(true);
|
|
319
|
+
expect(result.skipReason).toBe('no_evaluable_principles');
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('skips when no violating sessions found (only successful sessions)', () => {
|
|
323
|
+
forceIdleWorkspace();
|
|
324
|
+
seedPrinciples();
|
|
325
|
+
clearCooldownsSync();
|
|
326
|
+
|
|
327
|
+
// Only successful sessions (no violations)
|
|
328
|
+
const sessionId = 'session-success-only';
|
|
329
|
+
const now = new Date().toISOString();
|
|
330
|
+
seedSession(sessionId, now, { withToolCalls: 3, outcome: 'success' });
|
|
331
|
+
|
|
332
|
+
const idleResult = makeIdleResult();
|
|
333
|
+
const result = executeNocturnalReflection(workspaceDir, stateDir, { idleCheckOverride: idleResult });
|
|
334
|
+
expect(result.success).toBe(false);
|
|
335
|
+
expect(result.noTargetSelected).toBe(true);
|
|
336
|
+
expect(result.skipReason).toBe('no_violating_sessions');
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('returns failure when snapshot extraction fails', () => {
|
|
340
|
+
forceIdleWorkspace();
|
|
341
|
+
seedPrinciples();
|
|
342
|
+
clearCooldownsSync();
|
|
343
|
+
|
|
344
|
+
// Create a session with no tool calls - will fail snapshot extraction
|
|
345
|
+
const sessionId = 'session-no-toolcalls';
|
|
346
|
+
const now = new Date().toISOString();
|
|
347
|
+
seedSession(sessionId, now, { withToolCalls: 0, withPain: false, outcome: 'success' });
|
|
348
|
+
|
|
349
|
+
const idleResult = makeIdleResult();
|
|
350
|
+
const result = executeNocturnalReflection(workspaceDir, stateDir, { idleCheckOverride: idleResult });
|
|
351
|
+
// Session has no tool calls, so minToolCalls=1 filter removes it
|
|
352
|
+
// No sessions available → selection skips with insufficient_snapshot_data
|
|
353
|
+
expect(result.success).toBe(false);
|
|
354
|
+
expect(result.noTargetSelected).toBe(true);
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// -------------------------------------------------------------------------
|
|
359
|
+
// Tests: executeNocturnalReflection — reflector override
|
|
360
|
+
// -------------------------------------------------------------------------
|
|
361
|
+
|
|
362
|
+
describe('executeNocturnalReflection — reflector override', () => {
|
|
363
|
+
it('uses reflectorOutputOverride when skipReflector is true', () => {
|
|
364
|
+
forceIdleWorkspace();
|
|
365
|
+
seedPrinciples();
|
|
366
|
+
clearCooldownsSync();
|
|
367
|
+
|
|
368
|
+
const recentSessionId = 'session-override-test';
|
|
369
|
+
const now = new Date().toISOString();
|
|
370
|
+
seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
|
|
371
|
+
|
|
372
|
+
const overrideArtifact = {
|
|
373
|
+
artifactId: '11111111-2222-4333-aaaa-bbbbbbbbbbbb',
|
|
374
|
+
sessionId: recentSessionId,
|
|
375
|
+
principleId: 'T-08',
|
|
376
|
+
sourceSnapshotRef: 'snapshot-override',
|
|
377
|
+
badDecision: 'Overridden bad decision',
|
|
378
|
+
betterDecision: 'Read the error message before retrying the bash command',
|
|
379
|
+
rationale: 'Overridden rationale for testing purposes',
|
|
380
|
+
createdAt: new Date().toISOString(),
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const idleResult = makeIdleResult();
|
|
384
|
+
const result = executeNocturnalReflection(workspaceDir, stateDir, {
|
|
385
|
+
skipReflector: true,
|
|
386
|
+
reflectorOutputOverride: JSON.stringify(overrideArtifact),
|
|
387
|
+
idleCheckOverride: idleResult,
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
expect(result.success).toBe(true);
|
|
391
|
+
expect(result.artifact?.artifactId).toBe('11111111-2222-4333-aaaa-bbbbbbbbbbbb');
|
|
392
|
+
expect(result.artifact?.badDecision).toBe('Overridden bad decision');
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('fails if skipReflector is true but no override provided', () => {
|
|
396
|
+
forceIdleWorkspace();
|
|
397
|
+
seedPrinciples();
|
|
398
|
+
clearCooldownsSync();
|
|
399
|
+
|
|
400
|
+
// Seed a session so selector finds a target, then validation fails because no override
|
|
401
|
+
const recentSessionId = 'session-no-override';
|
|
402
|
+
const now = new Date().toISOString();
|
|
403
|
+
seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
|
|
404
|
+
|
|
405
|
+
const idleResult = makeIdleResult();
|
|
406
|
+
const result = executeNocturnalReflection(workspaceDir, stateDir, {
|
|
407
|
+
skipReflector: true,
|
|
408
|
+
idleCheckOverride: idleResult,
|
|
409
|
+
// no reflectorOutputOverride
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
expect(result.success).toBe(false);
|
|
413
|
+
expect(result.validationFailed).toBe(true);
|
|
414
|
+
expect(result.validationFailures.some(f => f.includes('reflectorOutputOverride'))).toBe(true);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('rejects invalid JSON in reflectorOutputOverride', () => {
|
|
418
|
+
forceIdleWorkspace();
|
|
419
|
+
seedPrinciples();
|
|
420
|
+
clearCooldownsSync();
|
|
421
|
+
|
|
422
|
+
const recentSessionId = 'session-bad-override';
|
|
423
|
+
const now = new Date().toISOString();
|
|
424
|
+
seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
|
|
425
|
+
|
|
426
|
+
const idleResult = makeIdleResult();
|
|
427
|
+
const result = executeNocturnalReflection(workspaceDir, stateDir, {
|
|
428
|
+
skipReflector: true,
|
|
429
|
+
reflectorOutputOverride: 'not valid json',
|
|
430
|
+
idleCheckOverride: idleResult,
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
expect(result.success).toBe(false);
|
|
434
|
+
expect(result.validationFailed).toBe(true);
|
|
435
|
+
expect(result.validationFailures.some(f => f.includes('parse'))).toBe(true);
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// -------------------------------------------------------------------------
|
|
440
|
+
// Tests: executeNocturnalReflection — arbiter/executability rejection
|
|
441
|
+
// -------------------------------------------------------------------------
|
|
442
|
+
|
|
443
|
+
describe('executeNocturnalReflection — validation rejection', () => {
|
|
444
|
+
it('rejects artifact with vague verb in betterDecision', () => {
|
|
445
|
+
forceIdleWorkspace();
|
|
446
|
+
seedPrinciples();
|
|
447
|
+
clearCooldownsSync();
|
|
448
|
+
|
|
449
|
+
const recentSessionId = 'session-vague';
|
|
450
|
+
const now = new Date().toISOString();
|
|
451
|
+
seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
|
|
452
|
+
|
|
453
|
+
const overrideArtifact = {
|
|
454
|
+
artifactId: '22222222-3333-4444-aaaa-cccccccccccc',
|
|
455
|
+
sessionId: recentSessionId,
|
|
456
|
+
principleId: 'T-08',
|
|
457
|
+
sourceSnapshotRef: 'snapshot-vague',
|
|
458
|
+
badDecision: 'Made a bad decision',
|
|
459
|
+
betterDecision: 'Understand the error first',
|
|
460
|
+
rationale: 'Testing executability rejection for vague verbs',
|
|
461
|
+
createdAt: new Date().toISOString(),
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
const idleResult = makeIdleResult();
|
|
465
|
+
const result = executeNocturnalReflection(workspaceDir, stateDir, {
|
|
466
|
+
skipReflector: true,
|
|
467
|
+
reflectorOutputOverride: JSON.stringify(overrideArtifact),
|
|
468
|
+
idleCheckOverride: idleResult,
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
expect(result.success).toBe(false);
|
|
472
|
+
expect(result.validationFailed).toBe(true);
|
|
473
|
+
expect(result.validationFailures.some(f => f.includes('vague verb'))).toBe(true);
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
// -------------------------------------------------------------------------
|
|
478
|
+
// Tests: listApprovedNocturnalArtifacts
|
|
479
|
+
// -------------------------------------------------------------------------
|
|
480
|
+
|
|
481
|
+
describe('listApprovedNocturnalArtifacts', () => {
|
|
482
|
+
it('returns empty array when no artifacts exist', () => {
|
|
483
|
+
const artifacts = listApprovedNocturnalArtifacts(workspaceDir);
|
|
484
|
+
expect(artifacts).toHaveLength(0);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('returns approved artifacts sorted by createdAt descending', () => {
|
|
488
|
+
// Create some sample artifacts
|
|
489
|
+
const samples = [
|
|
490
|
+
{
|
|
491
|
+
artifactId: 'older-artifact',
|
|
492
|
+
sessionId: 'session-1',
|
|
493
|
+
principleId: 'T-08',
|
|
494
|
+
sourceSnapshotRef: 'snap-1',
|
|
495
|
+
badDecision: 'Bad decision 1',
|
|
496
|
+
betterDecision: 'Better decision 1',
|
|
497
|
+
rationale: 'Rationale 1',
|
|
498
|
+
createdAt: '2026-03-27T10:00:00.000Z',
|
|
499
|
+
persistedAt: '2026-03-27T10:00:00.000Z',
|
|
500
|
+
status: 'approved',
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
artifactId: 'newer-artifact',
|
|
504
|
+
sessionId: 'session-2',
|
|
505
|
+
principleId: 'T-08',
|
|
506
|
+
sourceSnapshotRef: 'snap-2',
|
|
507
|
+
badDecision: 'Bad decision 2',
|
|
508
|
+
betterDecision: 'Better decision 2',
|
|
509
|
+
rationale: 'Rationale 2',
|
|
510
|
+
createdAt: '2026-03-27T12:00:00.000Z',
|
|
511
|
+
persistedAt: '2026-03-27T12:00:00.000Z',
|
|
512
|
+
status: 'approved',
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
artifactId: 'rejected-artifact',
|
|
516
|
+
sessionId: 'session-3',
|
|
517
|
+
principleId: 'T-08',
|
|
518
|
+
sourceSnapshotRef: 'snap-3',
|
|
519
|
+
badDecision: 'Bad decision 3',
|
|
520
|
+
betterDecision: 'Better decision 3',
|
|
521
|
+
rationale: 'Rationale 3',
|
|
522
|
+
createdAt: '2026-03-27T14:00:00.000Z',
|
|
523
|
+
persistedAt: '2026-03-27T14:00:00.000Z',
|
|
524
|
+
status: 'rejected', // Should be filtered out
|
|
525
|
+
},
|
|
526
|
+
];
|
|
527
|
+
|
|
528
|
+
for (const sample of samples) {
|
|
529
|
+
const samplePath = NocturnalPathResolver.samplePath(workspaceDir, sample.artifactId);
|
|
530
|
+
const sampleDir = path.dirname(samplePath);
|
|
531
|
+
if (!fs.existsSync(sampleDir)) {
|
|
532
|
+
fs.mkdirSync(sampleDir, { recursive: true });
|
|
533
|
+
}
|
|
534
|
+
fs.writeFileSync(samplePath, JSON.stringify(sample), 'utf-8');
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const artifacts = listApprovedNocturnalArtifacts(workspaceDir);
|
|
538
|
+
expect(artifacts).toHaveLength(2);
|
|
539
|
+
// Should be sorted by createdAt descending (newer first)
|
|
540
|
+
expect(artifacts[0].artifactId).toBe('newer-artifact');
|
|
541
|
+
expect(artifacts[1].artifactId).toBe('older-artifact');
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('skips malformed JSON files', () => {
|
|
545
|
+
const samplePath = NocturnalPathResolver.samplePath(workspaceDir, 'malformed');
|
|
546
|
+
const sampleDir = path.dirname(samplePath);
|
|
547
|
+
if (!fs.existsSync(sampleDir)) {
|
|
548
|
+
fs.mkdirSync(sampleDir, { recursive: true });
|
|
549
|
+
}
|
|
550
|
+
fs.writeFileSync(samplePath, 'not valid json {{{', 'utf-8');
|
|
551
|
+
|
|
552
|
+
const artifacts = listApprovedNocturnalArtifacts(workspaceDir);
|
|
553
|
+
expect(artifacts).toHaveLength(0);
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// -------------------------------------------------------------------------
|
|
558
|
+
// Tests: executeNocturnalReflectionAsync
|
|
559
|
+
// -------------------------------------------------------------------------
|
|
560
|
+
|
|
561
|
+
describe('executeNocturnalReflectionAsync', () => {
|
|
562
|
+
it('returns a Promise that resolves to the same result as sync version', async () => {
|
|
563
|
+
forceIdleWorkspace();
|
|
564
|
+
seedPrinciples();
|
|
565
|
+
clearCooldownsSync();
|
|
566
|
+
|
|
567
|
+
const recentSessionId = 'session-async-test';
|
|
568
|
+
const now = new Date().toISOString();
|
|
569
|
+
seedSession(recentSessionId, now, { withToolCalls: 3, withPain: true, outcome: 'failure' });
|
|
570
|
+
|
|
571
|
+
const idleResult = makeIdleResult();
|
|
572
|
+
const result = await executeNocturnalReflectionAsync(workspaceDir, stateDir, { idleCheckOverride: idleResult });
|
|
573
|
+
expect(result.success).toBe(true);
|
|
574
|
+
expect(result.artifact).toBeDefined();
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
});
|