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
|
@@ -36,22 +36,18 @@
|
|
|
36
36
|
* - diagnostics: scoring and filtering details
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
|
-
import {
|
|
39
|
+
import type {
|
|
40
40
|
NocturnalTrajectoryExtractor,
|
|
41
41
|
NocturnalSessionSummary,
|
|
42
42
|
} from '../core/nocturnal-trajectory-extractor.js';
|
|
43
43
|
import {
|
|
44
44
|
listEvaluablePrinciples,
|
|
45
|
-
loadStore,
|
|
46
45
|
type PrincipleTrainingState,
|
|
47
46
|
} from '../core/principle-training-state.js';
|
|
48
47
|
import {
|
|
49
48
|
checkWorkspaceIdle,
|
|
50
49
|
checkCooldown,
|
|
51
|
-
checkPreflight,
|
|
52
50
|
DEFAULT_IDLE_THRESHOLD_MS,
|
|
53
|
-
DEFAULT_PRINCIPLE_COOLDOWN_MS,
|
|
54
|
-
DEFAULT_GLOBAL_COOLDOWN_MS,
|
|
55
51
|
type IdleCheckResult,
|
|
56
52
|
} from './nocturnal-runtime.js';
|
|
57
53
|
import { detectViolation } from '../core/nocturnal-compliance.js';
|
|
@@ -290,6 +286,7 @@ export class NocturnalTargetSelector {
|
|
|
290
286
|
recentMaxPainScore: number;
|
|
291
287
|
};
|
|
292
288
|
|
|
289
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Constructor requires all parameters for proper initialization */
|
|
293
290
|
constructor(
|
|
294
291
|
workspaceDir: string,
|
|
295
292
|
stateDir: string,
|
|
@@ -425,7 +422,7 @@ export class NocturnalTargetSelector {
|
|
|
425
422
|
diagnostics.passedPrinciples = activeCandidates.map((p) => p.principleId);
|
|
426
423
|
|
|
427
424
|
// Select the top candidate
|
|
428
|
-
const selected = activeCandidates
|
|
425
|
+
const [selected] = activeCandidates;
|
|
429
426
|
diagnostics.selectedPrincipleScore = selected.score;
|
|
430
427
|
|
|
431
428
|
// Step 5: Find violating sessions for the selected principle
|
|
@@ -500,7 +497,7 @@ export class NocturnalTargetSelector {
|
|
|
500
497
|
|
|
501
498
|
// Sort by violation severity descending (most violating first)
|
|
502
499
|
violating.sort((a, b) => b.violationSeverity - a.violationSeverity);
|
|
503
|
-
const selectedSession = violating
|
|
500
|
+
const [selectedSession] = violating;
|
|
504
501
|
diagnostics.selectedSessionViolationDensity = selectedSession.violationSeverity;
|
|
505
502
|
|
|
506
503
|
return {
|
|
@@ -521,6 +518,7 @@ export class NocturnalTargetSelector {
|
|
|
521
518
|
*
|
|
522
519
|
* This is a convenience wrapper for the common case.
|
|
523
520
|
*/
|
|
521
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Function signature requires all parameters for type-safe selection */
|
|
524
522
|
export function selectNocturnalTarget(
|
|
525
523
|
workspaceDir: string,
|
|
526
524
|
stateDir: string,
|
|
@@ -5,7 +5,7 @@ import { listSessions } from '../core/session-tracker.js';
|
|
|
5
5
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
6
6
|
import { evaluatePhase3Inputs } from './phase3-input-filter.js';
|
|
7
7
|
import { TrajectoryRegistry } from '../core/trajectory.js';
|
|
8
|
-
import type { RuntimeTruth, AnalyticsTruth
|
|
8
|
+
import type { RuntimeTruth, AnalyticsTruth } from '../types/runtime-summary.js';
|
|
9
9
|
|
|
10
10
|
export type RuntimeDataQuality = 'authoritative' | 'partial';
|
|
11
11
|
export type RuntimeRewardPolicy =
|
|
@@ -418,6 +418,7 @@ export class RuntimeSummaryService {
|
|
|
418
418
|
* NOT a truth source for Phase 3 eligibility or decisions.
|
|
419
419
|
* Queue is the only authoritative execution truth source.
|
|
420
420
|
*/
|
|
421
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Directive summary requires queue, directive, timestamp, and warnings */
|
|
421
422
|
private static buildDirectiveSummary(
|
|
422
423
|
queue: QueueItem[] | null,
|
|
423
424
|
directive: DirectiveFile | null,
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import type { PluginLogger } from '../../openclaw-sdk.js';
|
|
2
2
|
import type {
|
|
3
|
-
WorkflowManager,
|
|
4
|
-
WorkflowHandle,
|
|
5
3
|
SubagentWorkflowSpec,
|
|
6
|
-
WorkflowMetadata,
|
|
7
|
-
WorkflowDebugSummary,
|
|
8
4
|
DeepReflectResult,
|
|
9
5
|
WorkflowResultContext,
|
|
10
6
|
WorkflowPersistContext,
|
|
7
|
+
WorkflowHandle,
|
|
11
8
|
} from './types.js';
|
|
12
9
|
|
|
13
10
|
// Re-export DeepReflectResult so index.ts can re-export it
|
|
14
11
|
export type { DeepReflectResult } from './types.js';
|
|
15
|
-
import { RuntimeDirectDriver
|
|
16
|
-
import { WorkflowStore } from './workflow-store.js';
|
|
12
|
+
import type { RuntimeDirectDriver } from './runtime-direct-driver.js';
|
|
17
13
|
import { isSubagentRuntimeAvailable } from '../../utils/subagent-probe.js';
|
|
18
14
|
import { buildCritiquePromptV2 } from '../../tools/critique-prompt.js';
|
|
15
|
+
import { WorkflowManagerBase } from './workflow-manager-base.js';
|
|
19
16
|
|
|
20
17
|
const WORKFLOW_SESSION_PREFIX = 'agent:main:subagent:workflow-';
|
|
21
18
|
|
|
@@ -26,23 +23,22 @@ export interface DeepReflectWorkflowOptions {
|
|
|
26
23
|
workspaceDir: string;
|
|
27
24
|
logger: PluginLogger;
|
|
28
25
|
subagent: RuntimeDirectDriver['subagent'];
|
|
26
|
+
/** Pass api.runtime.agent.session to enable heartbeat-safe cleanup (#188) */
|
|
27
|
+
agentSession?: RuntimeDirectDriver['agentSession'];
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
export class DeepReflectWorkflowManager
|
|
32
|
-
private readonly store: WorkflowStore;
|
|
33
|
-
private readonly driver: RuntimeDirectDriver;
|
|
34
|
-
private readonly logger: PluginLogger;
|
|
35
|
-
private readonly workspaceDir: string;
|
|
36
|
-
|
|
37
|
-
private activeWorkflows = new Map<string, NodeJS.Timeout>();
|
|
38
|
-
private completedWorkflows = new Map<string, number>();
|
|
39
|
-
private workflowSpecs = new Map<string, SubagentWorkflowSpec<unknown>>();
|
|
40
|
-
|
|
30
|
+
export class DeepReflectWorkflowManager extends WorkflowManagerBase {
|
|
41
31
|
constructor(opts: DeepReflectWorkflowOptions) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
32
|
+
super({
|
|
33
|
+
workspaceDir: opts.workspaceDir,
|
|
34
|
+
logger: opts.logger,
|
|
35
|
+
subagent: opts.subagent,
|
|
36
|
+
agentSession: opts.agentSession,
|
|
37
|
+
workflowType: 'deep-reflect',
|
|
38
|
+
sessionPrefix: WORKFLOW_SESSION_PREFIX,
|
|
39
|
+
defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
|
|
40
|
+
defaultTtlMs: DEFAULT_TTL_MS,
|
|
41
|
+
});
|
|
46
42
|
}
|
|
47
43
|
|
|
48
44
|
async startWorkflow<TResult>(
|
|
@@ -54,21 +50,6 @@ export class DeepReflectWorkflowManager implements WorkflowManager {
|
|
|
54
50
|
metadata?: Record<string, unknown>;
|
|
55
51
|
}
|
|
56
52
|
): Promise<WorkflowHandle> {
|
|
57
|
-
const workflowId = this.generateWorkflowId();
|
|
58
|
-
const childSessionKey = this.buildChildSessionKey(options.parentSessionId);
|
|
59
|
-
const now = Date.now();
|
|
60
|
-
|
|
61
|
-
const metadata: WorkflowMetadata = {
|
|
62
|
-
parentSessionId: options.parentSessionId,
|
|
63
|
-
workspaceDir: options.workspaceDir,
|
|
64
|
-
taskInput: options.taskInput,
|
|
65
|
-
startedAt: now,
|
|
66
|
-
workflowType: spec.workflowType,
|
|
67
|
-
...options.metadata,
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
this.logger.info(`[PD:DeepReflectWorkflow] Starting workflow: workflowId=${workflowId}, type=${spec.workflowType}`);
|
|
71
|
-
|
|
72
53
|
// Surface degrade: skip boot sessions
|
|
73
54
|
if (options.parentSessionId.startsWith('boot-')) {
|
|
74
55
|
this.logger.info(`[PD:DeepReflectWorkflow] Skipping workflow: boot session`);
|
|
@@ -85,311 +66,16 @@ export class DeepReflectWorkflowManager implements WorkflowManager {
|
|
|
85
66
|
throw new Error(`DeepReflectWorkflowManager only supports runtime_direct transport`);
|
|
86
67
|
}
|
|
87
68
|
|
|
88
|
-
|
|
89
|
-
const runResult = await this.driver.run(runParams);
|
|
90
|
-
|
|
91
|
-
this.store.createWorkflow({
|
|
92
|
-
workflow_id: workflowId,
|
|
93
|
-
workflow_type: spec.workflowType,
|
|
94
|
-
transport: spec.transport,
|
|
95
|
-
parent_session_id: options.parentSessionId,
|
|
96
|
-
child_session_key: childSessionKey,
|
|
97
|
-
run_id: runResult.runId,
|
|
98
|
-
state: 'active',
|
|
99
|
-
created_at: now,
|
|
100
|
-
updated_at: now,
|
|
101
|
-
metadata_json: JSON.stringify(metadata),
|
|
102
|
-
});
|
|
103
|
-
this.store.recordEvent(workflowId, 'spawned', null, 'active', 'subagent spawned', { runId: runResult.runId });
|
|
104
|
-
this.workflowSpecs.set(workflowId, spec as SubagentWorkflowSpec<unknown>);
|
|
105
|
-
|
|
106
|
-
this.scheduleWaitPoll(workflowId, spec.timeoutMs ?? DEFAULT_TIMEOUT_MS, runResult.runId);
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
workflowId,
|
|
110
|
-
childSessionKey,
|
|
111
|
-
runId: runResult.runId,
|
|
112
|
-
state: 'active',
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
private buildRunParams<TResult>(
|
|
117
|
-
spec: SubagentWorkflowSpec<TResult>,
|
|
118
|
-
options: {
|
|
119
|
-
parentSessionId: string;
|
|
120
|
-
workspaceDir?: string;
|
|
121
|
-
taskInput: unknown;
|
|
122
|
-
metadata?: Record<string, unknown>;
|
|
123
|
-
},
|
|
124
|
-
childSessionKey: string
|
|
125
|
-
): RunParams {
|
|
126
|
-
const message = spec.buildPrompt(options.taskInput, {
|
|
127
|
-
parentSessionId: options.parentSessionId,
|
|
128
|
-
workspaceDir: options.workspaceDir,
|
|
129
|
-
taskInput: options.taskInput,
|
|
130
|
-
startedAt: Date.now(),
|
|
131
|
-
workflowType: spec.workflowType,
|
|
132
|
-
...(options.metadata ?? {}),
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
return {
|
|
136
|
-
sessionKey: childSessionKey,
|
|
137
|
-
message,
|
|
138
|
-
lane: 'subagent',
|
|
139
|
-
deliver: false,
|
|
140
|
-
idempotencyKey: options.parentSessionId
|
|
141
|
-
? `${options.parentSessionId}:${Date.now()}`
|
|
142
|
-
: `pd:${childSessionKey}:${Date.now()}`,
|
|
143
|
-
expectsCompletionMessage: true,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
private scheduleWaitPoll(
|
|
148
|
-
workflowId: string,
|
|
149
|
-
timeoutMs: number,
|
|
150
|
-
runId: string
|
|
151
|
-
): void {
|
|
152
|
-
const effectiveTimeoutMs = timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
153
|
-
|
|
154
|
-
const timeout = setTimeout(async () => {
|
|
155
|
-
try {
|
|
156
|
-
const result = await this.driver.wait({ runId, timeoutMs: effectiveTimeoutMs });
|
|
157
|
-
await this.notifyWaitResult(workflowId, result.status, result.error);
|
|
158
|
-
} catch (error) {
|
|
159
|
-
this.logger.error(`[PD:DeepReflectWorkflow] Wait poll failed: ${String(error)}`);
|
|
160
|
-
await this.notifyWaitResult(workflowId, 'error', String(error));
|
|
161
|
-
}
|
|
162
|
-
}, 100);
|
|
163
|
-
|
|
164
|
-
this.activeWorkflows.set(workflowId, timeout);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
async notifyWaitResult(
|
|
168
|
-
workflowId: string,
|
|
169
|
-
status: 'ok' | 'error' | 'timeout',
|
|
170
|
-
error?: string
|
|
171
|
-
): Promise<void> {
|
|
172
|
-
const workflow = this.store.getWorkflow(workflowId);
|
|
173
|
-
if (!workflow) {
|
|
174
|
-
this.logger.warn(`[PD:DeepReflectWorkflow] notifyWaitResult: workflow not found: ${workflowId}`);
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (workflow.state === 'completed' || workflow.state === 'terminal_error' || workflow.state === 'expired') {
|
|
179
|
-
this.logger.info(`[PD:DeepReflectWorkflow] notifyWaitResult: ignoring terminal workflow: ${workflowId}, state=${workflow.state}`);
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
this.logger.info(`[PD:DeepReflectWorkflow] notifyWaitResult: workflowId=${workflowId}, status=${status}`);
|
|
184
|
-
|
|
185
|
-
const previousState = workflow.state;
|
|
186
|
-
this.store.updateWorkflowState(workflowId, 'wait_result');
|
|
187
|
-
this.store.recordEvent(workflowId, 'wait_result', previousState, 'wait_result', `wait completed: ${status}`, { error });
|
|
188
|
-
|
|
189
|
-
const spec = this.workflowSpecs.get(workflowId);
|
|
190
|
-
const shouldFinalize = spec ? spec.shouldFinalizeOnWaitStatus(status) : status === 'ok';
|
|
191
|
-
|
|
192
|
-
if (shouldFinalize) {
|
|
193
|
-
await this.finalizeOnce(workflowId);
|
|
194
|
-
} else {
|
|
195
|
-
this.store.updateWorkflowState(workflowId, 'terminal_error');
|
|
196
|
-
this.store.recordEvent(workflowId, 'finalize_skipped', 'wait_result', 'terminal_error', `wait status: ${status}`, { error });
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
async notifyLifecycleEvent(
|
|
201
|
-
workflowId: string,
|
|
202
|
-
event: 'subagent_spawned' | 'subagent_ended',
|
|
203
|
-
data?: { outcome?: 'ok' | 'error' | 'timeout' | 'killed' | 'reset' | 'deleted'; error?: string }
|
|
204
|
-
): Promise<void> {
|
|
205
|
-
this.logger.info(`[PD:DeepReflectWorkflow] notifyLifecycleEvent: workflowId=${workflowId}, event=${event}`);
|
|
206
|
-
|
|
207
|
-
if (event === 'subagent_ended' && data?.outcome) {
|
|
208
|
-
await this.notifyWaitResult(workflowId, data.outcome === 'ok' ? 'ok' : data.outcome === 'error' ? 'error' : 'timeout', data.error);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async finalizeOnce(workflowId: string): Promise<void> {
|
|
213
|
-
const workflow = this.store.getWorkflow(workflowId);
|
|
214
|
-
if (!workflow) {
|
|
215
|
-
this.logger.warn(`[PD:DeepReflectWorkflow] finalizeOnce: workflow not found: ${workflowId}`);
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const spec = this.workflowSpecs.get(workflowId);
|
|
220
|
-
if (!spec) {
|
|
221
|
-
throw new Error(`Workflow spec not registered for ${workflowId}`);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (this.isCompleted(workflowId)) {
|
|
225
|
-
this.logger.info(`[PD:DeepReflectWorkflow] finalizeOnce: already completed: ${workflowId}`);
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
this.logger.info(`[PD:DeepReflectWorkflow] Finalizing workflow: ${workflowId}`);
|
|
230
|
-
|
|
231
|
-
this.store.updateWorkflowState(workflowId, 'finalizing');
|
|
232
|
-
|
|
233
|
-
try {
|
|
234
|
-
const result = await this.driver.getResult({ sessionKey: workflow.child_session_key, limit: 20 });
|
|
235
|
-
|
|
236
|
-
const metadata = JSON.parse(workflow.metadata_json) as WorkflowMetadata;
|
|
237
|
-
const parsed = await spec.parseResult({
|
|
238
|
-
messages: result.messages,
|
|
239
|
-
assistantTexts: result.assistantTexts,
|
|
240
|
-
metadata,
|
|
241
|
-
waitStatus: 'ok',
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
if (!parsed) {
|
|
245
|
-
this.store.updateWorkflowState(workflowId, 'terminal_error');
|
|
246
|
-
this.store.recordEvent(workflowId, 'parse_failed', 'finalizing', 'terminal_error', 'spec.parseResult returned null', {});
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
await spec.persistResult({
|
|
251
|
-
result: parsed,
|
|
252
|
-
metadata,
|
|
253
|
-
workspaceDir: this.workspaceDir,
|
|
254
|
-
});
|
|
255
|
-
this.store.recordEvent(workflowId, 'persisted', 'finalizing', 'finalizing', 'result persisted', {});
|
|
256
|
-
|
|
257
|
-
if (spec.shouldDeleteSessionAfterFinalize && workflow.run_id) {
|
|
258
|
-
try {
|
|
259
|
-
await this.driver.cleanup({ sessionKey: workflow.child_session_key });
|
|
260
|
-
this.store.updateCleanupState(workflowId, 'completed');
|
|
261
|
-
} catch (cleanupError) {
|
|
262
|
-
this.logger.error(`[PD:DeepReflectWorkflow] cleanup failed after persistence: ${String(cleanupError)}`);
|
|
263
|
-
this.store.updateCleanupState(workflowId, 'failed');
|
|
264
|
-
this.store.updateWorkflowState(workflowId, 'cleanup_pending');
|
|
265
|
-
this.store.recordEvent(workflowId, 'cleanup_failed', 'finalizing', 'cleanup_pending', String(cleanupError), {});
|
|
266
|
-
this.markCompleted(workflowId);
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
this.store.updateWorkflowState(workflowId, 'completed');
|
|
272
|
-
this.store.recordEvent(workflowId, 'finalized', 'finalizing', 'completed', 'success', {});
|
|
273
|
-
this.markCompleted(workflowId);
|
|
274
|
-
|
|
275
|
-
} catch (error) {
|
|
276
|
-
this.logger.error(`[PD:DeepReflectWorkflow] finalizeOnce failed: ${String(error)}`);
|
|
277
|
-
this.store.updateWorkflowState(workflowId, 'terminal_error');
|
|
278
|
-
this.store.recordEvent(workflowId, 'finalize_error', 'finalizing', 'terminal_error', String(error), {});
|
|
279
|
-
throw error;
|
|
280
|
-
}
|
|
69
|
+
return super.startWorkflow(spec, options);
|
|
281
70
|
}
|
|
282
71
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
this.logger.info(`[PD:DeepReflectWorkflow] sweepExpiredWorkflows: found ${expired.length} expired`);
|
|
287
|
-
|
|
288
|
-
for (const workflow of expired) {
|
|
289
|
-
try {
|
|
290
|
-
this.logger.info(`[PD:DeepReflectWorkflow] Sweeping expired workflow: ${workflow.workflow_id}`);
|
|
291
|
-
|
|
292
|
-
await this.driver.cleanup({ sessionKey: workflow.child_session_key });
|
|
293
|
-
this.store.updateCleanupState(workflow.workflow_id, 'completed');
|
|
294
|
-
this.store.updateWorkflowState(workflow.workflow_id, 'expired');
|
|
295
|
-
this.store.recordEvent(workflow.workflow_id, 'swept', workflow.state, 'expired', 'TTL expired', {});
|
|
296
|
-
|
|
297
|
-
} catch (error) {
|
|
298
|
-
this.logger.error(`[PD:DeepReflectWorkflow] Sweep cleanup failed for ${workflow.workflow_id}: ${String(error)}`);
|
|
299
|
-
this.store.updateCleanupState(workflow.workflow_id, 'failed');
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Clean up memory Maps to prevent leaks
|
|
304
|
-
const cutoff = Date.now() - 60_000;
|
|
305
|
-
for (const [id, timestamp] of this.completedWorkflows) {
|
|
306
|
-
if (timestamp < cutoff) {
|
|
307
|
-
this.completedWorkflows.delete(id);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
for (const [id, timeout] of this.activeWorkflows) {
|
|
311
|
-
const wf = this.store.getWorkflow(id);
|
|
312
|
-
if (!wf || wf.state === 'expired' || wf.state === 'completed' || wf.state === 'terminal_error') {
|
|
313
|
-
clearTimeout(timeout);
|
|
314
|
-
this.activeWorkflows.delete(id);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
return expired.length;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
async getWorkflowDebugSummary(workflowId: string, eventLimit = 10): Promise<WorkflowDebugSummary | null> {
|
|
322
|
-
const workflow = this.store.getWorkflow(workflowId);
|
|
323
|
-
if (!workflow) return null;
|
|
324
|
-
|
|
325
|
-
const metadata = JSON.parse(workflow.metadata_json) as WorkflowMetadata;
|
|
326
|
-
const recentEvents = this.store
|
|
327
|
-
.getEvents(workflowId)
|
|
328
|
-
.slice(-eventLimit)
|
|
329
|
-
.map((event) => ({
|
|
330
|
-
eventType: event.event_type,
|
|
331
|
-
fromState: event.from_state,
|
|
332
|
-
toState: event.to_state,
|
|
333
|
-
reason: event.reason,
|
|
334
|
-
createdAt: event.created_at,
|
|
335
|
-
payload: JSON.parse(event.payload_json || '{}') as Record<string, unknown>,
|
|
336
|
-
}));
|
|
337
|
-
|
|
338
|
-
return {
|
|
339
|
-
workflowId: workflow.workflow_id,
|
|
340
|
-
workflowType: workflow.workflow_type,
|
|
341
|
-
transport: workflow.transport,
|
|
342
|
-
parentSessionId: workflow.parent_session_id,
|
|
343
|
-
childSessionKey: workflow.child_session_key,
|
|
344
|
-
runId: workflow.run_id,
|
|
345
|
-
state: workflow.state,
|
|
346
|
-
cleanupState: workflow.cleanup_state,
|
|
347
|
-
lastObservedAt: workflow.last_observed_at ?? null,
|
|
348
|
-
metadata,
|
|
349
|
-
recentEvents,
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
private generateWorkflowId(): string {
|
|
72
|
+
/* eslint-disable @typescript-eslint/class-methods-use-this -- Reason: Subclass overrides id generation pattern */
|
|
73
|
+
protected override generateWorkflowId(): string {
|
|
354
74
|
return `wf_dr_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
|
|
355
75
|
}
|
|
356
|
-
|
|
357
|
-
private buildChildSessionKey(parentSessionId: string): string {
|
|
358
|
-
const safeParentSessionId = parentSessionId
|
|
359
|
-
.replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
360
|
-
.substring(0, 64);
|
|
361
|
-
const timestamp = Date.now();
|
|
362
|
-
return `${WORKFLOW_SESSION_PREFIX}${safeParentSessionId}-${timestamp}`;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
private isCompleted(workflowId: string): boolean {
|
|
366
|
-
const timestamp = this.completedWorkflows.get(workflowId);
|
|
367
|
-
if (!timestamp) return false;
|
|
368
|
-
return Date.now() - timestamp < 60_000; // 1 minute dedup window
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
private markCompleted(workflowId: string): void {
|
|
372
|
-
const timeout = this.activeWorkflows.get(workflowId);
|
|
373
|
-
if (timeout) {
|
|
374
|
-
clearTimeout(timeout);
|
|
375
|
-
this.activeWorkflows.delete(workflowId);
|
|
376
|
-
}
|
|
377
|
-
this.completedWorkflows.set(workflowId, Date.now());
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
dispose(): void {
|
|
381
|
-
for (const [workflowId, timeout] of this.activeWorkflows) {
|
|
382
|
-
clearTimeout(timeout);
|
|
383
|
-
this.logger.info(`[PD:DeepReflectWorkflow] Disposed active workflow: ${workflowId}`);
|
|
384
|
-
}
|
|
385
|
-
this.activeWorkflows.clear();
|
|
386
|
-
this.completedWorkflows.clear();
|
|
387
|
-
this.workflowSpecs.clear();
|
|
388
|
-
this.store.dispose();
|
|
389
|
-
}
|
|
390
76
|
}
|
|
391
77
|
|
|
392
|
-
// ───
|
|
78
|
+
// ─── Interfaces ───────────────────────────────────────────────────────────────
|
|
393
79
|
|
|
394
80
|
export interface DeepReflectTaskInput {
|
|
395
81
|
context: string;
|
|
@@ -405,6 +91,8 @@ export interface DeepReflectBuildPromptContext {
|
|
|
405
91
|
workflowType: string;
|
|
406
92
|
}
|
|
407
93
|
|
|
94
|
+
// ─── Workflow Spec ─────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
408
96
|
export const deepReflectWorkflowSpec: SubagentWorkflowSpec<DeepReflectResult> = {
|
|
409
97
|
workflowType: 'deep-reflect',
|
|
410
98
|
transport: 'runtime_direct',
|
|
@@ -439,8 +127,9 @@ export const deepReflectWorkflowSpec: SubagentWorkflowSpec<DeepReflectResult> =
|
|
|
439
127
|
if (typeof lastMessage?.content === 'string') {
|
|
440
128
|
insights = lastMessage.content;
|
|
441
129
|
} else if (Array.isArray(lastMessage?.content)) {
|
|
442
|
-
insights = (lastMessage.content as
|
|
130
|
+
insights = (lastMessage.content as { type?: string; text?: string }[])
|
|
443
131
|
.filter((c) => c?.type === 'text' && typeof c.text === 'string')
|
|
132
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Reason: filter ensures c.text is a string
|
|
444
133
|
.map((c) => c.text!)
|
|
445
134
|
.join('\n');
|
|
446
135
|
}
|
|
@@ -507,7 +196,7 @@ ${result.insights}
|
|
|
507
196
|
fs.default.renameSync(tempPath, reflectionLogPath);
|
|
508
197
|
} catch (err) {
|
|
509
198
|
// Let the error propagate to finalizeOnce for proper event logging
|
|
510
|
-
throw new Error(`DeepReflectWorkflow persistResult failed: ${String(err)}
|
|
199
|
+
throw new Error(`DeepReflectWorkflow persistResult failed: ${String(err)}`, { cause: err });
|
|
511
200
|
}
|
|
512
201
|
},
|
|
513
202
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Timeout Calculator for Subagent Workflows
|
|
3
|
+
*
|
|
4
|
+
* This module is now a thin re-export layer for backwards compatibility.
|
|
5
|
+
* All functionality has been consolidated into utils/retry.ts.
|
|
6
|
+
*
|
|
7
|
+
* @module subagent-workflow/dynamic-timeout
|
|
8
|
+
* @deprecated Use `../../utils/retry.js` directly instead
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Re-export everything from the unified retry module
|
|
12
|
+
export {
|
|
13
|
+
// Constants
|
|
14
|
+
MIN_SAMPLES,
|
|
15
|
+
LOOKBACK_WINDOW,
|
|
16
|
+
SAFETY_MULTIPLIER,
|
|
17
|
+
MIN_TIMEOUT_MS,
|
|
18
|
+
MAX_TIMEOUT_MS,
|
|
19
|
+
MAX_TIMEOUT_RETRIES,
|
|
20
|
+
RETRY_BACKOFF_MULTIPLIER,
|
|
21
|
+
|
|
22
|
+
// Interfaces
|
|
23
|
+
DurationDataSource,
|
|
24
|
+
|
|
25
|
+
// Functions
|
|
26
|
+
percentile,
|
|
27
|
+
clampTimeout,
|
|
28
|
+
computeDynamicTimeout,
|
|
29
|
+
computeRetrySchedule,
|
|
30
|
+
} from '../../utils/retry.js';
|