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
|
@@ -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,7 +171,8 @@ 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 = {
|
|
@@ -142,6 +203,7 @@ function getOrCreateSession(sessionId, workspaceDir, sessionKey, trigger) {
|
|
|
142
203
|
};
|
|
143
204
|
sessions.set(sessionId, state);
|
|
144
205
|
}
|
|
206
|
+
|
|
145
207
|
if (workspaceDir && !state.workspaceDir) {
|
|
146
208
|
state.workspaceDir = workspaceDir;
|
|
147
209
|
}
|
|
@@ -154,31 +216,37 @@ function getOrCreateSession(sessionId, workspaceDir, sessionKey, trigger) {
|
|
|
154
216
|
}
|
|
155
217
|
return state;
|
|
156
218
|
}
|
|
157
|
-
|
|
219
|
+
|
|
220
|
+
function ensureGfiLedger(state: SessionState): Record<string, number> {
|
|
158
221
|
if (!state.gfiBySource || typeof state.gfiBySource !== 'object') {
|
|
159
222
|
state.gfiBySource = {};
|
|
160
223
|
}
|
|
161
224
|
return state.gfiBySource;
|
|
162
225
|
}
|
|
163
|
-
|
|
226
|
+
|
|
227
|
+
export function trackToolRead(sessionId: string, filePath: string, workspaceDir?: string): SessionState {
|
|
164
228
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
165
229
|
const normalizedPath = path.posix.normalize(filePath.replace(/\\/g, '/'));
|
|
166
230
|
state.toolReadsByFile[normalizedPath] = (state.toolReadsByFile[normalizedPath] || 0) + 1;
|
|
167
231
|
touchActivity(state);
|
|
168
232
|
return state;
|
|
169
233
|
}
|
|
170
|
-
|
|
234
|
+
|
|
235
|
+
export function trackLlmOutput(sessionId: string, usage: TokenUsage | undefined, config?: PainConfig, workspaceDir?: string, sessionKey?: string, trigger?: string): SessionState {
|
|
171
236
|
const state = getOrCreateSession(sessionId, workspaceDir, sessionKey, trigger);
|
|
172
237
|
state.llmTurns += 1;
|
|
173
238
|
touchActivity(state);
|
|
239
|
+
|
|
174
240
|
if (usage) {
|
|
175
241
|
state.totalInputTokens += usage.input || 0;
|
|
176
242
|
state.totalOutputTokens += usage.output || 0;
|
|
177
243
|
state.cacheHits += usage.cacheRead || 0;
|
|
244
|
+
|
|
178
245
|
// Use thresholds from config or defaults
|
|
179
246
|
const minTurns = 5; // Increased from 3 to 5 to prevent false positives on short tasks
|
|
180
247
|
const outputThreshold = 30; // Decreased from 50. Only penalize truly stunted outputs.
|
|
181
248
|
const inputThreshold = config ? config.get('thresholds.cognitive_paralysis_input') : 8000; // Increased base to 8k
|
|
249
|
+
|
|
182
250
|
// Very rough heuristic for empty/paralysis loops: high input context, tiny output, multiple turns
|
|
183
251
|
if (state.llmTurns > minTurns) {
|
|
184
252
|
const isTinyOutput = (usage.output || 0) < outputThreshold;
|
|
@@ -186,8 +254,7 @@ export function trackLlmOutput(sessionId, usage, config, workspaceDir, sessionKe
|
|
|
186
254
|
if (isTinyOutput && isLargeInput) {
|
|
187
255
|
state.stuckLoops += 1;
|
|
188
256
|
SystemLogger.log(state.workspaceDir, 'EFFICIENCY_ALARM', `Stuck loop detected (Turn ${state.llmTurns}). Input: ${usage.input}, Output: ${usage.output}. Consecutive: ${state.stuckLoops}`);
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
257
|
+
} else {
|
|
191
258
|
// Reset if we broke out of the tiny output loop
|
|
192
259
|
if (state.stuckLoops > 0) {
|
|
193
260
|
SystemLogger.log(state.workspaceDir, 'EFFICIENCY_OK', `Broke out of stuck loop after ${state.stuckLoops} turns.`);
|
|
@@ -196,22 +263,31 @@ export function trackLlmOutput(sessionId, usage, config, workspaceDir, sessionKe
|
|
|
196
263
|
}
|
|
197
264
|
}
|
|
198
265
|
}
|
|
266
|
+
|
|
199
267
|
return state;
|
|
200
268
|
}
|
|
269
|
+
|
|
201
270
|
/**
|
|
202
271
|
* Tracks physical friction based on tool execution failures.
|
|
203
272
|
*/
|
|
204
|
-
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 {
|
|
205
280
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
206
281
|
const ledger = ensureGfiLedger(state);
|
|
282
|
+
|
|
207
283
|
if (hash && hash === state.lastErrorHash) {
|
|
208
284
|
state.consecutiveErrors++;
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
285
|
+
} else {
|
|
211
286
|
state.lastErrorSource = options?.source || (hash ? `unattributed:${hash}` : 'unattributed:unknown');
|
|
212
287
|
state.lastErrorHash = hash;
|
|
213
288
|
state.consecutiveErrors = 1;
|
|
214
289
|
}
|
|
290
|
+
|
|
215
291
|
// GFI formula with multiplier: GFI = GFI + (Delta_F * 1.5^(n-1))
|
|
216
292
|
const multiplier = Math.pow(1.5, state.consecutiveErrors - 1);
|
|
217
293
|
const addedFriction = deltaF * multiplier;
|
|
@@ -219,32 +295,48 @@ export function trackFriction(sessionId, deltaF, hash, workspaceDir, options) {
|
|
|
219
295
|
const sourceKey = options?.source || (hash ? `unattributed:${hash}` : 'unattributed:unknown');
|
|
220
296
|
ledger[sourceKey] = (ledger[sourceKey] || 0) + addedFriction;
|
|
221
297
|
touchActivity(state, 'control');
|
|
298
|
+
|
|
222
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
|
+
|
|
223
301
|
// Update daily stats
|
|
224
302
|
state.dailyToolFailures++;
|
|
225
303
|
state.dailyGfiPeak = Math.max(state.dailyGfiPeak, state.currentGfi);
|
|
304
|
+
|
|
226
305
|
// Schedule persistence
|
|
227
306
|
schedulePersistence(state);
|
|
307
|
+
|
|
228
308
|
return state;
|
|
229
309
|
}
|
|
310
|
+
|
|
230
311
|
/**
|
|
231
312
|
* Resets the friction index upon successful action.
|
|
232
313
|
*/
|
|
233
|
-
export function resetFriction(
|
|
314
|
+
export function resetFriction(
|
|
315
|
+
sessionId: string,
|
|
316
|
+
workspaceDir?: string,
|
|
317
|
+
options?: { source?: string; amount?: number }
|
|
318
|
+
): SessionState {
|
|
234
319
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
235
320
|
const ledger = ensureGfiLedger(state);
|
|
321
|
+
|
|
236
322
|
if (options?.source) {
|
|
237
323
|
const sourceKey = options.source;
|
|
238
324
|
const currentSource = ledger[sourceKey] || 0;
|
|
239
325
|
const requestedAmount = Number.isFinite(options.amount) ? Number(options.amount) : currentSource;
|
|
240
326
|
const amountToRemove = Math.max(0, Math.min(currentSource, requestedAmount));
|
|
327
|
+
|
|
241
328
|
if (amountToRemove > 0) {
|
|
242
329
|
ledger[sourceKey] = Math.max(0, currentSource - amountToRemove);
|
|
243
330
|
if (ledger[sourceKey] === 0) {
|
|
244
331
|
delete ledger[sourceKey];
|
|
245
332
|
}
|
|
246
333
|
state.currentGfi = Math.max(0, (state.currentGfi || 0) - amountToRemove);
|
|
247
|
-
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
|
+
|
|
248
340
|
if (state.lastErrorSource === sourceKey) {
|
|
249
341
|
state.consecutiveErrors = 0;
|
|
250
342
|
state.lastErrorHash = '';
|
|
@@ -255,6 +347,7 @@ export function resetFriction(sessionId, workspaceDir, options) {
|
|
|
255
347
|
schedulePersistence(state);
|
|
256
348
|
return state;
|
|
257
349
|
}
|
|
350
|
+
|
|
258
351
|
if (state.currentGfi > 0) {
|
|
259
352
|
SystemLogger.log(state.workspaceDir, 'GFI_RESET', `Friction reset to 0 (Was: ${state.currentGfi.toFixed(1)}). Action successful.`);
|
|
260
353
|
}
|
|
@@ -264,15 +357,18 @@ export function resetFriction(sessionId, workspaceDir, options) {
|
|
|
264
357
|
state.consecutiveErrors = 0;
|
|
265
358
|
state.lastErrorHash = '';
|
|
266
359
|
touchActivity(state, 'control');
|
|
360
|
+
|
|
267
361
|
// Schedule persistence
|
|
268
362
|
schedulePersistence(state);
|
|
363
|
+
|
|
269
364
|
return state;
|
|
270
365
|
}
|
|
366
|
+
|
|
271
367
|
/**
|
|
272
368
|
* Records that deep thinking (Thinking OS) was performed in this session.
|
|
273
369
|
* Used by the Thinking OS checkpoint to allow high-risk operations.
|
|
274
370
|
*/
|
|
275
|
-
export function recordThinkingCheckpoint(sessionId, workspaceDir) {
|
|
371
|
+
export function recordThinkingCheckpoint(sessionId: string, workspaceDir?: string): SessionState {
|
|
276
372
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
277
373
|
state.lastThinkingTimestamp = Date.now();
|
|
278
374
|
touchActivity(state, 'control');
|
|
@@ -280,53 +376,61 @@ export function recordThinkingCheckpoint(sessionId, workspaceDir) {
|
|
|
280
376
|
schedulePersistence(state);
|
|
281
377
|
return state;
|
|
282
378
|
}
|
|
379
|
+
|
|
283
380
|
/**
|
|
284
381
|
* Checks if deep thinking was performed recently (within the given window).
|
|
285
382
|
* @param sessionId - The session to check
|
|
286
383
|
* @param windowMs - How recent the thinking must be (default: 5 minutes)
|
|
287
384
|
* @returns true if thinking was recorded within the window
|
|
288
385
|
*/
|
|
289
|
-
export function hasRecentThinking(sessionId, windowMs = 5 * 60 * 1000) {
|
|
386
|
+
export function hasRecentThinking(sessionId: string, windowMs: number = 5 * 60 * 1000): boolean {
|
|
290
387
|
const state = sessions.get(sessionId);
|
|
291
|
-
if (!state || !state.lastThinkingTimestamp)
|
|
292
|
-
return false;
|
|
388
|
+
if (!state || !state.lastThinkingTimestamp) return false;
|
|
293
389
|
return (Date.now() - state.lastThinkingTimestamp) < windowMs;
|
|
294
390
|
}
|
|
295
|
-
|
|
391
|
+
|
|
392
|
+
export function trackBlock(sessionId: string): SessionState {
|
|
296
393
|
const state = getOrCreateSession(sessionId);
|
|
297
394
|
state.blockedAttempts += 1;
|
|
298
395
|
touchActivity(state, 'control');
|
|
299
396
|
schedulePersistence(state);
|
|
300
397
|
return state;
|
|
301
398
|
}
|
|
302
|
-
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
export function setInjectedProbationIds(sessionId: string, ids: string[], workspaceDir?: string): SessionState {
|
|
303
402
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
304
403
|
state.injectedProbationIds = [...ids];
|
|
305
404
|
touchActivity(state, 'control');
|
|
306
405
|
schedulePersistence(state);
|
|
307
406
|
return state;
|
|
308
407
|
}
|
|
309
|
-
|
|
408
|
+
|
|
409
|
+
export function getInjectedProbationIds(sessionId: string, workspaceDir?: string): string[] {
|
|
310
410
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
311
411
|
return [...(state.injectedProbationIds || [])];
|
|
312
412
|
}
|
|
313
|
-
|
|
413
|
+
|
|
414
|
+
export function clearInjectedProbationIds(sessionId: string, workspaceDir?: string): SessionState {
|
|
314
415
|
return setInjectedProbationIds(sessionId, [], workspaceDir);
|
|
315
416
|
}
|
|
316
|
-
|
|
417
|
+
|
|
418
|
+
export function getSession(sessionId: string): SessionState | undefined {
|
|
317
419
|
return sessions.get(sessionId);
|
|
318
420
|
}
|
|
319
|
-
|
|
421
|
+
|
|
422
|
+
export function listSessions(workspaceDir?: string): SessionState[] {
|
|
320
423
|
return [...sessions.values()]
|
|
321
424
|
.filter((state) => !workspaceDir || !state.workspaceDir || state.workspaceDir === workspaceDir)
|
|
322
425
|
.map((state) => ({
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
426
|
+
...state,
|
|
427
|
+
toolReadsByFile: { ...state.toolReadsByFile },
|
|
428
|
+
gfiBySource: state.gfiBySource ? { ...state.gfiBySource } : undefined,
|
|
429
|
+
injectedProbationIds: state.injectedProbationIds ? [...state.injectedProbationIds] : undefined,
|
|
430
|
+
}));
|
|
328
431
|
}
|
|
329
|
-
|
|
432
|
+
|
|
433
|
+
export function clearSession(sessionId: string): void {
|
|
330
434
|
const timer = persistTimers.get(sessionId);
|
|
331
435
|
if (timer) {
|
|
332
436
|
clearTimeout(timer);
|
|
@@ -334,6 +438,7 @@ export function clearSession(sessionId) {
|
|
|
334
438
|
}
|
|
335
439
|
sessions.delete(sessionId);
|
|
336
440
|
}
|
|
441
|
+
|
|
337
442
|
/**
|
|
338
443
|
* Seed a session directly into SessionTracker.sessions for testing.
|
|
339
444
|
* This bypasses the normal tool-call flow to set up test data for
|
|
@@ -343,13 +448,14 @@ export function clearSession(sessionId) {
|
|
|
343
448
|
* @param workspaceDir - Workspace directory (optional, for filtering)
|
|
344
449
|
* @param lastActivityAt - Unix timestamp in ms (default: now)
|
|
345
450
|
*/
|
|
346
|
-
export function seedSessionForTest(sessionId, workspaceDir, lastActivityAt) {
|
|
451
|
+
export function seedSessionForTest(sessionId: string, workspaceDir?: string, lastActivityAt?: number): void {
|
|
347
452
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
348
453
|
state.lastActivityAt = lastActivityAt ?? Date.now();
|
|
349
454
|
state.lastControlActivityAt = state.lastActivityAt;
|
|
350
455
|
}
|
|
456
|
+
|
|
351
457
|
// Memory cleanup for abandoned sessions (older than 2 hours)
|
|
352
|
-
export function garbageCollectSessions() {
|
|
458
|
+
export function garbageCollectSessions(): void {
|
|
353
459
|
const twoHoursAgo = Date.now() - 2 * 60 * 60 * 1000;
|
|
354
460
|
for (const [id, state] of sessions.entries()) {
|
|
355
461
|
if (state.lastActivityAt < twoHoursAgo) {
|
|
@@ -359,14 +465,14 @@ export function garbageCollectSessions() {
|
|
|
359
465
|
persistTimers.delete(id);
|
|
360
466
|
}
|
|
361
467
|
sessions.delete(id);
|
|
468
|
+
|
|
362
469
|
// Also delete persisted file
|
|
363
470
|
if (persistDir) {
|
|
364
471
|
const sessionPath = getSessionPath(id);
|
|
365
472
|
if (sessionPath && fs.existsSync(sessionPath)) {
|
|
366
473
|
try {
|
|
367
474
|
fs.unlinkSync(sessionPath);
|
|
368
|
-
}
|
|
369
|
-
catch (error) {
|
|
475
|
+
} catch (error) {
|
|
370
476
|
logSessionTrackerWarning(`Failed to delete session snapshot for ${id}`, error);
|
|
371
477
|
}
|
|
372
478
|
}
|
|
@@ -374,13 +480,19 @@ export function garbageCollectSessions() {
|
|
|
374
480
|
}
|
|
375
481
|
}
|
|
376
482
|
}
|
|
483
|
+
|
|
377
484
|
/**
|
|
378
485
|
* Get daily statistics summary for a session.
|
|
379
486
|
*/
|
|
380
|
-
export function getDailySummary(sessionId) {
|
|
487
|
+
export function getDailySummary(sessionId: string): {
|
|
488
|
+
toolCalls: number;
|
|
489
|
+
toolFailures: number;
|
|
490
|
+
painSignals: number;
|
|
491
|
+
gfiPeak: number;
|
|
492
|
+
} | null {
|
|
381
493
|
const state = sessions.get(sessionId);
|
|
382
|
-
if (!state)
|
|
383
|
-
|
|
494
|
+
if (!state) return null;
|
|
495
|
+
|
|
384
496
|
return {
|
|
385
497
|
toolCalls: state.dailyToolCalls,
|
|
386
498
|
toolFailures: state.dailyToolFailures,
|
|
@@ -388,10 +500,11 @@ export function getDailySummary(sessionId) {
|
|
|
388
500
|
gfiPeak: state.dailyGfiPeak,
|
|
389
501
|
};
|
|
390
502
|
}
|
|
503
|
+
|
|
391
504
|
/**
|
|
392
505
|
* Reset daily statistics (call at midnight or on new day).
|
|
393
506
|
*/
|
|
394
|
-
export function resetDailyStats(sessionId) {
|
|
507
|
+
export function resetDailyStats(sessionId: string): void {
|
|
395
508
|
const state = sessions.get(sessionId);
|
|
396
509
|
if (state) {
|
|
397
510
|
state.dailyToolCalls = 0;
|