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