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
|
@@ -20,19 +20,29 @@ import type {
|
|
|
20
20
|
PrincipleSuggestedRule,
|
|
21
21
|
} from './evolution-types.js';
|
|
22
22
|
import { isCompleteDetectorMetadata } from './evolution-types.js';
|
|
23
|
+
import { updateTrainingStore, createPrinciple } from './principle-tree-ledger.js';
|
|
24
|
+
import type { LedgerPrinciple } from './principle-tree-ledger.js';
|
|
23
25
|
|
|
26
|
+
|
|
24
27
|
export interface EvolutionReducer {
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
|
|
29
|
+
emit(_event: EvolutionLoopEvent): void;
|
|
30
|
+
|
|
31
|
+
emitSync(_event: EvolutionLoopEvent): void;
|
|
27
32
|
getEventLog(): EvolutionLoopEvent[];
|
|
28
33
|
getCandidatePrinciples(): Principle[];
|
|
29
34
|
getProbationPrinciples(): Principle[];
|
|
30
35
|
getActivePrinciples(): Principle[];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
|
|
37
|
+
getPrincipleById(_id: string): Principle | null;
|
|
38
|
+
|
|
39
|
+
promote(_principleId: string, _reason?: string): void;
|
|
40
|
+
|
|
41
|
+
deprecate(_principleId: string, _reason: string): void;
|
|
42
|
+
|
|
43
|
+
rollbackPrinciple(_principleId: string, _reason: string): void;
|
|
44
|
+
|
|
45
|
+
recordProbationFeedback(_principleId: string, _success: boolean): void;
|
|
36
46
|
/**
|
|
37
47
|
* Creates a new principle with generalized trigger/action from diagnostician.
|
|
38
48
|
* Called after diagnostician analysis to create principle directly.
|
|
@@ -47,6 +57,8 @@ export interface EvolutionReducer {
|
|
|
47
57
|
detectorMetadata?: PrincipleDetectorSpec;
|
|
48
58
|
/** Highly abstracted principle text — if absent, falls back to action-based title */
|
|
49
59
|
abstractedPrinciple?: string;
|
|
60
|
+
/** Associated core axiom (e.g., "T-01") */
|
|
61
|
+
coreAxiomId?: string;
|
|
50
62
|
// ── Principle-Tree metadata (Phase 1, optional) ──
|
|
51
63
|
priority?: 'P0' | 'P1' | 'P2';
|
|
52
64
|
scope?: 'general' | 'domain';
|
|
@@ -61,6 +73,7 @@ export interface EvolutionReducer {
|
|
|
61
73
|
lastPromotedAt: string | null;
|
|
62
74
|
};
|
|
63
75
|
}
|
|
76
|
+
|
|
64
77
|
|
|
65
78
|
const PROBATION_SUCCESS_THRESHOLD = 3;
|
|
66
79
|
const CIRCUIT_BREAKER_THRESHOLD = 3;
|
|
@@ -72,14 +85,16 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
72
85
|
private readonly blacklistPath: string;
|
|
73
86
|
private readonly principlesPath: string;
|
|
74
87
|
private readonly workspaceDir: string;
|
|
88
|
+
private readonly stateDir: string | undefined;
|
|
75
89
|
private readonly memoryEvents: EvolutionLoopEvent[] = [];
|
|
76
90
|
private readonly principles = new Map<string, Principle>();
|
|
77
91
|
private readonly failureStreak = new Map<string, number>();
|
|
78
92
|
private lastPromotedAt: string | null = null;
|
|
79
93
|
private isReplaying = false;
|
|
80
94
|
|
|
81
|
-
constructor(opts: { workspaceDir: string }) {
|
|
95
|
+
constructor(opts: { workspaceDir: string; stateDir?: string }) {
|
|
82
96
|
this.workspaceDir = opts.workspaceDir;
|
|
97
|
+
this.stateDir = opts.stateDir;
|
|
83
98
|
const resolver = new PathResolver({ workspaceDir: opts.workspaceDir });
|
|
84
99
|
this.streamPath = resolver.resolve('EVOLUTION_STREAM');
|
|
85
100
|
this.lockTargetPath = resolver.resolve('EVOLUTION_LOCK');
|
|
@@ -222,6 +237,7 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
222
237
|
evaluability?: PrincipleEvaluatorLevel;
|
|
223
238
|
detectorMetadata?: PrincipleDetectorSpec;
|
|
224
239
|
abstractedPrinciple?: string;
|
|
240
|
+
coreAxiomId?: string;
|
|
225
241
|
priority?: 'P0' | 'P1' | 'P2';
|
|
226
242
|
scope?: 'general' | 'domain';
|
|
227
243
|
domain?: string;
|
|
@@ -237,17 +253,18 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
237
253
|
return null;
|
|
238
254
|
}
|
|
239
255
|
|
|
240
|
-
// Evaluability defaults to '
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
|
|
244
|
-
|
|
256
|
+
// Evaluability defaults to 'weak_heuristic' — allows basic pattern-matching
|
|
257
|
+
// evaluation without requiring full detectorMetadata.
|
|
258
|
+
// #212: Only 'deterministic' requires complete detectorMetadata.
|
|
259
|
+
// 'weak_heuristic' can work with just the trigger pattern.
|
|
260
|
+
let evaluability: PrincipleEvaluatorLevel = params.evaluability ?? 'weak_heuristic';
|
|
261
|
+
if (evaluability === 'deterministic' && !isCompleteDetectorMetadata(params.detectorMetadata)) {
|
|
245
262
|
SystemLogger.log(
|
|
246
263
|
this.workspaceDir,
|
|
247
264
|
'EVALUABILITY_DOWNGRADED',
|
|
248
|
-
`Principle for painId "${params.painId}" requested evaluability="
|
|
265
|
+
`Principle for painId "${params.painId}" requested evaluability="deterministic" without detectorMetadata — downgrading to "weak_heuristic". Provide valid detectorMetadata for deterministic evaluation.`
|
|
249
266
|
);
|
|
250
|
-
evaluability = '
|
|
267
|
+
evaluability = 'weak_heuristic';
|
|
251
268
|
}
|
|
252
269
|
|
|
253
270
|
// Check if a principle already exists for this painId
|
|
@@ -265,16 +282,17 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
265
282
|
existingPrinciple.version += 1;
|
|
266
283
|
if (params.evaluability !== undefined) {
|
|
267
284
|
// Apply normalization (params.evaluability may be invalid without complete metadata)
|
|
285
|
+
// #212: Only 'deterministic' requires detectorMetadata; 'weak_heuristic' does not.
|
|
268
286
|
const normalizedEvaluability = (() => {
|
|
269
|
-
if (params.evaluability === '
|
|
270
|
-
|
|
287
|
+
if (params.evaluability === 'deterministic' && !isCompleteDetectorMetadata(params.detectorMetadata)) {
|
|
288
|
+
SystemLogger.log(
|
|
289
|
+
this.workspaceDir,
|
|
290
|
+
'EVALUABILITY_DOWNGRADED',
|
|
291
|
+
`Principle update for painId "${params.painId}" requested evaluability="deterministic" without detectorMetadata — downgrading to "weak_heuristic".`
|
|
292
|
+
);
|
|
293
|
+
return 'weak_heuristic';
|
|
271
294
|
}
|
|
272
|
-
|
|
273
|
-
this.workspaceDir,
|
|
274
|
-
'EVALUABILITY_DOWNGRADED',
|
|
275
|
-
`Principle update for painId "${params.painId}" requested evaluability="${params.evaluability}" without detectorMetadata — downgrading to "manual_only".`
|
|
276
|
-
);
|
|
277
|
-
return 'manual_only';
|
|
295
|
+
return params.evaluability;
|
|
278
296
|
})();
|
|
279
297
|
existingPrinciple.evaluability = normalizedEvaluability;
|
|
280
298
|
}
|
|
@@ -328,6 +346,7 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
328
346
|
? structuredClone(params.detectorMetadata)
|
|
329
347
|
: undefined,
|
|
330
348
|
abstractedPrinciple: params.abstractedPrinciple,
|
|
349
|
+
coreAxiomId: params.coreAxiomId,
|
|
331
350
|
|
|
332
351
|
// ── Principle-Tree metadata (Phase 1, optional) ──
|
|
333
352
|
priority: params.priority ?? 'P1',
|
|
@@ -362,6 +381,61 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
362
381
|
SystemLogger.log(this.workspaceDir, 'PRINCIPLE_SYNC_WARN', `Principle ${principleId} created in memory but failed to sync to PRINCIPLES.md — manual file check required`);
|
|
363
382
|
}
|
|
364
383
|
|
|
384
|
+
// #204: Write to training store so listEvaluablePrinciples() can find this principle
|
|
385
|
+
// Phase 11: Also write to tree.principles for Rule/Implementation layer support
|
|
386
|
+
if (this.stateDir) {
|
|
387
|
+
try {
|
|
388
|
+
// Determine initial internalization status based on evaluability:
|
|
389
|
+
// - manual_only: prompt_only (can only be evaluated manually)
|
|
390
|
+
// - deterministic/weak_heuristic: needs_training (auto-evaluable, ready for training)
|
|
391
|
+
const initialStatus = evaluability === 'manual_only' ? 'prompt_only' : 'needs_training';
|
|
392
|
+
|
|
393
|
+
updateTrainingStore(this.stateDir, (trainingStore) => {
|
|
394
|
+
trainingStore[principleId] = {
|
|
395
|
+
principleId,
|
|
396
|
+
evaluability,
|
|
397
|
+
internalizationStatus: initialStatus,
|
|
398
|
+
applicableOpportunityCount: 0,
|
|
399
|
+
observedViolationCount: 0,
|
|
400
|
+
complianceRate: 0,
|
|
401
|
+
violationTrend: 0,
|
|
402
|
+
generatedSampleCount: 0,
|
|
403
|
+
approvedSampleCount: 0,
|
|
404
|
+
includedTrainRunIds: [],
|
|
405
|
+
deployedCheckpointIds: [],
|
|
406
|
+
};
|
|
407
|
+
});
|
|
408
|
+
SystemLogger.log(this.workspaceDir, 'TRAINING_STORE_UPDATED', `Principle ${principleId} added to training store with evaluability=${evaluability}, internalizationStatus=${initialStatus}`);
|
|
409
|
+
|
|
410
|
+
// Phase 11: Also create principle in tree.principles for Rule/Implementation layer
|
|
411
|
+
const treePrinciple: LedgerPrinciple = {
|
|
412
|
+
id: principleId,
|
|
413
|
+
version: 1,
|
|
414
|
+
text: principle.text,
|
|
415
|
+
coreAxiomId: principle.coreAxiomId,
|
|
416
|
+
triggerPattern: params.triggerPattern,
|
|
417
|
+
action: params.action,
|
|
418
|
+
status: principle.status,
|
|
419
|
+
priority: principle.priority ?? 'P1',
|
|
420
|
+
scope: principle.scope ?? 'general',
|
|
421
|
+
domain: principle.domain,
|
|
422
|
+
evaluability,
|
|
423
|
+
valueScore: 0,
|
|
424
|
+
adherenceRate: 0,
|
|
425
|
+
painPreventedCount: 0,
|
|
426
|
+
derivedFromPainIds: [params.painId],
|
|
427
|
+
ruleIds: [],
|
|
428
|
+
conflictsWithPrincipleIds: [],
|
|
429
|
+
createdAt: now,
|
|
430
|
+
updatedAt: now,
|
|
431
|
+
};
|
|
432
|
+
createPrinciple(this.stateDir, treePrinciple);
|
|
433
|
+
SystemLogger.log(this.workspaceDir, 'TREE_PRINCIPLE_CREATED', `Principle ${principleId} added to tree.principles`);
|
|
434
|
+
} catch (err) {
|
|
435
|
+
SystemLogger.log(this.workspaceDir, 'TRAINING_STORE_UPDATE_FAILED', `Failed to update training store for ${principleId}: ${String(err)}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
365
439
|
SystemLogger.log(
|
|
366
440
|
this.workspaceDir,
|
|
367
441
|
'PRINCIPLE_CREATED',
|
|
@@ -400,7 +474,7 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
400
474
|
|
|
401
475
|
const header = `### ${principle.id}:`;
|
|
402
476
|
const existingIdx = content.indexOf(header);
|
|
403
|
-
const formatted =
|
|
477
|
+
const formatted = EvolutionReducerImpl.formatPrincipleForFile(principle, content);
|
|
404
478
|
|
|
405
479
|
if (existingIdx >= 0) {
|
|
406
480
|
const nextPrincipleRe = /\n### [A-Za-z0-9_-]+:/g;
|
|
@@ -423,7 +497,7 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
423
497
|
}
|
|
424
498
|
}
|
|
425
499
|
|
|
426
|
-
private detectPrincipleLanguage(content: string): 'zh' | 'en' {
|
|
500
|
+
private static detectPrincipleLanguage(content: string): 'zh' | 'en' {
|
|
427
501
|
if (!content) return 'zh';
|
|
428
502
|
const zhMarkers = ['原则', '触发', '必须', '禁止', '验证', '例外', '来源'];
|
|
429
503
|
const enMarkers = ['Trigger', 'Constraint', 'Must', 'Forbidden', 'Verification', 'Exceptions', 'Source'];
|
|
@@ -434,8 +508,8 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
434
508
|
return zhCount >= enCount ? 'zh' : 'en';
|
|
435
509
|
}
|
|
436
510
|
|
|
437
|
-
private formatPrincipleForFile(principle: Principle, existingContent: string): string {
|
|
438
|
-
const lang =
|
|
511
|
+
private static formatPrincipleForFile(principle: Principle, existingContent: string): string {
|
|
512
|
+
const lang = EvolutionReducerImpl.detectPrincipleLanguage(existingContent);
|
|
439
513
|
const title = principle.abstractedPrinciple
|
|
440
514
|
? (principle.abstractedPrinciple.length > 60 ? principle.abstractedPrinciple.slice(0, 57) + '...' : principle.abstractedPrinciple)
|
|
441
515
|
: (principle.text.length > 60 ? principle.text.slice(0, 57) + '...' : principle.text);
|
|
@@ -550,6 +624,9 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
550
624
|
if (data.abstractedPrinciple) {
|
|
551
625
|
existing.abstractedPrinciple = data.abstractedPrinciple;
|
|
552
626
|
}
|
|
627
|
+
if (data.coreAxiomId) {
|
|
628
|
+
existing.coreAxiomId = data.coreAxiomId;
|
|
629
|
+
}
|
|
553
630
|
return;
|
|
554
631
|
}
|
|
555
632
|
|
|
@@ -577,6 +654,7 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
577
654
|
: undefined,
|
|
578
655
|
// Restore abstractedPrinciple from event if present
|
|
579
656
|
abstractedPrinciple: data.abstractedPrinciple,
|
|
657
|
+
coreAxiomId: data.coreAxiomId,
|
|
580
658
|
};
|
|
581
659
|
this.principles.set(principle.id, principle);
|
|
582
660
|
}
|
|
@@ -612,7 +690,8 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
612
690
|
});
|
|
613
691
|
}
|
|
614
692
|
|
|
615
|
-
|
|
693
|
+
|
|
694
|
+
private onPainDetected(data: PainDetectedData, _eventTs: string): void {
|
|
616
695
|
const trigger = String(data.reason ?? data.source ?? 'unknown trigger');
|
|
617
696
|
|
|
618
697
|
// Defense in depth: protocol/system tokens must never become principles,
|
|
@@ -703,15 +782,15 @@ export class EvolutionReducerImpl implements EvolutionReducer {
|
|
|
703
782
|
fs.writeFileSync(this.blacklistPath, JSON.stringify(list, null, 2), 'utf8');
|
|
704
783
|
}
|
|
705
784
|
|
|
706
|
-
private loadBlacklist():
|
|
785
|
+
private loadBlacklist(): { painId?: string; pattern?: string; reason: string; rolledBackAt: string }[] {
|
|
707
786
|
if (!fs.existsSync(this.blacklistPath)) return [];
|
|
708
787
|
try {
|
|
709
|
-
return JSON.parse(fs.readFileSync(this.blacklistPath, 'utf8')) as
|
|
788
|
+
return JSON.parse(fs.readFileSync(this.blacklistPath, 'utf8')) as {
|
|
710
789
|
painId?: string;
|
|
711
790
|
pattern?: string;
|
|
712
791
|
reason: string;
|
|
713
792
|
rolledBackAt: string;
|
|
714
|
-
}
|
|
793
|
+
}[];
|
|
715
794
|
} catch (e) {
|
|
716
795
|
SystemLogger.log(this.workspaceDir, 'EVOLUTION_WARN', `failed to parse blacklist: ${String(e)}`);
|
|
717
796
|
return [];
|
|
@@ -10,11 +10,17 @@
|
|
|
10
10
|
|
|
11
11
|
// ===== 等级定义 =====
|
|
12
12
|
|
|
13
|
+
|
|
13
14
|
export enum EvolutionTier {
|
|
15
|
+
// eslint-disable-next-line no-unused-vars -- Reason: enum members are used via EvolutionTier.Seed syntax - bare names unused but required for enum structure
|
|
14
16
|
Seed = 1, // 起步:150行 + 3文件 + 子智能体(现代 AI 能力已足够强)
|
|
17
|
+
// eslint-disable-next-line no-unused-vars -- Reason: enum members are used via EvolutionTier.Sprout syntax - bare names unused but required for enum structure
|
|
15
18
|
Sprout = 2, // 成长:300行 + 5文件
|
|
19
|
+
// eslint-disable-next-line no-unused-vars -- Reason: enum members are used via EvolutionTier.Sapling syntax - bare names unused but required for enum structure
|
|
16
20
|
Sapling = 3, // 独当:500行 + 10文件 + 风险路径
|
|
21
|
+
// eslint-disable-next-line no-unused-vars -- Reason: enum members are used via EvolutionTier.Tree syntax - bare names unused but required for enum structure
|
|
17
22
|
Tree = 4, // 专家:1000行 + 20文件
|
|
23
|
+
// eslint-disable-next-line no-unused-vars -- Reason: enum members are used via EvolutionTier.Forest syntax - bare names unused but required for enum structure
|
|
18
24
|
Forest = 5 // 大师:完全自主
|
|
19
25
|
}
|
|
20
26
|
|
|
@@ -312,6 +318,8 @@ export interface Principle {
|
|
|
312
318
|
detectorMetadata?: PrincipleDetectorSpec;
|
|
313
319
|
/** Highly abstracted principle text suitable for PRINCIPLES.md */
|
|
314
320
|
abstractedPrinciple?: string;
|
|
321
|
+
/** Associated core axiom (e.g., "T-01") */
|
|
322
|
+
coreAxiomId?: string;
|
|
315
323
|
|
|
316
324
|
// ── Principle-Tree metadata (Phase 1, optional) ──
|
|
317
325
|
|
|
@@ -406,6 +414,8 @@ export interface CandidateCreatedData {
|
|
|
406
414
|
detectorMetadata?: PrincipleDetectorSpec;
|
|
407
415
|
/** Optional abstracted principle title (≤40 chars) — preserved on replay */
|
|
408
416
|
abstractedPrinciple?: string;
|
|
417
|
+
/** Associated core axiom (e.g., "T-01") */
|
|
418
|
+
coreAxiomId?: string;
|
|
409
419
|
}
|
|
410
420
|
|
|
411
421
|
export interface PrinciplePromotedData {
|
|
@@ -403,6 +403,7 @@ export function computeConfigFingerprint(config: Partial<TrainingHyperparameters
|
|
|
403
403
|
* If the file cannot be read, falls back to path+count hash (legacy behavior).
|
|
404
404
|
*/
|
|
405
405
|
export function computeDatasetFingerprint(exportPath: string, sampleCount: number): string {
|
|
406
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in try, catch has fallback return
|
|
406
407
|
let contentHash: string;
|
|
407
408
|
try {
|
|
408
409
|
const content = fs.readFileSync(exportPath, 'utf-8');
|
|
@@ -41,10 +41,10 @@ export interface WorkingMemorySnapshot {
|
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
// 活动问题
|
|
44
|
-
activeProblems:
|
|
44
|
+
activeProblems: {
|
|
45
45
|
problem: string;
|
|
46
46
|
approach?: string;
|
|
47
|
-
}
|
|
47
|
+
}[];
|
|
48
48
|
|
|
49
49
|
// 下一步行动
|
|
50
50
|
nextActions: string[];
|
|
@@ -56,7 +56,7 @@ export interface WorkingMemorySnapshot {
|
|
|
56
56
|
function logError(message: string, error?: unknown): void {
|
|
57
57
|
const timestamp = new Date().toISOString();
|
|
58
58
|
const errorStr = error instanceof Error ? error.message : String(error);
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
console.error(`[focus-history] ${timestamp} ERROR: ${message}${errorStr ? ' - ' + errorStr : ''}`);
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -78,7 +78,7 @@ export function getHistoryDir(focusPath: string): string {
|
|
|
78
78
|
* 支持整数和小数版本(如 v1, v1.1, v1.2)
|
|
79
79
|
*/
|
|
80
80
|
export function extractVersion(content: string): string {
|
|
81
|
-
const match =
|
|
81
|
+
const match = /\*\*版本\*\*:\s*v([\d.]+)/i.exec(content);
|
|
82
82
|
return match ? match[1] : '1';
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -86,8 +86,8 @@ export function extractVersion(content: string): string {
|
|
|
86
86
|
* 从 CURRENT_FOCUS.md 提取更新日期
|
|
87
87
|
*/
|
|
88
88
|
export function extractDate(content: string): string {
|
|
89
|
-
const
|
|
90
|
-
return
|
|
89
|
+
const [, dateStr] = /\*\*更新\*\*:\s*(\d{4}-\d{2}-\d{2})/.exec(content) ?? [];
|
|
90
|
+
return dateStr ?? new Date().toISOString().split('T')[0];
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/**
|
|
@@ -228,7 +228,7 @@ export function compressFocus(focusPath: string, newContent: string): {
|
|
|
228
228
|
const versionParts = oldVersion.split('.');
|
|
229
229
|
const majorVersion = parseInt(versionParts[0], 10) || 1;
|
|
230
230
|
const newVersion = `${majorVersion + 1}`;
|
|
231
|
-
const today = new Date().toISOString().split('T')
|
|
231
|
+
const [today] = new Date().toISOString().split('T');
|
|
232
232
|
|
|
233
233
|
// 更新版本号和日期
|
|
234
234
|
const updatedContent = newContent
|
|
@@ -265,7 +265,7 @@ export function compressFocus(focusPath: string, newContent: string): {
|
|
|
265
265
|
* @param content CURRENT_FOCUS.md 内容
|
|
266
266
|
* @param maxLines 最大行数
|
|
267
267
|
*/
|
|
268
|
-
export function extractSummary(content: string, maxLines
|
|
268
|
+
export function extractSummary(content: string, maxLines = 30): string {
|
|
269
269
|
const lines = content.split('\n');
|
|
270
270
|
const sections: { [key: string]: string[] } = {
|
|
271
271
|
header: [], // 标题和元数据
|
|
@@ -361,7 +361,7 @@ const MAX_NEXT_ACTIONS = 5;
|
|
|
361
361
|
* @returns 提取的工作记忆快照
|
|
362
362
|
*/
|
|
363
363
|
export function extractWorkingMemory(
|
|
364
|
-
messages:
|
|
364
|
+
messages: { role?: string; content?: string | unknown[] }[],
|
|
365
365
|
workspaceDir?: string
|
|
366
366
|
): WorkingMemorySnapshot {
|
|
367
367
|
const snapshot: WorkingMemorySnapshot = {
|
|
@@ -378,7 +378,7 @@ export function extractWorkingMemory(
|
|
|
378
378
|
|
|
379
379
|
for (const msg of recentMessages) {
|
|
380
380
|
let text = '';
|
|
381
|
-
const toolUses:
|
|
381
|
+
const toolUses: { name: string; input: Record<string, unknown> }[] = [];
|
|
382
382
|
|
|
383
383
|
if (typeof msg.content === 'string') {
|
|
384
384
|
text = msg.content;
|
|
@@ -428,6 +428,7 @@ export function extractWorkingMemory(
|
|
|
428
428
|
toolUse.name === 'write_file' || toolUse.name === 'create_file' ? 'created' : 'modified';
|
|
429
429
|
|
|
430
430
|
// 尝试从文本中提取描述
|
|
431
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
431
432
|
const description = extractDescription(text, filePath);
|
|
432
433
|
|
|
433
434
|
snapshot.artifacts.push({
|
|
@@ -442,16 +443,20 @@ export function extractWorkingMemory(
|
|
|
442
443
|
if (!text) continue;
|
|
443
444
|
|
|
444
445
|
// 从文本中提取文件操作(备用方式)
|
|
446
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
445
447
|
extractFileArtifacts(text, snapshot.artifacts, workspaceDir);
|
|
446
448
|
|
|
447
449
|
// 提取问题
|
|
450
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
448
451
|
extractProblems(text, snapshot.activeProblems);
|
|
449
452
|
|
|
450
453
|
// 提取下一步
|
|
454
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
451
455
|
extractNextActions(text, snapshot.nextActions);
|
|
452
456
|
}
|
|
453
457
|
|
|
454
458
|
// 去重和限制数量
|
|
459
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
455
460
|
snapshot.artifacts = deduplicateArtifacts(snapshot.artifacts).slice(-MAX_ARTIFACTS);
|
|
456
461
|
snapshot.activeProblems = snapshot.activeProblems.slice(-MAX_PROBLEMS);
|
|
457
462
|
snapshot.nextActions = snapshot.nextActions.slice(-MAX_NEXT_ACTIONS);
|
|
@@ -470,10 +475,11 @@ function extractFileArtifacts(
|
|
|
470
475
|
// 匹配 write_file, replace 工具调用
|
|
471
476
|
// 格式: file_path: "/path/to/file" 或 absolute_path: "/path/to/file"
|
|
472
477
|
const filePathRegex = /(?:file_path|absolute_path)["']?\s*[:=]\s*["']([^"']+\.(ts|js|json|md|yaml|yml|py|sh|mjs|cjs))["']/gi;
|
|
473
|
-
|
|
478
|
+
|
|
479
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned immediately in while loop condition before use
|
|
474
480
|
let match;
|
|
475
481
|
while ((match = filePathRegex.exec(text)) !== null) {
|
|
476
|
-
const filePath = match
|
|
482
|
+
const [, filePath] = match;
|
|
477
483
|
|
|
478
484
|
// 跳过 node_modules 和配置文件
|
|
479
485
|
if (filePath.includes('node_modules') ||
|
|
@@ -500,6 +506,7 @@ function extractFileArtifacts(
|
|
|
500
506
|
}
|
|
501
507
|
|
|
502
508
|
// 尝试提取描述(从附近的文本)
|
|
509
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
503
510
|
const description = extractDescription(text, filePath);
|
|
504
511
|
|
|
505
512
|
artifacts.push({
|
|
@@ -512,10 +519,10 @@ function extractFileArtifacts(
|
|
|
512
519
|
// 匹配更通用的文件路径格式(如代码块中的路径)
|
|
513
520
|
// 格式: `path/to/file.ts` 或 "path/to/file.ts"
|
|
514
521
|
// 只匹配明确的代码相关路径
|
|
515
|
-
const genericPathRegex = /[`"']([a-zA-Z0-9_
|
|
522
|
+
const genericPathRegex = /[`"']([a-zA-Z0-9_./]+\.(ts|js|mjs|cjs|py))[`"']/g;
|
|
516
523
|
|
|
517
524
|
while ((match = genericPathRegex.exec(text)) !== null) {
|
|
518
|
-
const filePath = match
|
|
525
|
+
const [, filePath] = match;
|
|
519
526
|
|
|
520
527
|
// 跳过太短、node_modules、配置文件
|
|
521
528
|
if (filePath.length < 10 ||
|
|
@@ -532,8 +539,9 @@ function extractFileArtifacts(
|
|
|
532
539
|
continue;
|
|
533
540
|
}
|
|
534
541
|
|
|
542
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
535
543
|
const description = extractDescription(text, filePath);
|
|
536
|
-
|
|
544
|
+
|
|
537
545
|
artifacts.push({
|
|
538
546
|
path: filePath,
|
|
539
547
|
action: 'modified',
|
|
@@ -575,10 +583,11 @@ function extractDescription(text: string, filePath: string): string {
|
|
|
575
583
|
*/
|
|
576
584
|
function extractProblems(
|
|
577
585
|
text: string,
|
|
578
|
-
problems:
|
|
586
|
+
problems: { problem: string; approach?: string }[]
|
|
579
587
|
): void {
|
|
580
588
|
// 问题模式(匹配问题描述)
|
|
581
589
|
const problemPattern = /(?:问题|problem|error|错误|失败|failed)[::]\s*([^\n]{5,100})/gi;
|
|
590
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned immediately in while loop condition before use
|
|
582
591
|
let match;
|
|
583
592
|
while ((match = problemPattern.exec(text)) !== null) {
|
|
584
593
|
const content = match[1].trim();
|
|
@@ -621,6 +630,7 @@ function extractNextActions(text: string, actions: string[]): void {
|
|
|
621
630
|
];
|
|
622
631
|
|
|
623
632
|
for (const pattern of patterns) {
|
|
633
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned immediately in while loop condition before use
|
|
624
634
|
let match;
|
|
625
635
|
while ((match = pattern.exec(text)) !== null) {
|
|
626
636
|
const action = match[1].trim();
|
|
@@ -671,7 +681,7 @@ export function parseWorkingMemorySection(content: string): WorkingMemorySnapsho
|
|
|
671
681
|
};
|
|
672
682
|
|
|
673
683
|
// 解析 last updated
|
|
674
|
-
const updatedMatch =
|
|
684
|
+
const updatedMatch = /Last updated:\s*([^\n]+)/i.exec(wmContent);
|
|
675
685
|
if (updatedMatch) {
|
|
676
686
|
snapshot.lastUpdated = updatedMatch[1].trim();
|
|
677
687
|
}
|
|
@@ -679,6 +689,7 @@ export function parseWorkingMemorySection(content: string): WorkingMemorySnapsho
|
|
|
679
689
|
// 解析文件记录表格
|
|
680
690
|
// | 文件路径 | 操作 | 描述 |
|
|
681
691
|
const tableRegex = /\|\s*`?([^`|\n]+)`?\s*\|\s*(created|modified|deleted)\s*\|\s*([^|\n]*)\s*\|/gi;
|
|
692
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- Reason: assigned immediately in while loop condition before use
|
|
682
693
|
let match;
|
|
683
694
|
while ((match = tableRegex.exec(wmContent)) !== null) {
|
|
684
695
|
snapshot.artifacts.push({
|
|
@@ -715,8 +726,9 @@ export function parseWorkingMemorySection(content: string): WorkingMemorySnapsho
|
|
|
715
726
|
*/
|
|
716
727
|
export function mergeWorkingMemory(content: string, snapshot: WorkingMemorySnapshot): string {
|
|
717
728
|
const wmIndex = content.indexOf(WORKING_MEMORY_SECTION);
|
|
718
|
-
|
|
729
|
+
|
|
719
730
|
// 生成 Working Memory 章节
|
|
731
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
720
732
|
const wmSection = generateWorkingMemorySection(snapshot);
|
|
721
733
|
|
|
722
734
|
if (wmIndex === -1) {
|
|
@@ -727,7 +739,7 @@ export function mergeWorkingMemory(content: string, snapshot: WorkingMemorySnaps
|
|
|
727
739
|
const beforeWm = content.substring(0, wmIndex);
|
|
728
740
|
// 查找下一个 ## 标题(如果有的话)
|
|
729
741
|
const afterWm = content.substring(wmIndex);
|
|
730
|
-
const nextSectionMatch = afterWm.substring(WORKING_MEMORY_SECTION.length)
|
|
742
|
+
const nextSectionMatch = /\n##\s/.exec(afterWm.substring(WORKING_MEMORY_SECTION.length));
|
|
731
743
|
|
|
732
744
|
if (nextSectionMatch && nextSectionMatch.index !== undefined) {
|
|
733
745
|
const nextSectionStart = WORKING_MEMORY_SECTION.length + nextSectionMatch.index;
|
|
@@ -973,7 +985,7 @@ export function extractMilestones(content: string): {
|
|
|
973
985
|
|
|
974
986
|
// 提取文件引用(从工作记忆)
|
|
975
987
|
if (inWorkingMemory) {
|
|
976
|
-
const fileMatch =
|
|
988
|
+
const fileMatch = /^\|\s*`?([^`|\n]+)`?\s*\|/.exec(trimmed);
|
|
977
989
|
if (fileMatch && !fileMatch[1].includes('文件路径')) {
|
|
978
990
|
fileArtifacts.push(fileMatch[1].trim());
|
|
979
991
|
}
|
|
@@ -998,7 +1010,7 @@ export function archiveMilestonesToDaily(
|
|
|
998
1010
|
return null;
|
|
999
1011
|
}
|
|
1000
1012
|
|
|
1001
|
-
const dateStr = new Date().toISOString().split('T')
|
|
1013
|
+
const [dateStr] = new Date().toISOString().split('T');
|
|
1002
1014
|
const memoryDir = path.join(workspaceDir, 'memory');
|
|
1003
1015
|
const dailyLogPath = path.join(memoryDir, `${dateStr}.md`);
|
|
1004
1016
|
const timestamp = new Date().toISOString();
|
|
@@ -1088,7 +1100,7 @@ export function cleanupStaleInfo(
|
|
|
1088
1100
|
}
|
|
1089
1101
|
|
|
1090
1102
|
// 提取文件路径
|
|
1091
|
-
const match =
|
|
1103
|
+
const match = /^\|\s*`?([^`|\n]+)`?\s*\|/.exec(trimmed);
|
|
1092
1104
|
if (match) {
|
|
1093
1105
|
const filePath = match[1].trim();
|
|
1094
1106
|
artifactCount++;
|
|
@@ -1211,7 +1223,7 @@ export function autoCompressFocus(
|
|
|
1211
1223
|
|
|
1212
1224
|
// 5. 递增版本号和日期
|
|
1213
1225
|
const newVersion = `${parseInt(version, 10) + 1}`;
|
|
1214
|
-
const today = new Date().toISOString().split('T')
|
|
1226
|
+
const [today] = new Date().toISOString().split('T');
|
|
1215
1227
|
newContent = newContent
|
|
1216
1228
|
.replace(/\*\*版本\*\*:\s*v[\d.]+/i, `**版本**: v${newVersion}`)
|
|
1217
1229
|
.replace(/\*\*更新\*\*:\s*\d{4}-\d{2}-\d{2}/, `**更新**: ${today}`);
|
|
@@ -1350,7 +1362,7 @@ export function recoverFromTemplate(
|
|
|
1350
1362
|
let template = fs.readFileSync(templatePath, 'utf-8');
|
|
1351
1363
|
|
|
1352
1364
|
// 替换日期占位符
|
|
1353
|
-
const today = new Date().toISOString().split('T')
|
|
1365
|
+
const [today] = new Date().toISOString().split('T');
|
|
1354
1366
|
template = template.replace(/{YYYY-MM-DD}/g, today);
|
|
1355
1367
|
|
|
1356
1368
|
// 备份损坏的文件(如果存在)
|
|
@@ -1393,7 +1405,9 @@ export function recoverFromTemplate(
|
|
|
1393
1405
|
export function safeReadCurrentFocus(
|
|
1394
1406
|
focusPath: string,
|
|
1395
1407
|
extensionRoot: string,
|
|
1408
|
+
/* eslint-disable no-unused-vars -- Reason: type-only callback parameter names in function type */
|
|
1396
1409
|
logger?: { warn?: (msg: string) => void; info?: (msg: string) => void }
|
|
1410
|
+
/* eslint-enable no-unused-vars */
|
|
1397
1411
|
): {
|
|
1398
1412
|
content: string;
|
|
1399
1413
|
recovered: boolean;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
import { HygieneStats,
|
|
4
|
-
import {
|
|
3
|
+
import type { HygieneStats, PersistenceAction } from '../../types/hygiene-types.js';
|
|
4
|
+
import { createEmptyHygieneStats } from '../../types/hygiene-types.js';
|
|
5
|
+
import type { PluginLogger } from '../../openclaw-sdk.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* HygieneTracker - Tracks agent behavior regarding workspace organization and persistence.
|
|
@@ -28,7 +29,7 @@ export class HygieneTracker {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
private loadStats(): HygieneStats {
|
|
31
|
-
const today = new Date().toISOString().split('T')
|
|
32
|
+
const [today] = new Date().toISOString().split('T');
|
|
32
33
|
if (fs.existsSync(this.statsFile)) {
|
|
33
34
|
try {
|
|
34
35
|
const content = fs.readFileSync(this.statsFile, 'utf-8');
|
|
@@ -45,7 +46,10 @@ export class HygieneTracker {
|
|
|
45
46
|
const backupPath = `${this.statsFile}.bak`;
|
|
46
47
|
fs.renameSync(this.statsFile, backupPath);
|
|
47
48
|
this.logger?.warn(`[PD] Corrupted hygiene stats backed up to ${backupPath}`);
|
|
48
|
-
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars -- Reason: catch parameter intentionally unused - error handling only
|
|
50
|
+
} catch (_renameErr) {
|
|
51
|
+
// Empty - corrupted stats backup is non-fatal
|
|
52
|
+
}
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
return createEmptyHygieneStats(today);
|
|
@@ -55,7 +59,7 @@ export class HygieneTracker {
|
|
|
55
59
|
let allStats: Record<string, HygieneStats> = {};
|
|
56
60
|
|
|
57
61
|
// Check if we need to rotate date (reset currentStats if date changed)
|
|
58
|
-
const today = new Date().toISOString().split('T')
|
|
62
|
+
const [today] = new Date().toISOString().split('T');
|
|
59
63
|
if (this.currentStats.date !== today) {
|
|
60
64
|
this.currentStats = createEmptyHygieneStats(today);
|
|
61
65
|
}
|
|
@@ -108,7 +112,7 @@ export class HygieneTracker {
|
|
|
108
112
|
|
|
109
113
|
getStats(): HygieneStats {
|
|
110
114
|
// Check for date change on every get
|
|
111
|
-
const today = new Date().toISOString().split('T')
|
|
115
|
+
const [today] = new Date().toISOString().split('T');
|
|
112
116
|
if (this.currentStats.date !== today) {
|
|
113
117
|
this.currentStats = createEmptyHygieneStats(today);
|
|
114
118
|
}
|
package/src/core/init.ts
CHANGED
|
@@ -34,7 +34,7 @@ function hasOutdatedCoreGuidance(file: string, content: string): boolean {
|
|
|
34
34
|
* Ensures that the workspace has the necessary template files for Principles Disciple.
|
|
35
35
|
* This function flattens 'core' templates to the root so OpenClaw can find them.
|
|
36
36
|
*/
|
|
37
|
-
export function ensureWorkspaceTemplates(api: OpenClawPluginApi, workspaceDir: string, language
|
|
37
|
+
export function ensureWorkspaceTemplates(api: OpenClawPluginApi, workspaceDir: string, language = 'en') {
|
|
38
38
|
try {
|
|
39
39
|
const __filename = fileURLToPath(import.meta.url);
|
|
40
40
|
const __dirname = path.dirname(__filename);
|
|
@@ -43,6 +43,7 @@ export function ensureWorkspaceTemplates(api: OpenClawPluginApi, workspaceDir: s
|
|
|
43
43
|
const commonTemplatesDir = path.resolve(__dirname, '..', '..', 'templates', 'workspace');
|
|
44
44
|
if (fs.existsSync(commonTemplatesDir)) {
|
|
45
45
|
api.logger.info(`[PD] Syncing workspace templates: ${workspaceDir}...`);
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: copyRecursiveSync is defined later in this file, called here for organizational reasons
|
|
46
47
|
copyRecursiveSync(commonTemplatesDir, workspaceDir, api);
|
|
47
48
|
}
|
|
48
49
|
|
|
@@ -84,6 +85,7 @@ export function ensureWorkspaceTemplates(api: OpenClawPluginApi, workspaceDir: s
|
|
|
84
85
|
if (!fs.existsSync(painDestDir)) {
|
|
85
86
|
fs.mkdirSync(painDestDir, { recursive: true });
|
|
86
87
|
}
|
|
88
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: copyRecursiveSync is defined later in this file, called here for organizational reasons
|
|
87
89
|
copyRecursiveSync(painTemplatesDir, painDestDir, api);
|
|
88
90
|
}
|
|
89
91
|
|
|
@@ -108,6 +110,7 @@ export function ensureWorkspaceTemplates(api: OpenClawPluginApi, workspaceDir: s
|
|
|
108
110
|
* Standard recursive copy that preserves directory structure.
|
|
109
111
|
* Special handling: maps 'okr' directory to 'memory/okr' for runtime compatibility.
|
|
110
112
|
*/
|
|
113
|
+
|
|
111
114
|
function copyRecursiveSync(srcDir: string, destDir: string, api: OpenClawPluginApi | { logger: PluginLogger }) {
|
|
112
115
|
const items = fs.readdirSync(srcDir);
|
|
113
116
|
|
|
@@ -140,7 +143,7 @@ function copyRecursiveSync(srcDir: string, destDir: string, api: OpenClawPluginA
|
|
|
140
143
|
/**
|
|
141
144
|
* Ensures that the state directory has the necessary files (like pain_dictionary.json).
|
|
142
145
|
*/
|
|
143
|
-
export function ensureStateTemplates(ctx: { logger: PluginLogger }, stateDir: string, language
|
|
146
|
+
export function ensureStateTemplates(ctx: { logger: PluginLogger }, stateDir: string, language = 'en') {
|
|
144
147
|
try {
|
|
145
148
|
const __filename = fileURLToPath(import.meta.url);
|
|
146
149
|
const __dirname = path.dirname(__filename);
|