principles-disciple 1.11.0 → 1.13.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 +250 -6
- 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 +21 -47
- 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 +110 -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 +319 -61
- 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 +733 -0
- package/src/core/principle-tree-migration.ts +195 -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 +156 -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 +130 -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 +503 -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 +169 -284
- 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 +77 -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 +77 -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 +89 -31
- package/ui/src/pages/EvolutionPage.tsx +1 -1
- package/ui/src/pages/OverviewPage.tsx +441 -81
- package/ui/src/pages/ThinkingModelsPage.tsx +287 -69
- 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,22 @@ 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
|
+
const apiAvailable = !!(this.runtimeAdapter as any).api;
|
|
180
|
+
const runtimeAvailable = !!(this.runtimeAdapter as any).api?.runtime;
|
|
181
|
+
const subagentAvailable = isSubagentRuntimeAvailable(subagent);
|
|
182
|
+
|
|
183
|
+
this.logger.info(`[PD:NocturnalWorkflow] Subagent availability check: api=${apiAvailable}, runtime=${runtimeAvailable}, subagent=${subagentAvailable}`);
|
|
184
|
+
|
|
185
|
+
if (!subagentAvailable) {
|
|
186
|
+
this.logger.warn(`[PD:NocturnalWorkflow] Subagent runtime unavailable (api=${apiAvailable}, runtime=${runtimeAvailable}), skipping workflow`);
|
|
187
|
+
throw new Error(`NocturnalWorkflowManager: subagent runtime unavailable`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const workflowId = NocturnalWorkflowManager.generateWorkflowId();
|
|
265
191
|
const now = Date.now();
|
|
266
192
|
|
|
267
193
|
const metadata: WorkflowMetadata = {
|
|
@@ -286,13 +212,16 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
286
212
|
state: 'active',
|
|
287
213
|
created_at: now,
|
|
288
214
|
updated_at: now,
|
|
215
|
+
duration_ms: null,
|
|
289
216
|
metadata_json: JSON.stringify(metadata),
|
|
290
217
|
});
|
|
291
218
|
this.store.recordEvent(workflowId, 'nocturnal_started', null, 'active', 'TrinityRuntimeAdapter invoked', { workflowType: 'nocturnal' });
|
|
292
219
|
|
|
293
220
|
// Extract snapshot and principleId from taskInput.metadata (NOC-07: Trinity async path)
|
|
294
|
-
const snapshot = options.metadata?.snapshot as
|
|
221
|
+
const snapshot = options.metadata?.snapshot as NocturnalSessionSnapshot | undefined;
|
|
295
222
|
const principleId = options.metadata?.principleId as string | undefined;
|
|
223
|
+
// Extract painContext for Selector ranking bias
|
|
224
|
+
const painContext = options.metadata?.painContext as RecentPainContext | undefined;
|
|
296
225
|
|
|
297
226
|
// Validate required metadata (prevent runtime crashes from undefined snapshot)
|
|
298
227
|
if (!snapshot?.sessionId) {
|
|
@@ -304,186 +233,77 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
304
233
|
state: 'terminal_error' as const,
|
|
305
234
|
};
|
|
306
235
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
//
|
|
318
|
-
|
|
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'
|
|
236
|
+
// #205: Always use executeNocturnalReflectionAsync for unified pipeline
|
|
237
|
+
// This ensures the full post-processing flow is always executed:
|
|
238
|
+
// Selector → Trinity → Arbiter → Executability → Persist → Register
|
|
239
|
+
//
|
|
240
|
+
// When principleId is provided, we pass it as principleIdOverride to skip Selector.
|
|
241
|
+
// When principleId is missing, Selector will choose a principle from training store.
|
|
242
|
+
this.logger.info(`[PD:NocturnalWorkflow] Calling executeNocturnalReflectionAsync for full pipeline (principleId=${principleId ?? 'auto-select'})`);
|
|
243
|
+
const pipelineStart = Date.now();
|
|
244
|
+
|
|
245
|
+
// #213: Wrap fire-and-forget Promise with .catch() to prevent
|
|
246
|
+
// unhandled promise rejections if anything throws outside the try-catch
|
|
247
|
+
// (e.g., during parameter construction or environment errors).
|
|
340
248
|
Promise.resolve().then(async () => {
|
|
341
249
|
try {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
}
|
|
250
|
+
this.logger.info(`[PD:NocturnalWorkflow] [${workflowId}] Pipeline step 1/4: Starting executeNocturnalReflectionAsync`);
|
|
251
|
+
|
|
252
|
+
const result = await executeNocturnalReflectionAsync(
|
|
253
|
+
this.workspaceDir,
|
|
254
|
+
this.stateDir,
|
|
255
|
+
{
|
|
256
|
+
runtimeAdapter: this.runtimeAdapter,
|
|
257
|
+
trinityConfig: {
|
|
258
|
+
useTrinity: true,
|
|
259
|
+
maxCandidates: 3,
|
|
260
|
+
useStubs: false,
|
|
261
|
+
runtimeAdapter: this.runtimeAdapter,
|
|
262
|
+
stateDir: this.stateDir,
|
|
263
|
+
},
|
|
264
|
+
// Pass painContext for Selector ranking bias
|
|
265
|
+
painContext,
|
|
266
|
+
// Skip Selector if principleId and snapshot are provided
|
|
267
|
+
...(principleId && snapshot ? {
|
|
268
|
+
principleIdOverride: principleId,
|
|
269
|
+
snapshotOverride: snapshot,
|
|
270
|
+
} : {}),
|
|
384
271
|
}
|
|
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
272
|
);
|
|
429
273
|
|
|
430
|
-
|
|
431
|
-
|
|
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
|
-
}
|
|
274
|
+
const pipelineDuration = Date.now() - pipelineStart;
|
|
275
|
+
this.logger.info(`[PD:NocturnalWorkflow] [${workflowId}] Pipeline completed in ${pipelineDuration}ms, success=${result.success}`);
|
|
441
276
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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');
|
|
277
|
+
if (result.success) {
|
|
278
|
+
this.logger.info(`[PD:NocturnalWorkflow] [${workflowId}] Pipeline step 4/4: Completed successfully, artifactId=${result.diagnostics?.persistedPath}`);
|
|
279
|
+
this.store.recordEvent(workflowId, 'nocturnal_completed', null, 'completed', 'Full pipeline completed via executeNocturnalReflectionAsync', {
|
|
280
|
+
artifactId: result.diagnostics?.persistedPath,
|
|
281
|
+
});
|
|
282
|
+
this.completedWorkflows.set(workflowId, Date.now());
|
|
469
283
|
} else {
|
|
470
|
-
const
|
|
471
|
-
|
|
284
|
+
const reason = result.noTargetSelected ? 'no_target_selected' : 'validation_failed';
|
|
285
|
+
this.logger.warn(`[PD:NocturnalWorkflow] [${workflowId}] Pipeline failed: reason=${reason}, noTargetSelected=${result.noTargetSelected}, skipReason=${result.skipReason ?? 'none'}, validationFailures=${result.validationFailures?.length ?? 0}`);
|
|
286
|
+
this.store.recordEvent(workflowId, 'nocturnal_failed', null, 'terminal_error', reason, {
|
|
287
|
+
failures: result.validationFailures,
|
|
288
|
+
skipReason: result.skipReason,
|
|
289
|
+
});
|
|
472
290
|
}
|
|
473
291
|
} catch (err) {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
this.
|
|
477
|
-
await this.notifyWaitResult(workflowId, 'error', errorMsg);
|
|
292
|
+
const errDuration = Date.now() - pipelineStart;
|
|
293
|
+
this.logger.error(`[PD:NocturnalWorkflow] [${workflowId}] executeNocturnalReflectionAsync threw after ${errDuration}ms: ${String(err)}`);
|
|
294
|
+
this.store.recordEvent(workflowId, 'nocturnal_failed', null, 'terminal_error', String(err), { workflowId });
|
|
478
295
|
}
|
|
296
|
+
}).catch((err) => {
|
|
297
|
+
// #213: Outer catch — catches errors from the fire-and-forget promise
|
|
298
|
+
// itself (e.g., if the async callback throws before entering try).
|
|
299
|
+
this.logger.error(`[PD:NocturnalWorkflow] Unhandled error in async pipeline for ${workflowId}: ${String(err)}`);
|
|
300
|
+
this.store.recordEvent(workflowId, 'nocturnal_failed', null, 'terminal_error', String(err), { workflowId });
|
|
479
301
|
});
|
|
480
302
|
|
|
481
|
-
// Return immediately with state='active' (NOC-07)
|
|
482
303
|
return {
|
|
483
304
|
workflowId,
|
|
484
305
|
childSessionKey: `nocturnal:internal:${workflowId}`,
|
|
485
|
-
|
|
486
|
-
state: 'active',
|
|
306
|
+
state: 'active' as const,
|
|
487
307
|
};
|
|
488
308
|
}
|
|
489
309
|
|
|
@@ -537,13 +357,13 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
537
357
|
this.markCompleted(workflowId);
|
|
538
358
|
}
|
|
539
359
|
|
|
540
|
-
//
|
|
541
|
-
// WorkflowManager Interface: notifyLifecycleEvent (NOC-01, D-10: no-op)
|
|
542
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
543
|
-
|
|
360
|
+
// 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
361
|
async notifyLifecycleEvent(
|
|
362
|
+
// eslint-disable-next-line no-unused-vars -- Reason: interface contract requires these params but NocturnalWorkflowManager is a no-op per D-10
|
|
545
363
|
_workflowId: string,
|
|
364
|
+
// eslint-disable-next-line no-unused-vars -- Reason: interface contract requires these params but NocturnalWorkflowManager is a no-op per D-10
|
|
546
365
|
_event: 'subagent_spawned' | 'subagent_ended',
|
|
366
|
+
// eslint-disable-next-line no-unused-vars -- Reason: interface contract requires these params but NocturnalWorkflowManager is a no-op per D-10
|
|
547
367
|
_data?: Record<string, unknown>
|
|
548
368
|
): Promise<void> {
|
|
549
369
|
// D-10: No-op. NocturnalWorkflowManager does not use the wait-on-run pattern.
|
|
@@ -574,11 +394,38 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
574
394
|
this.markCompleted(workflowId);
|
|
575
395
|
}
|
|
576
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Force-expire a specific workflow by ID.
|
|
399
|
+
* Used by evolution-worker when a sleep_reflection task times out (#214).
|
|
400
|
+
*/
|
|
401
|
+
expireWorkflow(workflowId: string, reason: string): void {
|
|
402
|
+
this.store.updateWorkflowState(workflowId, 'expired');
|
|
403
|
+
this.store.recordEvent(
|
|
404
|
+
workflowId,
|
|
405
|
+
'nocturnal_expired',
|
|
406
|
+
'active',
|
|
407
|
+
'expired',
|
|
408
|
+
reason,
|
|
409
|
+
{ reason }
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
577
413
|
// ─────────────────────────────────────────────────────────────────────────
|
|
578
414
|
// WorkflowManager Interface: sweepExpiredWorkflows (NOC-05)
|
|
579
415
|
// ─────────────────────────────────────────────────────────────────────────
|
|
580
416
|
|
|
581
|
-
async sweepExpiredWorkflows(
|
|
417
|
+
async sweepExpiredWorkflows(
|
|
418
|
+
maxAgeMs = 30 * 60 * 1000,
|
|
419
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Reason: subagentRuntime param is intentionally any for backward compatibility with callers
|
|
420
|
+
subagentRuntime?: any,
|
|
421
|
+
/* eslint-disable no-unused-vars -- Reason: type parameter names are documentation, not actual variables */
|
|
422
|
+
agentSession?: {
|
|
423
|
+
resolveStorePath: () => string;
|
|
424
|
+
loadSessionStore: (storePath: string, opts?: { skipCache?: boolean }) => Record<string, unknown>;
|
|
425
|
+
saveSessionStore: (storePath: string, store: Record<string, unknown>) => Promise<void>;
|
|
426
|
+
},
|
|
427
|
+
/* eslint-enable no-unused-vars */
|
|
428
|
+
): Promise<number> {
|
|
582
429
|
const expired = this.store.getExpiredWorkflows(maxAgeMs);
|
|
583
430
|
|
|
584
431
|
this.logger.info(`[PD:NocturnalWorkflow] sweepExpiredWorkflows: found ${expired.length} expired`);
|
|
@@ -591,6 +438,44 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
591
438
|
this.store.updateWorkflowState(workflow.workflow_id, 'expired');
|
|
592
439
|
this.store.recordEvent(workflow.workflow_id, 'nocturnal_expired', workflow.state, 'expired', 'TTL expired', { workflowId: workflow.workflow_id });
|
|
593
440
|
|
|
441
|
+
// #180 + #188: Cleanup subagent session with gateway-safe fallback
|
|
442
|
+
if (workflow.child_session_key) {
|
|
443
|
+
try {
|
|
444
|
+
if (subagentRuntime) {
|
|
445
|
+
await subagentRuntime.deleteSession({
|
|
446
|
+
sessionKey: workflow.child_session_key,
|
|
447
|
+
deleteTranscript: true,
|
|
448
|
+
});
|
|
449
|
+
this.logger.info(`[PD:NocturnalWorkflow] Cleaned up subagent session: ${workflow.child_session_key}`);
|
|
450
|
+
} else if (agentSession) {
|
|
451
|
+
// Heartbeat-safe fallback: directly manipulate session store
|
|
452
|
+
const storePath = agentSession.resolveStorePath();
|
|
453
|
+
const store = agentSession.loadSessionStore(storePath, { skipCache: true });
|
|
454
|
+
const normalizedKey = workflow.child_session_key.toLowerCase();
|
|
455
|
+
if (store[normalizedKey]) {
|
|
456
|
+
delete store[normalizedKey];
|
|
457
|
+
await agentSession.saveSessionStore(storePath, store);
|
|
458
|
+
this.logger.info(`[PD:NocturnalWorkflow] Cleaned up subagent session via agentSession fallback: ${workflow.child_session_key}`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
} catch (sessionErr) {
|
|
462
|
+
const errMsg = String(sessionErr);
|
|
463
|
+
// #188: If gateway-scoped method fails, try agentSession fallback
|
|
464
|
+
if (errMsg.includes('gateway request') && agentSession) {
|
|
465
|
+
const storePath = agentSession.resolveStorePath();
|
|
466
|
+
const store = agentSession.loadSessionStore(storePath, { skipCache: true });
|
|
467
|
+
const normalizedKey = workflow.child_session_key.toLowerCase();
|
|
468
|
+
if (store[normalizedKey]) {
|
|
469
|
+
delete store[normalizedKey];
|
|
470
|
+
await agentSession.saveSessionStore(storePath, store);
|
|
471
|
+
this.logger.info(`[PD:NocturnalWorkflow] Cleaned up subagent session via agentSession fallback after gateway error: ${workflow.child_session_key}`);
|
|
472
|
+
}
|
|
473
|
+
} else {
|
|
474
|
+
this.logger.warn(`[PD:NocturnalWorkflow] Failed to cleanup session ${workflow.child_session_key}: ${errMsg}`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
594
479
|
// D-06: Clean partial artifact files by workflowId prefix
|
|
595
480
|
const samplesDir = resolveNocturnalDir(this.workspaceDir, 'SAMPLES');
|
|
596
481
|
if (fs.existsSync(samplesDir)) {
|
|
@@ -644,7 +529,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
644
529
|
}));
|
|
645
530
|
|
|
646
531
|
// NOC-16: Compute Trinity stage states from events
|
|
647
|
-
const trinityStageStates =
|
|
532
|
+
const trinityStageStates = NocturnalWorkflowManager.computeTrinityStageStates(allEvents);
|
|
648
533
|
|
|
649
534
|
return {
|
|
650
535
|
workflowId: workflow.workflow_id,
|
|
@@ -674,7 +559,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
674
559
|
// Helper Methods
|
|
675
560
|
// ─────────────────────────────────────────────────────────────────────────
|
|
676
561
|
|
|
677
|
-
private generateWorkflowId(): string {
|
|
562
|
+
private static generateWorkflowId(): string {
|
|
678
563
|
return `wf_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
|
|
679
564
|
}
|
|
680
565
|
|
|
@@ -700,19 +585,19 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
700
585
|
* Compute Trinity stage states from workflow events (NOC-16).
|
|
701
586
|
* Derives current/completed/failed state for each Trinity stage.
|
|
702
587
|
*/
|
|
703
|
-
private computeTrinityStageStates(events: WorkflowEventRow[]):
|
|
588
|
+
private static computeTrinityStageStates(events: WorkflowEventRow[]): {
|
|
704
589
|
stage: 'dreamer' | 'philosopher' | 'scribe';
|
|
705
590
|
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
706
591
|
reason?: string;
|
|
707
592
|
completedAt?: number;
|
|
708
|
-
}
|
|
709
|
-
const stages:
|
|
710
|
-
const result:
|
|
593
|
+
}[] {
|
|
594
|
+
const stages: ('dreamer' | 'philosopher' | 'scribe')[] = ['dreamer', 'philosopher', 'scribe'];
|
|
595
|
+
const result: {
|
|
711
596
|
stage: 'dreamer' | 'philosopher' | 'scribe';
|
|
712
597
|
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
713
598
|
reason?: string;
|
|
714
599
|
completedAt?: number;
|
|
715
|
-
}
|
|
600
|
+
}[] = [];
|
|
716
601
|
|
|
717
602
|
for (const stage of stages) {
|
|
718
603
|
const startEvent = events.find(e => e.event_type === `trinity_${stage}_start`);
|
|
@@ -725,7 +610,7 @@ export class NocturnalWorkflowManager implements WorkflowManager {
|
|
|
725
610
|
} else if (failedEvent) {
|
|
726
611
|
// Stage ran and failed
|
|
727
612
|
const payload = JSON.parse(failedEvent.payload_json || '{}') as Record<string, unknown>;
|
|
728
|
-
const failures = payload.failures as
|
|
613
|
+
const failures = payload.failures as { reason?: string }[] | undefined;
|
|
729
614
|
result.push({
|
|
730
615
|
stage,
|
|
731
616
|
status: 'failed',
|