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
|
@@ -1,64 +1,81 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import { isRisky } from '../utils/io.js';
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
export type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
|
5
|
+
|
|
6
|
+
export interface FileModification {
|
|
7
|
+
toolName: string;
|
|
8
|
+
params: any;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function estimateLineChanges(modification: FileModification): number {
|
|
4
12
|
const { toolName, params } = modification;
|
|
13
|
+
|
|
5
14
|
if (toolName === 'write_file' || toolName === 'write') {
|
|
6
15
|
const content = params.content || '';
|
|
7
16
|
return content.split('\n').length;
|
|
8
17
|
}
|
|
18
|
+
|
|
9
19
|
if (toolName === 'replace' || toolName === 'edit') {
|
|
10
20
|
const newContent = params.new_string || params.newText || '';
|
|
11
21
|
return newContent.split('\n').length;
|
|
12
22
|
}
|
|
23
|
+
|
|
13
24
|
if (toolName === 'apply_patch' || toolName === 'patch') {
|
|
14
25
|
const patch = params.patch || '';
|
|
15
26
|
// Rough estimate for patch files
|
|
16
|
-
return patch.split('\n').filter((l) => l.startsWith('+') || l.startsWith('-')).length;
|
|
27
|
+
return patch.split('\n').filter((l: string) => l.startsWith('+') || l.startsWith('-')).length;
|
|
17
28
|
}
|
|
29
|
+
|
|
18
30
|
if (toolName === 'delete_file') {
|
|
19
31
|
// Deleting a file is considered a significant change, but we don't know the size.
|
|
20
32
|
// We'll treat it as a medium-to-large size change.
|
|
21
33
|
return 50;
|
|
22
34
|
}
|
|
35
|
+
|
|
23
36
|
return 0;
|
|
24
37
|
}
|
|
25
|
-
|
|
38
|
+
|
|
39
|
+
export function assessRiskLevel(
|
|
40
|
+
filePath: string,
|
|
41
|
+
modification: FileModification,
|
|
42
|
+
riskPaths: string[]
|
|
43
|
+
): RiskLevel {
|
|
26
44
|
const isRiskPath = isRisky(filePath, riskPaths);
|
|
27
45
|
const estimatedLines = estimateLineChanges(modification);
|
|
46
|
+
|
|
28
47
|
if (isRiskPath) {
|
|
29
|
-
if (estimatedLines > 100)
|
|
30
|
-
return 'CRITICAL';
|
|
48
|
+
if (estimatedLines > 100) return 'CRITICAL';
|
|
31
49
|
return 'HIGH';
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (estimatedLines >
|
|
35
|
-
return 'HIGH';
|
|
36
|
-
if (estimatedLines > 10)
|
|
37
|
-
return 'MEDIUM';
|
|
50
|
+
} else {
|
|
51
|
+
if (estimatedLines > 100) return 'HIGH';
|
|
52
|
+
if (estimatedLines > 10) return 'MEDIUM';
|
|
38
53
|
return 'LOW';
|
|
39
54
|
}
|
|
40
55
|
}
|
|
56
|
+
|
|
41
57
|
/**
|
|
42
58
|
* Get the total line count of a target file.
|
|
43
59
|
* @param absoluteFilePath - Absolute path to the file
|
|
44
60
|
* @returns File line count, or null if file doesn't exist or can't be read
|
|
45
61
|
*/
|
|
46
|
-
export function getTargetFileLineCount(absoluteFilePath) {
|
|
62
|
+
export function getTargetFileLineCount(absoluteFilePath: string): number | null {
|
|
47
63
|
try {
|
|
48
64
|
if (!fs.existsSync(absoluteFilePath)) {
|
|
49
65
|
return null; // File genuinely doesn't exist
|
|
50
66
|
}
|
|
67
|
+
|
|
51
68
|
const stats = fs.statSync(absoluteFilePath);
|
|
52
69
|
if (!stats.isFile()) {
|
|
53
70
|
return null; // Not a regular file (directory, device, etc.)
|
|
54
71
|
}
|
|
72
|
+
|
|
55
73
|
const content = fs.readFileSync(absoluteFilePath, 'utf-8');
|
|
56
74
|
return content.split('\n').length;
|
|
57
|
-
}
|
|
58
|
-
catch (e) {
|
|
75
|
+
} catch (e) {
|
|
59
76
|
// Log error before falling back to null - this is intentional for security gates
|
|
60
77
|
const error = e instanceof Error ? e : new Error(String(e));
|
|
61
|
-
const errorCode = e.code;
|
|
78
|
+
const errorCode = (e as NodeJS.ErrnoException).code;
|
|
62
79
|
console.error(`[PD:RISK_CALC] Failed to read file for line count: ${absoluteFilePath}`, {
|
|
63
80
|
code: errorCode,
|
|
64
81
|
message: error.message,
|
|
@@ -66,6 +83,7 @@ export function getTargetFileLineCount(absoluteFilePath) {
|
|
|
66
83
|
return null;
|
|
67
84
|
}
|
|
68
85
|
}
|
|
86
|
+
|
|
69
87
|
/**
|
|
70
88
|
* Calculate the effective line limit based on percentage of target file.
|
|
71
89
|
* @param targetLineCount - Total lines in target file
|
|
@@ -74,14 +92,22 @@ export function getTargetFileLineCount(absoluteFilePath) {
|
|
|
74
92
|
* @param maxLines - Optional upper bound to prevent misconfiguration
|
|
75
93
|
* @returns Maximum allowed lines (at least minLines, at most maxLines if provided)
|
|
76
94
|
*/
|
|
77
|
-
export function calculatePercentageThreshold(
|
|
95
|
+
export function calculatePercentageThreshold(
|
|
96
|
+
targetLineCount: number,
|
|
97
|
+
percentage: number,
|
|
98
|
+
minLines: number,
|
|
99
|
+
maxLines?: number
|
|
100
|
+
): number {
|
|
78
101
|
// Clamp percentage to valid range [0, 100]
|
|
79
102
|
const clampedPercentage = Math.max(0, Math.min(100, percentage));
|
|
103
|
+
|
|
80
104
|
const calculated = Math.round(targetLineCount * (clampedPercentage / 100));
|
|
81
105
|
let effectiveLimit = Math.max(calculated, minLines);
|
|
106
|
+
|
|
82
107
|
// Apply optional upper bound
|
|
83
108
|
if (maxLines !== undefined && maxLines > 0) {
|
|
84
109
|
effectiveLimit = Math.min(effectiveLimit, maxLines);
|
|
85
110
|
}
|
|
111
|
+
|
|
86
112
|
return effectiveLimit;
|
|
87
113
|
}
|
|
@@ -1,94 +1,153 @@
|
|
|
1
|
+
import { PluginHookLlmOutputEvent } from '../openclaw-sdk.js';
|
|
1
2
|
import * as path from 'path';
|
|
2
3
|
import * as fs from 'fs';
|
|
4
|
+
import { PainConfig } from './config.js';
|
|
3
5
|
import { SystemLogger } from './system-logger.js';
|
|
4
|
-
|
|
6
|
+
import { EventLogService } from './event-log.js';
|
|
7
|
+
|
|
8
|
+
export interface TokenUsage {
|
|
9
|
+
input?: number;
|
|
10
|
+
output?: number;
|
|
11
|
+
cacheRead?: number;
|
|
12
|
+
cacheWrite?: number;
|
|
13
|
+
total?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SessionState {
|
|
17
|
+
sessionId: string;
|
|
18
|
+
sessionKey?: string; // Structured session key from OpenClaw (e.g., agent:main:cron:job-1:run:xxx)
|
|
19
|
+
trigger?: string; // Trigger source: "user" | "cron" | "heartbeat" | "memory" | "subagent"
|
|
20
|
+
workspaceDir?: string;
|
|
21
|
+
toolReadsByFile: Record<string, number>;
|
|
22
|
+
llmTurns: number;
|
|
23
|
+
blockedAttempts: number;
|
|
24
|
+
lastActivityAt: number;
|
|
25
|
+
lastControlActivityAt: number;
|
|
26
|
+
totalInputTokens: number;
|
|
27
|
+
totalOutputTokens: number;
|
|
28
|
+
cacheHits: number;
|
|
29
|
+
// Track consecutive loops of similar lengths/ratios (paralysis)
|
|
30
|
+
stuckLoops: number;
|
|
31
|
+
|
|
32
|
+
// GFI - Track A: Empirical Friction
|
|
33
|
+
currentGfi: number;
|
|
34
|
+
gfiBySource?: Record<string, number>;
|
|
35
|
+
lastErrorSource?: string;
|
|
36
|
+
lastErrorHash: string;
|
|
37
|
+
consecutiveErrors: number;
|
|
38
|
+
|
|
39
|
+
// Daily statistics (persisted)
|
|
40
|
+
dailyToolCalls: number;
|
|
41
|
+
dailyToolFailures: number;
|
|
42
|
+
dailyPainSignals: number;
|
|
43
|
+
dailyGfiPeak: number;
|
|
44
|
+
|
|
45
|
+
// Thinking OS checkpoint - tracks last deep thinking timestamp
|
|
46
|
+
lastThinkingTimestamp: number;
|
|
47
|
+
|
|
48
|
+
// Evolution loop feedback attribution
|
|
49
|
+
injectedProbationIds?: string[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
const sessions = new Map<string, SessionState>();
|
|
54
|
+
|
|
5
55
|
/** Directory for persisting session state */
|
|
6
|
-
let persistDir = null;
|
|
56
|
+
let persistDir: string | null = null;
|
|
57
|
+
|
|
7
58
|
/** Debounce timers for persistence, one per session */
|
|
8
|
-
const persistTimers = new Map();
|
|
9
|
-
|
|
59
|
+
const persistTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
|
60
|
+
|
|
61
|
+
function logSessionTrackerWarning(message: string, error?: unknown): void {
|
|
10
62
|
const detail = error instanceof Error ? error.message : error ? String(error) : '';
|
|
11
63
|
const suffix = detail ? `: ${detail}` : '';
|
|
64
|
+
// eslint-disable-next-line no-console
|
|
12
65
|
console.warn(`[PD:SessionTracker] ${message}${suffix}`);
|
|
13
66
|
}
|
|
14
|
-
|
|
67
|
+
|
|
68
|
+
function touchActivity(state: SessionState, kind: 'general' | 'control' = 'general'): void {
|
|
15
69
|
const now = Date.now();
|
|
16
70
|
state.lastActivityAt = now;
|
|
17
71
|
if (kind === 'control') {
|
|
18
72
|
state.lastControlActivityAt = now;
|
|
19
73
|
}
|
|
20
74
|
}
|
|
75
|
+
|
|
21
76
|
/**
|
|
22
77
|
* Initialize persistence for session state.
|
|
23
78
|
* Call this once during plugin startup.
|
|
24
79
|
*/
|
|
25
|
-
export function initPersistence(stateDir) {
|
|
80
|
+
export function initPersistence(stateDir: string): void {
|
|
26
81
|
persistDir = path.join(stateDir, 'sessions');
|
|
27
82
|
if (!fs.existsSync(persistDir)) {
|
|
28
83
|
fs.mkdirSync(persistDir, { recursive: true });
|
|
29
84
|
}
|
|
85
|
+
|
|
30
86
|
// Load all existing sessions
|
|
31
87
|
loadAllSessions();
|
|
32
88
|
}
|
|
89
|
+
|
|
33
90
|
/**
|
|
34
91
|
* Get the file path for a session's persisted state.
|
|
35
92
|
*/
|
|
36
|
-
function getSessionPath(sessionId) {
|
|
37
|
-
if (!persistDir)
|
|
38
|
-
return '';
|
|
93
|
+
function getSessionPath(sessionId: string): string {
|
|
94
|
+
if (!persistDir) return '';
|
|
39
95
|
// Sanitize sessionId for filesystem
|
|
40
96
|
const safeId = sessionId.replace(/[/\\:]/g, '_');
|
|
41
97
|
return path.join(persistDir, `${safeId}.json`);
|
|
42
98
|
}
|
|
99
|
+
|
|
43
100
|
/**
|
|
44
101
|
* Load all persisted sessions from disk.
|
|
45
102
|
*/
|
|
46
|
-
function loadAllSessions() {
|
|
47
|
-
if (!persistDir || !fs.existsSync(persistDir))
|
|
48
|
-
|
|
103
|
+
function loadAllSessions(): void {
|
|
104
|
+
if (!persistDir || !fs.existsSync(persistDir)) return;
|
|
105
|
+
|
|
49
106
|
try {
|
|
50
107
|
const files = fs.readdirSync(persistDir).filter(f => f.endsWith('.json'));
|
|
51
108
|
const now = Date.now();
|
|
52
109
|
const twoHoursAgo = now - 2 * 60 * 60 * 1000;
|
|
110
|
+
|
|
53
111
|
for (const file of files) {
|
|
54
112
|
try {
|
|
55
113
|
const content = fs.readFileSync(path.join(persistDir, file), 'utf-8');
|
|
56
|
-
const state = JSON.parse(content);
|
|
114
|
+
const state = JSON.parse(content) as SessionState;
|
|
115
|
+
|
|
57
116
|
// Skip abandoned sessions
|
|
58
117
|
if (state.lastActivityAt < twoHoursAgo) {
|
|
59
118
|
continue;
|
|
60
119
|
}
|
|
120
|
+
|
|
61
121
|
sessions.set(state.sessionId, state);
|
|
62
|
-
}
|
|
63
|
-
catch (error) {
|
|
122
|
+
} catch (error) {
|
|
64
123
|
logSessionTrackerWarning(`Failed to load session snapshot ${file}`, error);
|
|
65
124
|
}
|
|
66
125
|
}
|
|
67
|
-
}
|
|
68
|
-
catch (err) {
|
|
126
|
+
} catch (err) {
|
|
69
127
|
logSessionTrackerWarning('Failed to load persisted sessions', err);
|
|
70
128
|
}
|
|
71
129
|
}
|
|
130
|
+
|
|
72
131
|
/**
|
|
73
132
|
* Persist a single session to disk.
|
|
74
133
|
*/
|
|
75
|
-
function persistSession(state) {
|
|
76
|
-
if (!persistDir)
|
|
77
|
-
|
|
134
|
+
function persistSession(state: SessionState): void {
|
|
135
|
+
if (!persistDir) return;
|
|
136
|
+
|
|
78
137
|
const sessionPath = getSessionPath(state.sessionId);
|
|
79
|
-
if (!sessionPath)
|
|
80
|
-
|
|
138
|
+
if (!sessionPath) return;
|
|
139
|
+
|
|
81
140
|
try {
|
|
82
141
|
fs.writeFileSync(sessionPath, JSON.stringify(state, null, 2), 'utf-8');
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
142
|
+
} catch (error) {
|
|
85
143
|
logSessionTrackerWarning(`Failed to persist session ${state.sessionId}`, error);
|
|
86
144
|
}
|
|
87
145
|
}
|
|
146
|
+
|
|
88
147
|
/**
|
|
89
148
|
* Schedule persistence with debounce.
|
|
90
149
|
*/
|
|
91
|
-
function schedulePersistence(state) {
|
|
150
|
+
function schedulePersistence(state: SessionState): void {
|
|
92
151
|
const existing = persistTimers.get(state.sessionId);
|
|
93
152
|
if (existing) {
|
|
94
153
|
clearTimeout(existing);
|
|
@@ -96,13 +155,14 @@ function schedulePersistence(state) {
|
|
|
96
155
|
const timer = setTimeout(() => {
|
|
97
156
|
persistSession(state);
|
|
98
157
|
persistTimers.delete(state.sessionId);
|
|
99
|
-
}, 1000);
|
|
158
|
+
}, 1000); // 1 second debounce
|
|
100
159
|
persistTimers.set(state.sessionId, timer);
|
|
101
160
|
}
|
|
161
|
+
|
|
102
162
|
/**
|
|
103
163
|
* Force persist all sessions immediately.
|
|
104
164
|
*/
|
|
105
|
-
export function flushAllSessions() {
|
|
165
|
+
export function flushAllSessions(): void {
|
|
106
166
|
for (const timer of persistTimers.values()) {
|
|
107
167
|
clearTimeout(timer);
|
|
108
168
|
}
|
|
@@ -111,11 +171,14 @@ export function flushAllSessions() {
|
|
|
111
171
|
persistSession(state);
|
|
112
172
|
}
|
|
113
173
|
}
|
|
114
|
-
|
|
174
|
+
|
|
175
|
+
function getOrCreateSession(sessionId: string, workspaceDir?: string, sessionKey?: string, trigger?: string): SessionState {
|
|
115
176
|
let state = sessions.get(sessionId);
|
|
116
177
|
if (!state) {
|
|
117
178
|
state = {
|
|
118
179
|
sessionId,
|
|
180
|
+
sessionKey,
|
|
181
|
+
trigger,
|
|
119
182
|
workspaceDir,
|
|
120
183
|
toolReadsByFile: {},
|
|
121
184
|
llmTurns: 0,
|
|
@@ -140,36 +203,50 @@ function getOrCreateSession(sessionId, workspaceDir) {
|
|
|
140
203
|
};
|
|
141
204
|
sessions.set(sessionId, state);
|
|
142
205
|
}
|
|
206
|
+
|
|
143
207
|
if (workspaceDir && !state.workspaceDir) {
|
|
144
208
|
state.workspaceDir = workspaceDir;
|
|
145
209
|
}
|
|
210
|
+
// Update sessionKey and trigger if provided (they may be more recent)
|
|
211
|
+
if (sessionKey && !state.sessionKey) {
|
|
212
|
+
state.sessionKey = sessionKey;
|
|
213
|
+
}
|
|
214
|
+
if (trigger && !state.trigger) {
|
|
215
|
+
state.trigger = trigger;
|
|
216
|
+
}
|
|
146
217
|
return state;
|
|
147
218
|
}
|
|
148
|
-
|
|
219
|
+
|
|
220
|
+
function ensureGfiLedger(state: SessionState): Record<string, number> {
|
|
149
221
|
if (!state.gfiBySource || typeof state.gfiBySource !== 'object') {
|
|
150
222
|
state.gfiBySource = {};
|
|
151
223
|
}
|
|
152
224
|
return state.gfiBySource;
|
|
153
225
|
}
|
|
154
|
-
|
|
226
|
+
|
|
227
|
+
export function trackToolRead(sessionId: string, filePath: string, workspaceDir?: string): SessionState {
|
|
155
228
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
156
229
|
const normalizedPath = path.posix.normalize(filePath.replace(/\\/g, '/'));
|
|
157
230
|
state.toolReadsByFile[normalizedPath] = (state.toolReadsByFile[normalizedPath] || 0) + 1;
|
|
158
231
|
touchActivity(state);
|
|
159
232
|
return state;
|
|
160
233
|
}
|
|
161
|
-
|
|
162
|
-
|
|
234
|
+
|
|
235
|
+
export function trackLlmOutput(sessionId: string, usage: TokenUsage | undefined, config?: PainConfig, workspaceDir?: string, sessionKey?: string, trigger?: string): SessionState {
|
|
236
|
+
const state = getOrCreateSession(sessionId, workspaceDir, sessionKey, trigger);
|
|
163
237
|
state.llmTurns += 1;
|
|
164
238
|
touchActivity(state);
|
|
239
|
+
|
|
165
240
|
if (usage) {
|
|
166
241
|
state.totalInputTokens += usage.input || 0;
|
|
167
242
|
state.totalOutputTokens += usage.output || 0;
|
|
168
243
|
state.cacheHits += usage.cacheRead || 0;
|
|
244
|
+
|
|
169
245
|
// Use thresholds from config or defaults
|
|
170
246
|
const minTurns = 5; // Increased from 3 to 5 to prevent false positives on short tasks
|
|
171
247
|
const outputThreshold = 30; // Decreased from 50. Only penalize truly stunted outputs.
|
|
172
248
|
const inputThreshold = config ? config.get('thresholds.cognitive_paralysis_input') : 8000; // Increased base to 8k
|
|
249
|
+
|
|
173
250
|
// Very rough heuristic for empty/paralysis loops: high input context, tiny output, multiple turns
|
|
174
251
|
if (state.llmTurns > minTurns) {
|
|
175
252
|
const isTinyOutput = (usage.output || 0) < outputThreshold;
|
|
@@ -177,8 +254,7 @@ export function trackLlmOutput(sessionId, usage, config, workspaceDir) {
|
|
|
177
254
|
if (isTinyOutput && isLargeInput) {
|
|
178
255
|
state.stuckLoops += 1;
|
|
179
256
|
SystemLogger.log(state.workspaceDir, 'EFFICIENCY_ALARM', `Stuck loop detected (Turn ${state.llmTurns}). Input: ${usage.input}, Output: ${usage.output}. Consecutive: ${state.stuckLoops}`);
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
257
|
+
} else {
|
|
182
258
|
// Reset if we broke out of the tiny output loop
|
|
183
259
|
if (state.stuckLoops > 0) {
|
|
184
260
|
SystemLogger.log(state.workspaceDir, 'EFFICIENCY_OK', `Broke out of stuck loop after ${state.stuckLoops} turns.`);
|
|
@@ -187,22 +263,31 @@ export function trackLlmOutput(sessionId, usage, config, workspaceDir) {
|
|
|
187
263
|
}
|
|
188
264
|
}
|
|
189
265
|
}
|
|
266
|
+
|
|
190
267
|
return state;
|
|
191
268
|
}
|
|
269
|
+
|
|
192
270
|
/**
|
|
193
271
|
* Tracks physical friction based on tool execution failures.
|
|
194
272
|
*/
|
|
195
|
-
export function trackFriction(
|
|
273
|
+
export function trackFriction(
|
|
274
|
+
sessionId: string,
|
|
275
|
+
deltaF: number,
|
|
276
|
+
hash: string,
|
|
277
|
+
workspaceDir?: string,
|
|
278
|
+
options?: { source?: string }
|
|
279
|
+
): SessionState {
|
|
196
280
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
197
281
|
const ledger = ensureGfiLedger(state);
|
|
282
|
+
|
|
198
283
|
if (hash && hash === state.lastErrorHash) {
|
|
199
284
|
state.consecutiveErrors++;
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
285
|
+
} else {
|
|
202
286
|
state.lastErrorSource = options?.source || (hash ? `unattributed:${hash}` : 'unattributed:unknown');
|
|
203
287
|
state.lastErrorHash = hash;
|
|
204
288
|
state.consecutiveErrors = 1;
|
|
205
289
|
}
|
|
290
|
+
|
|
206
291
|
// GFI formula with multiplier: GFI = GFI + (Delta_F * 1.5^(n-1))
|
|
207
292
|
const multiplier = Math.pow(1.5, state.consecutiveErrors - 1);
|
|
208
293
|
const addedFriction = deltaF * multiplier;
|
|
@@ -210,32 +295,48 @@ export function trackFriction(sessionId, deltaF, hash, workspaceDir, options) {
|
|
|
210
295
|
const sourceKey = options?.source || (hash ? `unattributed:${hash}` : 'unattributed:unknown');
|
|
211
296
|
ledger[sourceKey] = (ledger[sourceKey] || 0) + addedFriction;
|
|
212
297
|
touchActivity(state, 'control');
|
|
298
|
+
|
|
213
299
|
SystemLogger.log(state.workspaceDir, 'GFI_INC', `Friction added: +${addedFriction.toFixed(1)} (Base: ${deltaF}, Mult: ${multiplier.toFixed(2)}). Total GFI: ${state.currentGfi.toFixed(1)}`);
|
|
300
|
+
|
|
214
301
|
// Update daily stats
|
|
215
302
|
state.dailyToolFailures++;
|
|
216
303
|
state.dailyGfiPeak = Math.max(state.dailyGfiPeak, state.currentGfi);
|
|
304
|
+
|
|
217
305
|
// Schedule persistence
|
|
218
306
|
schedulePersistence(state);
|
|
307
|
+
|
|
219
308
|
return state;
|
|
220
309
|
}
|
|
310
|
+
|
|
221
311
|
/**
|
|
222
312
|
* Resets the friction index upon successful action.
|
|
223
313
|
*/
|
|
224
|
-
export function resetFriction(
|
|
314
|
+
export function resetFriction(
|
|
315
|
+
sessionId: string,
|
|
316
|
+
workspaceDir?: string,
|
|
317
|
+
options?: { source?: string; amount?: number }
|
|
318
|
+
): SessionState {
|
|
225
319
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
226
320
|
const ledger = ensureGfiLedger(state);
|
|
321
|
+
|
|
227
322
|
if (options?.source) {
|
|
228
323
|
const sourceKey = options.source;
|
|
229
324
|
const currentSource = ledger[sourceKey] || 0;
|
|
230
325
|
const requestedAmount = Number.isFinite(options.amount) ? Number(options.amount) : currentSource;
|
|
231
326
|
const amountToRemove = Math.max(0, Math.min(currentSource, requestedAmount));
|
|
327
|
+
|
|
232
328
|
if (amountToRemove > 0) {
|
|
233
329
|
ledger[sourceKey] = Math.max(0, currentSource - amountToRemove);
|
|
234
330
|
if (ledger[sourceKey] === 0) {
|
|
235
331
|
delete ledger[sourceKey];
|
|
236
332
|
}
|
|
237
333
|
state.currentGfi = Math.max(0, (state.currentGfi || 0) - amountToRemove);
|
|
238
|
-
SystemLogger.log(
|
|
334
|
+
SystemLogger.log(
|
|
335
|
+
state.workspaceDir,
|
|
336
|
+
'GFI_SLICE_RESET',
|
|
337
|
+
`Friction slice reset for ${sourceKey}: -${amountToRemove.toFixed(1)}. Total GFI: ${state.currentGfi.toFixed(1)}`
|
|
338
|
+
);
|
|
339
|
+
|
|
239
340
|
if (state.lastErrorSource === sourceKey) {
|
|
240
341
|
state.consecutiveErrors = 0;
|
|
241
342
|
state.lastErrorHash = '';
|
|
@@ -246,6 +347,7 @@ export function resetFriction(sessionId, workspaceDir, options) {
|
|
|
246
347
|
schedulePersistence(state);
|
|
247
348
|
return state;
|
|
248
349
|
}
|
|
350
|
+
|
|
249
351
|
if (state.currentGfi > 0) {
|
|
250
352
|
SystemLogger.log(state.workspaceDir, 'GFI_RESET', `Friction reset to 0 (Was: ${state.currentGfi.toFixed(1)}). Action successful.`);
|
|
251
353
|
}
|
|
@@ -255,15 +357,18 @@ export function resetFriction(sessionId, workspaceDir, options) {
|
|
|
255
357
|
state.consecutiveErrors = 0;
|
|
256
358
|
state.lastErrorHash = '';
|
|
257
359
|
touchActivity(state, 'control');
|
|
360
|
+
|
|
258
361
|
// Schedule persistence
|
|
259
362
|
schedulePersistence(state);
|
|
363
|
+
|
|
260
364
|
return state;
|
|
261
365
|
}
|
|
366
|
+
|
|
262
367
|
/**
|
|
263
368
|
* Records that deep thinking (Thinking OS) was performed in this session.
|
|
264
369
|
* Used by the Thinking OS checkpoint to allow high-risk operations.
|
|
265
370
|
*/
|
|
266
|
-
export function recordThinkingCheckpoint(sessionId, workspaceDir) {
|
|
371
|
+
export function recordThinkingCheckpoint(sessionId: string, workspaceDir?: string): SessionState {
|
|
267
372
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
268
373
|
state.lastThinkingTimestamp = Date.now();
|
|
269
374
|
touchActivity(state, 'control');
|
|
@@ -271,53 +376,61 @@ export function recordThinkingCheckpoint(sessionId, workspaceDir) {
|
|
|
271
376
|
schedulePersistence(state);
|
|
272
377
|
return state;
|
|
273
378
|
}
|
|
379
|
+
|
|
274
380
|
/**
|
|
275
381
|
* Checks if deep thinking was performed recently (within the given window).
|
|
276
382
|
* @param sessionId - The session to check
|
|
277
383
|
* @param windowMs - How recent the thinking must be (default: 5 minutes)
|
|
278
384
|
* @returns true if thinking was recorded within the window
|
|
279
385
|
*/
|
|
280
|
-
export function hasRecentThinking(sessionId, windowMs = 5 * 60 * 1000) {
|
|
386
|
+
export function hasRecentThinking(sessionId: string, windowMs: number = 5 * 60 * 1000): boolean {
|
|
281
387
|
const state = sessions.get(sessionId);
|
|
282
|
-
if (!state || !state.lastThinkingTimestamp)
|
|
283
|
-
return false;
|
|
388
|
+
if (!state || !state.lastThinkingTimestamp) return false;
|
|
284
389
|
return (Date.now() - state.lastThinkingTimestamp) < windowMs;
|
|
285
390
|
}
|
|
286
|
-
|
|
391
|
+
|
|
392
|
+
export function trackBlock(sessionId: string): SessionState {
|
|
287
393
|
const state = getOrCreateSession(sessionId);
|
|
288
394
|
state.blockedAttempts += 1;
|
|
289
395
|
touchActivity(state, 'control');
|
|
290
396
|
schedulePersistence(state);
|
|
291
397
|
return state;
|
|
292
398
|
}
|
|
293
|
-
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
export function setInjectedProbationIds(sessionId: string, ids: string[], workspaceDir?: string): SessionState {
|
|
294
402
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
295
403
|
state.injectedProbationIds = [...ids];
|
|
296
404
|
touchActivity(state, 'control');
|
|
297
405
|
schedulePersistence(state);
|
|
298
406
|
return state;
|
|
299
407
|
}
|
|
300
|
-
|
|
408
|
+
|
|
409
|
+
export function getInjectedProbationIds(sessionId: string, workspaceDir?: string): string[] {
|
|
301
410
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
302
411
|
return [...(state.injectedProbationIds || [])];
|
|
303
412
|
}
|
|
304
|
-
|
|
413
|
+
|
|
414
|
+
export function clearInjectedProbationIds(sessionId: string, workspaceDir?: string): SessionState {
|
|
305
415
|
return setInjectedProbationIds(sessionId, [], workspaceDir);
|
|
306
416
|
}
|
|
307
|
-
|
|
417
|
+
|
|
418
|
+
export function getSession(sessionId: string): SessionState | undefined {
|
|
308
419
|
return sessions.get(sessionId);
|
|
309
420
|
}
|
|
310
|
-
|
|
421
|
+
|
|
422
|
+
export function listSessions(workspaceDir?: string): SessionState[] {
|
|
311
423
|
return [...sessions.values()]
|
|
312
424
|
.filter((state) => !workspaceDir || !state.workspaceDir || state.workspaceDir === workspaceDir)
|
|
313
425
|
.map((state) => ({
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
426
|
+
...state,
|
|
427
|
+
toolReadsByFile: { ...state.toolReadsByFile },
|
|
428
|
+
gfiBySource: state.gfiBySource ? { ...state.gfiBySource } : undefined,
|
|
429
|
+
injectedProbationIds: state.injectedProbationIds ? [...state.injectedProbationIds] : undefined,
|
|
430
|
+
}));
|
|
319
431
|
}
|
|
320
|
-
|
|
432
|
+
|
|
433
|
+
export function clearSession(sessionId: string): void {
|
|
321
434
|
const timer = persistTimers.get(sessionId);
|
|
322
435
|
if (timer) {
|
|
323
436
|
clearTimeout(timer);
|
|
@@ -325,6 +438,7 @@ export function clearSession(sessionId) {
|
|
|
325
438
|
}
|
|
326
439
|
sessions.delete(sessionId);
|
|
327
440
|
}
|
|
441
|
+
|
|
328
442
|
/**
|
|
329
443
|
* Seed a session directly into SessionTracker.sessions for testing.
|
|
330
444
|
* This bypasses the normal tool-call flow to set up test data for
|
|
@@ -334,13 +448,14 @@ export function clearSession(sessionId) {
|
|
|
334
448
|
* @param workspaceDir - Workspace directory (optional, for filtering)
|
|
335
449
|
* @param lastActivityAt - Unix timestamp in ms (default: now)
|
|
336
450
|
*/
|
|
337
|
-
export function seedSessionForTest(sessionId, workspaceDir, lastActivityAt) {
|
|
451
|
+
export function seedSessionForTest(sessionId: string, workspaceDir?: string, lastActivityAt?: number): void {
|
|
338
452
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
339
453
|
state.lastActivityAt = lastActivityAt ?? Date.now();
|
|
340
454
|
state.lastControlActivityAt = state.lastActivityAt;
|
|
341
455
|
}
|
|
456
|
+
|
|
342
457
|
// Memory cleanup for abandoned sessions (older than 2 hours)
|
|
343
|
-
export function garbageCollectSessions() {
|
|
458
|
+
export function garbageCollectSessions(): void {
|
|
344
459
|
const twoHoursAgo = Date.now() - 2 * 60 * 60 * 1000;
|
|
345
460
|
for (const [id, state] of sessions.entries()) {
|
|
346
461
|
if (state.lastActivityAt < twoHoursAgo) {
|
|
@@ -350,14 +465,14 @@ export function garbageCollectSessions() {
|
|
|
350
465
|
persistTimers.delete(id);
|
|
351
466
|
}
|
|
352
467
|
sessions.delete(id);
|
|
468
|
+
|
|
353
469
|
// Also delete persisted file
|
|
354
470
|
if (persistDir) {
|
|
355
471
|
const sessionPath = getSessionPath(id);
|
|
356
472
|
if (sessionPath && fs.existsSync(sessionPath)) {
|
|
357
473
|
try {
|
|
358
474
|
fs.unlinkSync(sessionPath);
|
|
359
|
-
}
|
|
360
|
-
catch (error) {
|
|
475
|
+
} catch (error) {
|
|
361
476
|
logSessionTrackerWarning(`Failed to delete session snapshot for ${id}`, error);
|
|
362
477
|
}
|
|
363
478
|
}
|
|
@@ -365,13 +480,19 @@ export function garbageCollectSessions() {
|
|
|
365
480
|
}
|
|
366
481
|
}
|
|
367
482
|
}
|
|
483
|
+
|
|
368
484
|
/**
|
|
369
485
|
* Get daily statistics summary for a session.
|
|
370
486
|
*/
|
|
371
|
-
export function getDailySummary(sessionId) {
|
|
487
|
+
export function getDailySummary(sessionId: string): {
|
|
488
|
+
toolCalls: number;
|
|
489
|
+
toolFailures: number;
|
|
490
|
+
painSignals: number;
|
|
491
|
+
gfiPeak: number;
|
|
492
|
+
} | null {
|
|
372
493
|
const state = sessions.get(sessionId);
|
|
373
|
-
if (!state)
|
|
374
|
-
|
|
494
|
+
if (!state) return null;
|
|
495
|
+
|
|
375
496
|
return {
|
|
376
497
|
toolCalls: state.dailyToolCalls,
|
|
377
498
|
toolFailures: state.dailyToolFailures,
|
|
@@ -379,10 +500,11 @@ export function getDailySummary(sessionId) {
|
|
|
379
500
|
gfiPeak: state.dailyGfiPeak,
|
|
380
501
|
};
|
|
381
502
|
}
|
|
503
|
+
|
|
382
504
|
/**
|
|
383
505
|
* Reset daily statistics (call at midnight or on new day).
|
|
384
506
|
*/
|
|
385
|
-
export function resetDailyStats(sessionId) {
|
|
507
|
+
export function resetDailyStats(sessionId: string): void {
|
|
386
508
|
const state = sessions.get(sessionId);
|
|
387
509
|
if (state) {
|
|
388
510
|
state.dailyToolCalls = 0;
|