principles-disciple 1.8.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ADVANCED_CONFIG_ZH.md +97 -0
- package/AGENT_INSTALL.md +173 -0
- package/AGENT_INSTALL_EN.md +173 -0
- package/INSTALL.md +256 -0
- package/SKILL.md +63 -0
- package/docs/COMMAND_REFERENCE.md +76 -0
- package/docs/COMMAND_REFERENCE_EN.md +79 -0
- package/esbuild.config.js +75 -0
- package/openclaw.plugin.json +6 -1
- package/package.json +13 -15
- package/scripts/build-web.mjs +46 -0
- package/scripts/install-dependencies.cjs +47 -0
- package/scripts/sync-plugin.mjs +802 -0
- package/scripts/verify-build.mjs +109 -0
- package/src/agents/nocturnal-dreamer.md +152 -0
- package/src/agents/nocturnal-philosopher.md +138 -0
- package/src/agents/nocturnal-reflector.md +126 -0
- package/src/agents/nocturnal-scribe.md +164 -0
- package/src/commands/capabilities.ts +85 -0
- package/{dist/commands/context.js → src/commands/context.ts} +78 -38
- package/src/commands/evolution-status.ts +146 -0
- package/src/commands/export.ts +111 -0
- package/src/commands/focus.ts +533 -0
- package/src/commands/nocturnal-review.ts +311 -0
- package/src/commands/nocturnal-rollout.ts +763 -0
- package/src/commands/nocturnal-train.ts +1002 -0
- package/{dist/commands/pain.js → src/commands/pain.ts} +68 -49
- package/src/commands/principle-rollback.ts +27 -0
- package/{dist/commands/rollback.js → src/commands/rollback.ts} +44 -12
- package/src/commands/samples.ts +60 -0
- package/src/commands/strategy.ts +38 -0
- package/{dist/commands/thinking-os.js → src/commands/thinking-os.ts} +59 -36
- package/src/commands/workflow-debug.ts +128 -0
- package/{dist/config/defaults/runtime.js → src/config/defaults/runtime.ts} +12 -5
- package/src/config/errors.ts +163 -0
- package/{dist/config/index.d.ts → src/config/index.ts} +2 -1
- package/src/constants/diagnostician.ts +66 -0
- package/src/constants/tools.ts +62 -0
- package/src/core/adaptive-thresholds.ts +476 -0
- package/{dist/core/config-service.js → src/core/config-service.ts} +7 -4
- package/{dist/core/config.js → src/core/config.ts} +158 -46
- package/src/core/control-ui-db.ts +435 -0
- package/{dist/core/detection-funnel.js → src/core/detection-funnel.ts} +36 -21
- package/{dist/core/detection-service.js → src/core/detection-service.ts} +7 -4
- package/{dist/core/dictionary-service.js → src/core/dictionary-service.ts} +7 -4
- package/{dist/core/dictionary.js → src/core/dictionary.ts} +57 -34
- package/src/core/empathy-keyword-matcher.ts +327 -0
- package/src/core/empathy-types.ts +218 -0
- package/src/core/event-log.ts +544 -0
- package/src/core/evolution-engine.ts +612 -0
- package/src/core/evolution-logger.ts +353 -0
- package/src/core/evolution-migration.ts +77 -0
- package/src/core/evolution-reducer.ts +731 -0
- package/src/core/evolution-types.ts +456 -0
- package/src/core/external-training-contract.ts +527 -0
- package/src/core/focus-history.ts +1458 -0
- package/src/core/hygiene/tracker.ts +117 -0
- package/{dist/core/init.js → src/core/init.ts} +39 -26
- package/src/core/local-worker-routing.ts +617 -0
- package/{dist/core/migration.js → src/core/migration.ts} +18 -11
- package/src/core/model-deployment-registry.ts +722 -0
- package/src/core/model-training-registry.ts +813 -0
- package/src/core/nocturnal-arbiter.ts +706 -0
- package/src/core/nocturnal-candidate-scoring.ts +392 -0
- package/src/core/nocturnal-compliance.ts +1075 -0
- package/src/core/nocturnal-dataset.ts +668 -0
- package/src/core/nocturnal-executability.ts +428 -0
- package/src/core/nocturnal-export.ts +390 -0
- package/{dist/core/nocturnal-paths.js → src/core/nocturnal-paths.ts} +49 -23
- package/src/core/nocturnal-trajectory-extractor.ts +484 -0
- package/src/core/nocturnal-trinity.ts +1384 -0
- package/src/core/pain.ts +122 -0
- package/{dist/core/path-resolver.js → src/core/path-resolver.ts} +157 -36
- package/{dist/core/paths.js → src/core/paths.ts} +13 -4
- package/src/core/principle-training-state.ts +450 -0
- package/src/core/profile.ts +226 -0
- package/src/core/promotion-gate.ts +822 -0
- package/{dist/core/risk-calculator.js → src/core/risk-calculator.ts} +42 -16
- package/{dist/core/session-tracker.js → src/core/session-tracker.ts} +185 -63
- package/src/core/shadow-observation-registry.ts +534 -0
- package/{dist/core/system-logger.js → src/core/system-logger.ts} +9 -5
- package/src/core/thinking-models.ts +217 -0
- package/src/core/training-program.ts +630 -0
- package/src/core/trajectory-types.ts +243 -0
- package/src/core/trajectory.ts +1673 -0
- package/{dist/core/workspace-context.js → src/core/workspace-context.ts} +57 -32
- package/src/hooks/bash-risk.ts +171 -0
- package/src/hooks/edit-verification.ts +295 -0
- package/src/hooks/gate-block-helper.ts +160 -0
- package/src/hooks/gate.ts +210 -0
- package/src/hooks/gfi-gate.ts +177 -0
- package/src/hooks/lifecycle.ts +326 -0
- package/{dist/hooks/llm.js → src/hooks/llm.ts} +166 -139
- package/src/hooks/message-sanitize.ts +45 -0
- package/src/hooks/pain.ts +384 -0
- package/src/hooks/progressive-trust-gate.ts +174 -0
- package/src/hooks/prompt.ts +920 -0
- package/src/hooks/subagent.ts +207 -0
- package/src/hooks/thinking-checkpoint.ts +73 -0
- package/src/hooks/trajectory-collector.ts +290 -0
- package/src/http/principles-console-route.ts +716 -0
- package/src/i18n/commands.ts +117 -0
- package/src/index.ts +694 -0
- package/src/service/central-database.ts +831 -0
- package/src/service/control-ui-query-service.ts +888 -0
- package/src/service/evolution-query-service.ts +405 -0
- package/src/service/evolution-worker.ts +1646 -0
- package/src/service/health-query-service.ts +836 -0
- package/{dist/service/nocturnal-runtime.js → src/service/nocturnal-runtime.ts} +263 -36
- package/src/service/nocturnal-service.ts +1015 -0
- package/src/service/nocturnal-target-selector.ts +532 -0
- package/src/service/phase3-input-filter.ts +237 -0
- package/src/service/runtime-summary-service.ts +757 -0
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +513 -0
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +603 -0
- package/src/service/subagent-workflow/index.ts +51 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +856 -0
- package/src/service/subagent-workflow/runtime-direct-driver.ts +166 -0
- package/src/service/subagent-workflow/types.ts +378 -0
- package/src/service/subagent-workflow/workflow-store.ts +328 -0
- package/src/service/trajectory-service.ts +15 -0
- package/{dist/tools/critique-prompt.js → src/tools/critique-prompt.ts} +25 -8
- package/src/tools/deep-reflect.ts +349 -0
- package/{dist/tools/model-index.js → src/tools/model-index.ts} +33 -17
- package/src/types/event-types.ts +453 -0
- package/src/types/hygiene-types.ts +31 -0
- package/src/types/principle-tree-schema.ts +244 -0
- package/src/types/runtime-summary.ts +49 -0
- package/src/types.ts +74 -0
- package/src/utils/file-lock.ts +391 -0
- package/{dist/utils/glob-match.js → src/utils/glob-match.ts} +21 -20
- package/{dist/utils/hashing.js → src/utils/hashing.ts} +6 -4
- package/src/utils/io.ts +110 -0
- package/{dist/utils/nlp.js → src/utils/nlp.ts} +19 -12
- package/{dist/utils/plugin-logger.js → src/utils/plugin-logger.ts} +33 -8
- package/src/utils/subagent-probe.ts +94 -0
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +70 -1
- package/templates/pain_settings.json +2 -1
- package/tests/README.md +120 -0
- package/tests/build-artifacts.test.ts +111 -0
- package/tests/commands/evolution-status.test.ts +222 -0
- package/tests/commands/evolver.test.ts +22 -0
- package/tests/commands/export.test.ts +78 -0
- package/tests/commands/nocturnal-review.test.ts +448 -0
- package/tests/commands/nocturnal-train.test.ts +97 -0
- package/tests/commands/pain.test.ts +108 -0
- package/tests/commands/samples.test.ts +65 -0
- package/tests/commands/strategy.test.ts +34 -0
- package/tests/commands/thinking-os.test.ts +88 -0
- package/tests/core/adaptive-thresholds.test.ts +261 -0
- package/tests/core/config-service.test.ts +89 -0
- package/tests/core/config.test.ts +90 -0
- package/tests/core/control-ui-db.test.ts +75 -0
- package/tests/core/core-template-guidance.test.ts +21 -0
- package/tests/core/detection-funnel.test.ts +63 -0
- package/tests/core/detection-service.test.ts +50 -0
- package/tests/core/dictionary-service.test.ts +116 -0
- package/tests/core/dictionary.test.ts +168 -0
- package/tests/core/empathy-keyword-matcher.test.ts +209 -0
- package/tests/core/event-log.test.ts +181 -0
- package/tests/core/evolution-e2e.test.ts +58 -0
- package/tests/core/evolution-engine-gate-integration.test.ts +543 -0
- package/tests/core/evolution-engine.test.ts +562 -0
- package/tests/core/evolution-logger.test.ts +148 -0
- package/tests/core/evolution-migration.test.ts +50 -0
- package/tests/core/evolution-paths.test.ts +21 -0
- package/tests/core/evolution-reducer.detector-metadata.test.ts +602 -0
- package/tests/core/evolution-reducer.test.ts +180 -0
- package/tests/core/evolution-types-loop.test.ts +48 -0
- package/tests/core/evolution-user-stories.e2e.test.ts +249 -0
- package/tests/core/external-training-contract.test.ts +463 -0
- package/tests/core/focus-history.test.ts +682 -0
- package/tests/core/init-flatten.test.ts +69 -0
- package/tests/core/init-refactor.test.ts +87 -0
- package/tests/core/init-v1.3.test.ts +46 -0
- package/tests/core/init.test.ts +190 -0
- package/tests/core/local-worker-routing.test.ts +757 -0
- package/tests/core/migration.test.ts +84 -0
- package/tests/core/model-deployment-registry.test.ts +845 -0
- package/tests/core/model-training-registry.test.ts +889 -0
- package/tests/core/nocturnal-arbiter.test.ts +494 -0
- package/tests/core/nocturnal-candidate-scoring.test.ts +400 -0
- package/tests/core/nocturnal-compliance.test.ts +646 -0
- package/tests/core/nocturnal-dataset.test.ts +892 -0
- package/tests/core/nocturnal-executability.test.ts +357 -0
- package/tests/core/nocturnal-export.test.ts +462 -0
- package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +428 -0
- package/tests/core/nocturnal-trajectory-extractor.test.ts +634 -0
- package/tests/core/nocturnal-trinity.test.ts +953 -0
- package/tests/core/pain.test.ts +33 -0
- package/tests/core/path-resolver.test.ts +57 -0
- package/tests/core/paths-refactor.test.ts +42 -0
- package/tests/core/phase7-rollout-integration.test.ts +477 -0
- package/tests/core/principle-training-state.test.ts +712 -0
- package/tests/core/profile.test.ts +56 -0
- package/tests/core/promotion-gate.test.ts +556 -0
- package/tests/core/risk-calculator.test.ts +168 -0
- package/tests/core/session-tracker.test.ts +191 -0
- package/tests/core/training-program.test.ts +472 -0
- package/tests/core/trajectory.test.ts +265 -0
- package/tests/core/workspace-context-factory.test.ts +18 -0
- package/tests/core/workspace-context.test.ts +134 -0
- package/tests/fixtures/nocturnal-reviewed-subset.json +183 -0
- package/tests/fixtures/production-compatibility.test.ts +147 -0
- package/tests/fixtures/production-mock-generator.ts +282 -0
- package/tests/hooks/bash-risk-integration.test.ts +137 -0
- package/tests/hooks/bash-risk.test.ts +81 -0
- package/tests/hooks/edit-verification.test.ts +678 -0
- package/tests/hooks/gate-edit-verification-p1.test.ts +632 -0
- package/tests/hooks/gate-edit-verification.test.ts +435 -0
- package/tests/hooks/gate-pipeline-integration.test.ts +404 -0
- package/tests/hooks/gate.test.ts +271 -0
- package/tests/hooks/gfi-gate-unit.test.ts +422 -0
- package/tests/hooks/gfi-gate.test.ts +669 -0
- package/tests/hooks/lifecycle.test.ts +248 -0
- package/tests/hooks/llm.test.ts +308 -0
- package/tests/hooks/message-sanitize.test.ts +36 -0
- package/tests/hooks/pain.test.ts +141 -0
- package/tests/hooks/progressive-trust-gate.test.ts +277 -0
- package/tests/hooks/prompt.test.ts +1411 -0
- package/tests/hooks/subagent.test.ts +467 -0
- package/tests/hooks/thinking-gate.test.ts +313 -0
- package/tests/http/principles-console-route.test.ts +140 -0
- package/tests/hygiene-tracker.test.ts +77 -0
- package/tests/index.integration.test.ts +179 -0
- package/tests/index.shadow-routing.integration.test.ts +140 -0
- package/tests/index.test.ts +9 -0
- package/tests/integration/empathy-workflow-integration.test.ts +627 -0
- package/tests/service/control-ui-query-service.test.ts +121 -0
- package/tests/service/empathy-observer-workflow-manager.test.ts +176 -0
- package/tests/service/evolution-worker.test.ts +585 -0
- package/tests/service/nocturnal-runtime.test.ts +470 -0
- package/tests/service/nocturnal-service.test.ts +577 -0
- package/tests/service/nocturnal-target-selector.test.ts +615 -0
- package/tests/service/nocturnal-workflow-manager.test.ts +439 -0
- package/tests/service/phase3-input-filter.test.ts +289 -0
- package/tests/service/runtime-summary-service.test.ts +919 -0
- package/tests/task-compliance.test.ts +166 -0
- package/tests/test-utils.ts +48 -0
- package/tests/tools/critique-prompt.test.ts +260 -0
- package/tests/tools/deep-reflect.test.ts +232 -0
- package/tests/tools/model-index.test.ts +246 -0
- package/tests/ui/app.test.tsx +114 -0
- package/tests/utils/file-lock.test.ts +407 -0
- package/tests/utils/hashing.test.ts +32 -0
- package/tests/utils/io.test.ts +39 -0
- package/tests/utils/nlp.test.ts +53 -0
- package/tests/utils/plugin-logger.test.ts +156 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/ui/src/App.tsx +45 -0
- package/ui/src/api.ts +216 -0
- package/ui/src/charts.tsx +586 -0
- package/ui/src/components/ErrorState.tsx +6 -0
- package/ui/src/components/Loading.tsx +13 -0
- package/ui/src/components/ProtectedRoute.tsx +12 -0
- package/ui/src/components/Shell.tsx +91 -0
- package/ui/src/components/WorkspaceConfig.tsx +146 -0
- package/ui/src/components/index.ts +5 -0
- package/ui/src/context/auth.tsx +80 -0
- package/ui/src/context/theme.tsx +66 -0
- package/ui/src/hooks/useAutoRefresh.ts +39 -0
- package/ui/src/i18n/ui.ts +363 -0
- package/ui/src/main.tsx +16 -0
- package/ui/src/pages/EvolutionPage.tsx +352 -0
- package/ui/src/pages/FeedbackPage.tsx +140 -0
- package/ui/src/pages/GateMonitorPage.tsx +136 -0
- package/ui/src/pages/LoginPage.tsx +88 -0
- package/ui/src/pages/OverviewPage.tsx +238 -0
- package/ui/src/pages/SamplesPage.tsx +174 -0
- package/ui/src/pages/ThinkingModelsPage.tsx +127 -0
- package/ui/src/styles.css +1661 -0
- package/ui/src/types.ts +368 -0
- package/ui/src/utils/format.ts +15 -0
- package/vitest.config.ts +23 -0
- package/dist/commands/capabilities.d.ts +0 -3
- package/dist/commands/capabilities.js +0 -73
- package/dist/commands/context.d.ts +0 -5
- package/dist/commands/evolution-status.d.ts +0 -4
- package/dist/commands/evolution-status.js +0 -117
- package/dist/commands/evolver.d.ts +0 -9
- package/dist/commands/evolver.js +0 -26
- package/dist/commands/export.d.ts +0 -2
- package/dist/commands/export.js +0 -98
- package/dist/commands/focus.d.ts +0 -14
- package/dist/commands/focus.js +0 -457
- package/dist/commands/nocturnal-review.d.ts +0 -24
- package/dist/commands/nocturnal-review.js +0 -265
- package/dist/commands/nocturnal-rollout.d.ts +0 -27
- package/dist/commands/nocturnal-rollout.js +0 -671
- package/dist/commands/nocturnal-train.d.ts +0 -25
- package/dist/commands/nocturnal-train.js +0 -919
- package/dist/commands/pain.d.ts +0 -5
- package/dist/commands/principle-rollback.d.ts +0 -4
- package/dist/commands/principle-rollback.js +0 -22
- package/dist/commands/rollback.d.ts +0 -19
- package/dist/commands/samples.d.ts +0 -2
- package/dist/commands/samples.js +0 -55
- package/dist/commands/strategy.d.ts +0 -3
- package/dist/commands/strategy.js +0 -29
- package/dist/commands/thinking-os.d.ts +0 -2
- package/dist/config/defaults/runtime.d.ts +0 -40
- package/dist/config/errors.d.ts +0 -84
- package/dist/config/errors.js +0 -94
- package/dist/config/index.js +0 -7
- package/dist/constants/diagnostician.d.ts +0 -12
- package/dist/constants/diagnostician.js +0 -56
- package/dist/constants/tools.d.ts +0 -17
- package/dist/constants/tools.js +0 -54
- package/dist/core/adaptive-thresholds.d.ts +0 -186
- package/dist/core/adaptive-thresholds.js +0 -300
- package/dist/core/config-service.d.ts +0 -15
- package/dist/core/config.d.ts +0 -127
- package/dist/core/control-ui-db.d.ts +0 -95
- package/dist/core/control-ui-db.js +0 -292
- package/dist/core/detection-funnel.d.ts +0 -33
- package/dist/core/detection-service.d.ts +0 -15
- package/dist/core/dictionary-service.d.ts +0 -15
- package/dist/core/dictionary.d.ts +0 -38
- package/dist/core/event-log.d.ts +0 -82
- package/dist/core/event-log.js +0 -463
- package/dist/core/evolution-engine.d.ts +0 -118
- package/dist/core/evolution-engine.js +0 -464
- package/dist/core/evolution-logger.d.ts +0 -137
- package/dist/core/evolution-logger.js +0 -256
- package/dist/core/evolution-migration.d.ts +0 -5
- package/dist/core/evolution-migration.js +0 -65
- package/dist/core/evolution-reducer.d.ts +0 -98
- package/dist/core/evolution-reducer.js +0 -465
- package/dist/core/evolution-types.d.ts +0 -287
- package/dist/core/evolution-types.js +0 -78
- package/dist/core/external-training-contract.d.ts +0 -276
- package/dist/core/external-training-contract.js +0 -269
- package/dist/core/focus-history.d.ts +0 -210
- package/dist/core/focus-history.js +0 -1185
- package/dist/core/hygiene/tracker.d.ts +0 -22
- package/dist/core/hygiene/tracker.js +0 -106
- package/dist/core/init.d.ts +0 -12
- package/dist/core/local-worker-routing.d.ts +0 -175
- package/dist/core/local-worker-routing.js +0 -525
- package/dist/core/migration.d.ts +0 -6
- package/dist/core/model-deployment-registry.d.ts +0 -218
- package/dist/core/model-deployment-registry.js +0 -503
- package/dist/core/model-training-registry.d.ts +0 -295
- package/dist/core/model-training-registry.js +0 -475
- package/dist/core/nocturnal-arbiter.d.ts +0 -159
- package/dist/core/nocturnal-arbiter.js +0 -534
- package/dist/core/nocturnal-candidate-scoring.d.ts +0 -137
- package/dist/core/nocturnal-candidate-scoring.js +0 -266
- package/dist/core/nocturnal-compliance.d.ts +0 -175
- package/dist/core/nocturnal-compliance.js +0 -824
- package/dist/core/nocturnal-dataset.d.ts +0 -224
- package/dist/core/nocturnal-dataset.js +0 -443
- package/dist/core/nocturnal-executability.d.ts +0 -85
- package/dist/core/nocturnal-executability.js +0 -331
- package/dist/core/nocturnal-export.d.ts +0 -124
- package/dist/core/nocturnal-export.js +0 -275
- package/dist/core/nocturnal-paths.d.ts +0 -124
- package/dist/core/nocturnal-trajectory-extractor.d.ts +0 -242
- package/dist/core/nocturnal-trajectory-extractor.js +0 -307
- package/dist/core/nocturnal-trinity.d.ts +0 -311
- package/dist/core/nocturnal-trinity.js +0 -880
- package/dist/core/pain.d.ts +0 -4
- package/dist/core/pain.js +0 -70
- package/dist/core/path-resolver.d.ts +0 -46
- package/dist/core/paths.d.ts +0 -65
- package/dist/core/principle-training-state.d.ts +0 -121
- package/dist/core/principle-training-state.js +0 -321
- package/dist/core/profile.d.ts +0 -62
- package/dist/core/profile.js +0 -210
- package/dist/core/promotion-gate.d.ts +0 -238
- package/dist/core/promotion-gate.js +0 -529
- package/dist/core/risk-calculator.d.ts +0 -22
- package/dist/core/session-tracker.d.ts +0 -99
- package/dist/core/shadow-observation-registry.d.ts +0 -217
- package/dist/core/shadow-observation-registry.js +0 -308
- package/dist/core/system-logger.d.ts +0 -8
- package/dist/core/thinking-models.d.ts +0 -38
- package/dist/core/thinking-models.js +0 -170
- package/dist/core/training-program.d.ts +0 -233
- package/dist/core/training-program.js +0 -433
- package/dist/core/trajectory.d.ts +0 -411
- package/dist/core/trajectory.js +0 -1307
- package/dist/core/workspace-context.d.ts +0 -71
- package/dist/hooks/bash-risk.d.ts +0 -57
- package/dist/hooks/bash-risk.js +0 -137
- package/dist/hooks/edit-verification.d.ts +0 -62
- package/dist/hooks/edit-verification.js +0 -256
- package/dist/hooks/gate-block-helper.d.ts +0 -44
- package/dist/hooks/gate-block-helper.js +0 -119
- package/dist/hooks/gate.d.ts +0 -24
- package/dist/hooks/gate.js +0 -173
- package/dist/hooks/gfi-gate.d.ts +0 -40
- package/dist/hooks/gfi-gate.js +0 -113
- package/dist/hooks/lifecycle.d.ts +0 -5
- package/dist/hooks/lifecycle.js +0 -284
- package/dist/hooks/llm.d.ts +0 -12
- package/dist/hooks/message-sanitize.d.ts +0 -3
- package/dist/hooks/message-sanitize.js +0 -37
- package/dist/hooks/pain.d.ts +0 -5
- package/dist/hooks/pain.js +0 -301
- package/dist/hooks/progressive-trust-gate.d.ts +0 -51
- package/dist/hooks/progressive-trust-gate.js +0 -89
- package/dist/hooks/prompt.d.ts +0 -47
- package/dist/hooks/prompt.js +0 -884
- package/dist/hooks/subagent.d.ts +0 -10
- package/dist/hooks/subagent.js +0 -387
- package/dist/hooks/thinking-checkpoint.d.ts +0 -37
- package/dist/hooks/thinking-checkpoint.js +0 -51
- package/dist/hooks/trajectory-collector.d.ts +0 -32
- package/dist/hooks/trajectory-collector.js +0 -256
- package/dist/http/principles-console-route.d.ts +0 -9
- package/dist/http/principles-console-route.js +0 -567
- package/dist/i18n/commands.d.ts +0 -26
- package/dist/i18n/commands.js +0 -116
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -581
- package/dist/service/central-database.d.ts +0 -104
- package/dist/service/central-database.js +0 -649
- package/dist/service/control-ui-query-service.d.ts +0 -221
- package/dist/service/control-ui-query-service.js +0 -543
- package/dist/service/empathy-observer-manager.d.ts +0 -52
- package/dist/service/empathy-observer-manager.js +0 -229
- package/dist/service/evolution-query-service.d.ts +0 -155
- package/dist/service/evolution-query-service.js +0 -258
- package/dist/service/evolution-worker.d.ts +0 -101
- package/dist/service/evolution-worker.js +0 -974
- package/dist/service/nocturnal-runtime.d.ts +0 -183
- package/dist/service/nocturnal-service.d.ts +0 -163
- package/dist/service/nocturnal-service.js +0 -787
- package/dist/service/nocturnal-target-selector.d.ts +0 -145
- package/dist/service/nocturnal-target-selector.js +0 -315
- package/dist/service/phase3-input-filter.d.ts +0 -73
- package/dist/service/phase3-input-filter.js +0 -172
- package/dist/service/runtime-summary-service.d.ts +0 -122
- package/dist/service/runtime-summary-service.js +0 -485
- package/dist/service/trajectory-service.d.ts +0 -2
- package/dist/service/trajectory-service.js +0 -15
- package/dist/tools/critique-prompt.d.ts +0 -14
- package/dist/tools/deep-reflect.d.ts +0 -39
- package/dist/tools/deep-reflect.js +0 -350
- package/dist/tools/model-index.d.ts +0 -9
- package/dist/types/event-types.d.ts +0 -306
- package/dist/types/event-types.js +0 -106
- package/dist/types/hygiene-types.d.ts +0 -20
- package/dist/types/hygiene-types.js +0 -12
- package/dist/types/runtime-summary.d.ts +0 -47
- package/dist/types/runtime-summary.js +0 -1
- package/dist/types.d.ts +0 -50
- package/dist/types.js +0 -22
- package/dist/utils/file-lock.d.ts +0 -71
- package/dist/utils/file-lock.js +0 -309
- package/dist/utils/glob-match.d.ts +0 -28
- package/dist/utils/hashing.d.ts +0 -9
- package/dist/utils/io.d.ts +0 -6
- package/dist/utils/io.js +0 -106
- package/dist/utils/nlp.d.ts +0 -9
- package/dist/utils/plugin-logger.d.ts +0 -39
- package/dist/utils/subagent-probe.d.ts +0 -34
- package/dist/utils/subagent-probe.js +0 -81
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for Empathy Observer Workflow
|
|
3
|
+
*
|
|
4
|
+
* These tests verify the complete chain from hook entry to workflow state/events.
|
|
5
|
+
* Unlike unit tests, these use real file system and SQLite for WorkflowStore.
|
|
6
|
+
*
|
|
7
|
+
* Coverage:
|
|
8
|
+
* 1. EmpathyObserverWorkflowManager startWorkflow triggers workflow helper
|
|
9
|
+
* 2. WorkflowStore records spawn/wait/finalize events
|
|
10
|
+
* 3. Lifecycle cleanup (sweepExpiredWorkflows) works when wired
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
14
|
+
import * as fs from 'node:fs';
|
|
15
|
+
import * as os from 'node:os';
|
|
16
|
+
import * as path from 'node:path';
|
|
17
|
+
import { EmpathyObserverWorkflowManager, empathyObserverWorkflowSpec } from '../../src/service/subagent-workflow/empathy-observer-workflow-manager.js';
|
|
18
|
+
import { WorkflowStore } from '../../src/service/subagent-workflow/workflow-store.js';
|
|
19
|
+
import { handleBeforePromptBuild } from '../../src/hooks/prompt.js';
|
|
20
|
+
import { WorkspaceContext } from '../../src/core/workspace-context.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Helper to create a mock function that properly reports as AsyncFunction.
|
|
24
|
+
* This is required because isSubagentRuntimeAvailable() checks constructor.name === 'AsyncFunction'.
|
|
25
|
+
*/
|
|
26
|
+
function mockAsyncFn<T extends (...args: any[]) => Promise<any>>(impl: (...args: any[]) => any) {
|
|
27
|
+
const fn = vi.fn(impl) as unknown as T;
|
|
28
|
+
Object.defineProperty(fn, 'constructor', {
|
|
29
|
+
value: function AsyncFunction() {},
|
|
30
|
+
writable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
});
|
|
33
|
+
return fn;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe('Empathy Workflow Integration (PR2)', () => {
|
|
37
|
+
let tempDir: string;
|
|
38
|
+
let stateDir: string;
|
|
39
|
+
let store: WorkflowStore;
|
|
40
|
+
|
|
41
|
+
const logger = {
|
|
42
|
+
info: vi.fn(),
|
|
43
|
+
warn: vi.fn(),
|
|
44
|
+
error: vi.fn(),
|
|
45
|
+
debug: vi.fn(),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-empathy-integration-'));
|
|
50
|
+
stateDir = path.join(tempDir, '.state');
|
|
51
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
store?.dispose();
|
|
56
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
57
|
+
vi.clearAllMocks();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Test 1: WorkflowStore records spawn/wait/finalize events
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
describe('WorkflowStore event chain', () => {
|
|
64
|
+
it('records spawned event when workflow starts', async () => {
|
|
65
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
66
|
+
|
|
67
|
+
const subagent = {
|
|
68
|
+
run: mockAsyncFn(async () => ({ runId: 'run-001' })),
|
|
69
|
+
waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
|
|
70
|
+
getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":false}'] })),
|
|
71
|
+
deleteSession: mockAsyncFn(async () => {}),
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
75
|
+
workspaceDir: tempDir,
|
|
76
|
+
logger,
|
|
77
|
+
subagent,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
81
|
+
parentSessionId: 'session-001',
|
|
82
|
+
taskInput: 'user message',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Verify workflow was created in store
|
|
86
|
+
const workflow = store.getWorkflow(handle.workflowId);
|
|
87
|
+
expect(workflow).not.toBeNull();
|
|
88
|
+
expect(workflow?.state).toBe('active');
|
|
89
|
+
expect(workflow?.parent_session_id).toBe('session-001');
|
|
90
|
+
|
|
91
|
+
// Verify spawned event was recorded
|
|
92
|
+
const events = store.getEvents(handle.workflowId);
|
|
93
|
+
const spawnedEvent = events.find(e => e.event_type === 'spawned');
|
|
94
|
+
expect(spawnedEvent).toBeDefined();
|
|
95
|
+
expect(spawnedEvent?.to_state).toBe('active');
|
|
96
|
+
|
|
97
|
+
manager.dispose();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('records wait_result and finalized events on successful completion', async () => {
|
|
101
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
102
|
+
|
|
103
|
+
const subagent = {
|
|
104
|
+
run: mockAsyncFn(async () => ({ runId: 'run-002' })),
|
|
105
|
+
waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
|
|
106
|
+
getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":false}'] })),
|
|
107
|
+
deleteSession: mockAsyncFn(async () => {}),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
111
|
+
workspaceDir: tempDir,
|
|
112
|
+
logger,
|
|
113
|
+
subagent,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
117
|
+
parentSessionId: 'session-002',
|
|
118
|
+
taskInput: 'user message',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Clear the scheduled poll timeout to manually trigger notifyWaitResult
|
|
122
|
+
const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
|
|
123
|
+
if (timeout) {
|
|
124
|
+
clearTimeout(timeout);
|
|
125
|
+
(manager as any).activeWorkflows.delete(handle.workflowId);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
await manager.notifyWaitResult(handle.workflowId, 'ok');
|
|
129
|
+
|
|
130
|
+
// Verify final state
|
|
131
|
+
const workflow = store.getWorkflow(handle.workflowId);
|
|
132
|
+
expect(workflow?.state).toBe('completed');
|
|
133
|
+
|
|
134
|
+
// Verify event chain: spawned -> wait_result -> finalized
|
|
135
|
+
const events = store.getEvents(handle.workflowId);
|
|
136
|
+
const eventTypes = events.map(e => e.event_type);
|
|
137
|
+
|
|
138
|
+
expect(eventTypes).toContain('spawned');
|
|
139
|
+
expect(eventTypes).toContain('wait_result');
|
|
140
|
+
expect(eventTypes).toContain('finalized');
|
|
141
|
+
|
|
142
|
+
manager.dispose();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
// Test 2: sweepExpiredWorkflows works when wired
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
describe('Lifecycle cleanup (sweepExpiredWorkflows)', () => {
|
|
150
|
+
it('sweeps workflows older than TTL', async () => {
|
|
151
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
152
|
+
|
|
153
|
+
const subagent = {
|
|
154
|
+
run: mockAsyncFn(async () => ({ runId: 'run-003' })),
|
|
155
|
+
waitForRun: mockAsyncFn(async () => ({ status: 'timeout' as const })),
|
|
156
|
+
getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: [] })),
|
|
157
|
+
deleteSession: mockAsyncFn(async () => {}),
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
161
|
+
workspaceDir: tempDir,
|
|
162
|
+
logger,
|
|
163
|
+
subagent,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Create a workflow
|
|
167
|
+
const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
168
|
+
parentSessionId: 'session-003',
|
|
169
|
+
taskInput: 'user message',
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Clear the scheduled poll timeout
|
|
173
|
+
const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
|
|
174
|
+
if (timeout) {
|
|
175
|
+
clearTimeout(timeout);
|
|
176
|
+
(manager as any).activeWorkflows.delete(handle.workflowId);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Manually set last_observed_at to simulate old workflow
|
|
180
|
+
const oldTime = Date.now() - 10 * 60 * 1000; // 10 minutes ago
|
|
181
|
+
const db = (store as any).db;
|
|
182
|
+
db.prepare('UPDATE subagent_workflows SET last_observed_at = ? WHERE workflow_id = ?').run(oldTime, handle.workflowId);
|
|
183
|
+
|
|
184
|
+
// Run sweep with 5 minute TTL
|
|
185
|
+
const sweptCount = await manager.sweepExpiredWorkflows(5 * 60 * 1000);
|
|
186
|
+
|
|
187
|
+
expect(sweptCount).toBe(1);
|
|
188
|
+
|
|
189
|
+
// Verify workflow is now expired
|
|
190
|
+
const workflow = store.getWorkflow(handle.workflowId);
|
|
191
|
+
expect(workflow?.state).toBe('expired');
|
|
192
|
+
|
|
193
|
+
// Verify swept event was recorded
|
|
194
|
+
const events = store.getEvents(handle.workflowId);
|
|
195
|
+
const sweptEvent = events.find(e => e.event_type === 'swept');
|
|
196
|
+
expect(sweptEvent).toBeDefined();
|
|
197
|
+
|
|
198
|
+
manager.dispose();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// Test 3: Debug summary is accessible
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
describe('Observability (getWorkflowDebugSummary)', () => {
|
|
206
|
+
it('provides debug summary with recent events', async () => {
|
|
207
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
208
|
+
|
|
209
|
+
const subagent = {
|
|
210
|
+
run: mockAsyncFn(async () => ({ runId: 'run-004' })),
|
|
211
|
+
waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
|
|
212
|
+
getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":false}'] })),
|
|
213
|
+
deleteSession: mockAsyncFn(async () => {}),
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
217
|
+
workspaceDir: tempDir,
|
|
218
|
+
logger,
|
|
219
|
+
subagent,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
223
|
+
parentSessionId: 'session-004',
|
|
224
|
+
taskInput: 'user message',
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const summary = await manager.getWorkflowDebugSummary(handle.workflowId);
|
|
228
|
+
|
|
229
|
+
expect(summary).not.toBeNull();
|
|
230
|
+
expect(summary?.workflowId).toBe(handle.workflowId);
|
|
231
|
+
expect(summary?.workflowType).toBe('empathy-observer');
|
|
232
|
+
expect(summary?.transport).toBe('runtime_direct');
|
|
233
|
+
expect(summary?.state).toBe('active');
|
|
234
|
+
expect(summary?.parentSessionId).toBe('session-004');
|
|
235
|
+
expect(summary?.recentEvents.length).toBeGreaterThan(0);
|
|
236
|
+
expect(summary?.recentEvents[0].eventType).toBe('spawned');
|
|
237
|
+
|
|
238
|
+
manager.dispose();
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
// Test 4: Workflow isolation
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
describe('Workflow isolation', () => {
|
|
246
|
+
it('each workflow is recorded independently in WorkflowStore', async () => {
|
|
247
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
248
|
+
|
|
249
|
+
const subagent = {
|
|
250
|
+
run: mockAsyncFn(async () => ({ runId: 'run-005' })),
|
|
251
|
+
waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
|
|
252
|
+
getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":false}'] })),
|
|
253
|
+
deleteSession: mockAsyncFn(async () => {}),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
257
|
+
workspaceDir: tempDir,
|
|
258
|
+
logger,
|
|
259
|
+
subagent,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Start workflow
|
|
263
|
+
const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
264
|
+
parentSessionId: 'session-005',
|
|
265
|
+
taskInput: 'user message',
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Verify workflow is recorded independently
|
|
269
|
+
const workflow = store.getWorkflow(handle.workflowId);
|
|
270
|
+
expect(workflow).not.toBeNull();
|
|
271
|
+
expect(workflow?.workflow_type).toBe('empathy-observer');
|
|
272
|
+
|
|
273
|
+
manager.dispose();
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
// Test 5: State transition validation
|
|
279
|
+
// ---------------------------------------------------------------------------
|
|
280
|
+
describe('Workflow state transitions', () => {
|
|
281
|
+
it('transitions: active -> wait_result -> finalizing -> completed', async () => {
|
|
282
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
283
|
+
|
|
284
|
+
const subagent = {
|
|
285
|
+
run: mockAsyncFn(async () => ({ runId: 'run-006' })),
|
|
286
|
+
waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
|
|
287
|
+
getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":false}'] })),
|
|
288
|
+
deleteSession: mockAsyncFn(async () => {}),
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
292
|
+
workspaceDir: tempDir,
|
|
293
|
+
logger,
|
|
294
|
+
subagent,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
298
|
+
parentSessionId: 'session-006',
|
|
299
|
+
taskInput: 'user message',
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Verify initial state
|
|
303
|
+
expect(store.getWorkflow(handle.workflowId)?.state).toBe('active');
|
|
304
|
+
|
|
305
|
+
// Clear timeout and trigger wait result
|
|
306
|
+
const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
|
|
307
|
+
if (timeout) {
|
|
308
|
+
clearTimeout(timeout);
|
|
309
|
+
(manager as any).activeWorkflows.delete(handle.workflowId);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
await manager.notifyWaitResult(handle.workflowId, 'ok');
|
|
313
|
+
|
|
314
|
+
// Verify final state
|
|
315
|
+
expect(store.getWorkflow(handle.workflowId)?.state).toBe('completed');
|
|
316
|
+
|
|
317
|
+
// Verify state transition events
|
|
318
|
+
const events = store.getEvents(handle.workflowId);
|
|
319
|
+
const stateChanges = events.filter(e => e.event_type === 'state_change' || e.event_type === 'wait_result' || e.event_type === 'finalized');
|
|
320
|
+
|
|
321
|
+
// Should have spawn, wait_result, finalized at minimum
|
|
322
|
+
expect(stateChanges.length).toBeGreaterThanOrEqual(2);
|
|
323
|
+
|
|
324
|
+
manager.dispose();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('transitions to terminal_error on timeout', async () => {
|
|
328
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
329
|
+
|
|
330
|
+
const subagent = {
|
|
331
|
+
run: mockAsyncFn(async () => ({ runId: 'run-007' })),
|
|
332
|
+
waitForRun: mockAsyncFn(async () => ({ status: 'timeout' as const })),
|
|
333
|
+
getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: [] })),
|
|
334
|
+
deleteSession: mockAsyncFn(async () => {}),
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
338
|
+
workspaceDir: tempDir,
|
|
339
|
+
logger,
|
|
340
|
+
subagent,
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
344
|
+
parentSessionId: 'session-007',
|
|
345
|
+
taskInput: 'user message',
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// Clear timeout and trigger error result
|
|
349
|
+
const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
|
|
350
|
+
if (timeout) {
|
|
351
|
+
clearTimeout(timeout);
|
|
352
|
+
(manager as any).activeWorkflows.delete(handle.workflowId);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
await manager.notifyWaitResult(handle.workflowId, 'timeout', 'wait timed out');
|
|
356
|
+
|
|
357
|
+
// Verify terminal state
|
|
358
|
+
expect(store.getWorkflow(handle.workflowId)?.state).toBe('terminal_error');
|
|
359
|
+
|
|
360
|
+
manager.dispose();
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// ---------------------------------------------------------------------------
|
|
365
|
+
// Test 6: PR2.1 Task 1 - subagent_ended -> notifyLifecycleEvent wiring
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
describe('PR2.1: subagent_ended lifecycle event wiring', () => {
|
|
368
|
+
it('triggers notifyWaitResult when subagent_ended event received', async () => {
|
|
369
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
370
|
+
|
|
371
|
+
const subagent = {
|
|
372
|
+
run: mockAsyncFn(async () => ({ runId: 'run-pr21-001' })),
|
|
373
|
+
waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
|
|
374
|
+
getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":false}'] })),
|
|
375
|
+
deleteSession: mockAsyncFn(async () => {}),
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
379
|
+
workspaceDir: tempDir,
|
|
380
|
+
logger,
|
|
381
|
+
subagent,
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Start a helper workflow
|
|
385
|
+
const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
386
|
+
parentSessionId: 'session-pr21-001',
|
|
387
|
+
taskInput: 'user message',
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// Get the child session key
|
|
391
|
+
const workflow = store.getWorkflow(handle.workflowId);
|
|
392
|
+
expect(workflow).not.toBeNull();
|
|
393
|
+
const childSessionKey = workflow!.child_session_key;
|
|
394
|
+
|
|
395
|
+
// Verify child session key has expected prefix
|
|
396
|
+
expect(childSessionKey).toMatch(/^agent:main:subagent:workflow-/);
|
|
397
|
+
|
|
398
|
+
// Clear the scheduled poll timeout to prevent auto-wait
|
|
399
|
+
const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
|
|
400
|
+
if (timeout) {
|
|
401
|
+
clearTimeout(timeout);
|
|
402
|
+
(manager as any).activeWorkflows.delete(handle.workflowId);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Verify initial state is active
|
|
406
|
+
expect(store.getWorkflow(handle.workflowId)?.state).toBe('active');
|
|
407
|
+
|
|
408
|
+
// Simulate subagent_ended event by calling notifyLifecycleEvent
|
|
409
|
+
// This triggers notifyWaitResult internally
|
|
410
|
+
await manager.notifyLifecycleEvent(handle.workflowId, 'subagent_ended', {
|
|
411
|
+
outcome: 'ok',
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// Verify workflow completed through lifecycle event path
|
|
415
|
+
const finalWorkflow = store.getWorkflow(handle.workflowId);
|
|
416
|
+
expect(finalWorkflow?.state).toBe('completed');
|
|
417
|
+
|
|
418
|
+
// Verify event chain includes finalized
|
|
419
|
+
const events = store.getEvents(handle.workflowId);
|
|
420
|
+
const finalizedEvent = events.find(e => e.event_type === 'finalized');
|
|
421
|
+
expect(finalizedEvent).toBeDefined();
|
|
422
|
+
|
|
423
|
+
manager.dispose();
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('handles error outcome via lifecycle event', async () => {
|
|
427
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
428
|
+
|
|
429
|
+
const subagent = {
|
|
430
|
+
run: mockAsyncFn(async () => ({ runId: 'run-pr21-002' })),
|
|
431
|
+
waitForRun: mockAsyncFn(async () => ({ status: 'error' as const, error: 'test error' })),
|
|
432
|
+
getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: [] })),
|
|
433
|
+
deleteSession: mockAsyncFn(async () => {}),
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
437
|
+
workspaceDir: tempDir,
|
|
438
|
+
logger,
|
|
439
|
+
subagent,
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
443
|
+
parentSessionId: 'session-pr21-002',
|
|
444
|
+
taskInput: 'test',
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// Clear timeout
|
|
448
|
+
const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
|
|
449
|
+
if (timeout) {
|
|
450
|
+
clearTimeout(timeout);
|
|
451
|
+
(manager as any).activeWorkflows.delete(handle.workflowId);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Notify error outcome
|
|
455
|
+
await manager.notifyLifecycleEvent(handle.workflowId, 'subagent_ended', {
|
|
456
|
+
outcome: 'error',
|
|
457
|
+
error: 'subagent failed',
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// Verify terminal state
|
|
461
|
+
const workflow = store.getWorkflow(handle.workflowId);
|
|
462
|
+
expect(workflow?.state).toBe('terminal_error');
|
|
463
|
+
|
|
464
|
+
manager.dispose();
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// ---------------------------------------------------------------------------
|
|
469
|
+
// Test 7: PR2.1 Task 2 - sweepExpiredWorkflows integration
|
|
470
|
+
// ---------------------------------------------------------------------------
|
|
471
|
+
describe('PR2.1: sweepExpiredWorkflows integration', () => {
|
|
472
|
+
it('WorkflowStore.getExpiredWorkflows returns workflows past TTL', async () => {
|
|
473
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
474
|
+
|
|
475
|
+
const subagent = {
|
|
476
|
+
run: mockAsyncFn(async () => ({ runId: 'run-sweep-001' })),
|
|
477
|
+
waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
|
|
478
|
+
getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{}'] })),
|
|
479
|
+
deleteSession: mockAsyncFn(async () => {}),
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
483
|
+
workspaceDir: tempDir,
|
|
484
|
+
logger,
|
|
485
|
+
subagent,
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
489
|
+
parentSessionId: 'session-sweep-001',
|
|
490
|
+
taskInput: 'test',
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
// Clear timeout
|
|
494
|
+
const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
|
|
495
|
+
if (timeout) {
|
|
496
|
+
clearTimeout(timeout);
|
|
497
|
+
(manager as any).activeWorkflows.delete(handle.workflowId);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Manually set last_observed_at to simulate expired workflow
|
|
501
|
+
const db = (store as any).db;
|
|
502
|
+
const oldTime = Date.now() - 10 * 60 * 1000; // 10 minutes ago
|
|
503
|
+
db.prepare('UPDATE subagent_workflows SET last_observed_at = ? WHERE workflow_id = ?').run(oldTime, handle.workflowId);
|
|
504
|
+
|
|
505
|
+
// Verify getExpiredWorkflows finds it
|
|
506
|
+
const expired = store.getExpiredWorkflows(5 * 60 * 1000);
|
|
507
|
+
expect(expired.length).toBe(1);
|
|
508
|
+
expect(expired[0].workflow_id).toBe(handle.workflowId);
|
|
509
|
+
|
|
510
|
+
manager.dispose();
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('sweepExpiredWorkflows marks workflows as expired', async () => {
|
|
514
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
515
|
+
|
|
516
|
+
const subagent = {
|
|
517
|
+
run: mockAsyncFn(async () => ({ runId: 'run-sweep-002' })),
|
|
518
|
+
waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
|
|
519
|
+
getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{}'] })),
|
|
520
|
+
deleteSession: mockAsyncFn(async () => {}),
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
524
|
+
workspaceDir: tempDir,
|
|
525
|
+
logger,
|
|
526
|
+
subagent,
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
530
|
+
parentSessionId: 'session-sweep-002',
|
|
531
|
+
taskInput: 'test',
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
// Clear timeout
|
|
535
|
+
const timeout = (manager as any).activeWorkflows.get(handle.workflowId);
|
|
536
|
+
if (timeout) {
|
|
537
|
+
clearTimeout(timeout);
|
|
538
|
+
(manager as any).activeWorkflows.delete(handle.workflowId);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Manually expire
|
|
542
|
+
const db = (store as any).db;
|
|
543
|
+
const oldTime = Date.now() - 10 * 60 * 1000;
|
|
544
|
+
db.prepare('UPDATE subagent_workflows SET last_observed_at = ? WHERE workflow_id = ?').run(oldTime, handle.workflowId);
|
|
545
|
+
|
|
546
|
+
// Sweep
|
|
547
|
+
const count = await manager.sweepExpiredWorkflows(5 * 60 * 1000);
|
|
548
|
+
expect(count).toBe(1);
|
|
549
|
+
|
|
550
|
+
// Verify state
|
|
551
|
+
const workflow = store.getWorkflow(handle.workflowId);
|
|
552
|
+
expect(workflow?.state).toBe('expired');
|
|
553
|
+
|
|
554
|
+
// Verify swept event
|
|
555
|
+
const events = store.getEvents(handle.workflowId);
|
|
556
|
+
const sweptEvent = events.find(e => e.event_type === 'swept');
|
|
557
|
+
expect(sweptEvent).toBeDefined();
|
|
558
|
+
|
|
559
|
+
manager.dispose();
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
// ---------------------------------------------------------------------------
|
|
564
|
+
// Test 8: PR2.1 Task 3 - getWorkflowDebugSummary slash command
|
|
565
|
+
// ---------------------------------------------------------------------------
|
|
566
|
+
describe('PR2.1: getWorkflowDebugSummary accessibility', () => {
|
|
567
|
+
it('provides complete debug summary with all fields', async () => {
|
|
568
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
569
|
+
|
|
570
|
+
const subagent = {
|
|
571
|
+
run: mockAsyncFn(async () => ({ runId: 'run-debug-001' })),
|
|
572
|
+
waitForRun: mockAsyncFn(async () => ({ status: 'ok' as const })),
|
|
573
|
+
getSessionMessages: mockAsyncFn(async () => ({ messages: [], assistantTexts: ['{"damageDetected":true,"severity":"moderate"}'] })),
|
|
574
|
+
deleteSession: mockAsyncFn(async () => {}),
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
578
|
+
workspaceDir: tempDir,
|
|
579
|
+
logger,
|
|
580
|
+
subagent,
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
const handle = await manager.startWorkflow(empathyObserverWorkflowSpec, {
|
|
584
|
+
parentSessionId: 'session-debug-001',
|
|
585
|
+
workspaceDir: tempDir,
|
|
586
|
+
taskInput: 'This is a test user message for debugging',
|
|
587
|
+
metadata: { customField: 'customValue' },
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
const summary = await manager.getWorkflowDebugSummary(handle.workflowId);
|
|
591
|
+
|
|
592
|
+
expect(summary).not.toBeNull();
|
|
593
|
+
expect(summary?.workflowId).toBe(handle.workflowId);
|
|
594
|
+
expect(summary?.workflowType).toBe('empathy-observer');
|
|
595
|
+
expect(summary?.transport).toBe('runtime_direct');
|
|
596
|
+
expect(summary?.state).toBe('active');
|
|
597
|
+
expect(summary?.cleanupState).toBe('none');
|
|
598
|
+
expect(summary?.parentSessionId).toBe('session-debug-001');
|
|
599
|
+
expect(summary?.childSessionKey).toContain('workflow-');
|
|
600
|
+
expect(summary?.runId).toBe('run-debug-001');
|
|
601
|
+
expect(summary?.metadata.taskInput).toBe('This is a test user message for debugging');
|
|
602
|
+
expect(summary?.metadata.customField).toBe('customValue');
|
|
603
|
+
expect(summary?.recentEvents.length).toBeGreaterThan(0);
|
|
604
|
+
|
|
605
|
+
// Verify first event is spawned
|
|
606
|
+
expect(summary?.recentEvents[0].eventType).toBe('spawned');
|
|
607
|
+
|
|
608
|
+
manager.dispose();
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it('returns null for non-existent workflow', async () => {
|
|
612
|
+
store = new WorkflowStore({ workspaceDir: tempDir });
|
|
613
|
+
|
|
614
|
+
const manager = new EmpathyObserverWorkflowManager({
|
|
615
|
+
workspaceDir: tempDir,
|
|
616
|
+
logger,
|
|
617
|
+
subagent: {} as any,
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
const summary = await manager.getWorkflowDebugSummary('non-existent-id');
|
|
621
|
+
expect(summary).toBeNull();
|
|
622
|
+
|
|
623
|
+
manager.dispose();
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
});
|