principles-disciple 1.10.0 → 1.12.0
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/CHANGELOG.md +26 -0
- package/README.md +66 -0
- package/esbuild.config.js +1 -1
- package/openclaw.plugin.json +4 -4
- package/package.json +2 -3
- package/run-nocturnal.mjs +30 -0
- package/scripts/db-migrate.mjs +170 -0
- package/scripts/sync-plugin.mjs +94 -5
- package/src/commands/archive-impl.ts +136 -0
- package/src/commands/capabilities.ts +4 -2
- package/src/commands/context.ts +5 -1
- package/src/commands/disable-impl.ts +151 -0
- package/src/commands/evolution-status.ts +64 -19
- package/src/commands/export.ts +8 -6
- package/src/commands/focus.ts +8 -20
- package/src/commands/nocturnal-review.ts +5 -7
- package/src/commands/nocturnal-rollout.ts +1 -12
- package/src/commands/nocturnal-train.ts +17 -42
- package/src/commands/pain.ts +10 -5
- package/src/commands/principle-rollback.ts +4 -2
- package/src/commands/promote-impl.ts +274 -0
- package/src/commands/rollback-impl.ts +234 -0
- package/src/commands/rollback.ts +6 -3
- package/src/commands/samples.ts +2 -0
- package/src/commands/thinking-os.ts +3 -4
- package/src/commands/workflow-debug.ts +2 -1
- package/src/config/errors.ts +1 -0
- package/src/core/AGENTS.md +34 -0
- package/src/core/adaptive-thresholds.ts +4 -3
- package/src/core/code-implementation-storage.ts +241 -0
- package/src/core/config.ts +5 -2
- package/src/core/control-ui-db.ts +29 -10
- package/src/core/detection-funnel.ts +12 -7
- package/src/core/diagnostician-task-store.ts +156 -0
- package/src/core/dictionary.ts +4 -4
- package/src/core/empathy-keyword-matcher.ts +7 -3
- package/src/core/empathy-types.ts +13 -2
- package/src/core/event-log.ts +14 -6
- package/src/core/evolution-engine.ts +27 -31
- package/src/core/evolution-logger.ts +3 -2
- package/src/core/evolution-reducer.ts +83 -31
- package/src/core/evolution-types.ts +10 -0
- package/src/core/external-training-contract.ts +1 -0
- package/src/core/focus-history.ts +38 -24
- package/src/core/hygiene/tracker.ts +10 -6
- package/src/core/init.ts +5 -2
- package/src/core/migration.ts +3 -3
- package/src/core/model-deployment-registry.ts +6 -4
- package/src/core/model-training-registry.ts +5 -3
- package/src/core/nocturnal-arbiter.ts +13 -14
- package/src/core/nocturnal-artifact-lineage.ts +117 -0
- package/src/core/nocturnal-artificer.ts +257 -0
- package/src/core/nocturnal-candidate-scoring.ts +4 -2
- package/src/core/nocturnal-compliance.ts +67 -19
- package/src/core/nocturnal-dataset.ts +95 -2
- package/src/core/nocturnal-executability.ts +2 -3
- package/src/core/nocturnal-export.ts +6 -3
- package/src/core/nocturnal-rule-implementation-validator.ts +245 -0
- package/src/core/nocturnal-trajectory-extractor.ts +10 -3
- package/src/core/nocturnal-trinity.ts +300 -57
- package/src/core/pain-context-extractor.ts +29 -15
- package/src/core/pain.ts +7 -5
- package/src/core/path-resolver.ts +16 -15
- package/src/core/paths.ts +2 -1
- package/src/core/pd-task-reconciler.ts +463 -0
- package/src/core/pd-task-service.ts +42 -0
- package/src/core/pd-task-store.ts +77 -0
- package/src/core/pd-task-types.ts +128 -0
- package/src/core/principle-internalization/deprecated-readiness.ts +91 -0
- package/src/core/principle-internalization/internalization-routing-policy.ts +208 -0
- package/src/core/principle-internalization/lifecycle-metrics.ts +149 -0
- package/src/core/principle-internalization/lifecycle-read-model.ts +243 -0
- package/src/core/principle-internalization/lifecycle-refresh.ts +11 -0
- package/src/core/principle-internalization/principle-lifecycle-service.ts +167 -0
- package/src/core/principle-training-state.ts +95 -370
- package/src/core/principle-tree-ledger.ts +713 -0
- package/src/core/profile.ts +3 -1
- package/src/core/promotion-gate.ts +14 -18
- package/src/core/replay-engine.ts +562 -0
- package/src/core/risk-calculator.ts +6 -4
- package/src/core/rule-host-helpers.ts +39 -0
- package/src/core/rule-host-types.ts +82 -0
- package/src/core/rule-host.ts +245 -0
- package/src/core/rule-implementation-runtime.ts +38 -0
- package/src/core/schema/db-types.ts +16 -0
- package/src/core/schema/index.ts +26 -0
- package/src/core/schema/migration-runner.ts +207 -0
- package/src/core/schema/migrations/001-init-trajectory.ts +211 -0
- package/src/core/schema/migrations/002-init-central.ts +122 -0
- package/src/core/schema/migrations/003-init-workflow.ts +55 -0
- package/src/core/schema/migrations/004-add-thinking-and-gfi.ts +74 -0
- package/src/core/schema/migrations/index.ts +31 -0
- package/src/core/schema/schema-definitions.ts +650 -0
- package/src/core/session-tracker.ts +6 -4
- package/src/core/shadow-observation-registry.ts +6 -3
- package/src/core/system-logger.ts +2 -2
- package/src/core/thinking-models.ts +182 -46
- package/src/core/thinking-os-parser.ts +164 -0
- package/src/core/training-program.ts +7 -7
- package/src/core/trajectory.ts +42 -36
- package/src/core/workspace-context.ts +77 -11
- package/src/core/workspace-dir-validation.ts +152 -0
- package/src/hooks/AGENTS.md +31 -0
- package/src/hooks/bash-risk.ts +3 -1
- package/src/hooks/edit-verification.ts +9 -5
- package/src/hooks/gate-block-helper.ts +5 -1
- package/src/hooks/gate.ts +152 -5
- package/src/hooks/gfi-gate.ts +9 -2
- package/src/hooks/lifecycle-routing.ts +124 -0
- package/src/hooks/lifecycle.ts +12 -12
- package/src/hooks/llm.ts +17 -109
- package/src/hooks/message-sanitize.ts +5 -3
- package/src/hooks/pain.ts +19 -15
- package/src/hooks/progressive-trust-gate.ts +7 -1
- package/src/hooks/prompt.ts +169 -60
- package/src/hooks/subagent.ts +5 -4
- package/src/hooks/thinking-checkpoint.ts +2 -0
- package/src/hooks/trajectory-collector.ts +15 -12
- package/src/http/principles-console-route.ts +31 -68
- package/src/i18n/commands.ts +2 -2
- package/src/index.ts +126 -40
- package/src/service/central-database.ts +131 -43
- package/src/service/central-health-service.ts +47 -0
- package/src/service/central-overview-service.ts +135 -0
- package/src/service/central-sync-service.ts +87 -0
- package/src/service/control-ui-query-service.ts +46 -36
- package/src/service/event-log-auditor.ts +261 -0
- package/src/service/evolution-query-service.ts +23 -22
- package/src/service/evolution-worker.ts +565 -261
- package/src/service/health-query-service.ts +213 -36
- package/src/service/nocturnal-runtime.ts +8 -4
- package/src/service/nocturnal-service.ts +499 -59
- package/src/service/nocturnal-target-selector.ts +5 -7
- package/src/service/runtime-summary-service.ts +2 -1
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +25 -336
- package/src/service/subagent-workflow/dynamic-timeout.ts +30 -0
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +48 -386
- package/src/service/subagent-workflow/index.ts +2 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +155 -285
- package/src/service/subagent-workflow/runtime-direct-driver.ts +114 -16
- package/src/service/subagent-workflow/subagent-error-utils.ts +25 -0
- package/src/service/subagent-workflow/types.ts +9 -4
- package/src/service/subagent-workflow/workflow-manager-base.ts +573 -0
- package/src/service/subagent-workflow/workflow-store.ts +71 -11
- package/src/service/trajectory-service.ts +2 -1
- package/src/tools/critique-prompt.ts +1 -1
- package/src/tools/deep-reflect.ts +175 -209
- package/src/tools/model-index.ts +2 -1
- package/src/types/event-types.ts +2 -2
- package/src/types/principle-tree-schema.ts +29 -23
- package/src/utils/file-lock.ts +5 -3
- package/src/utils/io.ts +5 -2
- package/src/utils/nlp.ts +5 -46
- package/src/utils/node-vm-polyfill.ts +11 -0
- package/src/utils/plugin-logger.ts +2 -0
- package/src/utils/retry.ts +572 -0
- package/src/utils/subagent-probe.ts +1 -1
- package/templates/langs/en/core/AGENTS.md +0 -13
- package/templates/langs/en/core/SOUL.md +1 -31
- package/templates/langs/en/core/TOOLS.md +0 -4
- package/templates/langs/en/principles/THINKING_OS.md +64 -0
- package/templates/langs/en/skills/admin/SKILL.md +0 -1
- package/templates/langs/en/skills/evolution-framework-update/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-diagnostician/SKILL.md +18 -5
- package/templates/langs/zh/core/AGENTS.md +0 -22
- package/templates/langs/zh/core/SOUL.md +1 -31
- package/templates/langs/zh/core/TOOLS.md +0 -4
- package/templates/langs/zh/principles/THINKING_OS.md +64 -0
- package/templates/langs/zh/skills/admin/SKILL.md +0 -1
- package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +25 -4
- package/tests/commands/evolution-status.test.ts +119 -0
- package/tests/commands/implementation-lifecycle.test.ts +362 -0
- package/tests/core/code-implementation-storage.test.ts +398 -0
- package/tests/core/evolution-reducer.detector-metadata.test.ts +28 -28
- package/tests/core/nocturnal-artifact-lineage.test.ts +53 -0
- package/tests/core/nocturnal-artificer.test.ts +241 -0
- package/tests/core/nocturnal-compliance-p-principles.test.ts +133 -0
- package/tests/core/nocturnal-rule-implementation-validator.test.ts +127 -0
- package/tests/core/pd-task-store.test.ts +126 -0
- package/tests/core/principle-internalization/deprecated-readiness.test.ts +193 -0
- package/tests/core/principle-internalization/internalization-routing-policy.test.ts +212 -0
- package/tests/core/principle-internalization/lifecycle-metrics.test.ts +350 -0
- package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +211 -0
- package/tests/core/principle-training-state.test.ts +228 -1
- package/tests/core/principle-tree-ledger.test.ts +423 -0
- package/tests/core/regression-v1-9-1.test.ts +265 -0
- package/tests/core/replay-engine.test.ts +234 -0
- package/tests/core/rule-host-helpers.test.ts +120 -0
- package/tests/core/rule-host.test.ts +389 -0
- package/tests/core/rule-implementation-runtime.test.ts +64 -0
- package/tests/core/workspace-context.test.ts +53 -0
- package/tests/core/workspace-dir-validation.test.ts +272 -0
- package/tests/hooks/gate-rule-host-pipeline.test.ts +385 -0
- package/tests/hooks/pain.test.ts +74 -10
- package/tests/hooks/prompt.test.ts +63 -1
- package/tests/integration/principle-lifecycle.e2e.test.ts +197 -0
- package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +211 -0
- package/tests/service/data-endpoints-regression.test.ts +834 -0
- package/tests/service/evolution-worker.test.ts +0 -123
- package/tests/service/nocturnal-service-code-candidate.test.ts +330 -0
- package/tests/utils/nlp.test.ts +1 -19
- package/tests/utils/retry.test.ts +327 -0
- package/ui/src/App.tsx +1 -1
- package/ui/src/api.ts +4 -0
- package/ui/src/charts.tsx +366 -0
- package/ui/src/components/WorkspaceConfig.tsx +107 -75
- package/ui/src/i18n/ui.ts +55 -22
- package/ui/src/pages/OverviewPage.tsx +441 -81
- package/ui/src/styles.css +43 -0
- package/ui/src/types.ts +17 -1
- package/src/agents/nocturnal-dreamer.md +0 -152
- package/src/agents/nocturnal-philosopher.md +0 -138
- package/src/agents/nocturnal-reflector.md +0 -126
- package/src/agents/nocturnal-scribe.md +0 -164
- package/templates/workspace/.principles/00-kernel.md +0 -51
- package/templates/workspace/.principles/DECISION_POLICY.json +0 -44
- package/templates/workspace/.principles/PRINCIPLES.md +0 -20
- package/templates/workspace/.principles/PROFILE.json +0 -54
- package/templates/workspace/.principles/PROFILE.schema.json +0 -56
- package/templates/workspace/.principles/THINKING_OS.md +0 -64
- package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +0 -7
- package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +0 -9
- package/templates/workspace/.principles/models/_INDEX.md +0 -27
- package/templates/workspace/.principles/models/first_principles.md +0 -62
- package/templates/workspace/.principles/models/marketing_4p.md +0 -52
- package/templates/workspace/.principles/models/porter_five.md +0 -63
- package/templates/workspace/.principles/models/swot.md +0 -60
- package/templates/workspace/.principles/models/user_story_map.md +0 -63
- package/templates/workspace/.state/WORKBOARD.json +0 -4
|
@@ -35,11 +35,12 @@ import {
|
|
|
35
35
|
type NocturnalRunResult,
|
|
36
36
|
} from '../nocturnal-service.js';
|
|
37
37
|
import { type TrinityStageFailure, type TrinityResult } from '../../core/nocturnal-trinity.js';
|
|
38
|
-
import type { TrinityRuntimeAdapter
|
|
38
|
+
import type { TrinityRuntimeAdapter } from '../../core/nocturnal-trinity.js';
|
|
39
39
|
import type { NocturnalSessionSnapshot } from '../../core/nocturnal-trajectory-extractor.js';
|
|
40
|
-
import {
|
|
40
|
+
import type { RecentPainContext } from '../evolution-worker.js';
|
|
41
41
|
import * as fs from 'fs';
|
|
42
42
|
import * as path from 'path';
|
|
43
|
+
import { isSubagentRuntimeAvailable } from '../../utils/subagent-probe.js';
|
|
43
44
|
|
|
44
45
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
45
46
|
// NocturnalResult Type Alias
|
|
@@ -51,50 +52,6 @@ import * as path from 'path';
|
|
|
51
52
|
*/
|
|
52
53
|
export type NocturnalResult = NocturnalRunResult;
|
|
53
54
|
|
|
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
55
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
99
56
|
// NocturnalWorkflowOptions
|
|
100
57
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -125,6 +82,7 @@ export interface NocturnalWorkflowOptions {
|
|
|
125
82
|
* - timeoutMs: 15 minutes (900000ms)
|
|
126
83
|
* - ttlMs: 30 minutes (1800000ms)
|
|
127
84
|
*/
|
|
85
|
+
/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars -- Reason: spec methods intentionally receive unused params per interface contract */
|
|
128
86
|
export const nocturnalWorkflowSpec: SubagentWorkflowSpec<NocturnalResult> = {
|
|
129
87
|
workflowType: 'nocturnal',
|
|
130
88
|
transport: 'runtime_direct',
|
|
@@ -132,7 +90,10 @@ export const nocturnalWorkflowSpec: SubagentWorkflowSpec<NocturnalResult> = {
|
|
|
132
90
|
ttlMs: 30 * 60 * 1000, // D-04: 30 minutes
|
|
133
91
|
shouldDeleteSessionAfterFinalize: false, // D-09: no external session to delete
|
|
134
92
|
|
|
135
|
-
buildPrompt(
|
|
93
|
+
buildPrompt(
|
|
94
|
+
_taskInput: unknown,
|
|
95
|
+
_metadata: WorkflowMetadata
|
|
96
|
+
): string {
|
|
136
97
|
// NocturnalWorkflowManager does not use prompt injection.
|
|
137
98
|
// Execution is driven by executeNocturnalReflectionAsync.
|
|
138
99
|
return '';
|
|
@@ -141,7 +102,7 @@ export const nocturnalWorkflowSpec: SubagentWorkflowSpec<NocturnalResult> = {
|
|
|
141
102
|
async parseResult(ctx: WorkflowResultContext): Promise<NocturnalResult | null> {
|
|
142
103
|
// NocturnalWorkflowManager handles execution directly in startWorkflow.
|
|
143
104
|
// This is not called via the standard subagent message path.
|
|
144
|
-
return (ctx.metadata
|
|
105
|
+
return (ctx.metadata.nocturnalResult as NocturnalResult) ?? null;
|
|
145
106
|
},
|
|
146
107
|
|
|
147
108
|
async persistResult(_ctx: WorkflowPersistContext<NocturnalResult>): Promise<void> {
|
|
@@ -154,57 +115,7 @@ export const nocturnalWorkflowSpec: SubagentWorkflowSpec<NocturnalResult> = {
|
|
|
154
115
|
return status === 'ok';
|
|
155
116
|
},
|
|
156
117
|
};
|
|
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
|
-
}
|
|
118
|
+
/* eslint-enable no-unused-vars */
|
|
208
119
|
|
|
209
120
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
210
121
|
// NocturnalWorkflowManager
|
|
@@ -230,15 +141,15 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
230
141
|
private readonly store: WorkflowStore;
|
|
231
142
|
|
|
232
143
|
/** Tracks completion timestamps for idempotency */
|
|
233
|
-
private completedWorkflows = new Map<string, number>();
|
|
144
|
+
private readonly completedWorkflows = new Map<string, number>();
|
|
234
145
|
/** Maps workflowId → spec (needed for finalizeOnce) */
|
|
235
|
-
private workflowSpecs = new Map<string, SubagentWorkflowSpec<unknown>>();
|
|
146
|
+
private readonly workflowSpecs = new Map<string, SubagentWorkflowSpec<unknown>>();
|
|
236
147
|
/** Maps workflowId → result (needed for finalizeOnce) */
|
|
237
|
-
private executionResults = new Map<string, NocturnalResult>();
|
|
148
|
+
private readonly executionResults = new Map<string, NocturnalResult>();
|
|
238
149
|
/** Maps workflowId → TrinityStageFailure[] (stored before async launch, used in notifyWaitResult) */
|
|
239
|
-
private pendingTrinityFailures = new Map<string, TrinityStageFailure[]>();
|
|
150
|
+
private readonly pendingTrinityFailures = new Map<string, TrinityStageFailure[]>();
|
|
240
151
|
/** Maps workflowId → TrinityResult (needed by notifyWaitResult for artifact persistence) */
|
|
241
|
-
private pendingTrinityResults = new Map<string, TrinityResult>();
|
|
152
|
+
private readonly pendingTrinityResults = new Map<string, TrinityResult>();
|
|
242
153
|
|
|
243
154
|
constructor(opts: NocturnalWorkflowOptions) {
|
|
244
155
|
this.workspaceDir = opts.workspaceDir;
|
|
@@ -261,7 +172,16 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
261
172
|
metadata?: Record<string, unknown>;
|
|
262
173
|
}
|
|
263
174
|
): Promise<WorkflowHandle> {
|
|
264
|
-
|
|
175
|
+
// #179: Check subagent runtime availability before starting
|
|
176
|
+
// Other workflow managers (empathy, deep-reflect) have this check
|
|
177
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Reason: TrinityRuntimeAdapter interface doesn't expose api.runtime.subagent, but OpenClawTrinityRuntimeAdapter has it
|
|
178
|
+
const subagent = (this.runtimeAdapter as any).api?.runtime?.subagent;
|
|
179
|
+
if (!isSubagentRuntimeAvailable(subagent)) {
|
|
180
|
+
this.logger.warn(`[PD:NocturnalWorkflow] Subagent runtime unavailable, skipping workflow`);
|
|
181
|
+
throw new Error(`NocturnalWorkflowManager: subagent runtime unavailable`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const workflowId = NocturnalWorkflowManager.generateWorkflowId();
|
|
265
185
|
const now = Date.now();
|
|
266
186
|
|
|
267
187
|
const metadata: WorkflowMetadata = {
|
|
@@ -286,13 +206,16 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
286
206
|
state: 'active',
|
|
287
207
|
created_at: now,
|
|
288
208
|
updated_at: now,
|
|
209
|
+
duration_ms: null,
|
|
289
210
|
metadata_json: JSON.stringify(metadata),
|
|
290
211
|
});
|
|
291
212
|
this.store.recordEvent(workflowId, 'nocturnal_started', null, 'active', 'TrinityRuntimeAdapter invoked', { workflowType: 'nocturnal' });
|
|
292
213
|
|
|
293
214
|
// Extract snapshot and principleId from taskInput.metadata (NOC-07: Trinity async path)
|
|
294
|
-
const snapshot = options.metadata?.snapshot as
|
|
215
|
+
const snapshot = options.metadata?.snapshot as NocturnalSessionSnapshot | undefined;
|
|
295
216
|
const principleId = options.metadata?.principleId as string | undefined;
|
|
217
|
+
// Extract painContext for Selector ranking bias
|
|
218
|
+
const painContext = options.metadata?.painContext as RecentPainContext | undefined;
|
|
296
219
|
|
|
297
220
|
// Validate required metadata (prevent runtime crashes from undefined snapshot)
|
|
298
221
|
if (!snapshot?.sessionId) {
|
|
@@ -304,186 +227,68 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
304
227
|
state: 'terminal_error' as const,
|
|
305
228
|
};
|
|
306
229
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
//
|
|
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'
|
|
230
|
+
// #205: Always use executeNocturnalReflectionAsync for unified pipeline
|
|
231
|
+
// This ensures the full post-processing flow is always executed:
|
|
232
|
+
// Selector → Trinity → Arbiter → Executability → Persist → Register
|
|
233
|
+
//
|
|
234
|
+
// When principleId is provided, we pass it as principleIdOverride to skip Selector.
|
|
235
|
+
// When principleId is missing, Selector will choose a principle from training store.
|
|
236
|
+
this.logger.info(`[PD:NocturnalWorkflow] Calling executeNocturnalReflectionAsync for full pipeline (principleId=${principleId ?? 'auto-select'})`);
|
|
237
|
+
|
|
238
|
+
// #213: Wrap fire-and-forget Promise with .catch() to prevent
|
|
239
|
+
// unhandled promise rejections if anything throws outside the try-catch
|
|
240
|
+
// (e.g., during parameter construction or environment errors).
|
|
340
241
|
Promise.resolve().then(async () => {
|
|
341
242
|
try {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
-
}
|
|
243
|
+
const result = await executeNocturnalReflectionAsync(
|
|
244
|
+
this.workspaceDir,
|
|
245
|
+
this.stateDir,
|
|
246
|
+
{
|
|
247
|
+
runtimeAdapter: this.runtimeAdapter,
|
|
248
|
+
trinityConfig: {
|
|
249
|
+
useTrinity: true,
|
|
250
|
+
maxCandidates: 3,
|
|
251
|
+
useStubs: false,
|
|
252
|
+
runtimeAdapter: this.runtimeAdapter,
|
|
253
|
+
stateDir: this.stateDir,
|
|
254
|
+
},
|
|
255
|
+
// Pass painContext for Selector ranking bias
|
|
256
|
+
painContext,
|
|
257
|
+
// Skip Selector if principleId and snapshot are provided
|
|
258
|
+
...(principleId && snapshot ? {
|
|
259
|
+
principleIdOverride: principleId,
|
|
260
|
+
snapshotOverride: snapshot,
|
|
261
|
+
} : {}),
|
|
417
262
|
}
|
|
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
263
|
);
|
|
429
264
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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');
|
|
265
|
+
if (result.success) {
|
|
266
|
+
this.store.recordEvent(workflowId, 'nocturnal_completed', null, 'completed', 'Full pipeline completed via executeNocturnalReflectionAsync', {
|
|
267
|
+
artifactId: result.diagnostics?.persistedPath,
|
|
268
|
+
});
|
|
269
|
+
this.completedWorkflows.set(workflowId, Date.now());
|
|
469
270
|
} else {
|
|
470
|
-
const
|
|
471
|
-
|
|
271
|
+
const reason = result.noTargetSelected ? 'no_target_selected' : 'validation_failed';
|
|
272
|
+
this.store.recordEvent(workflowId, 'nocturnal_failed', null, 'terminal_error', reason, {
|
|
273
|
+
failures: result.validationFailures,
|
|
274
|
+
skipReason: result.skipReason,
|
|
275
|
+
});
|
|
472
276
|
}
|
|
473
277
|
} catch (err) {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
this.pendingTrinityFailures.set(workflowId, [{ stage: 'dreamer' as const, reason: errorMsg }]);
|
|
477
|
-
await this.notifyWaitResult(workflowId, 'error', errorMsg);
|
|
278
|
+
this.logger.error(`[PD:NocturnalWorkflow] executeNocturnalReflectionAsync threw: ${String(err)}`);
|
|
279
|
+
this.store.recordEvent(workflowId, 'nocturnal_failed', null, 'terminal_error', String(err), { workflowId });
|
|
478
280
|
}
|
|
281
|
+
}).catch((err) => {
|
|
282
|
+
// #213: Outer catch — catches errors from the fire-and-forget promise
|
|
283
|
+
// itself (e.g., if the async callback throws before entering try).
|
|
284
|
+
this.logger.error(`[PD:NocturnalWorkflow] Unhandled error in async pipeline for ${workflowId}: ${String(err)}`);
|
|
285
|
+
this.store.recordEvent(workflowId, 'nocturnal_failed', null, 'terminal_error', String(err), { workflowId });
|
|
479
286
|
});
|
|
480
287
|
|
|
481
|
-
// Return immediately with state='active' (NOC-07)
|
|
482
288
|
return {
|
|
483
289
|
workflowId,
|
|
484
290
|
childSessionKey: `nocturnal:internal:${workflowId}`,
|
|
485
|
-
|
|
486
|
-
state: 'active',
|
|
291
|
+
state: 'active' as const,
|
|
487
292
|
};
|
|
488
293
|
}
|
|
489
294
|
|
|
@@ -537,13 +342,13 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
537
342
|
this.markCompleted(workflowId);
|
|
538
343
|
}
|
|
539
344
|
|
|
540
|
-
//
|
|
541
|
-
// WorkflowManager Interface: notifyLifecycleEvent (NOC-01, D-10: no-op)
|
|
542
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
543
|
-
|
|
345
|
+
// eslint-disable-next-line @typescript-eslint/class-methods-use-this -- Reason: interface contract requires this method signature, implementation is a no-op per D-10
|
|
544
346
|
async notifyLifecycleEvent(
|
|
347
|
+
// eslint-disable-next-line no-unused-vars -- Reason: interface contract requires these params but NocturnalWorkflowManager is a no-op per D-10
|
|
545
348
|
_workflowId: string,
|
|
349
|
+
// eslint-disable-next-line no-unused-vars -- Reason: interface contract requires these params but NocturnalWorkflowManager is a no-op per D-10
|
|
546
350
|
_event: 'subagent_spawned' | 'subagent_ended',
|
|
351
|
+
// eslint-disable-next-line no-unused-vars -- Reason: interface contract requires these params but NocturnalWorkflowManager is a no-op per D-10
|
|
547
352
|
_data?: Record<string, unknown>
|
|
548
353
|
): Promise<void> {
|
|
549
354
|
// D-10: No-op. NocturnalWorkflowManager does not use the wait-on-run pattern.
|
|
@@ -574,11 +379,38 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
574
379
|
this.markCompleted(workflowId);
|
|
575
380
|
}
|
|
576
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Force-expire a specific workflow by ID.
|
|
384
|
+
* Used by evolution-worker when a sleep_reflection task times out (#214).
|
|
385
|
+
*/
|
|
386
|
+
expireWorkflow(workflowId: string, reason: string): void {
|
|
387
|
+
this.store.updateWorkflowState(workflowId, 'expired');
|
|
388
|
+
this.store.recordEvent(
|
|
389
|
+
workflowId,
|
|
390
|
+
'nocturnal_expired',
|
|
391
|
+
'active',
|
|
392
|
+
'expired',
|
|
393
|
+
reason,
|
|
394
|
+
{ reason }
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
577
398
|
// ─────────────────────────────────────────────────────────────────────────
|
|
578
399
|
// WorkflowManager Interface: sweepExpiredWorkflows (NOC-05)
|
|
579
400
|
// ─────────────────────────────────────────────────────────────────────────
|
|
580
401
|
|
|
581
|
-
async sweepExpiredWorkflows(
|
|
402
|
+
async sweepExpiredWorkflows(
|
|
403
|
+
maxAgeMs = 30 * 60 * 1000,
|
|
404
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Reason: subagentRuntime param is intentionally any for backward compatibility with callers
|
|
405
|
+
subagentRuntime?: any,
|
|
406
|
+
/* eslint-disable no-unused-vars -- Reason: type parameter names are documentation, not actual variables */
|
|
407
|
+
agentSession?: {
|
|
408
|
+
resolveStorePath: () => string;
|
|
409
|
+
loadSessionStore: (storePath: string, opts?: { skipCache?: boolean }) => Record<string, unknown>;
|
|
410
|
+
saveSessionStore: (storePath: string, store: Record<string, unknown>) => Promise<void>;
|
|
411
|
+
},
|
|
412
|
+
/* eslint-enable no-unused-vars */
|
|
413
|
+
): Promise<number> {
|
|
582
414
|
const expired = this.store.getExpiredWorkflows(maxAgeMs);
|
|
583
415
|
|
|
584
416
|
this.logger.info(`[PD:NocturnalWorkflow] sweepExpiredWorkflows: found ${expired.length} expired`);
|
|
@@ -591,6 +423,44 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
591
423
|
this.store.updateWorkflowState(workflow.workflow_id, 'expired');
|
|
592
424
|
this.store.recordEvent(workflow.workflow_id, 'nocturnal_expired', workflow.state, 'expired', 'TTL expired', { workflowId: workflow.workflow_id });
|
|
593
425
|
|
|
426
|
+
// #180 + #188: Cleanup subagent session with gateway-safe fallback
|
|
427
|
+
if (workflow.child_session_key) {
|
|
428
|
+
try {
|
|
429
|
+
if (subagentRuntime) {
|
|
430
|
+
await subagentRuntime.deleteSession({
|
|
431
|
+
sessionKey: workflow.child_session_key,
|
|
432
|
+
deleteTranscript: true,
|
|
433
|
+
});
|
|
434
|
+
this.logger.info(`[PD:NocturnalWorkflow] Cleaned up subagent session: ${workflow.child_session_key}`);
|
|
435
|
+
} else if (agentSession) {
|
|
436
|
+
// Heartbeat-safe fallback: directly manipulate session store
|
|
437
|
+
const storePath = agentSession.resolveStorePath();
|
|
438
|
+
const store = agentSession.loadSessionStore(storePath, { skipCache: true });
|
|
439
|
+
const normalizedKey = workflow.child_session_key.toLowerCase();
|
|
440
|
+
if (store[normalizedKey]) {
|
|
441
|
+
delete store[normalizedKey];
|
|
442
|
+
await agentSession.saveSessionStore(storePath, store);
|
|
443
|
+
this.logger.info(`[PD:NocturnalWorkflow] Cleaned up subagent session via agentSession fallback: ${workflow.child_session_key}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
} catch (sessionErr) {
|
|
447
|
+
const errMsg = String(sessionErr);
|
|
448
|
+
// #188: If gateway-scoped method fails, try agentSession fallback
|
|
449
|
+
if (errMsg.includes('gateway request') && agentSession) {
|
|
450
|
+
const storePath = agentSession.resolveStorePath();
|
|
451
|
+
const store = agentSession.loadSessionStore(storePath, { skipCache: true });
|
|
452
|
+
const normalizedKey = workflow.child_session_key.toLowerCase();
|
|
453
|
+
if (store[normalizedKey]) {
|
|
454
|
+
delete store[normalizedKey];
|
|
455
|
+
await agentSession.saveSessionStore(storePath, store);
|
|
456
|
+
this.logger.info(`[PD:NocturnalWorkflow] Cleaned up subagent session via agentSession fallback after gateway error: ${workflow.child_session_key}`);
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
this.logger.warn(`[PD:NocturnalWorkflow] Failed to cleanup session ${workflow.child_session_key}: ${errMsg}`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
594
464
|
// D-06: Clean partial artifact files by workflowId prefix
|
|
595
465
|
const samplesDir = resolveNocturnalDir(this.workspaceDir, 'SAMPLES');
|
|
596
466
|
if (fs.existsSync(samplesDir)) {
|
|
@@ -644,7 +514,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
644
514
|
}));
|
|
645
515
|
|
|
646
516
|
// NOC-16: Compute Trinity stage states from events
|
|
647
|
-
const trinityStageStates =
|
|
517
|
+
const trinityStageStates = NocturnalWorkflowManager.computeTrinityStageStates(allEvents);
|
|
648
518
|
|
|
649
519
|
return {
|
|
650
520
|
workflowId: workflow.workflow_id,
|
|
@@ -674,7 +544,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
674
544
|
// Helper Methods
|
|
675
545
|
// ─────────────────────────────────────────────────────────────────────────
|
|
676
546
|
|
|
677
|
-
private generateWorkflowId(): string {
|
|
547
|
+
private static generateWorkflowId(): string {
|
|
678
548
|
return `wf_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
|
|
679
549
|
}
|
|
680
550
|
|
|
@@ -700,19 +570,19 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
700
570
|
* Compute Trinity stage states from workflow events (NOC-16).
|
|
701
571
|
* Derives current/completed/failed state for each Trinity stage.
|
|
702
572
|
*/
|
|
703
|
-
private computeTrinityStageStates(events: WorkflowEventRow[]):
|
|
573
|
+
private static computeTrinityStageStates(events: WorkflowEventRow[]): {
|
|
704
574
|
stage: 'dreamer' | 'philosopher' | 'scribe';
|
|
705
575
|
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
706
576
|
reason?: string;
|
|
707
577
|
completedAt?: number;
|
|
708
|
-
}
|
|
709
|
-
const stages:
|
|
710
|
-
const result:
|
|
578
|
+
}[] {
|
|
579
|
+
const stages: ('dreamer' | 'philosopher' | 'scribe')[] = ['dreamer', 'philosopher', 'scribe'];
|
|
580
|
+
const result: {
|
|
711
581
|
stage: 'dreamer' | 'philosopher' | 'scribe';
|
|
712
582
|
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
713
583
|
reason?: string;
|
|
714
584
|
completedAt?: number;
|
|
715
|
-
}
|
|
585
|
+
}[] = [];
|
|
716
586
|
|
|
717
587
|
for (const stage of stages) {
|
|
718
588
|
const startEvent = events.find(e => e.event_type === `trinity_${stage}_start`);
|
|
@@ -725,7 +595,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
725
595
|
} else if (failedEvent) {
|
|
726
596
|
// Stage ran and failed
|
|
727
597
|
const payload = JSON.parse(failedEvent.payload_json || '{}') as Record<string, unknown>;
|
|
728
|
-
const failures = payload.failures as
|
|
598
|
+
const failures = payload.failures as { reason?: string }[] | undefined;
|
|
729
599
|
result.push({
|
|
730
600
|
stage,
|
|
731
601
|
status: 'failed',
|