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
package/src/hooks/bash-risk.ts
CHANGED
|
@@ -38,11 +38,12 @@ export type BashRiskLevel = 'safe' | 'dangerous' | 'normal';
|
|
|
38
38
|
* @param logger - Optional logger for warnings about invalid patterns
|
|
39
39
|
* @returns The risk level: 'safe', 'dangerous', or 'normal'
|
|
40
40
|
*/
|
|
41
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Bash risk analysis requires command + pattern lists - refactoring to options object would be breaking API change */
|
|
41
42
|
export function analyzeBashCommand(
|
|
42
43
|
command: string,
|
|
43
44
|
safePatterns: string[],
|
|
44
45
|
dangerousPatterns: string[],
|
|
45
|
-
logger?: { warn?: (
|
|
46
|
+
logger?: { warn?: (/* eslint-disable-line no-unused-vars -- Reason: callback parameter, unused by design */_message: string) => void }
|
|
46
47
|
): BashRiskLevel {
|
|
47
48
|
let normalizedCmd = command.trim().toLowerCase();
|
|
48
49
|
|
|
@@ -64,6 +65,7 @@ export function analyzeBashCommand(
|
|
|
64
65
|
// - Zero-width joiner (U+200D)
|
|
65
66
|
// - Word joiner (U+2060)
|
|
66
67
|
// - Zero-width invisible separator (U+FEFF)
|
|
68
|
+
// eslint-disable-next-line no-misleading-character-class -- Reason: zero-width character class ranges are intentional - documented in comment above
|
|
67
69
|
const ZERO_WIDTH_CHARS = /[\u200B\u200C\u200D\u2060\uFEFF]/g;
|
|
68
70
|
if (ZERO_WIDTH_CHARS.test(command)) {
|
|
69
71
|
logger?.warn?.(`[PD_GATE] Bash command contains zero-width characters — blocking as dangerous`);
|
|
@@ -44,7 +44,7 @@ export function normalizeLine(line: string): string {
|
|
|
44
44
|
* @param threshold - Match threshold (0-1)
|
|
45
45
|
* @returns Match index or -1 if not found
|
|
46
46
|
*/
|
|
47
|
-
export function findFuzzyMatch(lines: string[], oldLines: string[], threshold
|
|
47
|
+
export function findFuzzyMatch(lines: string[], oldLines: string[], threshold = 0.8): number {
|
|
48
48
|
if (oldLines.length === 0) return -1; // P2 fix: empty array boundary check
|
|
49
49
|
|
|
50
50
|
const normalizedLines = lines.map(normalizeLine);
|
|
@@ -75,7 +75,7 @@ export function findFuzzyMatch(lines: string[], oldLines: string[], threshold: n
|
|
|
75
75
|
* @param threshold - Match threshold (0-1)
|
|
76
76
|
* @returns Object with found status and corrected text if found
|
|
77
77
|
*/
|
|
78
|
-
export function tryFuzzyMatch(currentContent: string, oldText: string, threshold
|
|
78
|
+
export function tryFuzzyMatch(currentContent: string, oldText: string, threshold = 0.8): { found: boolean; correctedText?: string } {
|
|
79
79
|
const lines = currentContent.split('\n');
|
|
80
80
|
const oldLines = oldText.split('\n');
|
|
81
81
|
|
|
@@ -126,9 +126,11 @@ This is enforced by P-03 (精确匹配前验证原则).`;
|
|
|
126
126
|
* Handle edit tool verification before allowing operation
|
|
127
127
|
* This enforces P-03 at the tool layer
|
|
128
128
|
*/
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/max-params -- Reason: Hook handler signature requires event + context + config - refactoring would break plugin interface
|
|
129
130
|
export function handleEditVerification(
|
|
130
131
|
event: PluginHookBeforeToolCallEvent,
|
|
131
132
|
wctx: WorkspaceContext,
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Reason: logger is typed as any by plugin framework - type not available
|
|
132
134
|
ctx: { logger?: any; sessionId?: string },
|
|
133
135
|
config: EditVerificationConfig = {}
|
|
134
136
|
): PluginHookBeforeToolCallResult | void {
|
|
@@ -153,10 +155,11 @@ export function handleEditVerification(
|
|
|
153
155
|
}
|
|
154
156
|
|
|
155
157
|
// 2. Resolve and read file
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in try, catch has early return
|
|
156
159
|
let absolutePath: string;
|
|
157
160
|
try {
|
|
158
161
|
absolutePath = wctx.resolve(filePath);
|
|
159
|
-
} catch (
|
|
162
|
+
} catch (_error) { // eslint-disable-line @typescript-eslint/no-unused-vars, no-unused-vars -- Reason: intentionally unused - let it fail naturally on path resolution error
|
|
160
163
|
// Path resolution error, let it fail naturally
|
|
161
164
|
return;
|
|
162
165
|
}
|
|
@@ -200,7 +203,7 @@ export function handleEditVerification(
|
|
|
200
203
|
} catch (statError) {
|
|
201
204
|
// File stat error (e.g., permission denied)
|
|
202
205
|
const errStr = statError instanceof Error ? statError.message : String(statError);
|
|
203
|
-
const errCode = (statError as
|
|
206
|
+
const errCode = (statError as { code?: string }).code;
|
|
204
207
|
|
|
205
208
|
if (errCode === 'EACCES' || errCode === 'EPERM') {
|
|
206
209
|
logger?.error?.(`[PD_GATE:EDIT_VERIFY] Permission denied accessing file: ${path.basename(filePath)} (${errStr})`);
|
|
@@ -219,12 +222,13 @@ export function handleEditVerification(
|
|
|
219
222
|
}
|
|
220
223
|
|
|
221
224
|
// 3. Read current file content with improved error handling
|
|
225
|
+
// eslint-disable-next-line @typescript-eslint/init-declarations -- assigned in try, all catch paths return early
|
|
222
226
|
let currentContent: string;
|
|
223
227
|
try {
|
|
224
228
|
currentContent = fs.readFileSync(absolutePath, 'utf-8');
|
|
225
229
|
} catch (readError) {
|
|
226
230
|
const errStr = readError instanceof Error ? readError.message : String(readError);
|
|
227
|
-
const errCode = (readError as
|
|
231
|
+
const errCode = (readError as { code?: string }).code;
|
|
228
232
|
|
|
229
233
|
if (errCode === 'EACCES' || errCode === 'EPERM') {
|
|
230
234
|
logger?.error?.(`[PD_GATE:EDIT_VERIFY] Permission denied reading file: ${path.basename(filePath)} (${errStr})`);
|
|
@@ -48,7 +48,9 @@ export interface BlockContext {
|
|
|
48
48
|
export function recordGateBlockAndReturn(
|
|
49
49
|
wctx: WorkspaceContext,
|
|
50
50
|
blockCtx: BlockContext,
|
|
51
|
-
|
|
51
|
+
/* eslint-disable no-unused-vars -- Reason: type-only callback parameters in logger type */
|
|
52
|
+
logger: { warn?: (_message: string) => void; error?: (_message: string) => void; info?: (_message: string) => void }
|
|
53
|
+
/* eslint-enable no-unused-vars */
|
|
52
54
|
): PluginHookBeforeToolCallResult {
|
|
53
55
|
const { filePath, reason, toolName, sessionId, blockSource } = blockCtx;
|
|
54
56
|
|
|
@@ -91,6 +93,7 @@ export function recordGateBlockAndReturn(
|
|
|
91
93
|
wctx.trajectory?.recordGateBlock?.(trajectoryPayload);
|
|
92
94
|
} catch (error: unknown) {
|
|
93
95
|
logWarn(`[PD_GATE] Failed to record trajectory gate block: ${String(error)}`);
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: function is defined later but called in this helper for retry logic
|
|
94
97
|
scheduleTrajectoryGateBlockRetry(wctx, trajectoryPayload, 1, logWarn, logError);
|
|
95
98
|
}
|
|
96
99
|
|
|
@@ -130,6 +133,7 @@ This is a mandatory security gate. The operation was blocked because the modific
|
|
|
130
133
|
* Uses exponential backoff with max retries.
|
|
131
134
|
* Failures are logged but do not affect the runtime block decision.
|
|
132
135
|
*/
|
|
136
|
+
/* eslint-disable @typescript-eslint/max-params, no-unused-vars -- Reason: Function requires all params for retry scheduling */
|
|
133
137
|
function scheduleTrajectoryGateBlockRetry(
|
|
134
138
|
wctx: WorkspaceContext,
|
|
135
139
|
payload: {
|
package/src/hooks/gate.ts
CHANGED
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
* 2. Thinking OS Checkpoint (P-10): Deep reflection enforcement
|
|
8
8
|
* 3. GFI Gate: Fatigue index-based blocking
|
|
9
9
|
* 4. Bash Mutation Detection: Heuristic for bash file modifications
|
|
10
|
+
* 4.5. Rule Host: Active code implementation evaluation (Phase 12)
|
|
10
11
|
* 5. Progressive Gate: EP tier-based access control
|
|
11
12
|
* 6. Edit Verification (P-03): Exact/fuzzy match for edit operations
|
|
12
13
|
*
|
|
13
14
|
* IMPORTANT: This is the SINGLE AUTHORITATIVE orchestration path.
|
|
14
|
-
* All policy modules (gfi-gate, progressive-trust-gate) use the shared
|
|
15
|
+
* All policy modules (gfi-gate, progressive-trust-gate, rule-host) use the shared
|
|
15
16
|
* `recordGateBlockAndReturn` helper to ensure consistent block persistence.
|
|
16
17
|
*
|
|
17
18
|
* Zero-width character detection is handled in bash-risk.ts.
|
|
@@ -28,16 +29,20 @@ import { handleEditVerification } from './edit-verification.js';
|
|
|
28
29
|
import { checkGfiGate } from './gfi-gate.js';
|
|
29
30
|
import { checkProgressiveTrustGate } from './progressive-trust-gate.js';
|
|
30
31
|
import { recordGateBlockAndReturn } from './gate-block-helper.js';
|
|
31
|
-
import
|
|
32
|
+
import { RuleHost } from '../core/rule-host.js';
|
|
33
|
+
import type { RuleHostInput } from '../core/rule-host-types.js';
|
|
34
|
+
import type { PluginHookBeforeToolCallEvent, PluginHookToolContext, PluginHookBeforeToolCallResult, PluginLogger } from '../openclaw-sdk.js';
|
|
32
35
|
import {
|
|
33
36
|
AGENT_TOOLS,
|
|
34
37
|
BASH_TOOLS_SET,
|
|
35
38
|
WRITE_TOOLS,
|
|
36
39
|
} from '../constants/tools.js';
|
|
40
|
+
import { getSession, hasRecentThinking } from '../core/session-tracker.js';
|
|
41
|
+
import { getEvolutionEngine } from '../core/evolution-engine.js';
|
|
37
42
|
|
|
38
43
|
export function handleBeforeToolCall(
|
|
39
44
|
event: PluginHookBeforeToolCallEvent,
|
|
40
|
-
ctx: PluginHookToolContext & { workspaceDir?: string; pluginConfig?: Record<string, unknown>; logger?:
|
|
45
|
+
ctx: PluginHookToolContext & { workspaceDir?: string; pluginConfig?: Record<string, unknown>; logger?: Partial<PluginLogger> }
|
|
41
46
|
): PluginHookBeforeToolCallResult | void {
|
|
42
47
|
const logger = ctx.logger || console;
|
|
43
48
|
|
|
@@ -126,9 +131,10 @@ export function handleBeforeToolCall(
|
|
|
126
131
|
// Heuristic for bash mutation detection
|
|
127
132
|
if (isBash && !filePath) {
|
|
128
133
|
const command = String(event.params.command || event.params.args || "");
|
|
129
|
-
const mutationMatch =
|
|
134
|
+
const mutationMatch = /(?:>|>>|sed\s+-i|rm|mv|mkdir|touch|cp)\s+(?:-[a-zA-Z]+\s+)*([^\s;&|<>]+)/.exec(command);
|
|
130
135
|
|
|
131
136
|
if (mutationMatch) {
|
|
137
|
+
// eslint-disable-next-line @typescript-eslint/prefer-destructuring -- Reason: mutationMatch[1] assigned to reassignable outer let - destructuring would shadow outer variable
|
|
132
138
|
filePath = mutationMatch[1];
|
|
133
139
|
} else {
|
|
134
140
|
const hasRiskPath = profile.risk_paths.some(rp => command.includes(rp));
|
|
@@ -149,6 +155,64 @@ export function handleBeforeToolCall(
|
|
|
149
155
|
? profile.risk_paths.some(rp => filePath.includes(rp))
|
|
150
156
|
: isRisky(relPath, profile.risk_paths);
|
|
151
157
|
|
|
158
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
159
|
+
// POLICY STEP 2.5: Rule Host Evaluation (Phase 12, D-01/D-03)
|
|
160
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
161
|
+
// Inserted between GFI gate and Progressive Gate so principle rules can act
|
|
162
|
+
// before the capability-boundary fallback. Active code implementations run
|
|
163
|
+
// through a constrained vm context with minimal helpers only.
|
|
164
|
+
try {
|
|
165
|
+
const ruleHost = new RuleHost(wctx.stateDir, logger);
|
|
166
|
+
const hostInput: RuleHostInput = {
|
|
167
|
+
action: {
|
|
168
|
+
toolName: event.toolName,
|
|
169
|
+
normalizedPath: relPath,
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
171
|
+
paramsSummary: _extractParamsSummary(event.params),
|
|
172
|
+
},
|
|
173
|
+
workspace: {
|
|
174
|
+
isRiskPath: risky,
|
|
175
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
176
|
+
planStatus: _getPlanStatus(ctx.workspaceDir),
|
|
177
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
178
|
+
hasPlanFile: _hasPlanFile(ctx.workspaceDir),
|
|
179
|
+
},
|
|
180
|
+
session: {
|
|
181
|
+
sessionId: ctx.sessionId,
|
|
182
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
183
|
+
currentGfi: _getCurrentGfi(ctx.sessionId),
|
|
184
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
185
|
+
recentThinking: _hasRecentThinking(ctx.sessionId),
|
|
186
|
+
},
|
|
187
|
+
evolution: {
|
|
188
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
189
|
+
epTier: _getEpTier(wctx.workspaceDir),
|
|
190
|
+
},
|
|
191
|
+
derived: {
|
|
192
|
+
estimatedLineChanges: estimateLineChanges({ toolName: event.toolName, params: event.params }),
|
|
193
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- Reason: mutual recursion between helper functions - reordering would break logical grouping
|
|
194
|
+
bashRisk: _getBashRisk(event, profile),
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const hostResult = ruleHost.evaluate(hostInput);
|
|
199
|
+
if (hostResult?.decision === 'block' || hostResult?.decision === 'requireApproval') {
|
|
200
|
+
const reason = hostResult.decision === 'requireApproval'
|
|
201
|
+
? `[Rule Host] Approval required: ${hostResult.reason}`
|
|
202
|
+
: hostResult.reason;
|
|
203
|
+
return recordGateBlockAndReturn(wctx, {
|
|
204
|
+
filePath: relPath,
|
|
205
|
+
reason,
|
|
206
|
+
toolName: event.toolName,
|
|
207
|
+
sessionId: ctx.sessionId,
|
|
208
|
+
blockSource: 'rule-host',
|
|
209
|
+
}, logger);
|
|
210
|
+
}
|
|
211
|
+
} catch (hostError: unknown) {
|
|
212
|
+
// D-08: Conservative degradation — log and continue to Progressive Gate
|
|
213
|
+
logger.warn?.(`[PD_GATE:RULE_HOST] Host evaluation failed, degrading conservatively: ${String(hostError)}`);
|
|
214
|
+
}
|
|
215
|
+
|
|
152
216
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
153
217
|
// POLICY STEP 3: Progressive Trust Gate (Stage 1-4 access control)
|
|
154
218
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -207,4 +271,87 @@ export function handleBeforeToolCall(
|
|
|
207
271
|
|
|
208
272
|
// All checks passed - allow the operation
|
|
209
273
|
return;
|
|
210
|
-
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
// Private helpers for building RuleHostInput snapshot
|
|
278
|
+
// These are NOT passed to hosted implementations — they only populate the
|
|
279
|
+
// frozen snapshot that implementations receive.
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
|
|
282
|
+
function _extractParamsSummary(params: Record<string, unknown>): Record<string, unknown> {
|
|
283
|
+
const summary: Record<string, unknown> = {};
|
|
284
|
+
if (params.file_path) summary.file_path = params.file_path;
|
|
285
|
+
if (params.path) summary.path = params.path;
|
|
286
|
+
if (params.command) summary.command = params.command;
|
|
287
|
+
if (params.args) summary.args = params.args;
|
|
288
|
+
if (params.old_string) summary.old_string = params.old_string;
|
|
289
|
+
if (params.new_string) summary.new_string = params.new_string;
|
|
290
|
+
return summary;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function _getPlanStatus(workspaceDir: string): 'NONE' | 'DRAFT' | 'READY' | 'UNKNOWN' {
|
|
294
|
+
try {
|
|
295
|
+
const status = getPlanStatus(workspaceDir);
|
|
296
|
+
if (status === 'READY') return 'READY';
|
|
297
|
+
if (status === 'DRAFT') return 'DRAFT';
|
|
298
|
+
if (status === '') return 'NONE';
|
|
299
|
+
return 'UNKNOWN';
|
|
300
|
+
} catch {
|
|
301
|
+
return 'UNKNOWN';
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function _hasPlanFile(workspaceDir: string): boolean {
|
|
306
|
+
try {
|
|
307
|
+
return fs.existsSync(path.join(workspaceDir, 'PLAN.md'));
|
|
308
|
+
} catch {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function _getCurrentGfi(sessionId?: string): number {
|
|
314
|
+
if (!sessionId) return 0;
|
|
315
|
+
try {
|
|
316
|
+
return getSession(sessionId)?.currentGfi ?? 0;
|
|
317
|
+
} catch {
|
|
318
|
+
return 0;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function _hasRecentThinking(sessionId?: string): boolean {
|
|
323
|
+
if (!sessionId) return false;
|
|
324
|
+
try {
|
|
325
|
+
return hasRecentThinking(sessionId);
|
|
326
|
+
} catch {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function _getEpTier(workspaceDir: string): number {
|
|
332
|
+
try {
|
|
333
|
+
const engine = getEvolutionEngine(workspaceDir);
|
|
334
|
+
return engine.getTier() as number;
|
|
335
|
+
} catch {
|
|
336
|
+
return 0;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars -- Reason: type-only parameter not used at runtime */
|
|
341
|
+
function _getBashRisk(
|
|
342
|
+
event: PluginHookBeforeToolCallEvent,
|
|
343
|
+
_profile: { risk_paths: string[] }
|
|
344
|
+
): 'safe' | 'normal' | 'dangerous' | 'unknown' {
|
|
345
|
+
/* eslint-enable no-unused-vars, @typescript-eslint/no-unused-vars */
|
|
346
|
+
if (!BASH_TOOLS_SET.has(event.toolName)) return 'unknown';
|
|
347
|
+
try {
|
|
348
|
+
const command = String(event.params.command || event.params.args || '');
|
|
349
|
+
const isDangerous = /\brm\s+-rf\b|\bchmod\b|\bchown\b|>\s*\/dev\//.test(command);
|
|
350
|
+
if (isDangerous) return 'dangerous';
|
|
351
|
+
const isMutation = /(?:>|>>|sed|rm|mv|mkdir|touch|cp|npm|yarn|pnpm|pip|cargo)/.test(command);
|
|
352
|
+
if (isMutation) return 'normal';
|
|
353
|
+
return 'safe';
|
|
354
|
+
} catch {
|
|
355
|
+
return 'unknown';
|
|
356
|
+
}
|
|
357
|
+
}
|
package/src/hooks/gfi-gate.ts
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
import { getSession } from '../core/session-tracker.js';
|
|
26
26
|
import { estimateLineChanges } from '../core/risk-calculator.js';
|
|
27
|
-
import { analyzeBashCommand, calculateDynamicThreshold
|
|
27
|
+
import { analyzeBashCommand, calculateDynamicThreshold } from './bash-risk.js';
|
|
28
28
|
import { BASH_TOOLS_SET, HIGH_RISK_TOOLS, LOW_RISK_WRITE_TOOLS, AGENT_TOOLS } from '../constants/tools.js';
|
|
29
29
|
import { AGENT_SPAWN_GFI_THRESHOLD } from '../config/index.js';
|
|
30
30
|
import { recordGateBlockAndReturn } from './gate-block-helper.js';
|
|
@@ -47,13 +47,16 @@ export interface GfiGateConfig {
|
|
|
47
47
|
/**
|
|
48
48
|
* Internal helper to call the shared block helper with gfi-gate source tag.
|
|
49
49
|
*/
|
|
50
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Helper function requires all params for block recording */
|
|
50
51
|
function block(
|
|
51
52
|
wctx: WorkspaceContext,
|
|
52
53
|
filePath: string,
|
|
53
54
|
reason: string,
|
|
54
55
|
toolName: string,
|
|
55
56
|
sessionId: string | undefined,
|
|
57
|
+
/* eslint-disable no-unused-vars -- Reason: type-only callback params in logger type */
|
|
56
58
|
logger?: { info?: (message: string) => void; warn?: (message: string) => void; error?: (message: string) => void }
|
|
59
|
+
/* eslint-enable no-unused-vars */
|
|
57
60
|
): PluginHookBeforeToolCallResult {
|
|
58
61
|
return recordGateBlockAndReturn(wctx, {
|
|
59
62
|
filePath,
|
|
@@ -61,15 +64,19 @@ function block(
|
|
|
61
64
|
toolName,
|
|
62
65
|
sessionId,
|
|
63
66
|
blockSource: 'gfi-gate',
|
|
64
|
-
}, logger ||
|
|
67
|
+
}, logger || // eslint-disable-next-line @typescript-eslint/no-empty-function -- empty warn/error no-op
|
|
68
|
+
{ warn: () => {}, error: () => {} } as const);
|
|
65
69
|
}
|
|
66
70
|
|
|
71
|
+
/* eslint-disable @typescript-eslint/max-params -- Reason: Gate function requires all params for comprehensive checks */
|
|
67
72
|
export function checkGfiGate(
|
|
68
73
|
event: PluginHookBeforeToolCallEvent,
|
|
69
74
|
wctx: WorkspaceContext,
|
|
70
75
|
sessionId: string | undefined,
|
|
71
76
|
config: GfiGateConfig,
|
|
77
|
+
/* eslint-disable no-unused-vars -- Reason: type-only callback params in logger type */
|
|
72
78
|
logger?: { info?: (message: string) => void; warn?: (message: string) => void }
|
|
79
|
+
/* eslint-enable no-unused-vars */
|
|
73
80
|
): PluginHookBeforeToolCallResult | undefined {
|
|
74
81
|
if (!config || config.enabled === false || !sessionId) {
|
|
75
82
|
return undefined;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle Routing Hook — Natural Language Intent Detection
|
|
3
|
+
* ==========================================================
|
|
4
|
+
*
|
|
5
|
+
* PURPOSE: Detect natural language intent for promotion, disable, and rollback
|
|
6
|
+
* of implementations. Supports both English and Chinese phrases.
|
|
7
|
+
*
|
|
8
|
+
* PATTERN: Extends the existing rollback natural language detection pattern
|
|
9
|
+
* from rollback.ts.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Natural Language Patterns
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
const PROMOTE_PATTERNS_CN = [
|
|
17
|
+
/促[进推]/,
|
|
18
|
+
/启[用用]/,
|
|
19
|
+
/激活/,
|
|
20
|
+
/设为活动/,
|
|
21
|
+
/启用.*实现/,
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const PROMOTE_PATTERNS_EN = [
|
|
25
|
+
/promote\s+(this|the|implementation)/i,
|
|
26
|
+
/activate\s+(this|the|implementation)/i,
|
|
27
|
+
/enable\s+(this|the|implementation)/i,
|
|
28
|
+
/set\s+(as|to)\s+active/i,
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const DISABLE_PATTERNS_CN = [
|
|
32
|
+
/禁[用止]/,
|
|
33
|
+
/关闭.*实现/,
|
|
34
|
+
/停止.*实现/,
|
|
35
|
+
/停用/,
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const DISABLE_PATTERNS_EN = [
|
|
39
|
+
/disable\s+(this|the|implementation)/i,
|
|
40
|
+
/turn\s+off\s+(this|the|implementation)/i,
|
|
41
|
+
/deactivate\s+(this|the|implementation)/i,
|
|
42
|
+
/stop\s+(this|the|implementation)/i,
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const ROLLBACK_PATTERNS_CN = [
|
|
46
|
+
/回滚/,
|
|
47
|
+
/撤销.*实现/,
|
|
48
|
+
/恢复.*实现/,
|
|
49
|
+
/退回.*实现/,
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
const ROLLBACK_PATTERNS_EN = [
|
|
53
|
+
/rollback\s+(this|the|implementation)/i,
|
|
54
|
+
/revert\s+(this|the|implementation)/i,
|
|
55
|
+
/undo\s+(this|the|implementation)/i,
|
|
56
|
+
/restore\s+(previous|last|implementation)/i,
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// Intent Detection
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
export type LifecycleIntent = 'promote' | 'disable' | 'rollback' | null;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Detect implementation lifecycle intent from user message.
|
|
67
|
+
* Returns the detected intent type or null.
|
|
68
|
+
*/
|
|
69
|
+
export function detectLifecycleIntent(message: string): LifecycleIntent {
|
|
70
|
+
// Check promote patterns
|
|
71
|
+
for (const p of PROMOTE_PATTERNS_EN) {
|
|
72
|
+
if (p.test(message)) return 'promote';
|
|
73
|
+
}
|
|
74
|
+
for (const p of PROMOTE_PATTERNS_CN) {
|
|
75
|
+
if (p.test(message)) return 'promote';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check disable patterns
|
|
79
|
+
for (const p of DISABLE_PATTERNS_EN) {
|
|
80
|
+
if (p.test(message)) return 'disable';
|
|
81
|
+
}
|
|
82
|
+
for (const p of DISABLE_PATTERNS_CN) {
|
|
83
|
+
if (p.test(message)) return 'disable';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check rollback patterns
|
|
87
|
+
for (const p of ROLLBACK_PATTERNS_EN) {
|
|
88
|
+
if (p.test(message)) return 'rollback';
|
|
89
|
+
}
|
|
90
|
+
for (const p of ROLLBACK_PATTERNS_CN) {
|
|
91
|
+
if (p.test(message)) return 'rollback';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Route a natural language lifecycle intent to the appropriate command handler.
|
|
99
|
+
* Returns command name and normalized message, or null if no intent detected.
|
|
100
|
+
*/
|
|
101
|
+
export function routeLifecycleIntent(
|
|
102
|
+
message: string
|
|
103
|
+
): { command: string; normalizedMessage: string } | null {
|
|
104
|
+
const intent = detectLifecycleIntent(message);
|
|
105
|
+
if (!intent) return null;
|
|
106
|
+
|
|
107
|
+
switch (intent) {
|
|
108
|
+
case 'promote':
|
|
109
|
+
return {
|
|
110
|
+
command: 'pd-promote-impl',
|
|
111
|
+
normalizedMessage: 'list',
|
|
112
|
+
};
|
|
113
|
+
case 'disable':
|
|
114
|
+
return {
|
|
115
|
+
command: 'pd-disable-impl',
|
|
116
|
+
normalizedMessage: 'list',
|
|
117
|
+
};
|
|
118
|
+
case 'rollback':
|
|
119
|
+
return {
|
|
120
|
+
command: 'pd-rollback-impl',
|
|
121
|
+
normalizedMessage: 'list',
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
package/src/hooks/lifecycle.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as readline from 'readline';
|
|
4
|
-
import {
|
|
4
|
+
import { buildPainFlag, writePainFlag } from '../core/pain.js';
|
|
5
5
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
6
6
|
import { PD_DIRS } from '../core/paths.js';
|
|
7
|
-
import {
|
|
8
|
-
extractWorkingMemory,
|
|
9
|
-
mergeWorkingMemory,
|
|
10
|
-
type WorkingMemorySnapshot
|
|
7
|
+
import {
|
|
8
|
+
extractWorkingMemory,
|
|
9
|
+
mergeWorkingMemory,
|
|
11
10
|
} from '../core/focus-history.js';
|
|
12
11
|
import type { PluginHookBeforeResetEvent, PluginHookBeforeCompactionEvent, PluginHookAfterCompactionEvent, PluginHookAgentContext } from '../openclaw-sdk.js';
|
|
13
12
|
|
|
@@ -55,7 +54,7 @@ interface JsonlMessage {
|
|
|
55
54
|
|
|
56
55
|
export async function extractPainFromSessionFile(sessionFile: string, ctx: PluginHookAgentContext): Promise<void> {
|
|
57
56
|
const painPoints: string[] = [];
|
|
58
|
-
const workspaceDir = ctx
|
|
57
|
+
const {workspaceDir} = ctx;
|
|
59
58
|
|
|
60
59
|
if (!workspaceDir) return;
|
|
61
60
|
|
|
@@ -123,13 +122,13 @@ export async function extractPainFromSessionFile(sessionFile: string, ctx: Plugi
|
|
|
123
122
|
try {
|
|
124
123
|
rl.close();
|
|
125
124
|
fileStream.destroy();
|
|
126
|
-
} catch (_e) {
|
|
125
|
+
} catch (_e) { // eslint-disable-line @typescript-eslint/no-unused-vars, no-unused-vars -- Reason: intentionally unused - cleanup errors ignored
|
|
127
126
|
// Ignore cleanup errors
|
|
128
127
|
}
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
if (painPoints.length > 0) {
|
|
132
|
-
const dateStr = new Date().toISOString().split('T')
|
|
131
|
+
const [dateStr] = new Date().toISOString().split('T');
|
|
133
132
|
const dailyLogPath = path.join(workspaceDir, PD_DIRS.MEMORY, `${dateStr}.md`);
|
|
134
133
|
const timestamp = new Date().toISOString();
|
|
135
134
|
|
|
@@ -177,7 +176,7 @@ export async function handleBeforeCompaction(
|
|
|
177
176
|
if (!ctx.workspaceDir) return;
|
|
178
177
|
|
|
179
178
|
const wctx = WorkspaceContext.fromHookContext(ctx);
|
|
180
|
-
const dateStr = new Date().toISOString().split('T')
|
|
179
|
+
const [dateStr] = new Date().toISOString().split('T');
|
|
181
180
|
const checkpointPath = path.join(ctx.workspaceDir, PD_DIRS.MEMORY, `${dateStr}.md`);
|
|
182
181
|
const log =
|
|
183
182
|
`\n## [${new Date().toISOString()}] Pre-Compaction Checkpoint\n` +
|
|
@@ -195,8 +194,9 @@ export async function handleBeforeCompaction(
|
|
|
195
194
|
// 提取工作记忆(从 sessionFile)
|
|
196
195
|
if (event.sessionFile) {
|
|
197
196
|
await extractPainFromSessionFile(event.sessionFile, ctx);
|
|
198
|
-
|
|
197
|
+
|
|
199
198
|
// 新增:提取并保存工作记忆
|
|
199
|
+
/* eslint-disable @typescript-eslint/no-use-before-define -- Reason: extractAndSaveWorkingMemory is defined later in this file */
|
|
200
200
|
await extractAndSaveWorkingMemory(event.sessionFile, ctx, wctx);
|
|
201
201
|
}
|
|
202
202
|
}
|
|
@@ -314,8 +314,8 @@ export async function handleAfterCompaction(
|
|
|
314
314
|
): Promise<void> {
|
|
315
315
|
if (!ctx.workspaceDir) return;
|
|
316
316
|
|
|
317
|
-
const
|
|
318
|
-
const checkpointPath = path.join(ctx.workspaceDir, PD_DIRS.MEMORY, `${
|
|
317
|
+
const [dateStrPost] = new Date().toISOString().split('T');
|
|
318
|
+
const checkpointPath = path.join(ctx.workspaceDir, PD_DIRS.MEMORY, `${dateStrPost}.md`);
|
|
319
319
|
const log =
|
|
320
320
|
`- Post-Compaction Complete. Reduced active context to ${event.messageCount} messages.\n`;
|
|
321
321
|
|