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,856 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NocturnalWorkflowManager — WorkflowManager interface for Nocturnal Reflection
|
|
3
|
+
* =============================================================================
|
|
4
|
+
*
|
|
5
|
+
* PURPOSE: Wraps OpenClawTrinityRuntimeAdapter in the WorkflowManager interface.
|
|
6
|
+
* Single-reflector path only (useTrinity=false). Trinity multi-stage chain comes
|
|
7
|
+
* in Phase 7.
|
|
8
|
+
*
|
|
9
|
+
* DESIGN:
|
|
10
|
+
* - NocturnalWorkflowManager calls executeNocturnalReflectionAsync synchronously
|
|
11
|
+
* within startWorkflow. There is no wait polling path.
|
|
12
|
+
* - All 5 nocturnal event types are recorded: nocturnal_started, nocturnal_completed,
|
|
13
|
+
* nocturnal_failed, nocturnal_fallback, nocturnal_expired.
|
|
14
|
+
* - sweepExpiredWorkflows marks expired workflows and cleans partial artifact files.
|
|
15
|
+
* - notifyWaitResult and notifyLifecycleEvent are no-ops per D-10.
|
|
16
|
+
*
|
|
17
|
+
* PHASE: Phase 6 — Foundation and Single-Reflector Mode
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { PluginLogger } from '../../openclaw-sdk.js';
|
|
21
|
+
import type {
|
|
22
|
+
WorkflowManager,
|
|
23
|
+
WorkflowHandle,
|
|
24
|
+
SubagentWorkflowSpec,
|
|
25
|
+
WorkflowMetadata,
|
|
26
|
+
WorkflowDebugSummary,
|
|
27
|
+
WorkflowResultContext,
|
|
28
|
+
WorkflowPersistContext,
|
|
29
|
+
WorkflowEventRow,
|
|
30
|
+
} from './types.js';
|
|
31
|
+
import { WorkflowStore } from './workflow-store.js';
|
|
32
|
+
import { resolveNocturnalDir } from '../../core/nocturnal-paths.js';
|
|
33
|
+
import {
|
|
34
|
+
executeNocturnalReflectionAsync,
|
|
35
|
+
type NocturnalRunResult,
|
|
36
|
+
} from '../nocturnal-service.js';
|
|
37
|
+
import { type TrinityStageFailure, type TrinityResult } from '../../core/nocturnal-trinity.js';
|
|
38
|
+
import type { TrinityRuntimeAdapter, TrinityConfig, DreamerOutput, PhilosopherOutput, TrinityTelemetry } from '../../core/nocturnal-trinity.js';
|
|
39
|
+
import type { NocturnalSessionSnapshot } from '../../core/nocturnal-trajectory-extractor.js';
|
|
40
|
+
import { createHash } from 'crypto';
|
|
41
|
+
import * as fs from 'fs';
|
|
42
|
+
import * as path from 'path';
|
|
43
|
+
|
|
44
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
45
|
+
// NocturnalResult Type Alias
|
|
46
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Nocturnal workflow result type (mirrors NocturnalRunResult per D-02).
|
|
50
|
+
* This is the result type returned by executeNocturnalReflectionAsync.
|
|
51
|
+
*/
|
|
52
|
+
export type NocturnalResult = NocturnalRunResult;
|
|
53
|
+
|
|
54
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
55
|
+
// Idempotency Key Computation (D-18)
|
|
56
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Compute idempotency key for the Dreamer stage (D-18).
|
|
60
|
+
* Key = SHA-256(workflowId + stage + inputDigest)
|
|
61
|
+
* inputDigest = SHA-256(snapshot.sessionId + principleId + maxCandidates)
|
|
62
|
+
* Uses simple concatenation (no delimiters) per D-18 spec.
|
|
63
|
+
*/
|
|
64
|
+
function computeDreamerIdempotencyKey(
|
|
65
|
+
workflowId: string,
|
|
66
|
+
snapshot: import('../../core/nocturnal-trajectory-extractor.js').NocturnalSessionSnapshot,
|
|
67
|
+
principleId: string,
|
|
68
|
+
maxCandidates: number
|
|
69
|
+
): string {
|
|
70
|
+
// Use delimiters to prevent collision (e.g., "sess10"+"2"+"3" vs "sess1"+"02"+"3")
|
|
71
|
+
const inputDigest = createHash('sha256')
|
|
72
|
+
.update(`${snapshot.sessionId}::${principleId}::${maxCandidates}`)
|
|
73
|
+
.digest('hex');
|
|
74
|
+
return createHash('sha256')
|
|
75
|
+
.update(`${workflowId}::dreamer::${inputDigest}`)
|
|
76
|
+
.digest('hex');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Compute idempotency key for the Philosopher stage (D-18).
|
|
81
|
+
* Key = SHA-256(workflowId + stage + inputDigest)
|
|
82
|
+
* inputDigest = SHA-256(workflowId + dreamerOutputJson)
|
|
83
|
+
* Uses simple concatenation (no delimiters) per D-18 spec.
|
|
84
|
+
*/
|
|
85
|
+
function computePhilosopherIdempotencyKey(
|
|
86
|
+
workflowId: string,
|
|
87
|
+
dreamerOutput: import('../../core/nocturnal-trinity.js').DreamerOutput
|
|
88
|
+
): string {
|
|
89
|
+
const dreamerOutputJson = JSON.stringify(dreamerOutput);
|
|
90
|
+
const inputDigest = createHash('sha256')
|
|
91
|
+
.update(workflowId + dreamerOutputJson)
|
|
92
|
+
.digest('hex');
|
|
93
|
+
return createHash('sha256')
|
|
94
|
+
.update(workflowId + 'philosopher' + inputDigest)
|
|
95
|
+
.digest('hex');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
99
|
+
// NocturnalWorkflowOptions
|
|
100
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
export interface NocturnalWorkflowOptions {
|
|
103
|
+
/** Workspace directory for artifact storage */
|
|
104
|
+
workspaceDir: string;
|
|
105
|
+
/** State directory for nocturnal runtime bookkeeping */
|
|
106
|
+
stateDir: string;
|
|
107
|
+
/** Plugin logger */
|
|
108
|
+
logger: PluginLogger;
|
|
109
|
+
/** Trinity runtime adapter for subagent execution */
|
|
110
|
+
runtimeAdapter: TrinityRuntimeAdapter;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
114
|
+
// NocturnalWorkflowSpec
|
|
115
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Nocturnal workflow specification.
|
|
119
|
+
* Drives NocturnalWorkflowManager for the nocturnal reflection workflow.
|
|
120
|
+
*
|
|
121
|
+
* Per D-07, D-08, D-09, D-03, D-04:
|
|
122
|
+
* - workflowType: 'nocturnal'
|
|
123
|
+
* - transport: 'runtime_direct'
|
|
124
|
+
* - shouldDeleteSessionAfterFinalize: false (no external session to delete)
|
|
125
|
+
* - timeoutMs: 15 minutes (900000ms)
|
|
126
|
+
* - ttlMs: 30 minutes (1800000ms)
|
|
127
|
+
*/
|
|
128
|
+
export const nocturnalWorkflowSpec: SubagentWorkflowSpec<NocturnalResult> = {
|
|
129
|
+
workflowType: 'nocturnal',
|
|
130
|
+
transport: 'runtime_direct',
|
|
131
|
+
timeoutMs: 15 * 60 * 1000, // D-03: 15 minutes
|
|
132
|
+
ttlMs: 30 * 60 * 1000, // D-04: 30 minutes
|
|
133
|
+
shouldDeleteSessionAfterFinalize: false, // D-09: no external session to delete
|
|
134
|
+
|
|
135
|
+
buildPrompt(_taskInput: unknown, _metadata: WorkflowMetadata): string {
|
|
136
|
+
// NocturnalWorkflowManager does not use prompt injection.
|
|
137
|
+
// Execution is driven by executeNocturnalReflectionAsync.
|
|
138
|
+
return '';
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
async parseResult(ctx: WorkflowResultContext): Promise<NocturnalResult | null> {
|
|
142
|
+
// NocturnalWorkflowManager handles execution directly in startWorkflow.
|
|
143
|
+
// This is not called via the standard subagent message path.
|
|
144
|
+
return (ctx.metadata['nocturnalResult'] as NocturnalResult) ?? null;
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
async persistResult(_ctx: WorkflowPersistContext<NocturnalResult>): Promise<void> {
|
|
148
|
+
// Artifact persistence is handled in startWorkflow after
|
|
149
|
+
// executeNocturnalReflectionAsync returns. The artifact is already
|
|
150
|
+
// persisted by the service; nothing more needed here.
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
shouldFinalizeOnWaitStatus(status: 'ok' | 'error' | 'timeout'): boolean {
|
|
154
|
+
return status === 'ok';
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
159
|
+
// Stub Fallback Runtime Adapter (NOC-15)
|
|
160
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Stub fallback runtime adapter for Trinity.
|
|
164
|
+
* Wraps a real adapter and provides stub implementations for fallback when stages fail.
|
|
165
|
+
* Per NOC-15: Fallback degrades to stub, NOT to EmpathyObserver/DeepReflect.
|
|
166
|
+
*/
|
|
167
|
+
class StubFallbackRuntimeAdapter implements TrinityRuntimeAdapter {
|
|
168
|
+
constructor(
|
|
169
|
+
private snapshot: NocturnalSessionSnapshot,
|
|
170
|
+
private principleId: string,
|
|
171
|
+
private maxCandidates: number
|
|
172
|
+
) {}
|
|
173
|
+
|
|
174
|
+
async invokeDreamer(
|
|
175
|
+
snapshot: NocturnalSessionSnapshot,
|
|
176
|
+
principleId: string,
|
|
177
|
+
maxCandidates: number
|
|
178
|
+
): Promise<DreamerOutput> {
|
|
179
|
+
const { invokeStubDreamer } = await import('../../core/nocturnal-trinity.js');
|
|
180
|
+
return invokeStubDreamer(snapshot, principleId, maxCandidates);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async invokePhilosopher(
|
|
184
|
+
dreamerOutput: DreamerOutput,
|
|
185
|
+
principleId: string
|
|
186
|
+
): Promise<PhilosopherOutput> {
|
|
187
|
+
const { invokeStubPhilosopher } = await import('../../core/nocturnal-trinity.js');
|
|
188
|
+
return invokeStubPhilosopher(dreamerOutput, principleId);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async invokeScribe(
|
|
192
|
+
dreamerOutput: DreamerOutput,
|
|
193
|
+
philosopherOutput: PhilosopherOutput,
|
|
194
|
+
snapshot: NocturnalSessionSnapshot,
|
|
195
|
+
principleId: string,
|
|
196
|
+
telemetry: TrinityTelemetry,
|
|
197
|
+
config: TrinityConfig
|
|
198
|
+
): Promise<import('../../core/nocturnal-trinity.js').TrinityDraftArtifact | null> {
|
|
199
|
+
// Use stub Scribe
|
|
200
|
+
const { invokeStubScribe } = await import('../../core/nocturnal-trinity.js');
|
|
201
|
+
return invokeStubScribe(dreamerOutput, philosopherOutput, snapshot, principleId, telemetry, config);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async close(): Promise<void> {
|
|
205
|
+
// No-op for stubs
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
210
|
+
// NocturnalWorkflowManager
|
|
211
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* NocturnalWorkflowManager — implements WorkflowManager for nocturnal reflection.
|
|
215
|
+
*
|
|
216
|
+
* Single-reflector path (useTrinity=false) only in Phase 6.
|
|
217
|
+
* Trinity multi-stage chain (Dreamer→Philosopher→Scribe) comes in Phase 7.
|
|
218
|
+
*
|
|
219
|
+
* Key behaviors:
|
|
220
|
+
* - startWorkflow calls executeNocturnalReflectionAsync with useTrinity=false
|
|
221
|
+
* - Records all 5 nocturnal event types to WorkflowStore
|
|
222
|
+
* - notifyWaitResult and notifyLifecycleEvent are no-ops
|
|
223
|
+
* - sweepExpiredWorkflows marks expired workflows and cleans partial artifacts
|
|
224
|
+
*/
|
|
225
|
+
export class NocturnalWorkflowManager implements WorkflowManager {
|
|
226
|
+
private readonly workspaceDir: string;
|
|
227
|
+
private readonly stateDir: string;
|
|
228
|
+
private readonly logger: PluginLogger;
|
|
229
|
+
private readonly runtimeAdapter: TrinityRuntimeAdapter;
|
|
230
|
+
private readonly store: WorkflowStore;
|
|
231
|
+
|
|
232
|
+
/** Tracks completion timestamps for idempotency */
|
|
233
|
+
private completedWorkflows = new Map<string, number>();
|
|
234
|
+
/** Maps workflowId → spec (needed for finalizeOnce) */
|
|
235
|
+
private workflowSpecs = new Map<string, SubagentWorkflowSpec<unknown>>();
|
|
236
|
+
/** Maps workflowId → result (needed for finalizeOnce) */
|
|
237
|
+
private executionResults = new Map<string, NocturnalResult>();
|
|
238
|
+
/** Maps workflowId → TrinityStageFailure[] (stored before async launch, used in notifyWaitResult) */
|
|
239
|
+
private pendingTrinityFailures = new Map<string, TrinityStageFailure[]>();
|
|
240
|
+
/** Maps workflowId → TrinityResult (needed by notifyWaitResult for artifact persistence) */
|
|
241
|
+
private pendingTrinityResults = new Map<string, TrinityResult>();
|
|
242
|
+
|
|
243
|
+
constructor(opts: NocturnalWorkflowOptions) {
|
|
244
|
+
this.workspaceDir = opts.workspaceDir;
|
|
245
|
+
this.stateDir = opts.stateDir;
|
|
246
|
+
this.logger = opts.logger;
|
|
247
|
+
this.runtimeAdapter = opts.runtimeAdapter;
|
|
248
|
+
this.store = new WorkflowStore({ workspaceDir: opts.workspaceDir });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
252
|
+
// WorkflowManager Interface: startWorkflow (NOC-01, NOC-02, NOC-03)
|
|
253
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
254
|
+
|
|
255
|
+
async startWorkflow<TResult>(
|
|
256
|
+
spec: SubagentWorkflowSpec<TResult>,
|
|
257
|
+
options: {
|
|
258
|
+
parentSessionId: string;
|
|
259
|
+
workspaceDir?: string;
|
|
260
|
+
taskInput: unknown;
|
|
261
|
+
metadata?: Record<string, unknown>;
|
|
262
|
+
}
|
|
263
|
+
): Promise<WorkflowHandle> {
|
|
264
|
+
const workflowId = this.generateWorkflowId();
|
|
265
|
+
const now = Date.now();
|
|
266
|
+
|
|
267
|
+
const metadata: WorkflowMetadata = {
|
|
268
|
+
parentSessionId: options.parentSessionId,
|
|
269
|
+
workspaceDir: options.workspaceDir,
|
|
270
|
+
taskInput: options.taskInput,
|
|
271
|
+
startedAt: now,
|
|
272
|
+
workflowType: spec.workflowType,
|
|
273
|
+
...options.metadata,
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
this.logger.info(`[PD:NocturnalWorkflow] Starting workflow: workflowId=${workflowId}, type=${spec.workflowType}`);
|
|
277
|
+
|
|
278
|
+
// Record nocturnal_started event (NOC-03)
|
|
279
|
+
this.store.createWorkflow({
|
|
280
|
+
workflow_id: workflowId,
|
|
281
|
+
workflow_type: spec.workflowType,
|
|
282
|
+
transport: spec.transport,
|
|
283
|
+
parent_session_id: options.parentSessionId,
|
|
284
|
+
child_session_key: `nocturnal:internal:${workflowId}`, // D-10: placeholder since adapter manages sessions internally
|
|
285
|
+
run_id: null,
|
|
286
|
+
state: 'active',
|
|
287
|
+
created_at: now,
|
|
288
|
+
updated_at: now,
|
|
289
|
+
metadata_json: JSON.stringify(metadata),
|
|
290
|
+
});
|
|
291
|
+
this.store.recordEvent(workflowId, 'nocturnal_started', null, 'active', 'TrinityRuntimeAdapter invoked', { workflowType: 'nocturnal' });
|
|
292
|
+
|
|
293
|
+
// Extract snapshot and principleId from taskInput.metadata (NOC-07: Trinity async path)
|
|
294
|
+
const snapshot = options.metadata?.snapshot as import('../../core/nocturnal-trajectory-extractor.js').NocturnalSessionSnapshot | undefined;
|
|
295
|
+
const principleId = options.metadata?.principleId as string | undefined;
|
|
296
|
+
|
|
297
|
+
// Validate required metadata (prevent runtime crashes from undefined snapshot)
|
|
298
|
+
if (!snapshot?.sessionId) {
|
|
299
|
+
this.logger.warn(`[PD:NocturnalWorkflow] Missing snapshot.sessionId in metadata for workflow=${workflowId}, terminating`);
|
|
300
|
+
this.store.recordEvent(workflowId, 'nocturnal_failed', null, 'terminal_error', 'Missing required metadata: snapshot.sessionId', { workflowId });
|
|
301
|
+
return {
|
|
302
|
+
workflowId,
|
|
303
|
+
childSessionKey: `nocturnal:internal:${workflowId}`,
|
|
304
|
+
state: 'terminal_error' as const,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
if (!principleId) {
|
|
308
|
+
this.logger.warn(`[PD:NocturnalWorkflow] Missing principleId in metadata for workflow=${workflowId}, terminating`);
|
|
309
|
+
this.store.recordEvent(workflowId, 'nocturnal_failed', null, 'terminal_error', 'Missing required metadata: principleId', { workflowId });
|
|
310
|
+
return {
|
|
311
|
+
workflowId,
|
|
312
|
+
childSessionKey: `nocturnal:internal:${workflowId}`,
|
|
313
|
+
state: 'terminal_error' as const,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Configure Trinity for async execution (NOC-06, NOC-07)
|
|
318
|
+
const trinityConfig: TrinityConfig = {
|
|
319
|
+
useTrinity: true, // NOC-07: Trinity chain, not single-reflector
|
|
320
|
+
maxCandidates: 3,
|
|
321
|
+
useStubs: false,
|
|
322
|
+
runtimeAdapter: this.runtimeAdapter,
|
|
323
|
+
stateDir: this.stateDir,
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// Create mutable telemetry object (passed to invokeScribe and mutated)
|
|
327
|
+
const telemetry: TrinityTelemetry = {
|
|
328
|
+
chainMode: 'trinity',
|
|
329
|
+
usedStubs: false,
|
|
330
|
+
dreamerPassed: false,
|
|
331
|
+
philosopherPassed: false,
|
|
332
|
+
scribePassed: false,
|
|
333
|
+
candidateCount: 0,
|
|
334
|
+
selectedCandidateIndex: -1,
|
|
335
|
+
stageFailures: [],
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// NOC-07: Launch Trinity async via Promise.resolve().then() WITHOUT awaiting
|
|
339
|
+
// This offloads the async chain so startWorkflow returns immediately with state='active'
|
|
340
|
+
Promise.resolve().then(async () => {
|
|
341
|
+
try {
|
|
342
|
+
// NOC-15: Track if stub fallback was used
|
|
343
|
+
let fallbackUsed = false;
|
|
344
|
+
|
|
345
|
+
// Step 1: Crash recovery — check for existing stage outputs (NOC-13)
|
|
346
|
+
// Query WorkflowStore for any existing outputs for this workflowId
|
|
347
|
+
const existingOutputs = this.store.getStageOutputs(workflowId);
|
|
348
|
+
const recoveredDreamerOutput = existingOutputs.find(o => o.stage === 'dreamer')?.output as DreamerOutput | undefined;
|
|
349
|
+
const recoveredPhilosopherOutput = existingOutputs.find(o => o.stage === 'philosopher')?.output as PhilosopherOutput | undefined;
|
|
350
|
+
|
|
351
|
+
let dreamerOutput: DreamerOutput;
|
|
352
|
+
let philosopherOutput: PhilosopherOutput;
|
|
353
|
+
|
|
354
|
+
// Step 2: Dreamer — skip if recovered (NOC-12 idempotency)
|
|
355
|
+
if (recoveredDreamerOutput) {
|
|
356
|
+
this.logger.info(`[PD:NocturnalWorkflow] Recovered Dreamer output for workflow=${workflowId}, skipping Dreamer stage`);
|
|
357
|
+
dreamerOutput = recoveredDreamerOutput;
|
|
358
|
+
} else {
|
|
359
|
+
// Compute idempotency key BEFORE calling invokeDreamer
|
|
360
|
+
const dreamerIdemKey = computeDreamerIdempotencyKey(workflowId, snapshot, principleId, trinityConfig.maxCandidates);
|
|
361
|
+
|
|
362
|
+
// Check idempotency — another concurrent run may have completed this stage
|
|
363
|
+
const existingDreamerByKey = this.store.getStageOutputByKey(dreamerIdemKey);
|
|
364
|
+
if (existingDreamerByKey) {
|
|
365
|
+
this.logger.info(`[PD:NocturnalWorkflow] Found existing Dreamer output by idempotency key for workflow=${workflowId}`);
|
|
366
|
+
dreamerOutput = existingDreamerByKey.output as DreamerOutput;
|
|
367
|
+
} else {
|
|
368
|
+
dreamerOutput = await this.runtimeAdapter.invokeDreamer(snapshot, principleId, trinityConfig.maxCandidates);
|
|
369
|
+
// NOC-15: Fallback to stub Dreamer if real Dreamer failed
|
|
370
|
+
if (!dreamerOutput.valid || dreamerOutput.candidates.length === 0) {
|
|
371
|
+
this.logger.info(`[PD:NocturnalWorkflow] Dreamer failed (${dreamerOutput.reason}), falling back to stub`);
|
|
372
|
+
fallbackUsed = true;
|
|
373
|
+
const stubAdapter = new StubFallbackRuntimeAdapter(
|
|
374
|
+
snapshot,
|
|
375
|
+
principleId,
|
|
376
|
+
trinityConfig.maxCandidates
|
|
377
|
+
);
|
|
378
|
+
dreamerOutput = await stubAdapter.invokeDreamer(snapshot, principleId, trinityConfig.maxCandidates);
|
|
379
|
+
}
|
|
380
|
+
// Persist Dreamer output (NOC-11)
|
|
381
|
+
if (dreamerOutput.valid) {
|
|
382
|
+
this.store.recordStageOutput(workflowId, 'dreamer', dreamerOutput, dreamerIdemKey);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Step 3: Philosopher — skip if recovered (NOC-12 idempotency)
|
|
388
|
+
if (recoveredPhilosopherOutput) {
|
|
389
|
+
this.logger.info(`[PD:NocturnalWorkflow] Recovered Philosopher output for workflow=${workflowId}, skipping Philosopher stage`);
|
|
390
|
+
philosopherOutput = recoveredPhilosopherOutput;
|
|
391
|
+
} else {
|
|
392
|
+
// Compute idempotency key BEFORE calling invokePhilosopher
|
|
393
|
+
const philosopherIdemKey = computePhilosopherIdempotencyKey(workflowId, dreamerOutput);
|
|
394
|
+
|
|
395
|
+
// Check idempotency
|
|
396
|
+
const existingPhilosopherByKey = this.store.getStageOutputByKey(philosopherIdemKey);
|
|
397
|
+
if (existingPhilosopherByKey) {
|
|
398
|
+
this.logger.info(`[PD:NocturnalWorkflow] Found existing Philosopher output by idempotency key for workflow=${workflowId}`);
|
|
399
|
+
philosopherOutput = existingPhilosopherByKey.output as PhilosopherOutput;
|
|
400
|
+
} else {
|
|
401
|
+
philosopherOutput = await this.runtimeAdapter.invokePhilosopher(dreamerOutput, principleId);
|
|
402
|
+
// NOC-15: Fallback to stub Philosopher if real Philosopher failed
|
|
403
|
+
if (!philosopherOutput.valid || philosopherOutput.judgments.length === 0) {
|
|
404
|
+
this.logger.info(`[PD:NocturnalWorkflow] Philosopher failed (${philosopherOutput.reason}), falling back to stub`);
|
|
405
|
+
fallbackUsed = true;
|
|
406
|
+
const stubAdapter = new StubFallbackRuntimeAdapter(
|
|
407
|
+
snapshot,
|
|
408
|
+
principleId,
|
|
409
|
+
trinityConfig.maxCandidates
|
|
410
|
+
);
|
|
411
|
+
philosopherOutput = await stubAdapter.invokePhilosopher(dreamerOutput, principleId);
|
|
412
|
+
}
|
|
413
|
+
// Persist Philosopher output (NOC-11)
|
|
414
|
+
if (philosopherOutput.valid) {
|
|
415
|
+
this.store.recordStageOutput(workflowId, 'philosopher', philosopherOutput, philosopherIdemKey);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Step 4: Scribe — always runs (no intermediate Scribe output to persist)
|
|
421
|
+
const draftArtifact = await this.runtimeAdapter.invokeScribe(
|
|
422
|
+
dreamerOutput,
|
|
423
|
+
philosopherOutput,
|
|
424
|
+
snapshot,
|
|
425
|
+
principleId,
|
|
426
|
+
telemetry,
|
|
427
|
+
trinityConfig
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
// Step 5: Build TrinityResult from stage outcomes
|
|
431
|
+
const failures: TrinityStageFailure[] = [];
|
|
432
|
+
if (!dreamerOutput.valid || dreamerOutput.candidates.length === 0) {
|
|
433
|
+
failures.push({ stage: 'dreamer', reason: dreamerOutput.reason ?? 'no valid candidates' });
|
|
434
|
+
}
|
|
435
|
+
if (!philosopherOutput.valid || philosopherOutput.judgments.length === 0) {
|
|
436
|
+
failures.push({ stage: 'philosopher', reason: philosopherOutput.reason ?? 'no judgments produced' });
|
|
437
|
+
}
|
|
438
|
+
if (!draftArtifact) {
|
|
439
|
+
failures.push({ stage: 'scribe', reason: 'Failed to synthesize artifact' });
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const trinityResult: TrinityResult = {
|
|
443
|
+
success: failures.length === 0 && !!draftArtifact,
|
|
444
|
+
artifact: draftArtifact ?? undefined,
|
|
445
|
+
telemetry: {
|
|
446
|
+
chainMode: 'trinity',
|
|
447
|
+
usedStubs: fallbackUsed, // NOC-15: reflect actual stub usage
|
|
448
|
+
dreamerPassed: dreamerOutput.valid && dreamerOutput.candidates.length > 0,
|
|
449
|
+
philosopherPassed: philosopherOutput.valid && philosopherOutput.judgments.length > 0,
|
|
450
|
+
scribePassed: !!draftArtifact,
|
|
451
|
+
candidateCount: dreamerOutput.candidates.length,
|
|
452
|
+
selectedCandidateIndex: draftArtifact?.selectedCandidateIndex ?? -1,
|
|
453
|
+
stageFailures: failures.map(f => `${f.stage}: ${f.reason}`),
|
|
454
|
+
},
|
|
455
|
+
failures,
|
|
456
|
+
fallbackOccurred: fallbackUsed, // NOC-15: mark when fallback was triggered
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// Store for notifyWaitResult and proceed with existing flow
|
|
460
|
+
this.pendingTrinityResults.set(workflowId, trinityResult);
|
|
461
|
+
this.pendingTrinityFailures.set(workflowId, failures);
|
|
462
|
+
|
|
463
|
+
// Record stage events (NOC-08, already implemented in Phase 07)
|
|
464
|
+
this.recordStageEvents(workflowId, trinityResult);
|
|
465
|
+
|
|
466
|
+
// Drive state transitions (NOC-10)
|
|
467
|
+
if (trinityResult.success) {
|
|
468
|
+
await this.notifyWaitResult(workflowId, 'ok');
|
|
469
|
+
} else {
|
|
470
|
+
const errorMsg = failures.map(f => `${f.stage}: ${f.reason}`).join('; ');
|
|
471
|
+
await this.notifyWaitResult(workflowId, 'error', errorMsg);
|
|
472
|
+
}
|
|
473
|
+
} catch (err) {
|
|
474
|
+
// Unexpected error - treat as Trinity failure
|
|
475
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
476
|
+
this.pendingTrinityFailures.set(workflowId, [{ stage: 'dreamer' as const, reason: errorMsg }]);
|
|
477
|
+
await this.notifyWaitResult(workflowId, 'error', errorMsg);
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// Return immediately with state='active' (NOC-07)
|
|
482
|
+
return {
|
|
483
|
+
workflowId,
|
|
484
|
+
childSessionKey: `nocturnal:internal:${workflowId}`,
|
|
485
|
+
runId: undefined,
|
|
486
|
+
state: 'active',
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
491
|
+
// WorkflowManager Interface: notifyWaitResult (NOC-01, D-10: no-op)
|
|
492
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
493
|
+
|
|
494
|
+
async notifyWaitResult(
|
|
495
|
+
workflowId: string,
|
|
496
|
+
status: 'ok' | 'error' | 'timeout',
|
|
497
|
+
error?: string
|
|
498
|
+
): Promise<void> {
|
|
499
|
+
const workflow = this.store.getWorkflow(workflowId);
|
|
500
|
+
if (!workflow) {
|
|
501
|
+
this.logger.warn(`[PD:NocturnalWorkflow] notifyWaitResult: workflow not found: ${workflowId}`);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Only handle workflows in 'active' state (Trinity async path)
|
|
506
|
+
if (workflow.state !== 'active') {
|
|
507
|
+
this.logger.info(`[PD:NocturnalWorkflow] notifyWaitResult: workflow ${workflowId} not in active state: ${workflow.state}`);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const trinityFailures = this.pendingTrinityFailures.get(workflowId) ?? [];
|
|
512
|
+
const trinityResult = this.pendingTrinityResults.get(workflowId);
|
|
513
|
+
|
|
514
|
+
if (status === 'ok') {
|
|
515
|
+
// Trinity succeeded: active -> finalizing -> completed (NOC-10)
|
|
516
|
+
this.store.updateWorkflowState(workflowId, 'finalizing');
|
|
517
|
+
this.store.recordEvent(workflowId, 'trinity_completed', 'active', 'finalizing', 'Trinity chain completed successfully', {
|
|
518
|
+
trinityTelemetry: trinityResult?.telemetry,
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
this.store.updateWorkflowState(workflowId, 'completed');
|
|
522
|
+
this.store.recordEvent(workflowId, 'nocturnal_completed', 'finalizing', 'completed', 'artifact persisted', {
|
|
523
|
+
persistedPath: trinityResult?.artifact ? 'trinity-draft' : undefined,
|
|
524
|
+
});
|
|
525
|
+
} else {
|
|
526
|
+
// Any stage failure: -> terminal_error immediately (NOC-09, NOC-10)
|
|
527
|
+
this.store.updateWorkflowState(workflowId, 'terminal_error');
|
|
528
|
+
this.store.recordEvent(workflowId, 'nocturnal_failed', 'active', 'terminal_error', error ?? 'Trinity stage failed', {
|
|
529
|
+
failures: trinityFailures, // NOC-09: TrinityStageFailure[] in payload
|
|
530
|
+
trinityTelemetry: trinityResult?.telemetry,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Clean up pending state (idempotent - also cleaned in markCompleted)
|
|
535
|
+
this.pendingTrinityFailures.delete(workflowId);
|
|
536
|
+
this.pendingTrinityResults.delete(workflowId);
|
|
537
|
+
this.markCompleted(workflowId);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
541
|
+
// WorkflowManager Interface: notifyLifecycleEvent (NOC-01, D-10: no-op)
|
|
542
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
543
|
+
|
|
544
|
+
async notifyLifecycleEvent(
|
|
545
|
+
_workflowId: string,
|
|
546
|
+
_event: 'subagent_spawned' | 'subagent_ended',
|
|
547
|
+
_data?: Record<string, unknown>
|
|
548
|
+
): Promise<void> {
|
|
549
|
+
// D-10: No-op. NocturnalWorkflowManager does not use the wait-on-run pattern.
|
|
550
|
+
// TrinityRuntimeAdapter manages its own internal subagent lifecycle.
|
|
551
|
+
// No external subagent_spawned/subagent_ended events need to be tracked.
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
555
|
+
// WorkflowManager Interface: finalizeOnce (NOC-01)
|
|
556
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
557
|
+
|
|
558
|
+
async finalizeOnce(workflowId: string): Promise<void> {
|
|
559
|
+
const workflow = this.store.getWorkflow(workflowId);
|
|
560
|
+
if (!workflow) {
|
|
561
|
+
this.logger.warn(`[PD:NocturnalWorkflow] finalizeOnce: workflow not found: ${workflowId}`);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (this.isCompleted(workflowId)) {
|
|
566
|
+
this.logger.info(`[PD:NocturnalWorkflow] finalizeOnce: already completed: ${workflowId}`);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// NocturnalWorkflowManager completes execution synchronously in startWorkflow.
|
|
571
|
+
// If we reach here, the workflow was already marked as completed/failed.
|
|
572
|
+
// Nothing more to do - result already persisted by executeNocturnalReflectionAsync.
|
|
573
|
+
this.logger.info(`[PD:NocturnalWorkflow] finalizeOnce: workflow already in terminal state: ${workflowId}, state=${workflow.state}`);
|
|
574
|
+
this.markCompleted(workflowId);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
578
|
+
// WorkflowManager Interface: sweepExpiredWorkflows (NOC-05)
|
|
579
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
580
|
+
|
|
581
|
+
async sweepExpiredWorkflows(maxAgeMs = 30 * 60 * 1000): Promise<number> {
|
|
582
|
+
const expired = this.store.getExpiredWorkflows(maxAgeMs);
|
|
583
|
+
|
|
584
|
+
this.logger.info(`[PD:NocturnalWorkflow] sweepExpiredWorkflows: found ${expired.length} expired`);
|
|
585
|
+
|
|
586
|
+
for (const workflow of expired) {
|
|
587
|
+
try {
|
|
588
|
+
this.logger.info(`[PD:NocturnalWorkflow] Sweeping expired workflow: ${workflow.workflow_id}`);
|
|
589
|
+
|
|
590
|
+
// D-06: Mark as expired in WorkflowStore
|
|
591
|
+
this.store.updateWorkflowState(workflow.workflow_id, 'expired');
|
|
592
|
+
this.store.recordEvent(workflow.workflow_id, 'nocturnal_expired', workflow.state, 'expired', 'TTL expired', { workflowId: workflow.workflow_id });
|
|
593
|
+
|
|
594
|
+
// D-06: Clean partial artifact files by workflowId prefix
|
|
595
|
+
const samplesDir = resolveNocturnalDir(this.workspaceDir, 'SAMPLES');
|
|
596
|
+
if (fs.existsSync(samplesDir)) {
|
|
597
|
+
const files = fs.readdirSync(samplesDir).filter(f => f.includes(workflow.workflow_id));
|
|
598
|
+
for (const file of files) {
|
|
599
|
+
const filePath = path.join(samplesDir, file);
|
|
600
|
+
try {
|
|
601
|
+
fs.unlinkSync(filePath);
|
|
602
|
+
this.logger.info(`[PD:NocturnalWorkflow] Removed partial artifact: ${filePath}`);
|
|
603
|
+
} catch (unlinkErr) {
|
|
604
|
+
this.logger.warn(`[PD:NocturnalWorkflow] Failed to remove partial artifact ${filePath}: ${String(unlinkErr)}`);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
} catch (error) {
|
|
610
|
+
this.logger.error(`[PD:NocturnalWorkflow] Sweep failed for ${workflow.workflow_id}: ${String(error)}`);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// Clean up memory Maps to prevent leaks
|
|
615
|
+
const cutoff = Date.now() - 60_000; // 1 minute dedup window
|
|
616
|
+
for (const [id, timestamp] of this.completedWorkflows) {
|
|
617
|
+
if (timestamp < cutoff) {
|
|
618
|
+
this.completedWorkflows.delete(id);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return expired.length;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
626
|
+
// WorkflowManager Interface: getWorkflowDebugSummary (NOC-01)
|
|
627
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
628
|
+
|
|
629
|
+
async getWorkflowDebugSummary(workflowId: string, eventLimit = 10): Promise<WorkflowDebugSummary | null> {
|
|
630
|
+
const workflow = this.store.getWorkflow(workflowId);
|
|
631
|
+
if (!workflow) return null;
|
|
632
|
+
|
|
633
|
+
const metadata = JSON.parse(workflow.metadata_json) as WorkflowMetadata;
|
|
634
|
+
const allEvents = this.store.getEvents(workflowId);
|
|
635
|
+
const recentEvents = allEvents
|
|
636
|
+
.slice(-eventLimit)
|
|
637
|
+
.map((event) => ({
|
|
638
|
+
eventType: event.event_type,
|
|
639
|
+
fromState: event.from_state,
|
|
640
|
+
toState: event.to_state,
|
|
641
|
+
reason: event.reason,
|
|
642
|
+
createdAt: event.created_at,
|
|
643
|
+
payload: JSON.parse(event.payload_json || '{}') as Record<string, unknown>,
|
|
644
|
+
}));
|
|
645
|
+
|
|
646
|
+
// NOC-16: Compute Trinity stage states from events
|
|
647
|
+
const trinityStageStates = this.computeTrinityStageStates(allEvents);
|
|
648
|
+
|
|
649
|
+
return {
|
|
650
|
+
workflowId: workflow.workflow_id,
|
|
651
|
+
workflowType: workflow.workflow_type,
|
|
652
|
+
transport: workflow.transport,
|
|
653
|
+
parentSessionId: workflow.parent_session_id,
|
|
654
|
+
childSessionKey: workflow.child_session_key,
|
|
655
|
+
runId: workflow.run_id,
|
|
656
|
+
state: workflow.state,
|
|
657
|
+
cleanupState: workflow.cleanup_state,
|
|
658
|
+
lastObservedAt: workflow.last_observed_at ?? null,
|
|
659
|
+
metadata,
|
|
660
|
+
recentEvents,
|
|
661
|
+
trinityStageStates, // NOC-16
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
666
|
+
// WorkflowManager Interface: dispose (NOC-01)
|
|
667
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
668
|
+
|
|
669
|
+
dispose(): void {
|
|
670
|
+
this.store.dispose();
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
674
|
+
// Helper Methods
|
|
675
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
676
|
+
|
|
677
|
+
private generateWorkflowId(): string {
|
|
678
|
+
return `wf_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
private isCompleted(workflowId: string): boolean {
|
|
682
|
+
const timestamp = this.completedWorkflows.get(workflowId);
|
|
683
|
+
if (!timestamp) return false;
|
|
684
|
+
if (Date.now() - timestamp > 5 * 60 * 1000) {
|
|
685
|
+
this.completedWorkflows.delete(workflowId);
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
return true;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
private markCompleted(workflowId: string): void {
|
|
692
|
+
this.completedWorkflows.set(workflowId, Date.now());
|
|
693
|
+
this.workflowSpecs.delete(workflowId);
|
|
694
|
+
this.executionResults.delete(workflowId);
|
|
695
|
+
this.pendingTrinityFailures.delete(workflowId);
|
|
696
|
+
this.pendingTrinityResults.delete(workflowId);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Compute Trinity stage states from workflow events (NOC-16).
|
|
701
|
+
* Derives current/completed/failed state for each Trinity stage.
|
|
702
|
+
*/
|
|
703
|
+
private computeTrinityStageStates(events: WorkflowEventRow[]): Array<{
|
|
704
|
+
stage: 'dreamer' | 'philosopher' | 'scribe';
|
|
705
|
+
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
706
|
+
reason?: string;
|
|
707
|
+
completedAt?: number;
|
|
708
|
+
}> {
|
|
709
|
+
const stages: Array<'dreamer' | 'philosopher' | 'scribe'> = ['dreamer', 'philosopher', 'scribe'];
|
|
710
|
+
const result: Array<{
|
|
711
|
+
stage: 'dreamer' | 'philosopher' | 'scribe';
|
|
712
|
+
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
713
|
+
reason?: string;
|
|
714
|
+
completedAt?: number;
|
|
715
|
+
}> = [];
|
|
716
|
+
|
|
717
|
+
for (const stage of stages) {
|
|
718
|
+
const startEvent = events.find(e => e.event_type === `trinity_${stage}_start`);
|
|
719
|
+
const completeEvent = events.find(e => e.event_type === `trinity_${stage}_complete`);
|
|
720
|
+
const failedEvent = events.find(e => e.event_type === `trinity_${stage}_failed`);
|
|
721
|
+
|
|
722
|
+
if (!startEvent) {
|
|
723
|
+
// Stage never ran
|
|
724
|
+
result.push({ stage, status: 'pending' });
|
|
725
|
+
} else if (failedEvent) {
|
|
726
|
+
// Stage ran and failed
|
|
727
|
+
const payload = JSON.parse(failedEvent.payload_json || '{}') as Record<string, unknown>;
|
|
728
|
+
const failures = payload.failures as Array<{ reason?: string }> | undefined;
|
|
729
|
+
result.push({
|
|
730
|
+
stage,
|
|
731
|
+
status: 'failed',
|
|
732
|
+
reason: failures?.[0]?.reason ?? failedEvent.reason,
|
|
733
|
+
completedAt: failedEvent.created_at,
|
|
734
|
+
});
|
|
735
|
+
} else if (completeEvent) {
|
|
736
|
+
// Stage ran and completed
|
|
737
|
+
result.push({
|
|
738
|
+
stage,
|
|
739
|
+
status: 'completed',
|
|
740
|
+
completedAt: completeEvent.created_at,
|
|
741
|
+
});
|
|
742
|
+
} else {
|
|
743
|
+
// Stage started but not completed or failed — currently running
|
|
744
|
+
result.push({ stage, status: 'running' });
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
return result;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Record Trinity stage events in batch after the chain completes (per NOC-08).
|
|
753
|
+
* Derives stage events from TrinityResult.telemetry and TrinityResult.failures.
|
|
754
|
+
* Always records _start event for each stage that ran, plus _complete or _failed based on outcome.
|
|
755
|
+
*/
|
|
756
|
+
private recordStageEvents(workflowId: string, result: TrinityResult): void {
|
|
757
|
+
const { telemetry, failures } = result;
|
|
758
|
+
|
|
759
|
+
// Dreamer events (always runs if we reach here)
|
|
760
|
+
this.store.recordEvent(
|
|
761
|
+
workflowId,
|
|
762
|
+
'trinity_dreamer_start',
|
|
763
|
+
null, // fromState: null for first event
|
|
764
|
+
'active',
|
|
765
|
+
'Trinity Dreamer stage began',
|
|
766
|
+
{}
|
|
767
|
+
);
|
|
768
|
+
|
|
769
|
+
if (telemetry.dreamerPassed) {
|
|
770
|
+
this.store.recordEvent(
|
|
771
|
+
workflowId,
|
|
772
|
+
'trinity_dreamer_complete',
|
|
773
|
+
'active',
|
|
774
|
+
'active',
|
|
775
|
+
'Dreamer completed successfully',
|
|
776
|
+
{ candidateCount: telemetry.candidateCount }
|
|
777
|
+
);
|
|
778
|
+
} else {
|
|
779
|
+
const dreamerFailure = failures.find(f => f.stage === 'dreamer');
|
|
780
|
+
this.store.recordEvent(
|
|
781
|
+
workflowId,
|
|
782
|
+
'trinity_dreamer_failed',
|
|
783
|
+
'active',
|
|
784
|
+
'active',
|
|
785
|
+
dreamerFailure?.reason ?? 'Dreamer stage failed',
|
|
786
|
+
{ failures: failures.filter(f => f.stage === 'dreamer') }
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// Philosopher events (only if Dreamer passed)
|
|
791
|
+
if (telemetry.dreamerPassed) {
|
|
792
|
+
this.store.recordEvent(
|
|
793
|
+
workflowId,
|
|
794
|
+
'trinity_philosopher_start',
|
|
795
|
+
'active',
|
|
796
|
+
'active',
|
|
797
|
+
'Trinity Philosopher stage began',
|
|
798
|
+
{}
|
|
799
|
+
);
|
|
800
|
+
|
|
801
|
+
if (telemetry.philosopherPassed) {
|
|
802
|
+
this.store.recordEvent(
|
|
803
|
+
workflowId,
|
|
804
|
+
'trinity_philosopher_complete',
|
|
805
|
+
'active',
|
|
806
|
+
'active',
|
|
807
|
+
'Philosopher completed successfully',
|
|
808
|
+
{}
|
|
809
|
+
);
|
|
810
|
+
} else {
|
|
811
|
+
const philosopherFailure = failures.find(f => f.stage === 'philosopher');
|
|
812
|
+
this.store.recordEvent(
|
|
813
|
+
workflowId,
|
|
814
|
+
'trinity_philosopher_failed',
|
|
815
|
+
'active',
|
|
816
|
+
'active',
|
|
817
|
+
philosopherFailure?.reason ?? 'Philosopher stage failed',
|
|
818
|
+
{ failures: failures.filter(f => f.stage === 'philosopher') }
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Scribe events (only if Philosopher passed)
|
|
824
|
+
if (telemetry.philosopherPassed) {
|
|
825
|
+
this.store.recordEvent(
|
|
826
|
+
workflowId,
|
|
827
|
+
'trinity_scribe_start',
|
|
828
|
+
'active',
|
|
829
|
+
'active',
|
|
830
|
+
'Trinity Scribe stage began',
|
|
831
|
+
{}
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
if (telemetry.scribePassed) {
|
|
835
|
+
this.store.recordEvent(
|
|
836
|
+
workflowId,
|
|
837
|
+
'trinity_scribe_complete',
|
|
838
|
+
'active',
|
|
839
|
+
'finalizing', // NOC-10: scribe complete -> finalizing state
|
|
840
|
+
'Scribe completed successfully',
|
|
841
|
+
{ selectedCandidateIndex: telemetry.selectedCandidateIndex }
|
|
842
|
+
);
|
|
843
|
+
} else {
|
|
844
|
+
const scribeFailure = failures.find(f => f.stage === 'scribe');
|
|
845
|
+
this.store.recordEvent(
|
|
846
|
+
workflowId,
|
|
847
|
+
'trinity_scribe_failed',
|
|
848
|
+
'active',
|
|
849
|
+
'terminal_error', // NOC-10: scribe failure -> terminal_error immediately
|
|
850
|
+
scribeFailure?.reason ?? 'Scribe stage failed',
|
|
851
|
+
{ failures: failures.filter(f => f.stage === 'scribe') }
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|