principles-disciple 1.72.0 → 1.74.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/INSTALL.md +1 -3
- package/openclaw.plugin.json +10 -5
- package/package.json +17 -19
- package/scripts/acceptance-test.mjs +16 -73
- package/scripts/sync-plugin.mjs +382 -77
- package/src/commands/archive-impl.ts +2 -1
- package/src/commands/capabilities.ts +2 -2
- package/src/commands/context.ts +2 -2
- package/src/commands/disable-impl.ts +2 -1
- package/src/commands/evolution-status.ts +16 -16
- package/src/commands/export.ts +12 -67
- package/src/commands/pain.ts +91 -1
- package/src/commands/principle-rollback.ts +2 -1
- package/src/commands/promote-impl.ts +7 -43
- package/src/commands/rollback-impl.ts +2 -1
- package/src/commands/rollback.ts +2 -1
- package/src/commands/samples.ts +2 -1
- package/src/commands/thinking-os.ts +2 -1
- package/src/config/errors.ts +18 -2
- package/src/constants/diagnostician.ts +2 -2
- package/src/constants/tools.ts +2 -1
- package/src/core/__tests__/focus-history.test.ts +210 -0
- package/src/core/config.ts +1 -1
- package/src/core/correction-cue-learner.ts +2 -136
- package/src/core/correction-types.ts +16 -88
- package/src/core/dictionary.ts +19 -20
- package/src/core/empathy-keyword-matcher.ts +17 -289
- package/src/core/empathy-types.ts +18 -229
- package/src/core/event-log.ts +29 -132
- package/src/core/evolution-reducer.ts +21 -2
- package/src/core/evolution-types.ts +76 -464
- package/src/core/file-store.ts +80 -0
- package/src/core/focus-history.ts +228 -955
- package/src/core/local-worker-routing.ts +34 -314
- package/src/core/merge-gate-audit.ts +0 -195
- package/src/core/migration.ts +0 -1
- package/src/core/pain-diagnostic-gate.ts +154 -0
- package/src/core/pain-signal.ts +21 -138
- package/src/core/pain.ts +15 -88
- package/src/core/path-resolver.ts +0 -1
- package/src/core/paths.ts +0 -1
- package/src/core/pd-task-reconciler.ts +26 -115
- package/src/core/pd-task-service.ts +9 -9
- package/src/core/pd-task-types.ts +23 -127
- package/src/core/principle-compiler/__tests__/compiler-replay-gate.test.ts +174 -0
- package/src/core/principle-compiler/code-validator.ts +15 -42
- package/src/core/principle-compiler/compiler.ts +100 -15
- package/src/core/principle-compiler/index.ts +5 -2
- package/src/core/principle-compiler/template-generator.ts +4 -104
- package/src/core/principle-injection.ts +10 -202
- package/src/core/principle-internalization/filesystem-lifecycle-datasource.ts +42 -0
- package/src/core/principle-internalization/lifecycle-read-model.ts +39 -242
- package/src/core/principle-internalization/principle-lifecycle-service.ts +12 -10
- package/src/core/principle-tree-ledger-adapter.ts +145 -0
- package/src/core/principle-tree-ledger.ts +8 -6
- package/src/core/reflection/reflection-context.ts +14 -109
- package/src/core/replay-engine.ts +8 -500
- package/src/core/rule-host-helpers.ts +5 -35
- package/src/core/rule-host-types.ts +10 -82
- package/src/core/rule-host.ts +6 -63
- package/src/core/runtime-v2-prompt-activation-reader.ts +231 -0
- package/src/core/session-tracker.ts +87 -101
- package/src/core/shadow-observation-registry.ts +19 -48
- package/src/core/trajectory.ts +3 -1
- package/src/core/workflow-funnel-loader.ts +62 -68
- package/src/core/workspace-context.ts +46 -0
- package/src/core/workspace-dir-service.ts +1 -1
- package/src/core/workspace-dir-validation.ts +18 -9
- package/src/hooks/AGENTS.md +1 -1
- package/src/hooks/gate-block-helper.ts +71 -64
- package/src/hooks/gate.ts +183 -31
- package/src/hooks/lifecycle.ts +30 -32
- package/src/hooks/llm.ts +60 -32
- package/src/hooks/pain.ts +297 -103
- package/src/hooks/prompt.ts +400 -440
- package/src/hooks/subagent.ts +2 -29
- package/src/i18n/commands.ts +2 -10
- package/src/index.ts +95 -85
- package/src/openclaw-sdk.ts +311 -0
- package/src/service/central-database.ts +8 -4
- package/src/service/evolution-queue-migration.ts +2 -1
- package/src/service/evolution-worker.ts +163 -1786
- package/src/service/internalization-trigger-adapter.ts +302 -0
- package/src/service/keyword-optimization-service.ts +4 -4
- package/src/service/monitoring-query-service.ts +1 -215
- package/src/service/queue-io.ts +60 -331
- package/src/service/runtime-summary-service.ts +59 -16
- package/src/service/subagent-workflow/index.ts +0 -41
- package/src/service/subagent-workflow/types.ts +9 -120
- package/src/service/subagent-workflow/workflow-store.ts +2 -119
- package/src/service/workflow-watchdog.ts +0 -43
- package/src/types/event-payload.ts +16 -74
- package/src/types/event-types.ts +38 -547
- package/src/types/hygiene-types.ts +7 -30
- package/src/types/principle-tree-schema.ts +20 -222
- package/src/types/queue.ts +15 -70
- package/src/types/runtime-summary.ts +5 -49
- package/src/utils/io.ts +8 -20
- package/src/utils/retry.ts +1 -1
- package/src/utils/shadow-fingerprint.ts +2 -2
- package/src/utils/workspace-resolver.ts +50 -0
- package/templates/langs/en/core/AGENTS.md +7 -7
- package/templates/langs/en/core/BOOT.md +1 -1
- package/templates/langs/en/core/HEARTBEAT.md +2 -2
- package/templates/langs/en/principles/THINKING_OS.md +3 -2
- package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
- package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
- package/templates/langs/en/skills/evolve-task/SKILL.md +3 -3
- package/templates/langs/en/skills/pd-cli-operator/SKILL.md +67 -0
- package/templates/langs/en/skills/pd-diagnostician/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-mentor/SKILL.md +2 -3
- package/templates/langs/en/skills/pd-pain-signal/SKILL.md +17 -39
- package/templates/langs/en/skills/pd-runtime-v2/SKILL.md +61 -0
- package/templates/langs/zh/core/AGENTS.md +7 -7
- package/templates/langs/zh/core/BOOT.md +1 -1
- package/templates/langs/zh/core/HEARTBEAT.md +2 -2
- package/templates/langs/zh/principles/THINKING_OS.md +3 -2
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +8 -8
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
- package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +21 -5
- package/templates/langs/zh/skills/evolve-task/SKILL.md +4 -4
- package/templates/langs/zh/skills/pd-cli-operator/SKILL.md +67 -0
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-mentor/SKILL.md +2 -3
- package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +17 -38
- package/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +61 -0
- package/tests/build-artifacts.test.ts +1 -3
- package/tests/commands/evolution-status.test.ts +0 -118
- package/tests/core/bootstrap-rules.test.ts +1 -1
- package/tests/core/config.test.ts +1 -1
- package/tests/core/event-log.test.ts +35 -0
- package/tests/core/evolution-engine.test.ts +610 -0
- package/tests/core/file-store.test.ts +102 -0
- package/tests/core/focus-history.test.ts +203 -11
- package/tests/core/merge-gate-audit.test.ts +2 -169
- package/tests/core/migration.test.ts +7 -7
- package/tests/core/model-deployment-registry.test.ts +7 -1
- package/tests/core/model-training-registry.test.ts +19 -0
- package/tests/core/observability.test.ts +0 -1
- package/tests/core/pain-diagnostic-gate.test.ts +498 -0
- package/tests/core/pain.test.ts +0 -1
- package/tests/core/path-resolver.test.ts +1 -1
- package/tests/core/paths-refactor.test.ts +0 -22
- package/tests/core/principle-internalization/deprecated-readiness.test.ts +2 -2
- package/tests/core/principle-internalization/lifecycle-metrics.test.ts +2 -2
- package/tests/core/principle-internalization/{internalization-routing-policy.test.ts → lifecycle-routing-policy.test.ts} +6 -6
- package/tests/core/principle-internalization/lineage-source-retired.test.ts +56 -0
- package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +1 -23
- package/tests/core/principle-tree-ledger-adapter.test.ts +253 -0
- package/tests/core/reflection-context.test.ts +0 -14
- package/tests/core/replay-engine.test.ts +127 -215
- package/tests/core/rule-host-helpers.test.ts +2 -2
- package/tests/core/rule-implementation-runtime.test.ts +0 -27
- package/tests/core/workflow-funnel-loader.test.ts +162 -0
- package/tests/core/workspace-context.test.ts +2 -2
- package/tests/core/workspace-dir-validation.test.ts +8 -1
- package/tests/core-anti-growth.test.ts +191 -0
- package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
- package/tests/hooks/confirm-first-removal.test.ts +188 -0
- package/tests/hooks/gate-auto-correct-shadow.test.ts +310 -0
- package/tests/hooks/gate-auto-correct.test.ts +665 -0
- package/tests/hooks/gate-no-path-write-tool.test.ts +172 -0
- package/tests/hooks/gate-rule-host-pipeline.test.ts +2 -1
- package/tests/hooks/pain.test.ts +269 -12
- package/tests/hooks/prompt-characterization.test.ts +500 -0
- package/tests/hooks/prompt-size-guard.test.ts +32 -17
- package/tests/hooks/runtime-v2-prompt-activation.test.ts +869 -0
- package/tests/index.test.ts +94 -1
- package/tests/integration/auto-entry-gate.test.ts +248 -0
- package/tests/integration/internalization-trigger-guard.test.ts +69 -0
- package/tests/integration/m8-legacy-paths.test.ts +63 -0
- package/tests/integration/runtime-v2-pain-guard.test.ts +125 -0
- package/tests/plugin-config-resolution-cutover.test.ts +359 -0
- package/tests/runtime-v2-discovery-guard.test.ts +154 -0
- package/tests/service/central-database.test.ts +457 -0
- package/tests/service/evolution-worker.correction-observer.test.ts +173 -0
- package/tests/service/evolution-worker.timeout.test.ts +11 -129
- package/tests/service/internalization-trigger-adapter.test.ts +251 -0
- package/tests/service/monitoring-query-service.test.ts +1 -47
- package/tests/service/queue-io.test.ts +1 -62
- package/tests/service/runtime-summary-service.test.ts +3 -1
- package/tests/service/workflow-watchdog.test.ts +0 -91
- package/tests/utils/file-lock.test.ts +5 -3
- package/tests/utils/session-key.test.ts +52 -0
- package/tests/utils/subagent-probe.test.ts +48 -1
- package/vitest.config.ts +4 -11
- package/.planning/codebase/ARCHITECTURE.md +0 -157
- package/.planning/codebase/CONCERNS.md +0 -145
- package/.planning/codebase/CONVENTIONS.md +0 -148
- package/.planning/codebase/INTEGRATIONS.md +0 -81
- package/.planning/codebase/STACK.md +0 -87
- package/.planning/codebase/STRUCTURE.md +0 -193
- package/.planning/codebase/TESTING.md +0 -243
- package/.planning/phases/01-basic-visualization/01-GAP-CLOSURE-VERIFICATION.md +0 -113
- package/docs/COMMAND_REFERENCE.md +0 -76
- package/docs/COMMAND_REFERENCE_EN.md +0 -79
- package/scripts/build-web.mjs +0 -46
- package/scripts/diagnose-nocturnal.mjs +0 -537
- package/scripts/seed-nocturnal-scenarios.mjs +0 -384
- package/src/commands/nocturnal-review.ts +0 -322
- package/src/commands/nocturnal-rollout.ts +0 -790
- package/src/commands/nocturnal-train.ts +0 -986
- package/src/commands/pd-reflect.ts +0 -88
- package/src/core/adaptive-thresholds.ts +0 -478
- package/src/core/diagnostician-task-store.ts +0 -192
- package/src/core/nocturnal-arbiter.ts +0 -715
- package/src/core/nocturnal-artifact-lineage.ts +0 -116
- package/src/core/nocturnal-artificer.ts +0 -257
- package/src/core/nocturnal-candidate-scoring.ts +0 -530
- package/src/core/nocturnal-compliance.ts +0 -1146
- package/src/core/nocturnal-dataset.ts +0 -763
- package/src/core/nocturnal-executability.ts +0 -428
- package/src/core/nocturnal-export.ts +0 -499
- package/src/core/nocturnal-paths.ts +0 -240
- package/src/core/nocturnal-reasoning-deriver.ts +0 -343
- package/src/core/nocturnal-rule-implementation-validator.ts +0 -246
- package/src/core/nocturnal-snapshot-contract.ts +0 -99
- package/src/core/nocturnal-trajectory-extractor.ts +0 -512
- package/src/core/nocturnal-trinity-types.ts +0 -218
- package/src/core/nocturnal-trinity.ts +0 -2680
- package/src/core/principle-internalization/deprecated-readiness.ts +0 -93
- package/src/core/principle-internalization/internalization-routing-policy.ts +0 -208
- package/src/core/principle-internalization/lifecycle-metrics.ts +0 -152
- package/src/http/principles-console-route.ts +0 -709
- package/src/service/central-health-service.ts +0 -49
- package/src/service/central-overview-service.ts +0 -138
- package/src/service/control-ui-query-service.ts +0 -900
- package/src/service/cooldown-strategy.ts +0 -97
- package/src/service/evolution-pain-context.ts +0 -79
- package/src/service/evolution-query-service.ts +0 -407
- package/src/service/health-query-service.ts +0 -1038
- package/src/service/nocturnal-config.ts +0 -214
- package/src/service/nocturnal-runtime.ts +0 -734
- package/src/service/nocturnal-service.ts +0 -1605
- package/src/service/nocturnal-target-selector.ts +0 -545
- package/src/service/sleep-cycle.ts +0 -157
- package/src/service/startup-reconciler.ts +0 -112
- package/src/service/subagent-workflow/correction-observer-types.ts +0 -82
- package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +0 -250
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +0 -1
- package/src/service/subagent-workflow/dynamic-timeout.ts +0 -30
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +0 -268
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +0 -795
- package/src/service/subagent-workflow/runtime-direct-driver.ts +0 -268
- package/src/service/subagent-workflow/workflow-manager-base.ts +0 -580
- package/src/tools/write-pain-flag.ts +0 -215
- package/templates/langs/en/skills/plan-script/SKILL.md +0 -32
- package/templates/langs/zh/skills/plan-script/SKILL.md +0 -32
- package/tests/commands/nocturnal-review.test.ts +0 -448
- package/tests/commands/nocturnal-train.test.ts +0 -97
- package/tests/commands/pd-reflect.test.ts +0 -49
- package/tests/core/adaptive-thresholds.test.ts +0 -261
- package/tests/core/nocturnal-arbiter.test.ts +0 -559
- package/tests/core/nocturnal-artifact-lineage.test.ts +0 -53
- package/tests/core/nocturnal-artificer.test.ts +0 -241
- package/tests/core/nocturnal-candidate-scoring.test.ts +0 -532
- package/tests/core/nocturnal-compliance-p-principles.test.ts +0 -133
- package/tests/core/nocturnal-compliance.test.ts +0 -646
- package/tests/core/nocturnal-dataset.test.ts +0 -892
- package/tests/core/nocturnal-e2e.test.ts +0 -234
- package/tests/core/nocturnal-executability.test.ts +0 -357
- package/tests/core/nocturnal-export.test.ts +0 -517
- package/tests/core/nocturnal-reasoning-deriver.test.ts +0 -372
- package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +0 -428
- package/tests/core/nocturnal-rule-implementation-validator.test.ts +0 -127
- package/tests/core/nocturnal-snapshot-contract.test.ts +0 -121
- package/tests/core/nocturnal-trajectory-extractor.test.ts +0 -634
- package/tests/core/nocturnal-trinity.test.ts +0 -2053
- package/tests/core/pain-auto-repair.test.ts +0 -96
- package/tests/core/pain-integration.test.ts +0 -510
- package/tests/fixtures/nocturnal-reviewed-subset.json +0 -183
- package/tests/http/principles-console-route.test.ts +0 -162
- package/tests/integration/chaos-resilience.test.ts +0 -348
- package/tests/integration/empathy-workflow-integration.test.ts +0 -626
- package/tests/integration/pain-diagnostician-loop.e2e.test.ts +0 -380
- package/tests/service/control-ui-query-service.test.ts +0 -121
- package/tests/service/cooldown-strategy.test.ts +0 -164
- package/tests/service/data-endpoints-regression.test.ts +0 -834
- package/tests/service/empathy-observer-workflow-manager.test.ts +0 -175
- package/tests/service/evolution-worker.nocturnal.test.ts +0 -601
- package/tests/service/nocturnal-runtime-hardening.test.ts +0 -118
- package/tests/service/nocturnal-runtime.test.ts +0 -473
- package/tests/service/nocturnal-service-code-candidate.test.ts +0 -330
- package/tests/service/nocturnal-target-selector.test.ts +0 -615
- package/tests/service/startup-reconciler.test.ts +0 -148
- package/tests/tools/write-pain-flag.test.ts +0 -358
- package/ui/src/App.tsx +0 -45
- package/ui/src/api.ts +0 -220
- package/ui/src/charts.tsx +0 -955
- package/ui/src/components/ErrorState.tsx +0 -6
- package/ui/src/components/Loading.tsx +0 -13
- package/ui/src/components/ProtectedRoute.tsx +0 -12
- package/ui/src/components/Shell.tsx +0 -91
- package/ui/src/components/WorkspaceConfig.tsx +0 -178
- package/ui/src/components/index.ts +0 -5
- package/ui/src/context/auth.tsx +0 -80
- package/ui/src/context/theme.tsx +0 -66
- package/ui/src/hooks/useAutoRefresh.ts +0 -39
- package/ui/src/i18n/ui.ts +0 -473
- package/ui/src/main.tsx +0 -16
- package/ui/src/pages/EvolutionPage.tsx +0 -333
- package/ui/src/pages/FeedbackPage.tsx +0 -138
- package/ui/src/pages/GateMonitorPage.tsx +0 -136
- package/ui/src/pages/LoginPage.tsx +0 -89
- package/ui/src/pages/OverviewPage.tsx +0 -599
- package/ui/src/pages/SamplesPage.tsx +0 -174
- package/ui/src/pages/ThinkingModelsPage.tsx +0 -702
- package/ui/src/styles.css +0 -2020
- package/ui/src/types.ts +0 -384
- package/ui/src/utils/format.ts +0 -15
|
@@ -10,14 +10,15 @@
|
|
|
10
10
|
* had their own block persistence implementations.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { trackBlock } from '../core/session-tracker.js';
|
|
13
|
+
import { getSession, trackBlock } from '../core/session-tracker.js';
|
|
14
14
|
import type { WorkspaceContext } from '../core/workspace-context.js';
|
|
15
15
|
import type { PluginHookBeforeToolCallResult } from '../openclaw-sdk.js';
|
|
16
|
+
import { evaluatePainDiagnosticGate } from '../core/pain-diagnostic-gate.js';
|
|
17
|
+
import { emitPainDetectedEvent } from './pain.js';
|
|
16
18
|
import {
|
|
17
19
|
TRAJECTORY_GATE_BLOCK_RETRY_DELAY_MS,
|
|
18
20
|
TRAJECTORY_GATE_BLOCK_MAX_RETRIES
|
|
19
21
|
} from '../config/index.js';
|
|
20
|
-
import { buildPainFlag, writePainFlag } from '../core/pain.js';
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Block context containing all information needed for block persistence
|
|
@@ -99,82 +100,88 @@ export function recordGateBlockAndReturn(
|
|
|
99
100
|
scheduleTrajectoryGateBlockRetry(wctx, trajectoryPayload, 1, logWarn, logError);
|
|
100
101
|
}
|
|
101
102
|
|
|
102
|
-
// 5.
|
|
103
|
-
//
|
|
104
|
-
// and was blocked by a principle gate. This should feed into the nocturnal pipeline.
|
|
103
|
+
// 5. Record gate block pain context. Runtime V2 diagnosis is gated by GFI
|
|
104
|
+
// so one mild block does not start a long diagnostician run.
|
|
105
105
|
if (sessionId) {
|
|
106
|
-
const GATE_BLOCK_PAIN_SCORE =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
106
|
+
const GATE_BLOCK_PAIN_SCORE = 45; // Must be >= pain_trigger (40) so single gate block can trigger diagnosis (PRI-274)
|
|
107
|
+
// Record to trajectory (fire-and-forget, no .pain_flag file needed)
|
|
108
|
+
wctx.trajectory?.recordPainEvent?.({
|
|
109
|
+
sessionId,
|
|
110
|
+
source: 'gate_blocked',
|
|
111
|
+
score: GATE_BLOCK_PAIN_SCORE,
|
|
112
|
+
reason: `Gate blocked ${toolName} on ${filePath}: ${reason}`,
|
|
113
|
+
severity: 'mild',
|
|
114
|
+
origin: 'system_infer',
|
|
115
|
+
});
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
const session = getSession(sessionId);
|
|
118
|
+
const gate = evaluatePainDiagnosticGate({
|
|
119
|
+
source: 'gate_blocked',
|
|
120
|
+
score: GATE_BLOCK_PAIN_SCORE,
|
|
121
|
+
currentGfi: session?.currentGfi ?? 0,
|
|
122
|
+
consecutiveErrors: session?.consecutiveErrors ?? 0,
|
|
123
|
+
sessionId,
|
|
124
|
+
errorHash: `${toolName}:${filePath}:${reason}`,
|
|
125
|
+
thresholds: {
|
|
126
|
+
painTrigger: wctx.config.get('thresholds.pain_trigger') || 40,
|
|
127
|
+
highSeverity: wctx.config.get('severity_thresholds.high') || 70,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
123
130
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
} catch (flagErr) {
|
|
143
|
-
logWarn(`[PD_GATE] Failed to update pain flag for gate block: ${String(flagErr)}`);
|
|
144
|
-
}
|
|
145
|
-
} catch (painErr) {
|
|
146
|
-
logWarn(`[PD_GATE] Failed to record gate block pain signal: ${String(painErr)}`);
|
|
131
|
+
if (gate.shouldDiagnose) {
|
|
132
|
+
void emitPainDetectedEvent(wctx, {
|
|
133
|
+
ts: new Date().toISOString(),
|
|
134
|
+
type: 'pain_detected',
|
|
135
|
+
data: {
|
|
136
|
+
painId: `gate_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,
|
|
137
|
+
painType: 'user_frustration',
|
|
138
|
+
source: 'gate_blocked',
|
|
139
|
+
reason: `Gate blocked ${toolName} on ${filePath}: ${reason}`,
|
|
140
|
+
score: GATE_BLOCK_PAIN_SCORE,
|
|
141
|
+
sessionId,
|
|
142
|
+
agentId: 'main',
|
|
143
|
+
},
|
|
144
|
+
}).catch((emitErr) => {
|
|
145
|
+
logWarn(`[PD_GATE] Failed to emit gate block pain event: ${String(emitErr)}`);
|
|
146
|
+
});
|
|
147
|
+
} else {
|
|
148
|
+
logger.info?.(`[PD_GATE] Gate block recorded without Runtime V2 diagnosis: ${gate.detail}`);
|
|
147
149
|
}
|
|
148
150
|
}
|
|
149
151
|
|
|
150
|
-
// 6. Return consistent block result with operator guidance
|
|
152
|
+
// 6. Return consistent block result with contextual operator guidance
|
|
153
|
+
const blockMessage = buildContextualBlockMessage({ filePath, reason });
|
|
154
|
+
|
|
151
155
|
return {
|
|
152
156
|
block: true,
|
|
153
|
-
blockReason:
|
|
157
|
+
blockReason: blockMessage,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Build contextual block message based on block source.
|
|
163
|
+
* - rule-host: principle-based guidance
|
|
164
|
+
* - default/gate: generic security gate message
|
|
165
|
+
*/
|
|
166
|
+
function buildContextualBlockMessage({
|
|
167
|
+
filePath,
|
|
168
|
+
reason,
|
|
169
|
+
}: {
|
|
170
|
+
filePath: string;
|
|
171
|
+
reason: string;
|
|
172
|
+
}): string {
|
|
173
|
+
// rule-host or generic gate blocks
|
|
174
|
+
return `[Principles Disciple] Security Gate Blocked this action.
|
|
154
175
|
File: ${filePath}
|
|
155
176
|
Reason: ${reason}
|
|
156
177
|
|
|
157
178
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
158
179
|
📋 How to unblock this operation:
|
|
159
180
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
- Target Files: ${filePath}
|
|
165
|
-
- Steps: What you want to do (be specific)
|
|
166
|
-
- Metrics: How to verify success
|
|
167
|
-
- Active Mental Models: Select 2 relevant models from .principles/THINKING_OS.md
|
|
168
|
-
- Rollback: How to restore if it fails
|
|
169
|
-
|
|
170
|
-
3. After completing the plan, set STATUS: READY in PLAN.md
|
|
171
|
-
|
|
172
|
-
4. Retry the operation
|
|
173
|
-
|
|
174
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
175
|
-
This is a mandatory security gate. The operation was blocked because the modification exceeds the allowed threshold for your current evolution tier.
|
|
176
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
177
|
-
};
|
|
181
|
+
This action was blocked by a Rule Host principle.
|
|
182
|
+
If the blocked path is correct and safe, explain the reasoning to the owner
|
|
183
|
+
and ask for explicit confirmation to proceed.
|
|
184
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`;
|
|
178
185
|
}
|
|
179
186
|
|
|
180
187
|
/**
|
package/src/hooks/gate.ts
CHANGED
|
@@ -9,13 +9,12 @@
|
|
|
9
9
|
* 2. Rule Host: Dynamic principle-based evaluation (sole gate)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import
|
|
13
|
-
import * as path from 'path';
|
|
14
|
-
import { normalizePath, planStatus } from '../utils/io.js';
|
|
12
|
+
import { normalizePath } from '../utils/io.js';
|
|
15
13
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
16
14
|
import { recordGateBlockAndReturn } from './gate-block-helper.js';
|
|
17
15
|
import { RuleHost } from '../core/rule-host.js';
|
|
18
|
-
import type { RuleHostInput } from '
|
|
16
|
+
import type { RuleHostInput } from '@principles/core/runtime-v2';
|
|
17
|
+
import { validateCorrectionProposal, validateProposedPathBounds } from '@principles/core/runtime-v2';
|
|
19
18
|
import type { PluginHookBeforeToolCallEvent, PluginHookToolContext, PluginHookBeforeToolCallResult, PluginLogger } from '../openclaw-sdk.js';
|
|
20
19
|
import { AGENT_TOOLS, BASH_TOOLS_SET, WRITE_TOOLS } from '../constants/tools.js';
|
|
21
20
|
import { getSession, hasRecentThinking } from '../core/session-tracker.js';
|
|
@@ -41,11 +40,11 @@ export function handleBeforeToolCall(
|
|
|
41
40
|
const wctx = WorkspaceContext.fromHookContext(ctx);
|
|
42
41
|
|
|
43
42
|
// 2. Resolve the target file path
|
|
44
|
-
let filePath = event.params
|
|
43
|
+
let filePath = event.params?.file_path || event.params?.path || event.params?.file || event.params?.target;
|
|
45
44
|
|
|
46
45
|
// Heuristic for bash mutation detection
|
|
47
46
|
if (isBash && !filePath) {
|
|
48
|
-
const command = String(event.params
|
|
47
|
+
const command = String(event.params?.command || event.params?.args || '');
|
|
49
48
|
const mutationMatch = /(?:>|>>|sed\s+-i|rm|mv|mkdir|touch|cp)\s+(?:-[a-zA-Z]+\s+)*([^\s;&|<>]+)/.exec(command);
|
|
50
49
|
|
|
51
50
|
if (mutationMatch) {
|
|
@@ -56,6 +55,12 @@ export function handleBeforeToolCall(
|
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
|
|
58
|
+
// Write tools without a file path must still go through RuleHost evaluation.
|
|
59
|
+
// Use a synthetic path so RuleHost can evaluate and potentially block.
|
|
60
|
+
if (!filePath && isWriteTool) {
|
|
61
|
+
filePath = `<tool:${event.toolName}>`;
|
|
62
|
+
}
|
|
63
|
+
|
|
59
64
|
if (typeof filePath !== 'string') return;
|
|
60
65
|
|
|
61
66
|
const relPath = normalizePath(filePath, ctx.workspaceDir);
|
|
@@ -67,12 +72,16 @@ export function handleBeforeToolCall(
|
|
|
67
72
|
action: {
|
|
68
73
|
toolName: event.toolName,
|
|
69
74
|
normalizedPath: relPath,
|
|
70
|
-
paramsSummary: _extractParamsSummary(event.params),
|
|
75
|
+
paramsSummary: _extractParamsSummary(event.params ?? {}),
|
|
71
76
|
},
|
|
72
77
|
workspace: {
|
|
73
78
|
isRiskPath: false, // Rule Host determines risk dynamically
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
// DEPRECATED (PRI-286): planStatus/hasPlanFile are legacy compatibility fields.
|
|
80
|
+
// Live PD no longer reads or manages PLAN.md state. These fields must not be
|
|
81
|
+
// used for new MVP behavior. Future "plan-first" enforcement must come from
|
|
82
|
+
// owner-approved RuleHost/code_tool_hook activation, not built-in state.
|
|
83
|
+
planStatus: 'NONE' as const,
|
|
84
|
+
hasPlanFile: false,
|
|
76
85
|
},
|
|
77
86
|
session: {
|
|
78
87
|
sessionId: ctx.sessionId,
|
|
@@ -83,7 +92,7 @@ export function handleBeforeToolCall(
|
|
|
83
92
|
epTier: _getEpTier(wctx.workspaceDir),
|
|
84
93
|
},
|
|
85
94
|
derived: {
|
|
86
|
-
estimatedLineChanges: estimateLineChanges({ toolName: event.toolName, params: event.params }),
|
|
95
|
+
estimatedLineChanges: estimateLineChanges({ toolName: event.toolName, params: event.params ?? {} }),
|
|
87
96
|
bashRisk: _getBashRisk(event),
|
|
88
97
|
},
|
|
89
98
|
};
|
|
@@ -153,8 +162,170 @@ export function handleBeforeToolCall(
|
|
|
153
162
|
logger?.warn?.(`[PD_GATE] Failed to record rule_enforced/rulehost_requireApproval: ${String(evErr)}`);
|
|
154
163
|
}
|
|
155
164
|
}
|
|
165
|
+
|
|
166
|
+
if (hostResult?.decision === 'auto_correct' && hostResult.correctionProposal) {
|
|
167
|
+
const proposal = hostResult.correctionProposal;
|
|
168
|
+
let validation: { valid: boolean; errors: string[] };
|
|
169
|
+
try {
|
|
170
|
+
validation = validateCorrectionProposal(proposal);
|
|
171
|
+
} catch (validationError: unknown) {
|
|
172
|
+
validation = { valid: false, errors: [`Validator threw: ${String(validationError)}`] };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
const eventLog = EventLogService.get(wctx.stateDir, logger as PluginLogger | undefined);
|
|
177
|
+
const correctedFields = Array.isArray(proposal.correctedFields)
|
|
178
|
+
? proposal.correctedFields.map((f: unknown) => typeof f === 'object' && f !== null ? String((f as { field?: string }).field) : String(f))
|
|
179
|
+
: [];
|
|
180
|
+
eventLog.recordRuleHostAutoCorrectProposed({
|
|
181
|
+
toolName: event.toolName,
|
|
182
|
+
filePath: relPath,
|
|
183
|
+
ruleId: String(proposal.ruleId ?? 'unknown'),
|
|
184
|
+
principleId: proposal.principleId != null ? String(proposal.principleId) : undefined,
|
|
185
|
+
confidence: typeof proposal.confidence === 'number' ? proposal.confidence : 0,
|
|
186
|
+
reason: hostResult.reason,
|
|
187
|
+
applicationMode: proposal.applicationMode === 'live' ? 'live' : 'shadow',
|
|
188
|
+
correctedFields,
|
|
189
|
+
validationValid: validation.valid,
|
|
190
|
+
});
|
|
191
|
+
} catch (evErr) {
|
|
192
|
+
logger?.warn?.(`[PD_GATE] Failed to record rulehost_auto_correct_proposed: ${String(evErr)}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (proposal.applicationMode === 'live' && validation.valid) {
|
|
196
|
+
if (!event.params) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const originalParams = { ...event.params };
|
|
200
|
+
const nextParams: Record<string, unknown> = {};
|
|
201
|
+
const appliedFields: Array<{ field: string; original: unknown; applied: unknown }> = [];
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
if (!Array.isArray(proposal.correctedFields)) {
|
|
205
|
+
throw new Error('proposal.correctedFields is not an array');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!proposal.proposedParams || typeof proposal.proposedParams !== 'object' || Array.isArray(proposal.proposedParams)) {
|
|
209
|
+
throw new Error('proposal.proposedParams must be an object');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const trustedWorkspaceDir = ctx.workspaceDir;
|
|
213
|
+
if (typeof trustedWorkspaceDir === 'string' && trustedWorkspaceDir.trim().length > 0) {
|
|
214
|
+
const pathBoundsResult = validateProposedPathBounds(proposal.proposedParams, trustedWorkspaceDir);
|
|
215
|
+
if (!pathBoundsResult.valid) {
|
|
216
|
+
throw new Error(`Path boundary violation: ${pathBoundsResult.reason}`);
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
const hasPathField = Object.keys(proposal.proposedParams).some(k => typeof proposal.proposedParams[k] === 'string' && (k === 'file_path' || k === 'path' || k === 'filePath'));
|
|
220
|
+
if (hasPathField) {
|
|
221
|
+
throw new Error('Cannot apply live auto-correction with path fields: no trusted workspace directory available');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
for (const cf of proposal.correctedFields) {
|
|
226
|
+
if (typeof cf !== 'object' || cf === null || typeof cf.field !== 'string') {
|
|
227
|
+
throw new Error('correctedFields entry must be an object with a string field');
|
|
228
|
+
}
|
|
229
|
+
const field = cf.field;
|
|
230
|
+
if (!Object.hasOwn(event.params, field)) {
|
|
231
|
+
throw new Error(`Field '${field}' not found in event.params`);
|
|
232
|
+
}
|
|
233
|
+
if (!Object.hasOwn(proposal.proposedParams, field)) {
|
|
234
|
+
throw new Error(`Field '${field}' not found in proposal.proposedParams`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
for (const cf of proposal.correctedFields) {
|
|
239
|
+
if (typeof cf === 'object' && cf !== null && typeof cf.field === 'string') {
|
|
240
|
+
const field = cf.field;
|
|
241
|
+
const originalValue = event.params[field];
|
|
242
|
+
const appliedValue = proposal.proposedParams[field];
|
|
243
|
+
nextParams[field] = appliedValue;
|
|
244
|
+
appliedFields.push({
|
|
245
|
+
field,
|
|
246
|
+
original: originalValue,
|
|
247
|
+
applied: appliedValue,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
Object.assign(event.params, nextParams);
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
const eventLog = EventLogService.get(wctx.stateDir, logger as PluginLogger | undefined);
|
|
256
|
+
eventLog.recordRuleHostAutoCorrectApplied({
|
|
257
|
+
toolName: event.toolName,
|
|
258
|
+
filePath: relPath,
|
|
259
|
+
ruleId: String(proposal.ruleId ?? 'unknown'),
|
|
260
|
+
principleId: proposal.principleId != null ? String(proposal.principleId) : undefined,
|
|
261
|
+
confidence: typeof proposal.confidence === 'number' ? proposal.confidence : 0,
|
|
262
|
+
reason: hostResult.reason || proposal.correctedFields?.[0]?.reason || 'auto-correct applied',
|
|
263
|
+
correctedFields: appliedFields,
|
|
264
|
+
});
|
|
265
|
+
} catch (evErr) {
|
|
266
|
+
logger?.warn?.(`[PD_GATE] Failed to record rulehost_auto_correct_applied: ${String(evErr)}`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (proposal.notifyAgent === true && appliedFields.length > 0) {
|
|
270
|
+
const messages = appliedFields.map(f =>
|
|
271
|
+
`[PD Auto-Correct] Rule ${proposal.ruleId}: ${proposal.correctedFields?.[0]?.reason || 'correction applied'}. Parameter '${f.field}' was adjusted from ${JSON.stringify(f.original)} to ${JSON.stringify(f.applied)}.`
|
|
272
|
+
);
|
|
273
|
+
return {
|
|
274
|
+
toolArgs: event.toolArgs,
|
|
275
|
+
skipToolCall: false,
|
|
276
|
+
_pdAutoCorrectWarning: messages.join('\n'),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
} catch (applyError: unknown) {
|
|
280
|
+
if (event.params) {
|
|
281
|
+
Object.assign(event.params, originalParams);
|
|
282
|
+
}
|
|
283
|
+
const errorMsg = String(applyError);
|
|
284
|
+
const isPathViolation = errorMsg.includes('Path boundary violation') || errorMsg.includes('no trusted workspace directory');
|
|
285
|
+
if (isPathViolation) {
|
|
286
|
+
logger?.warn?.(`[PD_GATE] Live auto-correction rejected — path out of bounds: ${errorMsg}`);
|
|
287
|
+
try {
|
|
288
|
+
const eventLog = EventLogService.get(wctx.stateDir, logger as PluginLogger | undefined);
|
|
289
|
+
eventLog.recordRuleHostAutoCorrectProposed({
|
|
290
|
+
toolName: event.toolName,
|
|
291
|
+
filePath: relPath,
|
|
292
|
+
ruleId: String(proposal.ruleId ?? 'unknown'),
|
|
293
|
+
principleId: proposal.principleId != null ? String(proposal.principleId) : undefined,
|
|
294
|
+
confidence: typeof proposal.confidence === 'number' ? proposal.confidence : 0,
|
|
295
|
+
reason: `Path boundary rejected: ${errorMsg}`,
|
|
296
|
+
applicationMode: 'shadow',
|
|
297
|
+
correctedFields: Array.isArray(proposal.correctedFields)
|
|
298
|
+
? proposal.correctedFields.map((f: unknown) => typeof f === 'object' && f !== null ? String((f as { field?: string }).field) : String(f))
|
|
299
|
+
: [],
|
|
300
|
+
validationValid: false,
|
|
301
|
+
});
|
|
302
|
+
} catch (evErr) {
|
|
303
|
+
logger?.warn?.(`[PD_GATE] Failed to record path rejection telemetry: ${String(evErr)}`);
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
logger?.warn?.(`[PD_GATE] Failed to apply auto-correction, using original params: ${errorMsg}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
} else if (hostResult?.decision === 'auto_correct') {
|
|
311
|
+
// auto_correct without correctionProposal — emit telemetry for observability
|
|
312
|
+
try {
|
|
313
|
+
const eventLog = EventLogService.get(wctx.stateDir, logger as PluginLogger | undefined);
|
|
314
|
+
eventLog.recordRuleHostAutoCorrectProposed({
|
|
315
|
+
toolName: event.toolName,
|
|
316
|
+
filePath: relPath,
|
|
317
|
+
ruleId: hostResult.ruleId ?? 'unknown',
|
|
318
|
+
confidence: 0,
|
|
319
|
+
reason: hostResult.reason ?? 'auto_correct without correctionProposal',
|
|
320
|
+
applicationMode: 'shadow',
|
|
321
|
+
correctedFields: [],
|
|
322
|
+
validationValid: false,
|
|
323
|
+
});
|
|
324
|
+
} catch (evErr) {
|
|
325
|
+
logger?.warn?.(`[PD_GATE] Failed to record rulehost_auto_correct_proposed (no proposal): ${String(evErr)}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
156
328
|
} catch (hostError: unknown) {
|
|
157
|
-
// D-08: Conservative degradation — log and allow on Rule Host failure
|
|
158
329
|
logger.warn?.(`[PD_GATE:RULE_HOST] Host evaluation failed, allowing conservatively: ${String(hostError)}`);
|
|
159
330
|
}
|
|
160
331
|
|
|
@@ -177,25 +348,6 @@ function _extractParamsSummary(params: Record<string, unknown>): Record<string,
|
|
|
177
348
|
return summary;
|
|
178
349
|
}
|
|
179
350
|
|
|
180
|
-
function _getPlanStatus(workspaceDir: string): 'NONE' | 'DRAFT' | 'READY' | 'UNKNOWN' {
|
|
181
|
-
try {
|
|
182
|
-
const status = planStatus(workspaceDir);
|
|
183
|
-
if (status === 'READY') return 'READY';
|
|
184
|
-
if (status === 'DRAFT') return 'DRAFT';
|
|
185
|
-
if (status === '') return 'NONE';
|
|
186
|
-
return 'UNKNOWN';
|
|
187
|
-
} catch {
|
|
188
|
-
return 'UNKNOWN';
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function _hasPlanFile(workspaceDir: string): boolean {
|
|
193
|
-
try {
|
|
194
|
-
return fs.existsSync(path.join(workspaceDir, 'PLAN.md'));
|
|
195
|
-
} catch {
|
|
196
|
-
return false;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
351
|
|
|
200
352
|
function _getCurrentGfi(sessionId?: string): number {
|
|
201
353
|
if (!sessionId) return 0;
|
|
@@ -227,7 +379,7 @@ function _getEpTier(workspaceDir: string): number {
|
|
|
227
379
|
function _getBashRisk(event: PluginHookBeforeToolCallEvent): 'safe' | 'normal' | 'dangerous' | 'unknown' {
|
|
228
380
|
if (!BASH_TOOLS_SET.has(event.toolName)) return 'unknown';
|
|
229
381
|
try {
|
|
230
|
-
const command = String(event.params
|
|
382
|
+
const command = String(event.params?.command || event.params?.args || '');
|
|
231
383
|
const isDangerous = /\brm\s+-rf\b|\bchmod\b|\bchown\b|>\s*\/dev\//.test(command);
|
|
232
384
|
if (isDangerous) return 'dangerous';
|
|
233
385
|
const isMutation = /(?:>|>>|sed|rm|mv|mkdir|touch|cp|npm|yarn|pnpm|pip|cargo)/.test(command);
|
package/src/hooks/lifecycle.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as readline from 'readline';
|
|
4
|
-
import { recordAndWritePainFlag } from '../core/pain.js';
|
|
5
4
|
import { atomicWriteFileSync } from '../utils/io.js';
|
|
6
5
|
import { WorkspaceContext } from '../core/workspace-context.js';
|
|
7
6
|
import { PD_DIRS } from '../core/paths.js';
|
|
@@ -10,6 +9,7 @@ import {
|
|
|
10
9
|
mergeWorkingMemory,
|
|
11
10
|
} from '../core/focus-history.js';
|
|
12
11
|
import type { PluginHookBeforeResetEvent, PluginHookBeforeCompactionEvent, PluginHookAfterCompactionEvent, PluginHookAgentContext } from '../openclaw-sdk.js';
|
|
12
|
+
import { emitPainDetectedEvent } from './pain.js';
|
|
13
13
|
|
|
14
14
|
export async function handleBeforeReset(
|
|
15
15
|
event: PluginHookBeforeResetEvent,
|
|
@@ -66,7 +66,7 @@ export async function extractPainFromSessionFile(sessionFile: string, ctx: Plugi
|
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
ctx.logger?.info?.(`[Pain Extractor] Scanning session transcript for pain signals: ${sessionFile}`);
|
|
70
70
|
|
|
71
71
|
const fileStream = fs.createReadStream(sessionFile);
|
|
72
72
|
const rl = readline.createInterface({
|
|
@@ -97,13 +97,13 @@ export async function extractPainFromSessionFile(sessionFile: string, ctx: Plugi
|
|
|
97
97
|
|
|
98
98
|
if (msg.openclawAbort?.aborted) {
|
|
99
99
|
const runIdSafe = msg.openclawAbort?.runId || 'unknown';
|
|
100
|
-
|
|
100
|
+
ctx.logger?.info?.(`[Pain Extractor] Detected hard-abort snapshot (runId: ${runIdSafe})`);
|
|
101
101
|
painPoints.push(`[FATAL INTERCEPT] 动作被沙箱防御机制强制击落。大模型被击落前的思考流 (未遂动机): ${text.substring(0, 250)}...`);
|
|
102
102
|
continue;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
if (msg.__openclaw?.truncated && msg.__openclaw?.reason === 'oversized') {
|
|
106
|
-
|
|
106
|
+
ctx.logger?.info?.(`[Pain Extractor] Detected oversized data truncation placeholder`);
|
|
107
107
|
painPoints.push(`[COGNITIVE OVERLOAD] 大模型尝试读取极大体积的输入,已被底层守护程序抹除/折叠防爆。请反思是否读取了不当的文件或日志: ${text.substring(0, 150)}...`);
|
|
108
108
|
continue;
|
|
109
109
|
}
|
|
@@ -154,21 +154,19 @@ export async function extractPainFromSessionFile(sessionFile: string, ctx: Plugi
|
|
|
154
154
|
|
|
155
155
|
const hasFatal = painPoints.some(p => p.includes('[FATAL INTERCEPT]'));
|
|
156
156
|
if (hasFatal) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
session_id: ctx.sessionId || '',
|
|
171
|
-
agent_id: ctx.agentId || '',
|
|
157
|
+
// Emit via the Runtime v2 pain chain — no .pain_flag file written
|
|
158
|
+
emitPainDetectedEvent(wctx, {
|
|
159
|
+
ts: new Date().toISOString(),
|
|
160
|
+
type: 'pain_detected',
|
|
161
|
+
data: {
|
|
162
|
+
painId: `intercept_${Date.now()}`,
|
|
163
|
+
painType: 'tool_failure' as const,
|
|
164
|
+
source: 'intercept_extraction',
|
|
165
|
+
reason: 'Hard intercept detected in session history compaction.',
|
|
166
|
+
score: 100,
|
|
167
|
+
sessionId: ctx.sessionId || 'unknown',
|
|
168
|
+
agentId: ctx.agentId,
|
|
169
|
+
},
|
|
172
170
|
});
|
|
173
171
|
}
|
|
174
172
|
} catch (err) {
|
|
@@ -200,13 +198,14 @@ export async function handleBeforeCompaction(
|
|
|
200
198
|
}
|
|
201
199
|
|
|
202
200
|
// 提取工作记忆(从 sessionFile)
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
const sessionFile = (event as { sessionFile?: string }).sessionFile;
|
|
202
|
+
if (sessionFile) {
|
|
203
|
+
await extractPainFromSessionFile(sessionFile, ctx);
|
|
205
204
|
|
|
206
205
|
// 新增:提取并保存工作记忆
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
await extractAndSaveWorkingMemory(
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
await extractAndSaveWorkingMemory(sessionFile, ctx, wctx);
|
|
210
209
|
}
|
|
211
210
|
}
|
|
212
211
|
|
|
@@ -296,20 +295,18 @@ async function extractAndSaveWorkingMemory(
|
|
|
296
295
|
// 写入文件
|
|
297
296
|
atomicWriteFileSync(focusPath, updatedContent);
|
|
298
297
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
`${snapshot.nextActions.length} next actions to CURRENT_FOCUS.md`);
|
|
303
|
-
}
|
|
298
|
+
ctx.logger?.info?.(`[WorkingMemory] Preserved ${snapshot.artifacts.length} artifacts, ` +
|
|
299
|
+
`${snapshot.activeProblems.length} problems, ` +
|
|
300
|
+
`${snapshot.nextActions.length} next actions to CURRENT_FOCUS.md`);
|
|
304
301
|
} catch (err) {
|
|
305
302
|
ctx.logger?.error?.(`[PD:Lifecycle] Failed to save working memory: ${String(err)}`);
|
|
306
|
-
|
|
303
|
+
|
|
307
304
|
// 尝试恢复备份
|
|
308
305
|
const backupPath = `${focusPath}.wm-backup`;
|
|
309
306
|
if (fs.existsSync(backupPath)) {
|
|
310
307
|
try {
|
|
311
308
|
fs.copyFileSync(backupPath, focusPath);
|
|
312
|
-
|
|
309
|
+
ctx.logger?.warn?.(`[WorkingMemory] Restored from backup after failure`);
|
|
313
310
|
} catch {
|
|
314
311
|
// 忽略恢复错误
|
|
315
312
|
}
|
|
@@ -325,8 +322,9 @@ export async function handleAfterCompaction(
|
|
|
325
322
|
|
|
326
323
|
const [dateStrPost] = new Date().toISOString().split('T');
|
|
327
324
|
const checkpointPath = path.join(ctx.workspaceDir, PD_DIRS.MEMORY, `${dateStrPost}.md`);
|
|
325
|
+
const messageCount = (event as { messageCount?: number }).messageCount ?? 0;
|
|
328
326
|
const log =
|
|
329
|
-
`- Post-Compaction Complete. Reduced active context to ${
|
|
327
|
+
`- Post-Compaction Complete. Reduced active context to ${messageCount} messages.\n`;
|
|
330
328
|
|
|
331
329
|
try {
|
|
332
330
|
fs.appendFileSync(checkpointPath, log, 'utf8');
|