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
|
@@ -17,47 +17,176 @@
|
|
|
17
17
|
* - cooldown/quota state is persisted in nocturnal-runtime.json
|
|
18
18
|
* - abandoned sessions (>2h inactive) must not block nocturnal flow
|
|
19
19
|
*/
|
|
20
|
+
|
|
20
21
|
import * as fs from 'fs';
|
|
21
22
|
import * as path from 'path';
|
|
22
|
-
import { listSessions } from '../core/session-tracker.js';
|
|
23
|
+
import { listSessions, SessionState } from '../core/session-tracker.js';
|
|
23
24
|
import { withLockAsync } from '../utils/file-lock.js';
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// System Session Detection
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Returns true if the session was created by a system process (cron, boot, probe, subagent, acp).
|
|
32
|
+
* Uses OpenClaw's native session key patterns to avoid false positives.
|
|
33
|
+
*
|
|
34
|
+
* Detection priority (most reliable first):
|
|
35
|
+
* 1. trigger field: Most reliable — explicitly set by OpenClaw ("cron", "heartbeat", "subagent")
|
|
36
|
+
* 2. sessionKey patterns: Secondary confirmation via structured key (agent:main:cron:...)
|
|
37
|
+
* 3. sessionId prefix: Fallback for boot-, probe- prefixed IDs
|
|
38
|
+
*
|
|
39
|
+
* Excluded (NOT system sessions):
|
|
40
|
+
* - User sessions like agent:main:feishu:user:xxx — third component is channel type
|
|
41
|
+
*/
|
|
42
|
+
function isSystemSession(state: SessionState): boolean {
|
|
43
|
+
const { sessionId, sessionKey, trigger } = state;
|
|
44
|
+
|
|
45
|
+
// Primary: trigger field is explicitly set by OpenClaw - most reliable
|
|
46
|
+
if (trigger === 'cron' || trigger === 'heartbeat' || trigger === 'subagent') {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Secondary: sessionKey pattern matching
|
|
51
|
+
if (sessionKey) {
|
|
52
|
+
const raw = sessionKey.toLowerCase();
|
|
53
|
+
if (raw.includes('cron:')) return true;
|
|
54
|
+
if (raw.includes('subagent:')) return true;
|
|
55
|
+
if (raw.includes('acp:')) return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Fallback: sessionId prefix patterns (boot-, probe-)
|
|
59
|
+
if (sessionId?.startsWith('boot-')) return true;
|
|
60
|
+
if (sessionId?.startsWith('probe-')) return true;
|
|
61
|
+
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
24
65
|
// ---------------------------------------------------------------------------
|
|
25
66
|
// Constants
|
|
26
67
|
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
27
69
|
/** File name for nocturnal runtime bookkeeping */
|
|
28
70
|
export const NOCTURNAL_RUNTIME_FILE = 'nocturnal-runtime.json';
|
|
71
|
+
|
|
29
72
|
/** Default idle threshold: workspace is considered idle if no activity for this duration (ms) */
|
|
30
73
|
export const DEFAULT_IDLE_THRESHOLD_MS = 30 * 60 * 1000; // 30 minutes
|
|
74
|
+
|
|
31
75
|
/** Default cooldown between nocturnal runs (ms) */
|
|
32
76
|
export const DEFAULT_GLOBAL_COOLDOWN_MS = 60 * 60 * 1000; // 1 hour
|
|
77
|
+
|
|
33
78
|
/** Default per-principle cooldown (ms) */
|
|
34
79
|
export const DEFAULT_PRINCIPLE_COOLDOWN_MS = 6 * 60 * 60 * 1000; // 6 hours
|
|
80
|
+
|
|
35
81
|
/** Default maximum nocturnal runs per quota window */
|
|
36
82
|
export const DEFAULT_MAX_RUNS_PER_WINDOW = 3;
|
|
83
|
+
|
|
37
84
|
/** Default quota window size (ms) */
|
|
38
85
|
export const DEFAULT_QUOTA_WINDOW_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
86
|
+
|
|
39
87
|
/** Abandoned session threshold: sessions inactive for longer than this are ignored (ms) */
|
|
40
88
|
export const DEFAULT_ABANDONED_THRESHOLD_MS = 2 * 60 * 60 * 1000; // 2 hours
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// Types
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Persisted state for nocturnal runtime bookkeeping.
|
|
96
|
+
* Stored in {stateDir}/nocturnal-runtime.json
|
|
97
|
+
*/
|
|
98
|
+
export interface NocturnalRuntimeState {
|
|
99
|
+
/** Last time a nocturnal run was started (ISO string) */
|
|
100
|
+
lastRunAt?: string;
|
|
101
|
+
|
|
102
|
+
/** Last time a nocturnal run completed successfully */
|
|
103
|
+
lastSuccessfulRunAt?: string;
|
|
104
|
+
|
|
105
|
+
/** Cooldown end time for global cooldown (ISO string) */
|
|
106
|
+
globalCooldownUntil?: string;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Per-principle cooldown map.
|
|
110
|
+
* Key: principleId, Value: ISO string of cooldown end time
|
|
111
|
+
*/
|
|
112
|
+
principleCooldowns: Record<string, string>;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Sliding window of recent run timestamps.
|
|
116
|
+
* Used for quota enforcement.
|
|
117
|
+
*/
|
|
118
|
+
recentRunTimestamps: string[];
|
|
119
|
+
|
|
120
|
+
/** Metadata about last run (for debugging) */
|
|
121
|
+
lastRunMeta?: {
|
|
122
|
+
targetPrincipleId?: string;
|
|
123
|
+
sampleCount?: number;
|
|
124
|
+
status: 'success' | 'failed' | 'skipped';
|
|
125
|
+
reason?: string;
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Result of an idle check */
|
|
130
|
+
export interface IdleCheckResult {
|
|
131
|
+
/** Whether the workspace is currently idle */
|
|
132
|
+
isIdle: boolean;
|
|
133
|
+
/** Most recent activity timestamp across all sessions (epoch ms) */
|
|
134
|
+
mostRecentActivityAt: number;
|
|
135
|
+
/** How long since the last activity (ms) */
|
|
136
|
+
idleForMs: number;
|
|
137
|
+
/** Number of active (non-abandoned) user sessions found */
|
|
138
|
+
userActiveSessions: number;
|
|
139
|
+
/** List of abandoned session IDs (inactive > abandoned threshold) */
|
|
140
|
+
abandonedSessionIds: string[];
|
|
141
|
+
/** Whether trajectory guardrail also confirms idle */
|
|
142
|
+
trajectoryGuardrailConfirmsIdle: boolean;
|
|
143
|
+
/** Reason for the idle determination */
|
|
144
|
+
reason: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Result of a cooldown check */
|
|
148
|
+
export interface CooldownCheckResult {
|
|
149
|
+
/** Whether the global cooldown is currently active */
|
|
150
|
+
globalCooldownActive: boolean;
|
|
151
|
+
/** When the global cooldown ends (ISO string), null if not in cooldown */
|
|
152
|
+
globalCooldownUntil: string | null;
|
|
153
|
+
/** Remaining ms until global cooldown expires */
|
|
154
|
+
globalCooldownRemainingMs: number;
|
|
155
|
+
/** Whether the principle-specific cooldown is active */
|
|
156
|
+
principleCooldownActive: boolean;
|
|
157
|
+
/** When the principle cooldown ends (ISO string), null if not in cooldown */
|
|
158
|
+
principleCooldownUntil: string | null;
|
|
159
|
+
/** Remaining ms until principle cooldown expires */
|
|
160
|
+
principleCooldownRemainingMs: number;
|
|
161
|
+
/** Whether the quota has been exhausted */
|
|
162
|
+
quotaExhausted: boolean;
|
|
163
|
+
/** Number of runs remaining in current window */
|
|
164
|
+
runsRemaining: number;
|
|
165
|
+
}
|
|
166
|
+
|
|
41
167
|
// ---------------------------------------------------------------------------
|
|
42
168
|
// Default State
|
|
43
169
|
// ---------------------------------------------------------------------------
|
|
44
|
-
|
|
170
|
+
|
|
171
|
+
function createDefaultState(): NocturnalRuntimeState {
|
|
45
172
|
return {
|
|
46
173
|
principleCooldowns: {},
|
|
47
174
|
recentRunTimestamps: [],
|
|
48
175
|
};
|
|
49
176
|
}
|
|
177
|
+
|
|
50
178
|
// ---------------------------------------------------------------------------
|
|
51
179
|
// File Operations (with locking)
|
|
52
180
|
// ---------------------------------------------------------------------------
|
|
53
|
-
|
|
181
|
+
|
|
182
|
+
async function readState(stateDir: string): Promise<NocturnalRuntimeState> {
|
|
54
183
|
const filePath = path.join(stateDir, NOCTURNAL_RUNTIME_FILE);
|
|
55
184
|
if (!fs.existsSync(filePath)) {
|
|
56
185
|
return createDefaultState();
|
|
57
186
|
}
|
|
58
187
|
try {
|
|
59
188
|
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
60
|
-
const parsed = JSON.parse(raw);
|
|
189
|
+
const parsed = JSON.parse(raw) as NocturnalRuntimeState;
|
|
61
190
|
// Ensure required fields exist (migration-safe)
|
|
62
191
|
return {
|
|
63
192
|
principleCooldowns: parsed.principleCooldowns ?? {},
|
|
@@ -67,20 +196,20 @@ async function readState(stateDir) {
|
|
|
67
196
|
globalCooldownUntil: parsed.globalCooldownUntil,
|
|
68
197
|
lastRunMeta: parsed.lastRunMeta,
|
|
69
198
|
};
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
199
|
+
} catch {
|
|
72
200
|
// Corrupted file — start fresh
|
|
73
201
|
return createDefaultState();
|
|
74
202
|
}
|
|
75
203
|
}
|
|
76
|
-
|
|
204
|
+
|
|
205
|
+
function readStateSync(stateDir: string): NocturnalRuntimeState {
|
|
77
206
|
const filePath = path.join(stateDir, NOCTURNAL_RUNTIME_FILE);
|
|
78
207
|
if (!fs.existsSync(filePath)) {
|
|
79
208
|
return createDefaultState();
|
|
80
209
|
}
|
|
81
210
|
try {
|
|
82
211
|
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
83
|
-
const parsed = JSON.parse(raw);
|
|
212
|
+
const parsed = JSON.parse(raw) as NocturnalRuntimeState;
|
|
84
213
|
return {
|
|
85
214
|
principleCooldowns: parsed.principleCooldowns ?? {},
|
|
86
215
|
recentRunTimestamps: parsed.recentRunTimestamps ?? [],
|
|
@@ -89,13 +218,13 @@ function readStateSync(stateDir) {
|
|
|
89
218
|
globalCooldownUntil: parsed.globalCooldownUntil,
|
|
90
219
|
lastRunMeta: parsed.lastRunMeta,
|
|
91
220
|
};
|
|
92
|
-
}
|
|
93
|
-
catch (err) {
|
|
221
|
+
} catch (err) {
|
|
94
222
|
console.warn(`[nocturnal-runtime] State file corrupted, resetting: ${err instanceof Error ? err.message : String(err)}`);
|
|
95
223
|
return createDefaultState();
|
|
96
224
|
}
|
|
97
225
|
}
|
|
98
|
-
|
|
226
|
+
|
|
227
|
+
async function writeState(stateDir: string, state: NocturnalRuntimeState): Promise<void> {
|
|
99
228
|
const filePath = path.join(stateDir, NOCTURNAL_RUNTIME_FILE);
|
|
100
229
|
const stateDirPath = path.dirname(filePath);
|
|
101
230
|
if (!fs.existsSync(stateDirPath)) {
|
|
@@ -105,9 +234,11 @@ async function writeState(stateDir, state) {
|
|
|
105
234
|
fs.writeFileSync(filePath, JSON.stringify(state, null, 2), 'utf-8');
|
|
106
235
|
});
|
|
107
236
|
}
|
|
237
|
+
|
|
108
238
|
// ---------------------------------------------------------------------------
|
|
109
239
|
// Idle Detection
|
|
110
240
|
// ---------------------------------------------------------------------------
|
|
241
|
+
|
|
111
242
|
/**
|
|
112
243
|
* Check if the workspace is currently idle based on session activity.
|
|
113
244
|
*
|
|
@@ -123,28 +254,45 @@ async function writeState(stateDir, state) {
|
|
|
123
254
|
* @param trajectoryLastActivityAt - Optional trajectory timestamp as secondary guardrail
|
|
124
255
|
* @returns IdleCheckResult with full diagnostic information
|
|
125
256
|
*/
|
|
126
|
-
export function checkWorkspaceIdle(
|
|
127
|
-
|
|
257
|
+
export function checkWorkspaceIdle(
|
|
258
|
+
workspaceDir: string,
|
|
259
|
+
options: {
|
|
260
|
+
idleThresholdMs?: number;
|
|
261
|
+
abandonedThresholdMs?: number;
|
|
262
|
+
} = {},
|
|
263
|
+
trajectoryLastActivityAt?: number
|
|
264
|
+
): IdleCheckResult {
|
|
265
|
+
const {
|
|
266
|
+
idleThresholdMs = DEFAULT_IDLE_THRESHOLD_MS,
|
|
267
|
+
abandonedThresholdMs = DEFAULT_ABANDONED_THRESHOLD_MS,
|
|
268
|
+
} = options;
|
|
269
|
+
|
|
128
270
|
const now = Date.now();
|
|
129
271
|
const sessions = listSessions(workspaceDir);
|
|
272
|
+
|
|
130
273
|
// Separate active vs abandoned sessions
|
|
131
|
-
const abandonedSessions = [];
|
|
274
|
+
const abandonedSessions: string[] = [];
|
|
132
275
|
let mostRecentActivityAt = 0;
|
|
133
|
-
let
|
|
276
|
+
let userActiveSessions = 0;
|
|
277
|
+
|
|
134
278
|
for (const session of sessions) {
|
|
279
|
+
// Skip system sessions (cron, boot, probe, subagent, acp) from idle determination
|
|
280
|
+
if (isSystemSession(session)) continue;
|
|
281
|
+
|
|
135
282
|
const inactiveFor = now - session.lastActivityAt;
|
|
136
283
|
if (inactiveFor > abandonedThresholdMs) {
|
|
137
284
|
abandonedSessions.push(session.sessionId);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
activeSessionCount++;
|
|
285
|
+
} else {
|
|
286
|
+
userActiveSessions++;
|
|
141
287
|
if (session.lastActivityAt > mostRecentActivityAt) {
|
|
142
288
|
mostRecentActivityAt = session.lastActivityAt;
|
|
143
289
|
}
|
|
144
290
|
}
|
|
145
291
|
}
|
|
292
|
+
|
|
146
293
|
const idleForMs = mostRecentActivityAt > 0 ? now - mostRecentActivityAt : now;
|
|
147
294
|
const isIdle = mostRecentActivityAt === 0 || idleForMs > idleThresholdMs;
|
|
295
|
+
|
|
148
296
|
// Trajectory guardrail: only used as a secondary check
|
|
149
297
|
// If trajectory says there's recent activity but session state says idle,
|
|
150
298
|
// that's a discrepancy we should note but still trust session state as primary
|
|
@@ -154,32 +302,35 @@ export function checkWorkspaceIdle(workspaceDir, options = {}, trajectoryLastAct
|
|
|
154
302
|
// Guardrail confirms if trajectory also shows idle or near-idle (>80% of threshold)
|
|
155
303
|
trajectoryGuardrailConfirmsIdle = trajectoryIdleFor > idleThresholdMs * 0.8;
|
|
156
304
|
}
|
|
157
|
-
|
|
305
|
+
|
|
306
|
+
let reason: string;
|
|
158
307
|
if (mostRecentActivityAt === 0) {
|
|
159
308
|
reason = 'No active sessions found — workspace is idle';
|
|
160
|
-
}
|
|
161
|
-
else if (isIdle) {
|
|
309
|
+
} else if (isIdle) {
|
|
162
310
|
reason = `Most recent activity ${idleForMs}ms ago (>${idleThresholdMs}ms threshold)`;
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
311
|
+
} else {
|
|
165
312
|
reason = `Recent activity ${idleForMs}ms ago (<${idleThresholdMs}ms threshold)`;
|
|
166
313
|
}
|
|
314
|
+
|
|
167
315
|
if (abandonedSessions.length > 0) {
|
|
168
316
|
reason += `; ${abandonedSessions.length} abandoned session(s) ignored`;
|
|
169
317
|
}
|
|
318
|
+
|
|
170
319
|
return {
|
|
171
320
|
isIdle,
|
|
172
321
|
mostRecentActivityAt,
|
|
173
322
|
idleForMs,
|
|
174
|
-
|
|
323
|
+
userActiveSessions,
|
|
175
324
|
abandonedSessionIds: abandonedSessions,
|
|
176
325
|
trajectoryGuardrailConfirmsIdle,
|
|
177
326
|
reason,
|
|
178
327
|
};
|
|
179
328
|
}
|
|
329
|
+
|
|
180
330
|
// ---------------------------------------------------------------------------
|
|
181
331
|
// Cooldown Management
|
|
182
332
|
// ---------------------------------------------------------------------------
|
|
333
|
+
|
|
183
334
|
/**
|
|
184
335
|
* Check if the workspace is currently in a cooldown period.
|
|
185
336
|
*
|
|
@@ -188,14 +339,31 @@ export function checkWorkspaceIdle(workspaceDir, options = {}, trajectoryLastAct
|
|
|
188
339
|
* @param options - Cooldown configuration options
|
|
189
340
|
* @returns CooldownCheckResult
|
|
190
341
|
*/
|
|
191
|
-
export function checkCooldown(
|
|
192
|
-
|
|
342
|
+
export function checkCooldown(
|
|
343
|
+
stateDir: string,
|
|
344
|
+
principleId?: string,
|
|
345
|
+
options: {
|
|
346
|
+
globalCooldownMs?: number;
|
|
347
|
+
principleCooldownMs?: number;
|
|
348
|
+
maxRunsPerWindow?: number;
|
|
349
|
+
quotaWindowMs?: number;
|
|
350
|
+
} = {}
|
|
351
|
+
): CooldownCheckResult {
|
|
352
|
+
const {
|
|
353
|
+
globalCooldownMs = DEFAULT_GLOBAL_COOLDOWN_MS,
|
|
354
|
+
principleCooldownMs = DEFAULT_PRINCIPLE_COOLDOWN_MS,
|
|
355
|
+
maxRunsPerWindow = DEFAULT_MAX_RUNS_PER_WINDOW,
|
|
356
|
+
quotaWindowMs = DEFAULT_QUOTA_WINDOW_MS,
|
|
357
|
+
} = options;
|
|
358
|
+
|
|
193
359
|
const now = Date.now();
|
|
194
360
|
const state = readStateSync(stateDir);
|
|
361
|
+
|
|
195
362
|
// Global cooldown check
|
|
196
363
|
let globalCooldownActive = false;
|
|
197
364
|
let globalCooldownRemainingMs = 0;
|
|
198
|
-
let globalCooldownUntil = null;
|
|
365
|
+
let globalCooldownUntil: string | null = null;
|
|
366
|
+
|
|
199
367
|
if (state.globalCooldownUntil) {
|
|
200
368
|
const cooldownEnd = new Date(state.globalCooldownUntil).getTime();
|
|
201
369
|
if (cooldownEnd > now) {
|
|
@@ -204,10 +372,12 @@ export function checkCooldown(stateDir, principleId, options = {}) {
|
|
|
204
372
|
globalCooldownUntil = state.globalCooldownUntil;
|
|
205
373
|
}
|
|
206
374
|
}
|
|
375
|
+
|
|
207
376
|
// Principle-specific cooldown check
|
|
208
377
|
let principleCooldownActive = false;
|
|
209
378
|
let principleCooldownRemainingMs = 0;
|
|
210
|
-
let principleCooldownUntil = null;
|
|
379
|
+
let principleCooldownUntil: string | null = null;
|
|
380
|
+
|
|
211
381
|
if (principleId && state.principleCooldowns[principleId]) {
|
|
212
382
|
const cooldownEnd = new Date(state.principleCooldowns[principleId]).getTime();
|
|
213
383
|
if (cooldownEnd > now) {
|
|
@@ -216,13 +386,16 @@ export function checkCooldown(stateDir, principleId, options = {}) {
|
|
|
216
386
|
principleCooldownUntil = state.principleCooldowns[principleId];
|
|
217
387
|
}
|
|
218
388
|
}
|
|
389
|
+
|
|
219
390
|
// Quota check: count runs in sliding window
|
|
220
391
|
const windowStart = now - quotaWindowMs;
|
|
221
392
|
const recentRuns = state.recentRunTimestamps
|
|
222
393
|
.map(ts => new Date(ts).getTime())
|
|
223
394
|
.filter(ts => ts > windowStart);
|
|
395
|
+
|
|
224
396
|
const quotaExhausted = recentRuns.length >= maxRunsPerWindow;
|
|
225
397
|
const runsRemaining = Math.max(0, maxRunsPerWindow - recentRuns.length);
|
|
398
|
+
|
|
226
399
|
return {
|
|
227
400
|
globalCooldownActive,
|
|
228
401
|
globalCooldownUntil,
|
|
@@ -234,6 +407,7 @@ export function checkCooldown(stateDir, principleId, options = {}) {
|
|
|
234
407
|
runsRemaining,
|
|
235
408
|
};
|
|
236
409
|
}
|
|
410
|
+
|
|
237
411
|
/**
|
|
238
412
|
* Record that a nocturnal run has started.
|
|
239
413
|
* Updates global cooldown and quota tracking.
|
|
@@ -241,27 +415,36 @@ export function checkCooldown(stateDir, principleId, options = {}) {
|
|
|
241
415
|
* @param stateDir - State directory
|
|
242
416
|
* @param principleId - Target principle ID for this run
|
|
243
417
|
*/
|
|
244
|
-
export async function recordRunStart(
|
|
418
|
+
export async function recordRunStart(
|
|
419
|
+
stateDir: string,
|
|
420
|
+
principleId: string
|
|
421
|
+
): Promise<void> {
|
|
245
422
|
const state = await readState(stateDir);
|
|
246
423
|
const now = new Date().toISOString();
|
|
424
|
+
|
|
247
425
|
state.lastRunAt = now;
|
|
248
426
|
state.lastRunMeta = {
|
|
249
427
|
targetPrincipleId: principleId,
|
|
250
428
|
status: 'skipped', // Will be updated on completion
|
|
251
429
|
};
|
|
430
|
+
|
|
252
431
|
// Set global cooldown
|
|
253
432
|
const cooldownUntil = new Date(Date.now() + DEFAULT_GLOBAL_COOLDOWN_MS).toISOString();
|
|
254
433
|
state.globalCooldownUntil = cooldownUntil;
|
|
434
|
+
|
|
255
435
|
// Add to recent runs for quota tracking
|
|
256
436
|
state.recentRunTimestamps.push(now);
|
|
437
|
+
|
|
257
438
|
// Prune old timestamps outside the quota window
|
|
258
439
|
const windowStart = Date.now() - DEFAULT_QUOTA_WINDOW_MS;
|
|
259
440
|
state.recentRunTimestamps = state.recentRunTimestamps
|
|
260
441
|
.map(ts => new Date(ts).getTime())
|
|
261
442
|
.filter(ts => ts > windowStart)
|
|
262
443
|
.map(ts => new Date(ts).toISOString());
|
|
444
|
+
|
|
263
445
|
await writeState(stateDir, state);
|
|
264
446
|
}
|
|
447
|
+
|
|
265
448
|
/**
|
|
266
449
|
* Record the outcome of a nocturnal run.
|
|
267
450
|
*
|
|
@@ -269,17 +452,29 @@ export async function recordRunStart(stateDir, principleId) {
|
|
|
269
452
|
* @param outcome - 'success', 'failed', or 'skipped'
|
|
270
453
|
* @param details - Optional details about the run
|
|
271
454
|
*/
|
|
272
|
-
export async function recordRunEnd(
|
|
455
|
+
export async function recordRunEnd(
|
|
456
|
+
stateDir: string,
|
|
457
|
+
outcome: 'success' | 'failed' | 'skipped',
|
|
458
|
+
details?: {
|
|
459
|
+
sampleCount?: number;
|
|
460
|
+
reason?: string;
|
|
461
|
+
}
|
|
462
|
+
): Promise<void> {
|
|
273
463
|
const state = await readState(stateDir);
|
|
274
464
|
const now = new Date().toISOString();
|
|
465
|
+
|
|
275
466
|
if (outcome === 'success') {
|
|
276
467
|
state.lastSuccessfulRunAt = now;
|
|
468
|
+
|
|
277
469
|
// Also set per-principle cooldown if we know which principle was targeted
|
|
278
470
|
if (state.lastRunMeta?.targetPrincipleId) {
|
|
279
471
|
const pid = state.lastRunMeta.targetPrincipleId;
|
|
280
|
-
state.principleCooldowns[pid] = new Date(
|
|
472
|
+
state.principleCooldowns[pid] = new Date(
|
|
473
|
+
Date.now() + DEFAULT_PRINCIPLE_COOLDOWN_MS
|
|
474
|
+
).toISOString();
|
|
281
475
|
}
|
|
282
476
|
}
|
|
477
|
+
|
|
283
478
|
// Update run metadata
|
|
284
479
|
state.lastRunMeta = {
|
|
285
480
|
...state.lastRunMeta,
|
|
@@ -287,16 +482,19 @@ export async function recordRunEnd(stateDir, outcome, details) {
|
|
|
287
482
|
sampleCount: details?.sampleCount ?? state.lastRunMeta?.sampleCount,
|
|
288
483
|
reason: details?.reason ?? state.lastRunMeta?.reason,
|
|
289
484
|
};
|
|
485
|
+
|
|
290
486
|
// Note: global cooldown remains active (set at run start) - we don't clear it on failure
|
|
291
487
|
// This prevents rapid retry loops
|
|
488
|
+
|
|
292
489
|
await writeState(stateDir, state);
|
|
293
490
|
}
|
|
491
|
+
|
|
294
492
|
/**
|
|
295
493
|
* Clear all cooldowns (for testing or admin reset).
|
|
296
494
|
*
|
|
297
495
|
* @param stateDir - State directory
|
|
298
496
|
*/
|
|
299
|
-
export async function clearAllCooldowns(stateDir) {
|
|
497
|
+
export async function clearAllCooldowns(stateDir: string): Promise<void> {
|
|
300
498
|
const state = await readState(stateDir);
|
|
301
499
|
state.globalCooldownUntil = undefined;
|
|
302
500
|
state.principleCooldowns = {};
|
|
@@ -304,15 +502,31 @@ export async function clearAllCooldowns(stateDir) {
|
|
|
304
502
|
state.lastRunMeta = undefined;
|
|
305
503
|
await writeState(stateDir, state);
|
|
306
504
|
}
|
|
505
|
+
|
|
307
506
|
/**
|
|
308
507
|
* Get the current runtime state (for debugging/inspection).
|
|
309
508
|
*
|
|
310
509
|
* @param stateDir - State directory
|
|
311
510
|
* @returns The current NocturnalRuntimeState
|
|
312
511
|
*/
|
|
313
|
-
export async function getRuntimeState(stateDir) {
|
|
512
|
+
export async function getRuntimeState(stateDir: string): Promise<NocturnalRuntimeState> {
|
|
314
513
|
return readState(stateDir);
|
|
315
514
|
}
|
|
515
|
+
|
|
516
|
+
// ---------------------------------------------------------------------------
|
|
517
|
+
// Convenience: Full Pre-Flight Check
|
|
518
|
+
// ---------------------------------------------------------------------------
|
|
519
|
+
|
|
520
|
+
export interface PreflightCheckResult {
|
|
521
|
+
canRun: boolean;
|
|
522
|
+
idle: IdleCheckResult;
|
|
523
|
+
cooldown: CooldownCheckResult;
|
|
524
|
+
/**
|
|
525
|
+
* Human-readable reasons why run is blocked (if canRun is false)
|
|
526
|
+
*/
|
|
527
|
+
blockers: string[];
|
|
528
|
+
}
|
|
529
|
+
|
|
316
530
|
/**
|
|
317
531
|
* Combined pre-flight check for whether a nocturnal run should proceed.
|
|
318
532
|
* Integrates idle + cooldown + quota checks.
|
|
@@ -323,26 +537,39 @@ export async function getRuntimeState(stateDir) {
|
|
|
323
537
|
* @param trajectoryLastActivityAt - Optional trajectory timestamp as secondary guardrail
|
|
324
538
|
* @param idleCheckOverride - Optional override for idle check result (for testing)
|
|
325
539
|
*/
|
|
326
|
-
export function checkPreflight(
|
|
540
|
+
export function checkPreflight(
|
|
541
|
+
workspaceDir: string,
|
|
542
|
+
stateDir: string,
|
|
543
|
+
principleId?: string,
|
|
544
|
+
trajectoryLastActivityAt?: number,
|
|
545
|
+
idleCheckOverride?: IdleCheckResult
|
|
546
|
+
): PreflightCheckResult {
|
|
327
547
|
const idle = idleCheckOverride ?? checkWorkspaceIdle(workspaceDir, {}, trajectoryLastActivityAt);
|
|
328
548
|
const cooldown = checkCooldown(stateDir, principleId);
|
|
329
|
-
|
|
549
|
+
|
|
550
|
+
const blockers: string[] = [];
|
|
551
|
+
|
|
330
552
|
if (!idle.isIdle) {
|
|
331
553
|
blockers.push(`Workspace not idle (active for ${idle.idleForMs}ms, threshold=${DEFAULT_IDLE_THRESHOLD_MS}ms)`);
|
|
332
554
|
}
|
|
555
|
+
|
|
333
556
|
if (cooldown.globalCooldownActive) {
|
|
334
557
|
blockers.push(`Global cooldown active until ${cooldown.globalCooldownUntil}`);
|
|
335
558
|
}
|
|
559
|
+
|
|
336
560
|
if (cooldown.principleCooldownActive) {
|
|
337
561
|
blockers.push(`Principle cooldown active until ${cooldown.principleCooldownUntil}`);
|
|
338
562
|
}
|
|
563
|
+
|
|
339
564
|
if (cooldown.quotaExhausted) {
|
|
340
565
|
blockers.push(`Quota exhausted (${DEFAULT_MAX_RUNS_PER_WINDOW} runs per ${DEFAULT_QUOTA_WINDOW_MS / 3600000}h window)`);
|
|
341
566
|
}
|
|
342
|
-
|
|
567
|
+
|
|
568
|
+
if (idle.abandonedSessionIds.length > 0 && idle.userActiveSessions === 0) {
|
|
343
569
|
// Only block if ALL sessions are abandoned (meaning workspace truly has no activity)
|
|
344
570
|
// If some sessions are active, we trust the session-based idle check
|
|
345
571
|
}
|
|
572
|
+
|
|
346
573
|
return {
|
|
347
574
|
canRun: blockers.length === 0,
|
|
348
575
|
idle,
|