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
package/src/hooks/pain.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import { isRisky, normalizePath } from '../utils/io.js';
|
|
3
3
|
import { normalizeProfile } from '../core/profile.js';
|
|
4
|
-
import { computePainScore,
|
|
4
|
+
import { computePainScore, trackPrincipleValue } from '../core/pain.js';
|
|
5
5
|
import { getSession, trackFriction, resetFriction, getInjectedProbationIds, clearInjectedProbationIds, type SessionState } from '../core/session-tracker.js';
|
|
6
6
|
import { denoiseError, computeHash } from '../utils/hashing.js';
|
|
7
7
|
import { SystemLogger } from '../core/system-logger.js';
|
|
@@ -10,8 +10,10 @@ import { getEvolutionLogger, createTraceId } from '../core/evolution-logger.js';
|
|
|
10
10
|
import { recordEvolutionSuccess, recordEvolutionFailure } from '../core/evolution-engine.js';
|
|
11
11
|
import type { EvolutionLoopEvent } from '../core/evolution-types.js';
|
|
12
12
|
import type { PluginHookAfterToolCallEvent, PluginHookToolContext, OpenClawPluginApi } from '../openclaw-sdk.js';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { resolveWorkspaceDirForRuntimeV2 } from '../utils/workspace-resolver.js';
|
|
14
|
+
import { PainToPrincipleService, PrincipleTreeLedgerAdapter, type PainDetectedData, type PainEvidenceEntry, MAX_EVIDENCE_ENTRIES, MAX_EVIDENCE_NOTE_CHARS } from '@principles/core/runtime-v2';
|
|
15
|
+
import { evaluatePainDiagnosticGate } from '../core/pain-diagnostic-gate.js';
|
|
16
|
+
import { sanitizeAssistantText } from './message-sanitize.js';
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Interface for tool parameters to avoid 'any'
|
|
@@ -30,49 +32,182 @@ interface ToolParams {
|
|
|
30
32
|
|
|
31
33
|
const WRITE_TOOLS = ['write', 'edit', 'apply_patch', 'write_file', 'edit_file', 'replace'];
|
|
32
34
|
|
|
35
|
+
function createPainToPrincipleService(wctx: WorkspaceContext): PainToPrincipleService {
|
|
36
|
+
const ledgerAdapter = new PrincipleTreeLedgerAdapter({ stateDir: wctx.stateDir });
|
|
37
|
+
return new PainToPrincipleService({
|
|
38
|
+
workspaceDir: wctx.workspaceDir,
|
|
39
|
+
stateDir: wctx.stateDir,
|
|
40
|
+
ledgerAdapter,
|
|
41
|
+
owner: 'openclaw-plugin',
|
|
42
|
+
autoIntakeEnabled: true,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function buildTrajectoryEvidence(wctx: WorkspaceContext, sessionId: string): PainEvidenceEntry[] {
|
|
47
|
+
const evidence: PainEvidenceEntry[] = [];
|
|
48
|
+
|
|
49
|
+
if (!wctx.trajectory || sessionId === 'unknown') {
|
|
50
|
+
evidence.push({
|
|
51
|
+
sourceRef: 'owner_message:unavailable',
|
|
52
|
+
note: `trajectory_unavailable: ${!wctx.trajectory ? 'no_trajectory_db' : 'unknown_session'}`,
|
|
53
|
+
});
|
|
54
|
+
return evidence.slice(0, MAX_EVIDENCE_ENTRIES);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const userTurns = wctx.trajectory.listUserTurnsForSession(sessionId) ?? [];
|
|
59
|
+
const lastCorrectionTurn = [...userTurns].reverse().find(t => t.correctionDetected);
|
|
60
|
+
if (lastCorrectionTurn) {
|
|
61
|
+
const sanitizedOwnerMessage = sanitizeAssistantText(
|
|
62
|
+
(lastCorrectionTurn.rawExcerpt ?? '').slice(0, MAX_EVIDENCE_NOTE_CHARS)
|
|
63
|
+
);
|
|
64
|
+
evidence.push({
|
|
65
|
+
sourceRef: `owner_message:${lastCorrectionTurn.createdAt}`,
|
|
66
|
+
note: sanitizedOwnerMessage,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
} catch (e) {
|
|
70
|
+
evidence.push({
|
|
71
|
+
sourceRef: 'owner_message:unavailable',
|
|
72
|
+
note: `trajectory_user_turns_unavailable: ${String(e).slice(0, 100)}`,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const assistantTurns = wctx.trajectory.listAssistantTurns(sessionId) ?? [];
|
|
78
|
+
const recentAssistant = assistantTurns.slice(-3);
|
|
79
|
+
for (const turn of recentAssistant) {
|
|
80
|
+
if (evidence.length >= MAX_EVIDENCE_ENTRIES) break;
|
|
81
|
+
const sanitizedNote = sanitizeAssistantText(
|
|
82
|
+
(turn.sanitizedText ?? '').slice(0, MAX_EVIDENCE_NOTE_CHARS)
|
|
83
|
+
);
|
|
84
|
+
evidence.push({
|
|
85
|
+
sourceRef: `agent_turn:${turn.createdAt}`,
|
|
86
|
+
note: sanitizedNote,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
} catch (e) {
|
|
90
|
+
if (evidence.length < MAX_EVIDENCE_ENTRIES) {
|
|
91
|
+
evidence.push({
|
|
92
|
+
sourceRef: 'agent_turn:unavailable',
|
|
93
|
+
note: `trajectory_assistant_turns_unavailable: ${String(e).slice(0, 100)}`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return evidence.slice(0, MAX_EVIDENCE_ENTRIES);
|
|
99
|
+
}
|
|
100
|
+
|
|
33
101
|
function shouldAttributePrincipleToTool(principle: { contextTags: string[]; trigger: string; }, toolName: string): boolean {
|
|
34
102
|
return principle.contextTags.includes(toolName) || principle.trigger.includes(toolName);
|
|
35
103
|
}
|
|
36
104
|
|
|
37
|
-
function emitPainDetectedEvent(wctx: WorkspaceContext, event: EvolutionLoopEvent): void {
|
|
105
|
+
export async function emitPainDetectedEvent(wctx: WorkspaceContext, event: EvolutionLoopEvent): Promise<void> {
|
|
38
106
|
try {
|
|
39
107
|
wctx.evolutionReducer.emitSync(event);
|
|
40
108
|
} catch (e) {
|
|
41
109
|
SystemLogger.log(wctx.workspaceDir, 'EVOLUTION_EMIT_WARN', `Failed to emit evolution event: ${String(e)}`);
|
|
42
110
|
}
|
|
111
|
+
// M8: Bridge pain_detected → diagnostician pipeline
|
|
112
|
+
if (event.type === 'pain_detected') {
|
|
113
|
+
const painData = event.data as PainDetectedData;
|
|
114
|
+
try {
|
|
115
|
+
const service = createPainToPrincipleService(wctx);
|
|
116
|
+
const result = await service.recordPain({
|
|
117
|
+
painId: painData.painId,
|
|
118
|
+
painType: painData.painType,
|
|
119
|
+
source: painData.source,
|
|
120
|
+
reason: painData.reason,
|
|
121
|
+
score: painData.score,
|
|
122
|
+
sessionId: painData.sessionId,
|
|
123
|
+
agentId: painData.agentId,
|
|
124
|
+
taskId: painData.taskId,
|
|
125
|
+
traceId: painData.traceId,
|
|
126
|
+
provenance: painData.provenance,
|
|
127
|
+
evidence: painData.evidence,
|
|
128
|
+
recordObservability: true,
|
|
129
|
+
});
|
|
130
|
+
if (result.status === 'failed' && result.failureCategory) {
|
|
131
|
+
SystemLogger.log(wctx.workspaceDir, 'PAIN_SERVICE_FAILED', JSON.stringify({
|
|
132
|
+
painId: result.painId,
|
|
133
|
+
taskId: result.taskId,
|
|
134
|
+
failureCategory: result.failureCategory,
|
|
135
|
+
latencyMs: result.latencyMs,
|
|
136
|
+
message: result.message,
|
|
137
|
+
}));
|
|
138
|
+
} else if (result.status === 'skipped') {
|
|
139
|
+
SystemLogger.log(wctx.workspaceDir, 'PAIN_SERVICE_SKIPPED', JSON.stringify({
|
|
140
|
+
painId: result.painId,
|
|
141
|
+
taskId: result.taskId,
|
|
142
|
+
latencyMs: result.latencyMs,
|
|
143
|
+
message: result.message,
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
} catch (err) {
|
|
147
|
+
SystemLogger.log(wctx.workspaceDir, 'PAIN_SERVICE_ERROR', `recordPain threw: ${String(err)}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
43
150
|
}
|
|
44
151
|
|
|
45
152
|
function createPainId(sessionId: string): string {
|
|
46
153
|
return `pain_${Date.now()}_${computeHash(sessionId).slice(0, 8)}`;
|
|
47
154
|
}
|
|
48
155
|
|
|
156
|
+
export function classifyToolFailureSource(toolName: string | undefined, error: unknown): 'dispatch_error' | 'tool_failure' {
|
|
157
|
+
if (!toolName || toolName.trim() === '') return 'dispatch_error';
|
|
158
|
+
const msg = String(error ?? '');
|
|
159
|
+
// Dropped "error:" prefix to catch "failed: unknown tool read_file" style messages.
|
|
160
|
+
// Catches: "tool not found", "tool <name> not found", "unknown tool".
|
|
161
|
+
// Word-boundary anchors prevent "report_tool_not_found" from matching.
|
|
162
|
+
if (/\btool\s+(?:\S+\s+)?not\s+found\b/i.test(msg)) return 'dispatch_error';
|
|
163
|
+
if (/\bunknown\s+tool\b/i.test(msg)) return 'dispatch_error';
|
|
164
|
+
return 'tool_failure';
|
|
165
|
+
}
|
|
166
|
+
|
|
49
167
|
export function handleAfterToolCall(
|
|
50
168
|
event: PluginHookAfterToolCallEvent,
|
|
51
169
|
ctx: PluginHookToolContext & { workspaceDir?: string; pluginConfig?: Record<string, unknown> },
|
|
52
170
|
api?: OpenClawPluginApi
|
|
53
171
|
): void {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
172
|
+
let effectiveWorkspaceDir: string;
|
|
173
|
+
try {
|
|
174
|
+
effectiveWorkspaceDir = resolveWorkspaceDirForRuntimeV2(ctx, api, 'after_tool_call');
|
|
175
|
+
} catch {
|
|
58
176
|
return;
|
|
59
177
|
}
|
|
60
178
|
|
|
61
|
-
const wctx = WorkspaceContext.
|
|
179
|
+
const wctx = WorkspaceContext.fromHookContextExplicit({ ...ctx, workspaceDir: effectiveWorkspaceDir });
|
|
62
180
|
const {config} = wctx;
|
|
63
181
|
const {eventLog} = wctx;
|
|
64
182
|
const sessionId = ctx.sessionId || 'unknown';
|
|
65
183
|
const sessionState = ctx.sessionId ? getSession(ctx.sessionId) : undefined;
|
|
66
184
|
const gfiBefore = sessionState?.currentGfi ?? 0;
|
|
185
|
+
let latestFailureState: SessionState | undefined;
|
|
67
186
|
const params = event.params as ToolParams;
|
|
68
187
|
|
|
188
|
+
// Load profile once (with 1MB size guard) — used by both failure and legacy risky-write paths
|
|
189
|
+
const profilePath = wctx.resolve('PROFILE');
|
|
190
|
+
let profile = normalizeProfile({});
|
|
191
|
+
if (fs.existsSync(profilePath)) {
|
|
192
|
+
try {
|
|
193
|
+
const content = fs.readFileSync(profilePath, 'utf8');
|
|
194
|
+
if (content.length > 1024 * 1024) {
|
|
195
|
+
SystemLogger.log(effectiveWorkspaceDir, 'PROFILE_PARSE_WARN', 'PROFILE.json exceeds 1 MB, skipping');
|
|
196
|
+
} else {
|
|
197
|
+
profile = normalizeProfile(JSON.parse(content));
|
|
198
|
+
}
|
|
199
|
+
} catch (e) {
|
|
200
|
+
SystemLogger.log(effectiveWorkspaceDir, 'PROFILE_PARSE_WARN', `Failed to parse PROFILE.json: ${String(e)}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
69
204
|
// ── Track A: Empirical Friction (GFI) ──
|
|
70
|
-
|
|
205
|
+
|
|
71
206
|
// 0. Special Case: Manual Pain Intervention
|
|
72
207
|
if (event.toolName === 'pain' || event.toolName === 'skill:pain') {
|
|
73
208
|
const reason = params.input || params.arguments || 'Manual intervention';
|
|
74
209
|
const traceId = createTraceId();
|
|
75
|
-
trackFriction(sessionId, 100, 'manual_pain', effectiveWorkspaceDir);
|
|
210
|
+
trackFriction(sessionId, 100, 'manual_pain', effectiveWorkspaceDir, { source: 'manual_pain' });
|
|
76
211
|
SystemLogger.log(effectiveWorkspaceDir, 'MANUAL_PAIN', `User manually triggered pain: ${reason}`);
|
|
77
212
|
eventLog.recordPainSignal(sessionId, {
|
|
78
213
|
score: 100,
|
|
@@ -100,6 +235,34 @@ export function handleAfterToolCall(
|
|
|
100
235
|
sessionId,
|
|
101
236
|
});
|
|
102
237
|
|
|
238
|
+
// Apply PainDiagnosticGate with cooldown to prevent duplicate diagnoses
|
|
239
|
+
const session = getSession(sessionId);
|
|
240
|
+
const gate = evaluatePainDiagnosticGate({
|
|
241
|
+
source: 'manual',
|
|
242
|
+
score: 100,
|
|
243
|
+
currentGfi: session?.currentGfi ?? 0,
|
|
244
|
+
sessionId,
|
|
245
|
+
});
|
|
246
|
+
if (!gate.shouldDiagnose) {
|
|
247
|
+
SystemLogger.log(effectiveWorkspaceDir, 'MANUAL_PAIN_SKIPPED', `Manual pain within cooldown: ${gate.detail}`);
|
|
248
|
+
let payload: string;
|
|
249
|
+
try {
|
|
250
|
+
payload = JSON.stringify({
|
|
251
|
+
reason: gate.reason,
|
|
252
|
+
detail: gate.detail,
|
|
253
|
+
source: 'manual',
|
|
254
|
+
sessionId,
|
|
255
|
+
gfi: 0,
|
|
256
|
+
score: 100,
|
|
257
|
+
});
|
|
258
|
+
} catch (e) {
|
|
259
|
+
SystemLogger.log(effectiveWorkspaceDir, 'PAYLOAD_SERIALIZE_FAILED', String(e));
|
|
260
|
+
payload = JSON.stringify({ reason: gate.reason, detail: '(log serialization failed)' });
|
|
261
|
+
}
|
|
262
|
+
SystemLogger.log(effectiveWorkspaceDir, 'PAIN_GATE_REJECTED', payload);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
103
266
|
emitPainDetectedEvent(wctx, {
|
|
104
267
|
ts: new Date().toISOString(),
|
|
105
268
|
type: 'pain_detected',
|
|
@@ -112,22 +275,34 @@ export function handleAfterToolCall(
|
|
|
112
275
|
sessionId,
|
|
113
276
|
traceId,
|
|
114
277
|
agentId: ctx.agentId,
|
|
278
|
+
provenance: (sessionId && sessionId !== 'unknown') ? 'openclaw_context_bound' : 'owner_reported_no_host_trace',
|
|
279
|
+
evidence: buildTrajectoryEvidence(wctx, sessionId),
|
|
115
280
|
},
|
|
116
281
|
});
|
|
117
282
|
return;
|
|
118
283
|
}
|
|
119
284
|
|
|
120
285
|
// 1. Determine if this was a failure
|
|
121
|
-
|
|
122
|
-
|
|
286
|
+
// Support nested details structure where OpenClaw exec tool stores exitCode in result.details.exitCode
|
|
287
|
+
// Prefer the first *numeric* exit code: if result.exitCode is non-numeric, fall back to details.exitCode
|
|
288
|
+
const resultObj = (event.result && typeof event.result === 'object') ? event.result as Record<string, unknown> : null;
|
|
289
|
+
const details = resultObj?.details && typeof resultObj.details === 'object' ? resultObj.details as Record<string, unknown> : null;
|
|
290
|
+
const topExitCode = resultObj?.exitCode;
|
|
291
|
+
const detailExitCode = details?.exitCode;
|
|
292
|
+
const exitCode = typeof topExitCode === 'number' ? topExitCode
|
|
293
|
+
: typeof detailExitCode === 'number' ? detailExitCode
|
|
294
|
+
: 0;
|
|
295
|
+
const isFailure = !!event.error || exitCode !== 0;
|
|
123
296
|
|
|
124
297
|
if (isFailure) {
|
|
125
|
-
const
|
|
298
|
+
const failureSource = classifyToolFailureSource(event.toolName, event.error);
|
|
299
|
+
const errorText = String(event.error ?? (typeof event.result === 'string' ? event.result : JSON.stringify(event.result)));
|
|
126
300
|
const denoised = denoiseError(errorText);
|
|
127
301
|
const hash = computeHash(denoised);
|
|
128
|
-
|
|
302
|
+
|
|
129
303
|
const deltaF = config.get('scores.tool_failure_friction') || 30;
|
|
130
|
-
const updatedState = trackFriction(sessionId, deltaF, hash, effectiveWorkspaceDir, { source:
|
|
304
|
+
const updatedState = trackFriction(sessionId, deltaF, hash, effectiveWorkspaceDir, { source: failureSource });
|
|
305
|
+
latestFailureState = updatedState;
|
|
131
306
|
|
|
132
307
|
// ── Trust Engine: Record failure ──
|
|
133
308
|
|
|
@@ -136,17 +311,7 @@ export function handleAfterToolCall(
|
|
|
136
311
|
const filePath = params.file_path || params.path || params.file;
|
|
137
312
|
const relPath = typeof filePath === 'string' ? normalizePath(filePath, effectiveWorkspaceDir) : 'unknown';
|
|
138
313
|
|
|
139
|
-
//
|
|
140
|
-
const profilePath = wctx.resolve('PROFILE');
|
|
141
|
-
let profile = normalizeProfile({});
|
|
142
|
-
if (fs.existsSync(profilePath)) {
|
|
143
|
-
try {
|
|
144
|
-
profile = normalizeProfile(JSON.parse(fs.readFileSync(profilePath, 'utf8')));
|
|
145
|
-
} catch (e) {
|
|
146
|
-
SystemLogger.log(effectiveWorkspaceDir, 'PROFILE_PARSE_WARN', `Failed to parse PROFILE.json: ${String(e)}`);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
314
|
+
// Use profile loaded at function scope (1MB guard already applied)
|
|
150
315
|
const isRisk = isRisky(relPath, profile.risk_paths);
|
|
151
316
|
|
|
152
317
|
recordEvolutionFailure(effectiveWorkspaceDir, event.toolName, {
|
|
@@ -164,6 +329,8 @@ export function handleAfterToolCall(
|
|
|
164
329
|
gfi: updatedState.currentGfi,
|
|
165
330
|
consecutiveErrors: updatedState.consecutiveErrors,
|
|
166
331
|
exitCode: exitCode as number | undefined,
|
|
332
|
+
gfiBefore,
|
|
333
|
+
gfiAfter: updatedState.currentGfi,
|
|
167
334
|
});
|
|
168
335
|
wctx.trajectory?.recordToolCall?.({
|
|
169
336
|
sessionId,
|
|
@@ -189,23 +356,29 @@ export function handleAfterToolCall(
|
|
|
189
356
|
clearInjectedProbationIds(sessionId, effectiveWorkspaceDir);
|
|
190
357
|
} else {
|
|
191
358
|
// ── SUCCESS BRANCH ──
|
|
192
|
-
//
|
|
193
|
-
// This prevents "read file success" from wiping
|
|
359
|
+
// PRI-80: Relieve both dispatch_error and tool_failure on success.
|
|
360
|
+
// This prevents "read file success" from wiping dispatch error signals.
|
|
194
361
|
const session = getSession(sessionId);
|
|
195
362
|
const toolFailureGfi = session?.gfiBySource?.tool_failure || 0;
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
let resetState: SessionState;
|
|
199
|
-
if (toolFailureGfi > 0) {
|
|
200
|
-
//
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
363
|
+
const dispatchErrorGfi = session?.gfiBySource?.dispatch_error || 0;
|
|
364
|
+
|
|
365
|
+
let resetState: SessionState = session || resetFriction(sessionId, effectiveWorkspaceDir);
|
|
366
|
+
if (toolFailureGfi > 0 || dispatchErrorGfi > 0) {
|
|
367
|
+
// Relieve both sources proportionally (50% relief each)
|
|
368
|
+
if (toolFailureGfi > 0) {
|
|
369
|
+
const reliefAmount = toolFailureGfi * 0.5;
|
|
370
|
+
resetState = resetFriction(sessionId, effectiveWorkspaceDir, {
|
|
371
|
+
source: 'tool_failure',
|
|
372
|
+
amount: reliefAmount,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
if (dispatchErrorGfi > 0) {
|
|
376
|
+
const reliefAmount = dispatchErrorGfi * 0.5;
|
|
377
|
+
resetState = resetFriction(sessionId, effectiveWorkspaceDir, {
|
|
378
|
+
source: 'dispatch_error',
|
|
379
|
+
amount: reliefAmount,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
209
382
|
}
|
|
210
383
|
|
|
211
384
|
recordEvolutionSuccess(effectiveWorkspaceDir, event.toolName, {
|
|
@@ -233,31 +406,29 @@ export function handleAfterToolCall(
|
|
|
233
406
|
paramsJson: event.params,
|
|
234
407
|
});
|
|
235
408
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
409
|
+
const filePath = params.file_path || params.path || params.file;
|
|
410
|
+
eventLog.recordToolCall(sessionId, {
|
|
411
|
+
toolName: event.toolName,
|
|
412
|
+
filePath: typeof filePath === 'string' ? filePath : undefined,
|
|
413
|
+
gfi: resetState.currentGfi,
|
|
414
|
+
gfiBefore,
|
|
415
|
+
gfiAfter: resetState.currentGfi,
|
|
416
|
+
});
|
|
243
417
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
}
|
|
418
|
+
// ── Hygiene Tracking: Record persistence actions ──
|
|
419
|
+
const normalized = typeof filePath === 'string' ? filePath.replace(/\\/g, '/') : '';
|
|
420
|
+
const isMemory = /(?:^|\/)memory\//.test(normalized) || normalized.endsWith('/MEMORY.md') || normalized === 'MEMORY.md';
|
|
421
|
+
const isPlan = normalized === 'PLAN.md' || normalized.endsWith('/PLAN.md');
|
|
422
|
+
|
|
423
|
+
if (isMemory || isPlan) {
|
|
424
|
+
const content = params.content || params.new_string || '';
|
|
425
|
+
wctx.hygiene.recordPersistence({
|
|
426
|
+
ts: new Date().toISOString(),
|
|
427
|
+
tool: event.toolName,
|
|
428
|
+
path: typeof filePath === 'string' ? filePath : 'unknown',
|
|
429
|
+
type: isMemory ? 'memory' : 'plan',
|
|
430
|
+
contentLength: content.length,
|
|
431
|
+
});
|
|
261
432
|
}
|
|
262
433
|
|
|
263
434
|
// Special case for memory_store tool (Success only)
|
|
@@ -278,29 +449,59 @@ export function handleAfterToolCall(
|
|
|
278
449
|
return;
|
|
279
450
|
}
|
|
280
451
|
|
|
452
|
+
const failureSource = classifyToolFailureSource(event.toolName, event.error);
|
|
453
|
+
|
|
281
454
|
const filePath = params.file_path || params.path || params.file;
|
|
282
455
|
const relPath = typeof filePath === 'string' ? normalizePath(filePath, effectiveWorkspaceDir) : 'unknown';
|
|
283
456
|
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
457
|
+
const isRisk = isRisky(relPath, profile.risk_paths);
|
|
458
|
+
const painScore = computePainScore(1, false, false, isRisk ? 20 : 0, effectiveWorkspaceDir);
|
|
459
|
+
const traceId = createTraceId();
|
|
460
|
+
const diagnosticGate = evaluatePainDiagnosticGate({
|
|
461
|
+
source: failureSource,
|
|
462
|
+
score: painScore,
|
|
463
|
+
currentGfi: (latestFailureState ?? getSession(sessionId) ?? sessionState)?.currentGfi ?? 0,
|
|
464
|
+
consecutiveErrors: (latestFailureState ?? getSession(sessionId) ?? sessionState)?.consecutiveErrors ?? 0,
|
|
465
|
+
isRisky: isRisk,
|
|
466
|
+
errorHash: latestFailureState?.lastErrorHash,
|
|
467
|
+
sessionId,
|
|
468
|
+
thresholds: {
|
|
469
|
+
painTrigger: config.get('thresholds.pain_trigger') || 40,
|
|
470
|
+
highSeverity: config.get('severity_thresholds.high') || 70,
|
|
471
|
+
repeatedFailure: config.get('thresholds.stuck_loops_trigger') || 4,
|
|
472
|
+
},
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
if (!diagnosticGate.shouldDiagnose) {
|
|
476
|
+
SystemLogger.log(
|
|
477
|
+
effectiveWorkspaceDir,
|
|
478
|
+
'PAIN_DIAGNOSE_SKIPPED',
|
|
479
|
+
`Tool failure recorded as friction only: ${diagnosticGate.detail}; tool=${event.toolName}; path=${relPath}`,
|
|
480
|
+
);
|
|
481
|
+
// Structured gate rejection event for traceability
|
|
482
|
+
let rejectPayload: string;
|
|
287
483
|
try {
|
|
288
|
-
|
|
484
|
+
rejectPayload = JSON.stringify({
|
|
485
|
+
reason: diagnosticGate.reason,
|
|
486
|
+
detail: diagnosticGate.detail,
|
|
487
|
+
source: failureSource,
|
|
488
|
+
sessionId: sessionId,
|
|
489
|
+
gfi: (latestFailureState ?? getSession(sessionId) ?? sessionState)?.currentGfi ?? 0,
|
|
490
|
+
score: painScore,
|
|
491
|
+
});
|
|
289
492
|
} catch (e) {
|
|
290
|
-
SystemLogger.log(effectiveWorkspaceDir, '
|
|
493
|
+
SystemLogger.log(effectiveWorkspaceDir, 'PAYLOAD_SERIALIZE_FAILED', String(e));
|
|
494
|
+
rejectPayload = JSON.stringify({ reason: diagnosticGate.reason, detail: '(log serialization failed)' });
|
|
291
495
|
}
|
|
496
|
+
SystemLogger.log(effectiveWorkspaceDir, 'PAIN_GATE_REJECTED', rejectPayload);
|
|
497
|
+
return;
|
|
292
498
|
}
|
|
293
499
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
// Record to trajectory FIRST so we get the real auto-increment ID.
|
|
299
|
-
// This ID propagates through the pain flag → evolution task → principle,
|
|
300
|
-
// so the compiler can later resolve derivedFromPainIds correctly.
|
|
301
|
-
const trajectoryPainId = wctx.trajectory?.recordPainEvent({
|
|
500
|
+
// Record to trajectory before Runtime V2 diagnosis so the compiler can later
|
|
501
|
+
// resolve derivedFromPainIds to the originating failed action.
|
|
502
|
+
wctx.trajectory?.recordPainEvent({
|
|
302
503
|
sessionId,
|
|
303
|
-
source:
|
|
504
|
+
source: failureSource,
|
|
304
505
|
score: painScore,
|
|
305
506
|
reason: `Tool ${event.toolName} failed on ${relPath}`,
|
|
306
507
|
severity: painScore >= 70 ? 'severe' : painScore >= 40 ? 'moderate' : 'mild',
|
|
@@ -308,28 +509,17 @@ export function handleAfterToolCall(
|
|
|
308
509
|
text: params.text ?? params.content ?? undefined,
|
|
309
510
|
});
|
|
310
511
|
|
|
311
|
-
|
|
312
|
-
source: 'tool_failure',
|
|
313
|
-
score: String(painScore),
|
|
314
|
-
reason: `Tool ${event.toolName} failed on ${relPath}. Error: ${event.error ?? 'Non-zero exit code'}`,
|
|
315
|
-
is_risky: isRisk,
|
|
316
|
-
trace_id: traceId,
|
|
317
|
-
session_id: sessionId,
|
|
318
|
-
agent_id: ctx.agentId || '',
|
|
319
|
-
pain_event_id: trajectoryPainId !== undefined && trajectoryPainId >= 0 ? String(trajectoryPainId) : undefined,
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
try {
|
|
323
|
-
writePainFlag(effectiveWorkspaceDir, painData);
|
|
324
|
-
} catch (e) {
|
|
325
|
-
SystemLogger.log(effectiveWorkspaceDir, 'WRITE_PAIN_FLAG_WARN', `Failed to write pain flag: ${String(e)}`);
|
|
326
|
-
}
|
|
512
|
+
// Pain signal emitted via emitPainDetectedEvent below — no .pain_flag file written (M8: single-path chain)
|
|
327
513
|
|
|
328
514
|
// Observe: track which principles would have prevented this pain (Phase 1, observation-only)
|
|
329
515
|
try {
|
|
330
516
|
trackPrincipleValue(
|
|
331
517
|
effectiveWorkspaceDir,
|
|
332
|
-
{
|
|
518
|
+
{
|
|
519
|
+
reason: `Tool ${event.toolName} failed on ${relPath}. Error: ${event.error ?? 'Non-zero exit code'}`,
|
|
520
|
+
source: failureSource,
|
|
521
|
+
score: String(painScore),
|
|
522
|
+
},
|
|
333
523
|
() => wctx.evolutionReducer.getActivePrinciples().map((p) => ({
|
|
334
524
|
id: p.id,
|
|
335
525
|
trigger: p.trigger,
|
|
@@ -353,19 +543,21 @@ export function handleAfterToolCall(
|
|
|
353
543
|
implementationCost: 0,
|
|
354
544
|
benefitScore: 0,
|
|
355
545
|
});
|
|
356
|
-
} catch {
|
|
546
|
+
} catch (e) {
|
|
357
547
|
// Non-critical — metrics tracked in memory
|
|
548
|
+
SystemLogger.log(effectiveWorkspaceDir, 'METRICS_UPDATE_SKIP', String(e));
|
|
358
549
|
}
|
|
359
550
|
}
|
|
360
551
|
},
|
|
361
552
|
);
|
|
362
|
-
} catch {
|
|
553
|
+
} catch (e) {
|
|
363
554
|
// Observation only — never disrupt the pain pipeline
|
|
555
|
+
SystemLogger.log(effectiveWorkspaceDir, ' PRINCIPLE_TRACK_SKIP', String(e));
|
|
364
556
|
}
|
|
365
557
|
|
|
366
558
|
eventLog.recordPainSignal(sessionId, {
|
|
367
559
|
score: painScore,
|
|
368
|
-
source:
|
|
560
|
+
source: failureSource,
|
|
369
561
|
reason: `Tool ${event.toolName} failed on ${relPath}`,
|
|
370
562
|
isRisky: isRisk,
|
|
371
563
|
});
|
|
@@ -374,7 +566,7 @@ export function handleAfterToolCall(
|
|
|
374
566
|
const evoLogger = getEvolutionLogger(effectiveWorkspaceDir, wctx.trajectory);
|
|
375
567
|
evoLogger.logPainDetected({
|
|
376
568
|
traceId,
|
|
377
|
-
source:
|
|
569
|
+
source: failureSource,
|
|
378
570
|
reason: `Tool ${event.toolName} failed on ${relPath}`,
|
|
379
571
|
score: painScore,
|
|
380
572
|
toolName: event.toolName,
|
|
@@ -387,13 +579,15 @@ export function handleAfterToolCall(
|
|
|
387
579
|
type: 'pain_detected',
|
|
388
580
|
data: {
|
|
389
581
|
painId: createPainId(sessionId),
|
|
390
|
-
painType:
|
|
582
|
+
painType: failureSource,
|
|
391
583
|
source: event.toolName,
|
|
392
|
-
reason: `Tool ${event.toolName} failed on ${relPath}`,
|
|
584
|
+
reason: `Tool ${event.toolName} failed on ${relPath}; diagnosticGate=${diagnosticGate.reason}`,
|
|
393
585
|
score: painScore,
|
|
394
586
|
sessionId,
|
|
395
587
|
traceId,
|
|
396
588
|
agentId: ctx.agentId,
|
|
589
|
+
provenance: 'automatic_hook',
|
|
590
|
+
evidence: buildTrajectoryEvidence(wctx, sessionId),
|
|
397
591
|
},
|
|
398
592
|
});
|
|
399
593
|
}
|