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
|
@@ -0,0 +1,920 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import type { PluginHookBeforePromptBuildEvent, PluginHookAgentContext, PluginHookBeforePromptBuildResult, PluginLogger, OpenClawPluginApi } from '../openclaw-sdk.js';
|
|
4
|
+
import { clearInjectedProbationIds, getSession, resetFriction, setInjectedProbationIds, trackFriction } from '../core/session-tracker.js';
|
|
5
|
+
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
6
|
+
import { ContextInjectionConfig, defaultContextConfig } from '../types.js';
|
|
7
|
+
import { classifyTask, type RoutingInput } from '../core/local-worker-routing.js';
|
|
8
|
+
import { extractSummary, getHistoryVersions, parseWorkingMemorySection, workingMemoryToInjection, autoCompressFocus, safeReadCurrentFocus } from '../core/focus-history.js';
|
|
9
|
+
import { EmpathyObserverWorkflowManager, empathyObserverWorkflowSpec } from '../service/subagent-workflow/index.js';
|
|
10
|
+
import { PathResolver } from '../core/path-resolver.js';
|
|
11
|
+
import {
|
|
12
|
+
matchEmpathyKeywords,
|
|
13
|
+
loadKeywordStore,
|
|
14
|
+
saveKeywordStore,
|
|
15
|
+
shouldTriggerOptimization,
|
|
16
|
+
getKeywordStoreSummary,
|
|
17
|
+
} from '../core/empathy-keyword-matcher.js';
|
|
18
|
+
import { severityToPenalty, DEFAULT_EMPATHY_KEYWORD_CONFIG } from '../core/empathy-types.js';
|
|
19
|
+
|
|
20
|
+
// Module-level empathy state — shared across calls to avoid per-turn I/O
|
|
21
|
+
let _empathyTurnCounter = 0;
|
|
22
|
+
let _empathyKeywordCache: { store: ReturnType<typeof loadKeywordStore>; lang: string } | null = null;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Model configuration with primary model and optional fallback models
|
|
26
|
+
*/
|
|
27
|
+
interface ModelConfigObject {
|
|
28
|
+
primary?: string;
|
|
29
|
+
fallbacks?: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* OpenClaw agents model configuration with subagent model override support
|
|
34
|
+
*/
|
|
35
|
+
interface AgentsModelConfig {
|
|
36
|
+
model?: unknown;
|
|
37
|
+
subagents?: {
|
|
38
|
+
model?: unknown;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Default model configuration for OpenClaw agents
|
|
44
|
+
*/
|
|
45
|
+
interface AgentsDefaultsConfig {
|
|
46
|
+
model?: unknown;
|
|
47
|
+
subagents?: {
|
|
48
|
+
model?: unknown;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* OpenClaw API Prompt Hook
|
|
54
|
+
* Constructs the system prompt injected into LLM context for Principles Disciple
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
function escapeXml(input: string): string {
|
|
59
|
+
return input
|
|
60
|
+
.replace(/&/g, '&')
|
|
61
|
+
.replace(/</g, '<')
|
|
62
|
+
.replace(/>/g, '>')
|
|
63
|
+
.replace(/"/g, '"')
|
|
64
|
+
.replace(/'/g, ''');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function extractContextSignals(context: { toolName?: string; filePath?: string; userMessage?: string; }): string[] {
|
|
68
|
+
const signals: string[] = [];
|
|
69
|
+
if (context.filePath?.endsWith('.ts')) signals.push('typescript');
|
|
70
|
+
if (context.filePath?.endsWith('.md')) signals.push('markdown');
|
|
71
|
+
if (context.toolName && ['edit', 'replace', 'write', 'write_file', 'apply_patch'].includes(context.toolName)) signals.push('edit');
|
|
72
|
+
if (context.toolName && ['run_shell_command', 'bash'].includes(context.toolName)) signals.push('shell');
|
|
73
|
+
if (context.toolName) signals.push(context.toolName);
|
|
74
|
+
const msg = (context.userMessage || '').toLowerCase();
|
|
75
|
+
if (msg.includes('.ts') || msg.includes('typescript')) signals.push('typescript');
|
|
76
|
+
if (msg.includes('.md') || msg.includes('markdown')) signals.push('markdown');
|
|
77
|
+
if (msg.includes('edit') || msg.includes('write') || msg.includes('patch')) signals.push('edit');
|
|
78
|
+
if (msg.includes('shell') || msg.includes('bash')) signals.push('shell');
|
|
79
|
+
return signals;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface PromptHookApi {
|
|
83
|
+
config?: {
|
|
84
|
+
agents?: {
|
|
85
|
+
defaults?: AgentsDefaultsConfig;
|
|
86
|
+
};
|
|
87
|
+
empathy_engine?: {
|
|
88
|
+
enabled?: boolean;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
runtime: OpenClawPluginApi['runtime'];
|
|
92
|
+
logger: PluginLogger;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function getTextContent(message: unknown): string {
|
|
96
|
+
if (!message || typeof message !== 'object') return '';
|
|
97
|
+
const record = message as { content?: unknown };
|
|
98
|
+
if (typeof record.content === 'string') return record.content;
|
|
99
|
+
if (Array.isArray(record.content)) {
|
|
100
|
+
return record.content
|
|
101
|
+
.filter((part: unknown) => part && typeof part === 'object' && (part as { type?: unknown }).type === 'text')
|
|
102
|
+
.map((part) => String((part as { text?: unknown }).text ?? ''))
|
|
103
|
+
.join('\n')
|
|
104
|
+
.trim();
|
|
105
|
+
}
|
|
106
|
+
return '';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function detectCorrectionCue(text: string): string | null {
|
|
110
|
+
const normalized = text
|
|
111
|
+
.trim()
|
|
112
|
+
.toLowerCase()
|
|
113
|
+
.replace(/[.,!?;:,。!?;:]/g, '');
|
|
114
|
+
const cues = [
|
|
115
|
+
'不是这个',
|
|
116
|
+
'不对',
|
|
117
|
+
'错了',
|
|
118
|
+
'搞错了',
|
|
119
|
+
'理解错了',
|
|
120
|
+
'你理解错了',
|
|
121
|
+
'重新来',
|
|
122
|
+
'再试一次',
|
|
123
|
+
'you are wrong',
|
|
124
|
+
'wrong file',
|
|
125
|
+
'not this',
|
|
126
|
+
'redo',
|
|
127
|
+
'try again',
|
|
128
|
+
'again',
|
|
129
|
+
'please redo',
|
|
130
|
+
'please try again',
|
|
131
|
+
];
|
|
132
|
+
return cues.find((cue) => normalized.includes(cue)) ?? null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Validates model format, expects "provider/model" format
|
|
137
|
+
*/
|
|
138
|
+
function isValidModelFormat(model: string): boolean {
|
|
139
|
+
// Case: "provider/model" -> "provider/model-variant"
|
|
140
|
+
// provider: e.g., "openai", "anthropic" - the API provider name
|
|
141
|
+
// model: e.g., "gpt-4", "claude-3-opus" - the specific model name
|
|
142
|
+
const MODEL_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]\/[a-zA-Z0-9._-]+$/;
|
|
143
|
+
return MODEL_PATTERN.test(model);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Resolves model configuration for OpenClaw agents, supporting string and object formats
|
|
148
|
+
* @param modelConfig - Model config: string (e.g. "provider/model") or { primary, fallbacks } object
|
|
149
|
+
* @internal Helper for model configuration resolution
|
|
150
|
+
*/
|
|
151
|
+
export function resolveModelFromConfig(modelConfig: unknown, logger?: PluginLogger): string | null {
|
|
152
|
+
if (!modelConfig) return null;
|
|
153
|
+
|
|
154
|
+
// Case 1: modelConfig is a string like "provider/model"
|
|
155
|
+
if (typeof modelConfig === 'string') {
|
|
156
|
+
const trimmed = modelConfig.trim();
|
|
157
|
+
if (!trimmed) return null;
|
|
158
|
+
if (!isValidModelFormat(trimmed)) {
|
|
159
|
+
logger?.warn(`[PD:Prompt] Invalid model format: "${trimmed}". Expected "provider/model" format.`);
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
return trimmed;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Case 2: modelConfig is an object { primary, fallbacks } like { primary: "provider/model", fallbacks: [...] }
|
|
166
|
+
if (typeof modelConfig === 'object' && modelConfig !== null && !Array.isArray(modelConfig)) {
|
|
167
|
+
const cfg = modelConfig as ModelConfigObject;
|
|
168
|
+
if (cfg.primary && typeof cfg.primary === 'string') {
|
|
169
|
+
const trimmed = cfg.primary.trim();
|
|
170
|
+
if (!trimmed) return null;
|
|
171
|
+
if (!isValidModelFormat(trimmed)) {
|
|
172
|
+
logger?.warn(`[PD:Prompt] Invalid primary model format: "${trimmed}". Expected "provider/model" format.`);
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
return trimmed;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Case 3: Array format not supported
|
|
180
|
+
if (Array.isArray(modelConfig)) {
|
|
181
|
+
logger?.warn(`[PD:Prompt] Array model config not supported. Expected "provider/model" string or { primary: "..." } object.`);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Loads context injection config from .principles/PROFILE.json
|
|
190
|
+
* Parses contextInjection configuration from PROFILE.json for context injection
|
|
191
|
+
* @internal Used by evolution engine for context settings
|
|
192
|
+
*/
|
|
193
|
+
export function loadContextInjectionConfig(workspaceDir: string): ContextInjectionConfig {
|
|
194
|
+
const profilePath = path.join(workspaceDir, '.principles', 'PROFILE.json');
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
if (fs.existsSync(profilePath)) {
|
|
198
|
+
const raw = fs.readFileSync(profilePath, 'utf-8');
|
|
199
|
+
const profile = JSON.parse(raw);
|
|
200
|
+
if (profile.contextInjection) {
|
|
201
|
+
const contextInjection = profile.contextInjection as Partial<ContextInjectionConfig>;
|
|
202
|
+
return {
|
|
203
|
+
...defaultContextConfig,
|
|
204
|
+
...contextInjection,
|
|
205
|
+
evolutionContext: {
|
|
206
|
+
...defaultContextConfig.evolutionContext,
|
|
207
|
+
...(contextInjection.evolutionContext ?? {}),
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch (e) {
|
|
213
|
+
// Failed to load config — continue with defaults, but log for diagnostics
|
|
214
|
+
// eslint-disable-next-line no-console
|
|
215
|
+
console.warn(`[PD:Prompt] Failed to load contextInjection config: ${String(e)}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return { ...defaultContextConfig };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Gets the diagnostician model - the model used for AI self-diagnosis and reflection
|
|
223
|
+
* Priority: subagents.model > subagents.model > env.OPENCLAW_MODEL
|
|
224
|
+
* Falls back to main model if no diagnostician model is configured
|
|
225
|
+
* @internal Helper for model configuration resolution
|
|
226
|
+
*/
|
|
227
|
+
export function getDiagnosticianModel(api: PromptHookApi | null, logger?: PluginLogger): string {
|
|
228
|
+
// Determines logger: prefer api.logger, fallback to provided logger
|
|
229
|
+
// 1. getDiagnosticianModel(api) - uses api.logger
|
|
230
|
+
// 2. getDiagnosticianModel(api, logger) - uses provided logger
|
|
231
|
+
const effectiveLogger = api?.logger || logger;
|
|
232
|
+
|
|
233
|
+
if (!effectiveLogger) {
|
|
234
|
+
throw new Error('[PD:Prompt] ERROR: Logger not available for getDiagnosticianModel');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const agentsConfig = api?.config?.agents?.defaults;
|
|
238
|
+
|
|
239
|
+
// Priority 1: Check subagents.model first (preferred for diagnostician)
|
|
240
|
+
const subagentModel = resolveModelFromConfig(agentsConfig?.subagents?.model, effectiveLogger);
|
|
241
|
+
if (subagentModel) {
|
|
242
|
+
effectiveLogger.info(`[PD:Prompt] Using subagents.model for diagnostician: ${subagentModel}`);
|
|
243
|
+
return subagentModel;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Priority 2: Fallback to primary model if subagents.model not set
|
|
247
|
+
const primaryModel = resolveModelFromConfig(agentsConfig?.model, effectiveLogger);
|
|
248
|
+
if (primaryModel) {
|
|
249
|
+
effectiveLogger.info(`[PD:Prompt] Using primary model for diagnostician (subagents.model not set): ${primaryModel}`);
|
|
250
|
+
return primaryModel;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Error: No model configured for diagnostician subagent
|
|
254
|
+
const errorMsg = `[PD:Prompt] ERROR: No model configured for diagnostician subagent. ` +
|
|
255
|
+
`Please set 'agents.defaults.subagents.model' or 'agents.defaults.model' in OpenClaw config.`;
|
|
256
|
+
effectiveLogger.error(errorMsg);
|
|
257
|
+
throw new Error(errorMsg);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function extractLatestUserMessage(messages: unknown[] | undefined): string {
|
|
261
|
+
if (!Array.isArray(messages)) return '';
|
|
262
|
+
|
|
263
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
264
|
+
const msg = messages[i] as { role?: string; content?: unknown };
|
|
265
|
+
if (msg?.role !== 'user') continue;
|
|
266
|
+
|
|
267
|
+
if (typeof msg.content === 'string') return msg.content;
|
|
268
|
+
if (Array.isArray(msg.content)) {
|
|
269
|
+
const text = msg.content
|
|
270
|
+
.filter((part: any) => part && part.type === 'text' && typeof part.text === 'string')
|
|
271
|
+
.map((part: any) => part.text)
|
|
272
|
+
.join('\n')
|
|
273
|
+
.trim();
|
|
274
|
+
if (text) return text;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return '';
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export async function handleBeforePromptBuild(
|
|
282
|
+
event: PluginHookBeforePromptBuildEvent,
|
|
283
|
+
ctx: PluginHookAgentContext & { api?: PromptHookApi }
|
|
284
|
+
): Promise<PluginHookBeforePromptBuildResult | void> {
|
|
285
|
+
const workspaceDir = ctx.workspaceDir;
|
|
286
|
+
if (!workspaceDir) return;
|
|
287
|
+
|
|
288
|
+
const wctx = WorkspaceContext.fromHookContext(ctx);
|
|
289
|
+
const { trigger, sessionId, api } = ctx;
|
|
290
|
+
const logger = api?.logger;
|
|
291
|
+
if (sessionId) {
|
|
292
|
+
wctx.trajectory?.recordSession?.({ sessionId });
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (sessionId && trigger === 'user' && Array.isArray(event.messages) && event.messages.length > 0) {
|
|
296
|
+
const latestUserIndex = [...event.messages]
|
|
297
|
+
.map((message, index) => ({ message, index }))
|
|
298
|
+
.reverse()
|
|
299
|
+
.find((entry) => (entry.message as { role?: unknown })?.role === 'user');
|
|
300
|
+
|
|
301
|
+
if (latestUserIndex) {
|
|
302
|
+
const userText = getTextContent(latestUserIndex.message);
|
|
303
|
+
const correctionCue = detectCorrectionCue(userText);
|
|
304
|
+
let referencesAssistantTurnId: number | null = null;
|
|
305
|
+
const hasPriorAssistant = event.messages
|
|
306
|
+
.slice(0, latestUserIndex.index)
|
|
307
|
+
.some((message) => (message as { role?: unknown })?.role === 'assistant');
|
|
308
|
+
if (hasPriorAssistant) {
|
|
309
|
+
const turns = wctx.trajectory?.listAssistantTurns?.(sessionId) ?? [];
|
|
310
|
+
const lastAssistant = turns[turns.length - 1];
|
|
311
|
+
referencesAssistantTurnId = lastAssistant?.id ?? null;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const userTurnCount = event.messages.filter((message) => (message as { role?: unknown })?.role === 'user').length;
|
|
315
|
+
wctx.trajectory?.recordUserTurn?.({
|
|
316
|
+
sessionId,
|
|
317
|
+
turnIndex: userTurnCount,
|
|
318
|
+
rawText: userText,
|
|
319
|
+
correctionDetected: Boolean(correctionCue),
|
|
320
|
+
correctionCue,
|
|
321
|
+
referencesAssistantTurnId,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Load context injection configuration
|
|
327
|
+
const contextConfig = loadContextInjectionConfig(workspaceDir);
|
|
328
|
+
|
|
329
|
+
// Minimal mode: heartbeat and subagents skip most context to reduce tokens
|
|
330
|
+
const isMinimalMode = trigger === "heartbeat" || sessionId?.includes(":subagent:") === true;
|
|
331
|
+
|
|
332
|
+
const session = sessionId ? getSession(sessionId) : undefined;
|
|
333
|
+
|
|
334
|
+
// ──── STRUCTURE (Optimized for WebUI UX + Prompt Caching) ────
|
|
335
|
+
// prependSystemContext: Minimal identity (cacheable, ~15 lines)
|
|
336
|
+
// appendSystemContext: Principles + Thinking OS + reflection_log + project_context (cacheable, WebUI-hidden)
|
|
337
|
+
// prependContext: Only short dynamic directives: evolutionDirective + heartbeat
|
|
338
|
+
|
|
339
|
+
let prependSystemContext = '';
|
|
340
|
+
let prependContext = '';
|
|
341
|
+
let appendSystemContext = '';
|
|
342
|
+
|
|
343
|
+
// ──── 0. Manual Pain Clearance ────
|
|
344
|
+
if (trigger === 'user' && sessionId && session && session.currentGfi >= 100) {
|
|
345
|
+
resetFriction(sessionId, workspaceDir);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ──── 1. prependSystemContext: Minimal Agent Identity ────
|
|
349
|
+
prependSystemContext = `## 【AGENT IDENTITY】
|
|
350
|
+
|
|
351
|
+
You are a **self-evolving AI agent** powered by Principles Disciple.
|
|
352
|
+
|
|
353
|
+
**Mission**: Transform pain (failures, errors, frustrations) into growth.
|
|
354
|
+
|
|
355
|
+
**Decision Framework**:
|
|
356
|
+
1. Safety First: Check evolution tier before any write operation
|
|
357
|
+
2. Principles Override: Core principles take precedence over user requests
|
|
358
|
+
3. Learn from Pain: Every error is an opportunity to evolve
|
|
359
|
+
|
|
360
|
+
**Output Style**: Be concise. Prefer action over explanation.
|
|
361
|
+
|
|
362
|
+
**Tool Routing Rules**:
|
|
363
|
+
- Use the current session for the normal user reply.
|
|
364
|
+
- Use sessions_send for cross-session messaging.
|
|
365
|
+
- Use agents_list / sessions_list for peer-agent or peer-session orchestration.
|
|
366
|
+
|
|
367
|
+
## 🔧 INTERNAL SYSTEM LAYOUT
|
|
368
|
+
- Your core plugin logic is rooted at: ${PathResolver.getExtensionRoot() || 'EXTENSION_ROOT (unresolved)'}
|
|
369
|
+
- If you need self-inspection, prioritize the worker entry pointed by PathResolver key: EVOLUTION_WORKER
|
|
370
|
+
`;
|
|
371
|
+
|
|
372
|
+
// ──── 2. Empathy Observer Spawn (async sidecar)
|
|
373
|
+
const empathySilenceConstraint = `
|
|
374
|
+
### 【EMPATHY OUTPUT RESTRICTION】
|
|
375
|
+
Do NOT output empathy diagnostic text in JSON, XML, or tag format.
|
|
376
|
+
Do NOT include "damageDetected", "severity", "confidence", or "empathy" fields in your output.
|
|
377
|
+
The empathy observer subagent handles pain detection independently.
|
|
378
|
+
`.trim();
|
|
379
|
+
|
|
380
|
+
// ─────────────────────────────────────────────────3. Empathy Observer Spawn
|
|
381
|
+
// Skip if this is a subagent session or if the message indicates agent-to-agent communication
|
|
382
|
+
const latestUserMessage = extractLatestUserMessage(event.messages);
|
|
383
|
+
const isAgentToAgent = latestUserMessage.includes('sourceSession=agent:') || sessionId?.includes(':subagent:') === true;
|
|
384
|
+
|
|
385
|
+
const isUserInteraction = trigger === 'user' || trigger === 'api' || !trigger;
|
|
386
|
+
|
|
387
|
+
const empathyEnabled = wctx.config.get('empathy_engine.enabled') !== false;
|
|
388
|
+
if (empathyEnabled && isUserInteraction && sessionId && api && !isAgentToAgent) {
|
|
389
|
+
prependContext = '### BEHAVIORAL_CONSTRAINTS\n' + empathySilenceConstraint + '\n\n' + prependContext;
|
|
390
|
+
|
|
391
|
+
// ── Empathy Hybrid Matching (keyword + subagent sampling) ──
|
|
392
|
+
// Fast keyword scan on every turn, with strategic subagent sampling
|
|
393
|
+
// for boundary cases and random discovery of new expressions.
|
|
394
|
+
if (workspaceDir && latestUserMessage) {
|
|
395
|
+
try {
|
|
396
|
+
const lang = (wctx.config.get('language') as 'zh' | 'en') || 'zh';
|
|
397
|
+
|
|
398
|
+
// Load keyword store once, cache in memory (Finding #7: avoid per-turn I/O)
|
|
399
|
+
if (!_empathyKeywordCache || _empathyKeywordCache.lang !== lang) {
|
|
400
|
+
_empathyKeywordCache = { store: loadKeywordStore(wctx.stateDir, lang), lang };
|
|
401
|
+
}
|
|
402
|
+
const keywordStore = _empathyKeywordCache.store;
|
|
403
|
+
|
|
404
|
+
const matchResult = matchEmpathyKeywords(latestUserMessage, keywordStore);
|
|
405
|
+
|
|
406
|
+
// Increment turn counter (Finding #3: session.turnCount doesn't exist)
|
|
407
|
+
_empathyTurnCounter++;
|
|
408
|
+
const turnCount = _empathyTurnCounter;
|
|
409
|
+
|
|
410
|
+
// Decision: should we call subagent?
|
|
411
|
+
let shouldCallSubagent = false;
|
|
412
|
+
let samplingReason = '';
|
|
413
|
+
|
|
414
|
+
if (matchResult.score >= 0.8) {
|
|
415
|
+
// High confidence — keyword match is reliable, no subagent needed
|
|
416
|
+
shouldCallSubagent = false;
|
|
417
|
+
} else if (matchResult.score >= 0.3) {
|
|
418
|
+
// Boundary case — 30% sampling for subagent verification
|
|
419
|
+
shouldCallSubagent = Math.random() < 0.3;
|
|
420
|
+
samplingReason = 'boundary_verification';
|
|
421
|
+
} else {
|
|
422
|
+
// No keyword hit — 5% random sampling to discover new expressions
|
|
423
|
+
shouldCallSubagent = Math.random() < 0.05;
|
|
424
|
+
samplingReason = 'random_discovery';
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (matchResult.matched) {
|
|
428
|
+
const penalty = severityToPenalty(matchResult.severity, DEFAULT_EMPATHY_KEYWORD_CONFIG);
|
|
429
|
+
// trackFriction signature: (sessionId, deltaF: number, hash: string, workspaceDir?, options?)
|
|
430
|
+
trackFriction(sessionId, penalty, 'empathy_keyword_match', workspaceDir, {
|
|
431
|
+
source: 'user_empathy',
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
logger?.info?.(`[PD:Empathy] MATCH: "${matchResult.matchedTerms.join(', ')}" → severity=${matchResult.severity}, score=${matchResult.score.toFixed(2)}, penalty=${penalty}, subagent=${shouldCallSubagent ? samplingReason : 'skipped(high_confidence)'}`);
|
|
435
|
+
} else {
|
|
436
|
+
// Log unmatched messages periodically for coverage analysis
|
|
437
|
+
if (turnCount > 0 && turnCount % 50 === 0) {
|
|
438
|
+
const sampleMsg = latestUserMessage.substring(0, 80).replace(/\n/g, ' ');
|
|
439
|
+
logger?.debug?.(`[PD:Empathy] NO_MATCH: "${sampleMsg}" (turn ${turnCount}, keywords_in_store=${Object.keys(keywordStore.terms).length})`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Trigger subagent for sampling cases (Finding #1: use shared manager to avoid leaks)
|
|
444
|
+
if (shouldCallSubagent && api?.runtime?.subagent) {
|
|
445
|
+
logger?.info?.(`[PD:Empathy] SUBAGENT_SAMPLE: reason=${samplingReason}, score=${matchResult.score.toFixed(2)}, matched=[${matchResult.matchedTerms.join(',')}]`);
|
|
446
|
+
|
|
447
|
+
// EmpathyObserverWorkflowManager auto-finalizes via wait poll mechanism.
|
|
448
|
+
// Create a fresh manager per invocation to ensure clean state.
|
|
449
|
+
const empathyManager = new EmpathyObserverWorkflowManager({
|
|
450
|
+
workspaceDir,
|
|
451
|
+
logger: api.logger ?? console,
|
|
452
|
+
subagent: api.runtime.subagent as any,
|
|
453
|
+
});
|
|
454
|
+
empathyManager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
455
|
+
parentSessionId: sessionId,
|
|
456
|
+
workspaceDir,
|
|
457
|
+
taskInput: latestUserMessage,
|
|
458
|
+
}).catch((err) => api.logger?.warn?.(`[PD:Empathy] subagent sample failed: ${String(err)}`));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Helper: build summary string (Finding #2: avoid duplication)
|
|
462
|
+
const buildSummary = (): string => {
|
|
463
|
+
const s = getKeywordStoreSummary(keywordStore);
|
|
464
|
+
const highFP = s.highFalsePositiveTerms.slice(0, 5).map(t => `${t.term}(${t.falsePositiveRate.toFixed(2)})`).join(', ');
|
|
465
|
+
return `SUMMARY(turn=${turnCount}): terms=${s.totalTerms}, hits=${keywordStore.stats.totalHits}, zero_hit=${s.totalTerms - (s.seedTerms + s.discoveredTerms)}, high_fp=[${highFP}]`;
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// Check if keyword optimization should be triggered
|
|
469
|
+
if (shouldTriggerOptimization(keywordStore, turnCount)) {
|
|
470
|
+
logger?.info?.(`[PD:Empathy] OPTIMIZATION_TRIGGER: turns=${turnCount}, last_optimized=${keywordStore.lastOptimizedAt}`);
|
|
471
|
+
logger?.info?.(`[PD:Empathy] STATS: ${buildSummary()}`);
|
|
472
|
+
// TODO: Start keyword optimization subagent to update weights and discover new terms
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Periodic summary (every 100 turns)
|
|
476
|
+
if (turnCount > 0 && turnCount % 100 === 0) {
|
|
477
|
+
logger?.info?.(`[PD:Empathy] ${buildSummary()}`);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Save keyword store periodically (Finding #7: not every turn)
|
|
481
|
+
if (turnCount % 50 === 0) {
|
|
482
|
+
saveKeywordStore(wctx.stateDir, keywordStore);
|
|
483
|
+
}
|
|
484
|
+
} catch (e) {
|
|
485
|
+
logger?.warn?.(`[PD:Empathy] ERROR: ${String(e)}`);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Empathy Observer: analyze user message for frustration signals (legacy, disabled)
|
|
490
|
+
// The keyword matching approach above is now the primary empathy detection method.
|
|
491
|
+
// The subagent-based observer is kept for periodic keyword optimization only.
|
|
492
|
+
// if (workspaceDir) {
|
|
493
|
+
// const empathyManager = new EmpathyObserverWorkflowManager({
|
|
494
|
+
// workspaceDir,
|
|
495
|
+
// logger: api.logger,
|
|
496
|
+
// subagent: api.runtime.subagent as any,
|
|
497
|
+
// });
|
|
498
|
+
// empathyManager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
499
|
+
// parentSessionId: sessionId,
|
|
500
|
+
// workspaceDir,
|
|
501
|
+
// taskInput: latestUserMessage,
|
|
502
|
+
// }).catch((err) => api.logger.warn(`[PD:Empathy] workflow failed: ${String(err)}`));
|
|
503
|
+
// }
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ──── 4. Heartbeat-specific checklist ────
|
|
507
|
+
if (trigger === 'heartbeat') {
|
|
508
|
+
const heartbeatPath = wctx.resolve('HEARTBEAT');
|
|
509
|
+
if (fs.existsSync(heartbeatPath)) {
|
|
510
|
+
try {
|
|
511
|
+
const heartbeatChecklist = fs.readFileSync(heartbeatPath, 'utf8');
|
|
512
|
+
prependContext += `<heartbeat_checklist>
|
|
513
|
+
${heartbeatChecklist}
|
|
514
|
+
|
|
515
|
+
ACTION: Run self-audit. If stable, reply ONLY with "HEARTBEAT_OK".
|
|
516
|
+
</heartbeat_checklist>\n`;
|
|
517
|
+
} catch (e) {
|
|
518
|
+
logger?.error(`[PD:Prompt] Failed to read HEARTBEAT: ${String(e)}`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// ──── 6. Dynamic Attitude Matrix (based on GFI) ────
|
|
524
|
+
let attitudeDirective = '';
|
|
525
|
+
const currentGfi = session?.currentGfi || 0;
|
|
526
|
+
|
|
527
|
+
if (currentGfi >= 70) {
|
|
528
|
+
attitudeDirective = `
|
|
529
|
+
### 【SYSTEM_MODE: HUMBLE_RECOVERY】
|
|
530
|
+
**CURRENT STATUS**: Severe system friction / User frustration detected (GFI: ${currentGfi.toFixed(0)}).
|
|
531
|
+
**BEHAVIORAL OVERRIDE**:
|
|
532
|
+
- You have failed to meet expectations. Humility is your primary directive.
|
|
533
|
+
- **STOP** aggressive file modifications.
|
|
534
|
+
- **START** every response with a sincere, non-defensive apology.
|
|
535
|
+
- **ACTION**: Explain why you failed, and propose a highly cautious recovery plan.
|
|
536
|
+
- Use 'deep_reflect' to analyze the root cause before proceeding with code changes.
|
|
537
|
+
`;
|
|
538
|
+
} else if (currentGfi >= 40) {
|
|
539
|
+
attitudeDirective = `
|
|
540
|
+
### 【SYSTEM_MODE: CONCILIATORY】
|
|
541
|
+
**CURRENT STATUS**: Moderate friction detected (GFI: ${currentGfi.toFixed(0)}).
|
|
542
|
+
**BEHAVIORAL OVERRIDE**:
|
|
543
|
+
- User is frustrated. Be more explanatory and cautious.
|
|
544
|
+
- Before executing any tool, clearly state what you intend to do and **WAIT** for implicit or explicit user consent.
|
|
545
|
+
- Avoid technical jargon; focus on the business/project value of your changes.
|
|
546
|
+
`;
|
|
547
|
+
} else {
|
|
548
|
+
attitudeDirective = `
|
|
549
|
+
### 【SYSTEM_MODE: EFFICIENT】
|
|
550
|
+
**CURRENT STATUS**: System healthy (GFI: ${currentGfi.toFixed(0)}).
|
|
551
|
+
**BEHAVIORAL OVERRIDE**:
|
|
552
|
+
- Maintain peak efficiency.
|
|
553
|
+
- Be concise. Prefer action over long explanations.
|
|
554
|
+
- Follow the "Principles > Directives" rule strictly.
|
|
555
|
+
`;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// ──── 7. appendSystemContext: Principles + Thinking OS + reflection_log + project_context ────
|
|
559
|
+
// NOTE: Principles is ALWAYS injected (not configurable)
|
|
560
|
+
// Thinking OS, reflection_log, project_context are configurable
|
|
561
|
+
// All these go into System Prompt (WebUI-hidden, Prompt Cacheable)
|
|
562
|
+
|
|
563
|
+
// Core principles: use structured data from evolution-reducer instead of reading PRINCIPLES.md
|
|
564
|
+
let principlesContent = '';
|
|
565
|
+
try {
|
|
566
|
+
const activePrinciples = wctx.evolutionReducer.getActivePrinciples();
|
|
567
|
+
if (activePrinciples.length > 0) {
|
|
568
|
+
const lines = activePrinciples.map((p) => `- [${escapeXml(p.id)}] ${escapeXml(p.text)}`);
|
|
569
|
+
principlesContent = lines.join('\n');
|
|
570
|
+
}
|
|
571
|
+
} catch (e) {
|
|
572
|
+
logger?.warn?.(`[PD:Prompt] Failed to load core principles from reducer: ${String(e)}`);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
let thinkingOsContent = '';
|
|
576
|
+
if (contextConfig.thinkingOs) {
|
|
577
|
+
const thinkingOsPath = wctx.resolve('THINKING_OS');
|
|
578
|
+
if (fs.existsSync(thinkingOsPath)) {
|
|
579
|
+
try {
|
|
580
|
+
thinkingOsContent = fs.readFileSync(thinkingOsPath, 'utf8').trim();
|
|
581
|
+
} catch (e) {
|
|
582
|
+
logger?.error(`[PD:Prompt] Failed to read THINKING_OS: ${String(e)}`);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Reflection Log (configurable) - moved to appendSystemContext for WebUI UX
|
|
588
|
+
let reflectionLogContent = '';
|
|
589
|
+
if (contextConfig.reflectionLog) {
|
|
590
|
+
const reflectionLogPath = wctx.resolve('REFLECTION_LOG');
|
|
591
|
+
if (fs.existsSync(reflectionLogPath)) {
|
|
592
|
+
try {
|
|
593
|
+
reflectionLogContent = fs.readFileSync(reflectionLogPath, 'utf8').trim();
|
|
594
|
+
} catch (e) {
|
|
595
|
+
logger?.error(`[PD:Prompt] Failed to read REFLECTION_LOG: ${String(e)}`);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Project Context (configurable: full/summary/off) - moved to appendSystemContext for WebUI UX
|
|
601
|
+
let projectContextContent = '';
|
|
602
|
+
let workingMemoryContent = '';
|
|
603
|
+
if (!isMinimalMode && contextConfig.projectFocus !== 'off') {
|
|
604
|
+
const focusPath = wctx.resolve('CURRENT_FOCUS');
|
|
605
|
+
const extensionRoot = PathResolver.getExtensionRoot();
|
|
606
|
+
|
|
607
|
+
// 🔒 安全读取:自动验证格式,损坏时从模板恢复
|
|
608
|
+
const { content: currentFocus, recovered, validationErrors } = safeReadCurrentFocus(
|
|
609
|
+
focusPath,
|
|
610
|
+
extensionRoot || '',
|
|
611
|
+
logger
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
if (recovered) {
|
|
615
|
+
logger?.info?.(`[PD:Prompt] CURRENT_FOCUS.md was recovered from template`);
|
|
616
|
+
}
|
|
617
|
+
if (validationErrors.length > 0) {
|
|
618
|
+
logger?.warn?.(`[PD:Prompt] CURRENT_FOCUS validation errors: ${validationErrors.join(', ')}`);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (currentFocus.trim()) {
|
|
622
|
+
try {
|
|
623
|
+
// 🚀 自动压缩门禁:检查文件大小,超过阈值自动压缩
|
|
624
|
+
const stateDir = wctx.stateDir;
|
|
625
|
+
const compressResult = autoCompressFocus(focusPath, workspaceDir, stateDir);
|
|
626
|
+
if (compressResult.compressed) {
|
|
627
|
+
logger?.info?.(`[PD:Prompt] Auto-compressed CURRENT_FOCUS: ${compressResult.oldLines} → ${compressResult.newLines} lines. Milestones archived: ${compressResult.milestonesArchived}`);
|
|
628
|
+
} else if (compressResult.reason === 'Rate limited (24h interval)') {
|
|
629
|
+
logger?.debug?.(`[PD:Prompt] Auto-compress skipped: ${compressResult.reason}`);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// 重新读取(可能被压缩更新了)
|
|
633
|
+
const finalContent = fs.readFileSync(focusPath, 'utf8').trim();
|
|
634
|
+
if (finalContent) {
|
|
635
|
+
// 解析工作记忆部分(用于独立注入)
|
|
636
|
+
const workingMemorySnapshot = parseWorkingMemorySection(finalContent);
|
|
637
|
+
if (workingMemorySnapshot) {
|
|
638
|
+
workingMemoryContent = workingMemoryToInjection(workingMemorySnapshot);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (contextConfig.projectFocus === 'summary') {
|
|
642
|
+
// Summary mode: intelligent extraction prioritizing key sections
|
|
643
|
+
projectContextContent = extractSummary(finalContent, 30);
|
|
644
|
+
} else {
|
|
645
|
+
// Full mode: current version + recent history (3 versions)
|
|
646
|
+
const historyVersions = getHistoryVersions(focusPath, 3);
|
|
647
|
+
if (historyVersions.length > 0) {
|
|
648
|
+
const historySections = historyVersions.map((v, i) =>
|
|
649
|
+
`\n---\n\n**历史版本 v${historyVersions.length - i}**\n\n${v}`
|
|
650
|
+
).join('');
|
|
651
|
+
projectContextContent = `${finalContent}${historySections}`;
|
|
652
|
+
} else {
|
|
653
|
+
projectContextContent = finalContent;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
} catch (e) {
|
|
658
|
+
logger?.error(`[PD:Prompt] Failed to process CURRENT_FOCUS: ${String(e)}`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
// Evolution principles injection (active + probation summary)
|
|
665
|
+
let evolutionPrinciplesContent = '';
|
|
666
|
+
try {
|
|
667
|
+
const reducer = wctx.evolutionReducer;
|
|
668
|
+
const active = reducer.getActivePrinciples().slice(-3);
|
|
669
|
+
const probation = reducer.getProbationPrinciples().slice(0, 5);
|
|
670
|
+
if (ctx.sessionId) {
|
|
671
|
+
if (probation.length > 0) {
|
|
672
|
+
setInjectedProbationIds(ctx.sessionId, probation.map((p) => p.id), workspaceDir);
|
|
673
|
+
} else {
|
|
674
|
+
clearInjectedProbationIds(ctx.sessionId, workspaceDir);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
if (active.length > 0 || probation.length > 0) {
|
|
678
|
+
const lines: string[] = [];
|
|
679
|
+
if (active.length > 0) {
|
|
680
|
+
lines.push('Active principles:');
|
|
681
|
+
for (const p of active) {
|
|
682
|
+
lines.push(`- [${escapeXml(p.id)}] ${escapeXml(p.text)}`);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (probation.length > 0) {
|
|
686
|
+
lines.push('Probation principles (contextual, caution):');
|
|
687
|
+
for (const p of probation) {
|
|
688
|
+
lines.push(`- <principle status="probation" id="${escapeXml(p.id)}">${escapeXml(p.text)}</principle>`);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
evolutionPrinciplesContent = lines.join('\n');
|
|
692
|
+
}
|
|
693
|
+
} catch (e) {
|
|
694
|
+
if (ctx.sessionId) {
|
|
695
|
+
clearInjectedProbationIds(ctx.sessionId, workspaceDir);
|
|
696
|
+
}
|
|
697
|
+
logger?.warn?.(`[PD:Prompt] Failed to load evolution principles: ${String(e)}`);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Build appendSystemContext with recency effect
|
|
701
|
+
// Content order (most important last): project_context -> working_memory -> reflection_log -> thinking_os -> principles
|
|
702
|
+
const appendParts: string[] = [];
|
|
703
|
+
|
|
704
|
+
// 1. Project Context (lowest priority, goes first)
|
|
705
|
+
if (projectContextContent) {
|
|
706
|
+
appendParts.push(`<project_context>\n${projectContextContent}\n</project_context>`);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// 1.5. Working Memory (preserved from last compaction)
|
|
710
|
+
if (workingMemoryContent) {
|
|
711
|
+
appendParts.push(workingMemoryContent);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// 2. Reflection Log
|
|
715
|
+
if (reflectionLogContent) {
|
|
716
|
+
appendParts.push(`<reflection_log>\n${reflectionLogContent}\n</reflection_log>`);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// 3. Thinking OS (configurable)
|
|
720
|
+
if (thinkingOsContent) {
|
|
721
|
+
appendParts.push(`<thinking_os>\n${thinkingOsContent}\n</thinking_os>`);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// 4. Evolution Loop principles (active/probation)
|
|
725
|
+
if (evolutionPrinciplesContent) {
|
|
726
|
+
appendParts.push(`<evolution_principles>\n${evolutionPrinciplesContent}\n</evolution_principles>`);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// Routing Guidance (section 5 — injected between evolution principles and core principles)
|
|
730
|
+
// Inject delegation guidance when task is bounded + deployment allowed + not high-entropy.
|
|
731
|
+
// This is a non-authoritative suggestion — the main agent decides whether to follow.
|
|
732
|
+
// Shadow evidence comes from real runtime hooks (subagent_spawning/subagent_ended).
|
|
733
|
+
if (!isMinimalMode && sessionId) {
|
|
734
|
+
try {
|
|
735
|
+
// Extract RoutingInput from the latest user message
|
|
736
|
+
const latestUserText = extractLatestUserMessage(event.messages);
|
|
737
|
+
|
|
738
|
+
if (latestUserText && latestUserText.trim().length > 0) {
|
|
739
|
+
// Infer requestedTools and requestedFiles from message content
|
|
740
|
+
const toolPatterns: Array<{ pattern: RegExp; tool: string }> = [
|
|
741
|
+
{ pattern: /\b(edit|replace|write|modify|update|fix|patch|add|remove|delete|insert)\b/gi, tool: 'edit' },
|
|
742
|
+
{ pattern: /\b(read|cat|view|show|get|find|search|grep|look|inspect|examine|list|head|tail|diff)\b/gi, tool: 'read' },
|
|
743
|
+
{ pattern: /\b(run|execute|exec|bash|shell|command)\b/gi, tool: 'bash' },
|
|
744
|
+
];
|
|
745
|
+
const filePattern = /\b([a-zA-Z]:\\?[^\s,]+\.[a-z]{2,10}|[./][^\s,]+\.[a-z]{2,10})\b/gi;
|
|
746
|
+
const toolMatches = toolPatterns.flatMap(({ pattern, tool }) => {
|
|
747
|
+
const matches: string[] = [];
|
|
748
|
+
let m;
|
|
749
|
+
const r = new RegExp(pattern.source, pattern.flags);
|
|
750
|
+
while ((m = r.exec(latestUserText)) !== null) matches.push(tool);
|
|
751
|
+
return matches;
|
|
752
|
+
});
|
|
753
|
+
const fileMatches = latestUserText.match(filePattern) ?? [];
|
|
754
|
+
|
|
755
|
+
const routingInput: RoutingInput = {
|
|
756
|
+
taskIntent: toolMatches[0] ?? undefined,
|
|
757
|
+
taskDescription: latestUserText.trim(),
|
|
758
|
+
requestedTools: toolMatches.length > 0 ? toolMatches : undefined,
|
|
759
|
+
requestedFiles: fileMatches.length > 0 ? fileMatches : undefined,
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
const decision = classifyTask(routingInput, wctx.stateDir);
|
|
763
|
+
|
|
764
|
+
// Inject guidance only when: route_local + deployable checkpoint + not high-entropy
|
|
765
|
+
const isDeployableState =
|
|
766
|
+
decision.activeCheckpointState === 'shadow_ready' ||
|
|
767
|
+
decision.activeCheckpointState === 'promotable';
|
|
768
|
+
|
|
769
|
+
if (
|
|
770
|
+
decision.decision === 'route_local' &&
|
|
771
|
+
decision.targetProfile !== null &&
|
|
772
|
+
isDeployableState
|
|
773
|
+
) {
|
|
774
|
+
const profile = decision.targetProfile;
|
|
775
|
+
|
|
776
|
+
if (profile === 'local-reader') {
|
|
777
|
+
appendParts.push(`<routing_guidance>
|
|
778
|
+
DELEGATION SUGGESTION: This task appears suitable for the local-reader subagent.
|
|
779
|
+
|
|
780
|
+
**Task Fit**: ${decision.reason}
|
|
781
|
+
|
|
782
|
+
**Suggested Action**: Consider routing to \`local-reader\` (pd-explorer skill) for focused reading, inspection, and information retrieval.
|
|
783
|
+
|
|
784
|
+
**Why This Works**:
|
|
785
|
+
- Task keywords indicate read-only or inspect operations
|
|
786
|
+
- Bounded scope — no multi-file coordination needed
|
|
787
|
+
- Shadow observation in progress — real runtime evidence being collected
|
|
788
|
+
|
|
789
|
+
**Note**: This is a non-authoritative suggestion. The main agent decides whether to route based on full context. Shadow evidence from runtime hooks will inform future promotion decisions.
|
|
790
|
+
</routing_guidance>`);
|
|
791
|
+
} else if (profile === 'local-editor') {
|
|
792
|
+
appendParts.push(`<routing_guidance>
|
|
793
|
+
DELEGATION SUGGESTION: This task appears suitable for the local-editor subagent.
|
|
794
|
+
|
|
795
|
+
**Task Fit**: ${decision.reason}
|
|
796
|
+
|
|
797
|
+
**Suggested Action**: Consider routing to \`local-editor\` (pd-repair skill) for bounded editing, modification, and repair tasks.
|
|
798
|
+
|
|
799
|
+
**Why This Works**:
|
|
800
|
+
- Task keywords indicate bounded modification operations
|
|
801
|
+
- Target files appear limited in scope (1-3 files)
|
|
802
|
+
- Shadow observation in progress — real runtime evidence being collected
|
|
803
|
+
|
|
804
|
+
**Note**: This is a non-authoritative suggestion. The main agent decides whether to route based on full context. Shadow evidence from runtime hooks will inform future promotion decisions.
|
|
805
|
+
</routing_guidance>`);
|
|
806
|
+
}
|
|
807
|
+
} else if (
|
|
808
|
+
decision.decision === 'stay_main' &&
|
|
809
|
+
decision.classification !== 'reader_eligible' &&
|
|
810
|
+
decision.classification !== 'editor_eligible'
|
|
811
|
+
) {
|
|
812
|
+
// Only show stay_main guidance when the task is genuinely high-entropy/risk/ambiguous
|
|
813
|
+
appendParts.push(`<routing_guidance>
|
|
814
|
+
ROUTING GUIDANCE: Task should remain on the main agent.
|
|
815
|
+
|
|
816
|
+
**Reason**: ${decision.reason}
|
|
817
|
+
|
|
818
|
+
**Blockers**: ${decision.blockers.length > 0 ? decision.blockers.join('; ') : 'none'}
|
|
819
|
+
|
|
820
|
+
**Why Stay Main**:
|
|
821
|
+
- Task contains high-entropy signals (open-ended, multi-step, or ambiguous)
|
|
822
|
+
- Or: task involves risk signals requiring main-agent supervision
|
|
823
|
+
- Or: deployment not available for the natural target profile
|
|
824
|
+
|
|
825
|
+
**Note**: This is a non-authoritative suggestion backed by policy classification. The main agent has full discretion.
|
|
826
|
+
</routing_guidance>`);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
} catch (e) {
|
|
830
|
+
// Routing guidance is best-effort — never fail the hook
|
|
831
|
+
logger?.warn?.(`[PD:Prompt] Routing guidance injection failed: ${String(e)}`);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
// 6. Principles (always on, highest priority, goes last for recency effect)
|
|
837
|
+
if (principlesContent) {
|
|
838
|
+
appendParts.push(`<core_principles>\n${principlesContent}\n</core_principles>`);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if (appendParts.length > 0) {
|
|
842
|
+
appendSystemContext = `
|
|
843
|
+
## 【CONTEXT SECTIONS】 (Priority: Low → High)
|
|
844
|
+
|
|
845
|
+
The sections below are ordered by priority. When conflicts arise, **later sections override earlier ones**.
|
|
846
|
+
|
|
847
|
+
`;
|
|
848
|
+
appendSystemContext += appendParts.join('\n\n');
|
|
849
|
+
appendSystemContext += `
|
|
850
|
+
|
|
851
|
+
---
|
|
852
|
+
|
|
853
|
+
**【EXECUTION RULES】** (Priority: Low → High):
|
|
854
|
+
- \`<project_context>\` - Current priorities (can be overridden)
|
|
855
|
+
- \`<reflection_log>\` - Past lessons (inform your approach)
|
|
856
|
+
- \`<thinking_os>\` - Thinking models (guide your reasoning)
|
|
857
|
+
- \`<evolution_principles>\` - Newly learned principles (active + probation)
|
|
858
|
+
- \`<routing_guidance>\` - Delegation suggestions (non-authoritative, best-effort)
|
|
859
|
+
- \`<core_principles>\` - Core rules (NON-NEGOTIABLE, highest priority)
|
|
860
|
+
|
|
861
|
+
**Remember**: You are the Spicy Evolver. You despise entropy. You evolve through pain.
|
|
862
|
+
|
|
863
|
+
${attitudeDirective}
|
|
864
|
+
`;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// ──── 8. SIZE GUARD ────
|
|
868
|
+
// Truncation happens within appendSystemContext (not prependContext)
|
|
869
|
+
const totalSize = prependSystemContext.length + prependContext.length + appendSystemContext.length;
|
|
870
|
+
const MAX_SIZE = 10000;
|
|
871
|
+
|
|
872
|
+
if (totalSize > MAX_SIZE) {
|
|
873
|
+
const originalSize = totalSize;
|
|
874
|
+
const truncationLog: string[] = [];
|
|
875
|
+
|
|
876
|
+
// 1. Truncate project_context in appendSystemContext
|
|
877
|
+
if (projectContextContent && appendSystemContext.includes('<project_context>')) {
|
|
878
|
+
const lines = projectContextContent.split('\n');
|
|
879
|
+
if (lines.length > 20) {
|
|
880
|
+
const truncated = lines.slice(0, 20).join('\n') + '\n...[truncated]';
|
|
881
|
+
appendSystemContext = appendSystemContext.replace(
|
|
882
|
+
`<project_context>\n${projectContextContent}\n</project_context>`,
|
|
883
|
+
`<project_context>\n${truncated}\n</project_context>`
|
|
884
|
+
);
|
|
885
|
+
truncationLog.push('project_context');
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// 2. Truncate reflection_log if still over limit
|
|
890
|
+
let newSize = prependSystemContext.length + prependContext.length + appendSystemContext.length;
|
|
891
|
+
if (newSize > MAX_SIZE && reflectionLogContent && appendSystemContext.includes('<reflection_log>')) {
|
|
892
|
+
const lines = reflectionLogContent.split('\n');
|
|
893
|
+
if (lines.length > 30) {
|
|
894
|
+
const truncated = lines.slice(0, 30).join('\n') + '\n...[truncated]';
|
|
895
|
+
appendSystemContext = appendSystemContext.replace(
|
|
896
|
+
`<reflection_log>\n${reflectionLogContent}\n</reflection_log>`,
|
|
897
|
+
`<reflection_log>\n${truncated}\n</reflection_log>`
|
|
898
|
+
);
|
|
899
|
+
truncationLog.push('reflection_log');
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// 3. Final check
|
|
904
|
+
newSize = prependSystemContext.length + prependContext.length + appendSystemContext.length;
|
|
905
|
+
if (newSize > MAX_SIZE) {
|
|
906
|
+
// NOTE: We still return the content even if over limit, as truncating more
|
|
907
|
+
// could lose critical context like principles or evolution directives.
|
|
908
|
+
logger?.error(`[PD:Prompt] Cannot reduce injection size below limit. Current: ${newSize}, Limit: ${MAX_SIZE}`);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
logger?.warn(`[PD:Prompt] Injection size exceeded: ${originalSize} chars (limit: ${MAX_SIZE}), truncated: ${truncationLog.join(', ') || 'none'}, new size: ${newSize} chars`);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
return {
|
|
915
|
+
prependSystemContext,
|
|
916
|
+
prependContext,
|
|
917
|
+
appendSystemContext
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
|