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/core/rule-host.ts
CHANGED
|
@@ -25,21 +25,19 @@ import {
|
|
|
25
25
|
listImplementationsByLifecycleState,
|
|
26
26
|
} from './principle-tree-ledger.js';
|
|
27
27
|
import { loadEntrySource } from './code-implementation-storage.js';
|
|
28
|
-
import { createRuleHostHelpers } from '
|
|
28
|
+
import { createRuleHostHelpers } from '@principles/core/runtime-v2';
|
|
29
|
+
import { mergeDecisions } from '@principles/core/runtime-v2';
|
|
29
30
|
import { loadRuleImplementationModule } from './rule-implementation-runtime.js';
|
|
30
31
|
import type {
|
|
31
32
|
RuleHostInput,
|
|
32
33
|
RuleHostResult,
|
|
33
34
|
RuleHostMeta,
|
|
34
35
|
LoadedImplementation,
|
|
35
|
-
} from '
|
|
36
|
+
} from '@principles/core/runtime-v2';
|
|
36
37
|
import type { Implementation } from '../types/principle-tree-schema.js';
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
warn?: (_message: string) => void;
|
|
41
|
-
|
|
42
|
-
}
|
|
39
|
+
import type { RuleHostLogger } from '@principles/core/runtime-v2';
|
|
40
|
+
export type { RuleHostLogger } from '@principles/core/runtime-v2';
|
|
43
41
|
|
|
44
42
|
export class RuleHost {
|
|
45
43
|
private readonly stateDir: string;
|
|
@@ -62,63 +60,8 @@ export class RuleHost {
|
|
|
62
60
|
|
|
63
61
|
evaluate(input: RuleHostInput): RuleHostResult | undefined {
|
|
64
62
|
try {
|
|
65
|
-
// Load active code implementations from the ledger
|
|
66
63
|
const activeImpls = this._loadActiveCodeImplementations();
|
|
67
|
-
|
|
68
|
-
if (activeImpls.length === 0) {
|
|
69
|
-
return undefined;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Merge decisions from all active implementations
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
let blocked: RuleHostResult | undefined;
|
|
76
|
-
const approvals: RuleHostResult[] = [];
|
|
77
|
-
|
|
78
|
-
for (const impl of activeImpls) {
|
|
79
|
-
try {
|
|
80
|
-
const result = impl.evaluate(input);
|
|
81
|
-
|
|
82
|
-
if (!result.matched) {
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (result.decision === 'block') {
|
|
87
|
-
blocked = result;
|
|
88
|
-
break; // Short-circuit on block
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (result.decision === 'requireApproval') {
|
|
92
|
-
approvals.push(result);
|
|
93
|
-
}
|
|
94
|
-
// 'allow' is implicit — no action needed
|
|
95
|
-
} catch (evalError: unknown) {
|
|
96
|
-
// Individual implementation error: log and continue (D-08)
|
|
97
|
-
this.logger.warn?.(
|
|
98
|
-
`[RuleHost] Implementation ${impl.implId} evaluation failed: ${String(evalError)}`
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (blocked) {
|
|
104
|
-
return blocked;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (approvals.length > 0) {
|
|
108
|
-
// Merge multiple requireApproval results
|
|
109
|
-
return {
|
|
110
|
-
decision: 'requireApproval',
|
|
111
|
-
matched: true,
|
|
112
|
-
reason: approvals.map((a) => a.reason).join('; '),
|
|
113
|
-
diagnostics: approvals.reduce<Record<string, unknown>>(
|
|
114
|
-
(acc, a) => ({ ...acc, ...a.diagnostics }),
|
|
115
|
-
{}
|
|
116
|
-
),
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// All implementations returned allow or matched=false — no opinion
|
|
121
|
-
return undefined;
|
|
64
|
+
return mergeDecisions(activeImpls, input, this.logger);
|
|
122
65
|
} catch (hostError: unknown) {
|
|
123
66
|
// Conservative degradation: log and return undefined (D-08)
|
|
124
67
|
this.logger.warn?.(
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as yaml from 'js-yaml';
|
|
4
|
+
import { SqliteConnection, SqliteActivationStateStore, computeEffectiveFlags, DEFAULT_FEATURE_FLAGS } from '@principles/core/runtime-v2';
|
|
5
|
+
import type { ActivationStatusRecord, EffectiveFeatureFlags } from '@principles/core/runtime-v2';
|
|
6
|
+
|
|
7
|
+
export const RUNTIME_V2_PRINCIPLE_BUDGET = 2000;
|
|
8
|
+
|
|
9
|
+
export interface ActivatedPrinciple {
|
|
10
|
+
principleId: string;
|
|
11
|
+
text: string;
|
|
12
|
+
artifactId: string;
|
|
13
|
+
activationId: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PromptActivationReaderResult {
|
|
17
|
+
principles: ActivatedPrinciple[];
|
|
18
|
+
warnings: string[];
|
|
19
|
+
source: 'runtime_v2';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PromptActivationReaderDeps {
|
|
23
|
+
logger?: { warn?: (msg: string) => void; info?: (msg: string) => void; error?: (msg: string) => void };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
27
|
+
|
|
28
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
29
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class PromptActivationReader {
|
|
33
|
+
private readonly workspaceDir: string;
|
|
34
|
+
private readonly deps: PromptActivationReaderDeps;
|
|
35
|
+
|
|
36
|
+
constructor(workspaceDir: string, deps?: PromptActivationReaderDeps) {
|
|
37
|
+
this.workspaceDir = workspaceDir;
|
|
38
|
+
this.deps = deps ?? {};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async readActivatedPrinciples(): Promise<PromptActivationReaderResult> {
|
|
42
|
+
const warnings: string[] = [];
|
|
43
|
+
const principles: ActivatedPrinciple[] = [];
|
|
44
|
+
|
|
45
|
+
const flags = this.loadFeatureFlags();
|
|
46
|
+
const promptFlag = flags.flags['prompt'];
|
|
47
|
+
if (!promptFlag || !promptFlag.enabled) {
|
|
48
|
+
this.deps.logger?.info?.(`[PD:RuntimeV2] Prompt feature flag disabled — skipping Runtime V2 activation read`);
|
|
49
|
+
return { principles, warnings: ['prompt_feature_disabled'], source: 'runtime_v2' };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let sqliteConn: SqliteConnection | null = null;
|
|
53
|
+
try {
|
|
54
|
+
sqliteConn = new SqliteConnection(this.workspaceDir);
|
|
55
|
+
const store = new SqliteActivationStateStore(sqliteConn);
|
|
56
|
+
|
|
57
|
+
const activations = await store.listPromptActivations();
|
|
58
|
+
|
|
59
|
+
for (const activation of activations) {
|
|
60
|
+
if (activation.channel !== 'prompt' || activation.action !== 'prompt_activate') {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const result = this.resolvePrincipleFromActivation(sqliteConn, activation);
|
|
65
|
+
if (result.ok) {
|
|
66
|
+
principles.push(result.principle);
|
|
67
|
+
} else {
|
|
68
|
+
warnings.push(result.warning);
|
|
69
|
+
this.deps.logger?.warn?.(`[PD:RuntimeV2] ${result.warning}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} catch (e) {
|
|
73
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
74
|
+
const warning = `activation_db_unreadable: ${msg}; nextAction=check_workspace_pd_state_db`;
|
|
75
|
+
warnings.push(warning);
|
|
76
|
+
this.deps.logger?.warn?.(`[PD:RuntimeV2] ${warning}`);
|
|
77
|
+
} finally {
|
|
78
|
+
try {
|
|
79
|
+
sqliteConn?.close();
|
|
80
|
+
} catch {
|
|
81
|
+
// best-effort
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { principles, warnings, source: 'runtime_v2' };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private loadFeatureFlags(): EffectiveFeatureFlags {
|
|
89
|
+
const configPath = path.join(this.workspaceDir, '.pd', 'feature-flags.yaml');
|
|
90
|
+
|
|
91
|
+
if (!fs.existsSync(configPath)) {
|
|
92
|
+
return computeEffectiveFlags({}, DEFAULT_FEATURE_FLAGS, configPath);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let raw: string;
|
|
96
|
+
try {
|
|
97
|
+
raw = fs.readFileSync(configPath, 'utf8');
|
|
98
|
+
} catch (e) {
|
|
99
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
100
|
+
this.deps.logger?.warn?.(`[PD:RuntimeV2] Feature flags unreadable: ${msg} — using defaults`);
|
|
101
|
+
return computeEffectiveFlags({}, DEFAULT_FEATURE_FLAGS, configPath);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let parsed: unknown;
|
|
105
|
+
try {
|
|
106
|
+
parsed = yaml.load(raw, { schema: yaml.JSON_SCHEMA });
|
|
107
|
+
} catch {
|
|
108
|
+
this.deps.logger?.warn?.(`[PD:RuntimeV2] Feature flags YAML parse error — using defaults`);
|
|
109
|
+
return {
|
|
110
|
+
...computeEffectiveFlags({}, DEFAULT_FEATURE_FLAGS, configPath),
|
|
111
|
+
warnings: ['feature-flags.yaml: YAML parse error, using defaults'],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!isRecord(parsed)) {
|
|
116
|
+
this.deps.logger?.warn?.(`[PD:RuntimeV2] Feature flags not a mapping — using defaults`);
|
|
117
|
+
return {
|
|
118
|
+
...computeEffectiveFlags({}, DEFAULT_FEATURE_FLAGS, configPath),
|
|
119
|
+
warnings: ['feature-flags.yaml: expected a mapping, using defaults'],
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const parsedRecord: Record<string, unknown> = Object.create(null);
|
|
124
|
+
const yamlWarnings: string[] = [];
|
|
125
|
+
for (const key of Object.keys(parsed)) {
|
|
126
|
+
if (DANGEROUS_KEYS.has(key)) {
|
|
127
|
+
yamlWarnings.push(`feature-flags.yaml: dangerous key '${key}' rejected`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (Object.hasOwn(parsed, key)) {
|
|
131
|
+
parsedRecord[key] = parsed[key];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const result = computeEffectiveFlags(parsedRecord, DEFAULT_FEATURE_FLAGS, configPath);
|
|
136
|
+
if (yamlWarnings.length > 0) {
|
|
137
|
+
result.warnings = [...yamlWarnings, ...result.warnings];
|
|
138
|
+
for (const w of yamlWarnings) {
|
|
139
|
+
this.deps.logger?.warn?.(`[PD:RuntimeV2] ${w}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private resolvePrincipleFromActivation(
|
|
146
|
+
sqliteConn: SqliteConnection,
|
|
147
|
+
activation: ActivationStatusRecord,
|
|
148
|
+
): { ok: true; principle: ActivatedPrinciple } | { ok: false; warning: string } {
|
|
149
|
+
const db = sqliteConn.getDb();
|
|
150
|
+
|
|
151
|
+
let contentJson: string;
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const row = db.prepare(`
|
|
155
|
+
SELECT artifact_id, artifact_kind, content_json, validation_status
|
|
156
|
+
FROM pi_artifacts
|
|
157
|
+
WHERE artifact_id = ?
|
|
158
|
+
`).get(activation.artifactId);
|
|
159
|
+
|
|
160
|
+
if (!isRecord(row)) {
|
|
161
|
+
return { ok: false, warning: `artifact_query_unexpected: artifactId=${activation.artifactId}; nextAction=check_pi_artifacts_table` };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const artifact_id = Object.hasOwn(row, 'artifact_id') && typeof row.artifact_id === 'string' && row.artifact_id.length > 0 ? row.artifact_id : null;
|
|
165
|
+
const artifact_kind = Object.hasOwn(row, 'artifact_kind') && typeof row.artifact_kind === 'string' ? row.artifact_kind : null;
|
|
166
|
+
const raw_content_json = Object.hasOwn(row, 'content_json') && typeof row.content_json === 'string' ? row.content_json : null;
|
|
167
|
+
const validation_status = Object.hasOwn(row, 'validation_status') && typeof row.validation_status === 'string' ? row.validation_status : null;
|
|
168
|
+
|
|
169
|
+
if (!artifact_id) {
|
|
170
|
+
return { ok: false, warning: `artifact_not_found: artifactId=${activation.artifactId} activationId=${activation.activationId}; nextAction=verify_artifact_exists_or_remove_stale_activation` };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (artifact_kind !== 'principle') {
|
|
174
|
+
return { ok: false, warning: `artifact_not_principle: artifactId=${artifact_id} kind=${artifact_kind ?? 'missing'}; nextAction=skip_non_principle_activations` };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (validation_status !== 'validated') {
|
|
178
|
+
return { ok: false, warning: `artifact_not_validated: artifactId=${artifact_id} status=${validation_status ?? 'missing'}; nextAction=skip_unvalidated_artifacts` };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (raw_content_json === null) {
|
|
182
|
+
return { ok: false, warning: `artifact_missing_content_json: artifactId=${artifact_id}; nextAction=ensure_artifact_has_content_json` };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
contentJson = raw_content_json;
|
|
186
|
+
} catch (e) {
|
|
187
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
188
|
+
return { ok: false, warning: `artifact_query_failed: artifactId=${activation.artifactId} reason=${msg}; nextAction=check_pi_artifacts_table` };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
let parsed: unknown;
|
|
192
|
+
try {
|
|
193
|
+
parsed = JSON.parse(contentJson);
|
|
194
|
+
} catch (e) {
|
|
195
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
196
|
+
return { ok: false, warning: `artifact_content_json_parse_error: artifactId=${activation.artifactId} reason=${msg}; nextAction=fix_artifact_content_json` };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!isRecord(parsed)) {
|
|
200
|
+
return { ok: false, warning: `artifact_content_malformed: artifactId=${activation.artifactId} reason=parsed_to_non_object; nextAction=fix_artifact_content_json` };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const principleId = Object.hasOwn(parsed, 'principleId') && typeof parsed.principleId === 'string' ? parsed.principleId : undefined;
|
|
204
|
+
const text = Object.hasOwn(parsed, 'text') && typeof parsed.text === 'string' ? parsed.text : undefined;
|
|
205
|
+
|
|
206
|
+
const draftObj = Object.hasOwn(parsed, 'principleDraft') && isRecord(parsed.principleDraft) ? parsed.principleDraft : null;
|
|
207
|
+
const draftTitle = draftObj && Object.hasOwn(draftObj, 'title') && typeof draftObj.title === 'string' ? draftObj.title : undefined;
|
|
208
|
+
const draftStatement = draftObj && Object.hasOwn(draftObj, 'statement') && typeof draftObj.statement === 'string' ? draftObj.statement : undefined;
|
|
209
|
+
|
|
210
|
+
const resolvedPrincipleId = principleId && principleId.length > 0 ? principleId : draftTitle;
|
|
211
|
+
const resolvedText = text && text.length > 0 ? text : draftStatement;
|
|
212
|
+
|
|
213
|
+
if (!resolvedPrincipleId || resolvedPrincipleId.length === 0) {
|
|
214
|
+
return { ok: false, warning: `artifact_missing_principle_id: artifactId=${activation.artifactId}; nextAction=ensure_artifact_has_principleId_or_principleDraft_title` };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (!resolvedText || resolvedText.length === 0) {
|
|
218
|
+
return { ok: false, warning: `artifact_missing_text: artifactId=${activation.artifactId} principleId=${resolvedPrincipleId}; nextAction=ensure_artifact_has_text_or_principleDraft_statement` };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
ok: true,
|
|
223
|
+
principle: {
|
|
224
|
+
principleId: resolvedPrincipleId,
|
|
225
|
+
text: resolvedText,
|
|
226
|
+
artifactId: activation.artifactId,
|
|
227
|
+
activationId: activation.activationId,
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -5,6 +5,14 @@ import { atomicWriteFileSync } from '../utils/io.js';
|
|
|
5
5
|
import type { PainConfig } from './config.js';
|
|
6
6
|
import { SystemLogger } from './system-logger.js';
|
|
7
7
|
import { TWO_HOURS_MS } from '../config/defaults/runtime.js';
|
|
8
|
+
import {
|
|
9
|
+
applyFriction as coreApplyFriction,
|
|
10
|
+
applyDecay as coreApplyDecay,
|
|
11
|
+
applyRelief as coreApplyRelief,
|
|
12
|
+
classifyGfiStage,
|
|
13
|
+
DEFAULT_GFI_POLICY,
|
|
14
|
+
} from '@principles/core/runtime-v2';
|
|
15
|
+
import type { GfiState, GfiEvent, GfiSource } from '@principles/core/runtime-v2';
|
|
8
16
|
|
|
9
17
|
export interface TokenUsage {
|
|
10
18
|
input?: number;
|
|
@@ -14,6 +22,32 @@ export interface TokenUsage {
|
|
|
14
22
|
total?: number;
|
|
15
23
|
}
|
|
16
24
|
|
|
25
|
+
// ── GFI Core Kernel Adapter ──────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
/** Convert SessionState GFI fields to GfiState for core kernel */
|
|
28
|
+
function toGfiState(state: SessionState): GfiState {
|
|
29
|
+
return {
|
|
30
|
+
currentGfi: state.currentGfi ?? 0,
|
|
31
|
+
gfiBySource: (state.gfiBySource ?? {}) as Partial<Record<GfiSource, number>>,
|
|
32
|
+
lastErrorHash: state.lastErrorHash === '' ? undefined : state.lastErrorHash,
|
|
33
|
+
lastErrorSource: state.lastErrorSource || undefined,
|
|
34
|
+
consecutiveErrors: state.consecutiveErrors ?? 0,
|
|
35
|
+
lastGfiDecayAt: state.lastGfiDecayAt,
|
|
36
|
+
dailyGfiPeak: state.dailyGfiPeak,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Apply GfiState result back onto SessionState */
|
|
41
|
+
function applyGfiResult(state: SessionState, result: GfiState): void {
|
|
42
|
+
state.currentGfi = result.currentGfi;
|
|
43
|
+
state.gfiBySource = { ...result.gfiBySource } as Record<string, number>;
|
|
44
|
+
state.lastErrorHash = result.lastErrorHash ?? '';
|
|
45
|
+
state.lastErrorSource = result.lastErrorSource ?? '';
|
|
46
|
+
state.consecutiveErrors = result.consecutiveErrors;
|
|
47
|
+
state.lastGfiDecayAt = result.lastGfiDecayAt;
|
|
48
|
+
state.dailyGfiPeak = result.dailyGfiPeak ?? state.dailyGfiPeak;
|
|
49
|
+
}
|
|
50
|
+
|
|
17
51
|
export interface SessionState {
|
|
18
52
|
sessionId: string;
|
|
19
53
|
sessionKey?: string; // Structured session key from OpenClaw (e.g., agent:main:cron:job-1:run:xxx)
|
|
@@ -233,13 +267,6 @@ function getOrCreateSession(sessionId: string, workspaceDir?: string, sessionKey
|
|
|
233
267
|
return state;
|
|
234
268
|
}
|
|
235
269
|
|
|
236
|
-
function ensureGfiLedger(state: SessionState): Record<string, number> {
|
|
237
|
-
if (!state.gfiBySource || typeof state.gfiBySource !== 'object') {
|
|
238
|
-
state.gfiBySource = {};
|
|
239
|
-
}
|
|
240
|
-
return state.gfiBySource;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
270
|
export function trackToolRead(sessionId: string, filePath: string, workspaceDir?: string): SessionState {
|
|
244
271
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
245
272
|
const normalizedPath = path.posix.normalize(filePath.replace(/\\/g, '/'));
|
|
@@ -287,9 +314,8 @@ export function trackLlmOutput(sessionId: string, usage: TokenUsage | undefined,
|
|
|
287
314
|
|
|
288
315
|
/**
|
|
289
316
|
* Tracks physical friction based on tool execution failures.
|
|
317
|
+
* Delegates to core GFI kernel for scoring.
|
|
290
318
|
*/
|
|
291
|
-
|
|
292
|
-
|
|
293
319
|
export function trackFriction(
|
|
294
320
|
sessionId: string,
|
|
295
321
|
deltaF: number,
|
|
@@ -298,40 +324,46 @@ export function trackFriction(
|
|
|
298
324
|
options?: { source?: string }
|
|
299
325
|
): SessionState {
|
|
300
326
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
327
|
+
|
|
328
|
+
const source: GfiSource = (options?.source as GfiSource) ?? 'unknown';
|
|
329
|
+
const event: GfiEvent = {
|
|
330
|
+
source,
|
|
331
|
+
baseScore: deltaF,
|
|
332
|
+
hash: hash || undefined,
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const coreState = toGfiState(state);
|
|
336
|
+
const nextCore = coreApplyFriction(coreState, event, DEFAULT_GFI_POLICY);
|
|
337
|
+
|
|
338
|
+
// Preserve composite source key for unattributed sources
|
|
339
|
+
if (!options?.source && hash) {
|
|
340
|
+
const val = nextCore.gfiBySource['unknown'];
|
|
341
|
+
if (val !== undefined) {
|
|
342
|
+
const s = nextCore.gfiBySource as Record<string, number>;
|
|
343
|
+
delete s['unknown'];
|
|
344
|
+
const key = `unattributed:${hash}`;
|
|
345
|
+
s[key] = (s[key] ?? 0) + val;
|
|
346
|
+
}
|
|
309
347
|
}
|
|
310
348
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const addedFriction = deltaF * multiplier;
|
|
314
|
-
state.currentGfi = (state.currentGfi || 0) + addedFriction;
|
|
315
|
-
const sourceKey = options?.source || (hash ? `unattributed:${hash}` : 'unattributed:unknown');
|
|
316
|
-
ledger[sourceKey] = (ledger[sourceKey] || 0) + addedFriction;
|
|
349
|
+
applyGfiResult(state, nextCore);
|
|
350
|
+
|
|
317
351
|
touchActivity(state, 'control');
|
|
318
|
-
|
|
319
|
-
SystemLogger.log(state.workspaceDir, 'GFI_INC', `Friction added: +${addedFriction.toFixed(1)} (Base: ${deltaF}, Mult: ${multiplier.toFixed(2)}). Total GFI: ${state.currentGfi.toFixed(1)}`);
|
|
320
|
-
|
|
352
|
+
|
|
321
353
|
// Update daily stats
|
|
322
354
|
state.dailyToolFailures++;
|
|
323
355
|
state.dailyGfiPeak = Math.max(state.dailyGfiPeak, state.currentGfi);
|
|
324
|
-
|
|
356
|
+
|
|
325
357
|
// Schedule persistence
|
|
326
|
-
// Update decay anchor to prevent retroactive decay of the new friction
|
|
327
358
|
state.lastGfiDecayAt = Date.now();
|
|
328
359
|
schedulePersistence(state);
|
|
329
|
-
|
|
360
|
+
|
|
330
361
|
return state;
|
|
331
362
|
}
|
|
332
363
|
|
|
333
364
|
/**
|
|
334
365
|
* Resets the friction index upon successful action.
|
|
366
|
+
* Delegates to core GFI kernel for relief computation.
|
|
335
367
|
*/
|
|
336
368
|
export function resetFriction(
|
|
337
369
|
sessionId: string,
|
|
@@ -339,50 +371,36 @@ export function resetFriction(
|
|
|
339
371
|
options?: { source?: string; amount?: number }
|
|
340
372
|
): SessionState {
|
|
341
373
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
342
|
-
const ledger = ensureGfiLedger(state);
|
|
343
374
|
|
|
344
375
|
if (options?.source) {
|
|
345
|
-
const
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
if (amountToRemove > 0) {
|
|
351
|
-
ledger[sourceKey] = Math.max(0, currentSource - amountToRemove);
|
|
352
|
-
if (ledger[sourceKey] === 0) {
|
|
353
|
-
delete ledger[sourceKey];
|
|
354
|
-
}
|
|
355
|
-
state.currentGfi = Math.max(0, (state.currentGfi || 0) - amountToRemove);
|
|
376
|
+
const coreState = toGfiState(state);
|
|
377
|
+
const nextCore = coreApplyRelief(coreState, { source: options.source, amount: options.amount ?? 0 }, Date.now(), DEFAULT_GFI_POLICY);
|
|
378
|
+
applyGfiResult(state, nextCore);
|
|
379
|
+
|
|
380
|
+
if (state.currentGfi > 0) {
|
|
356
381
|
SystemLogger.log(
|
|
357
382
|
state.workspaceDir,
|
|
358
383
|
'GFI_SLICE_RESET',
|
|
359
|
-
`Friction slice reset for ${
|
|
384
|
+
`Friction slice reset for ${options.source}: remaining GFI=${state.currentGfi.toFixed(1)}`
|
|
360
385
|
);
|
|
361
|
-
|
|
362
|
-
if (state.lastErrorSource === sourceKey) {
|
|
363
|
-
state.consecutiveErrors = 0;
|
|
364
|
-
state.lastErrorHash = '';
|
|
365
|
-
state.lastErrorSource = '';
|
|
366
|
-
}
|
|
367
386
|
}
|
|
368
387
|
touchActivity(state, 'control');
|
|
369
388
|
schedulePersistence(state);
|
|
370
389
|
return state;
|
|
371
390
|
}
|
|
372
391
|
|
|
373
|
-
|
|
374
|
-
|
|
392
|
+
// Full reset via core kernel
|
|
393
|
+
const previousGfi = state.currentGfi;
|
|
394
|
+
const coreState = toGfiState(state);
|
|
395
|
+
const nextCore = coreApplyRelief(coreState, { source: 'all', amount: 100 }, Date.now(), DEFAULT_GFI_POLICY);
|
|
396
|
+
applyGfiResult(state, nextCore);
|
|
397
|
+
|
|
398
|
+
if (previousGfi > 0) {
|
|
399
|
+
SystemLogger.log(state.workspaceDir, 'GFI_RESET', `Friction reset to 0 (Was: ${previousGfi.toFixed(1)}). Action successful.`);
|
|
375
400
|
}
|
|
376
|
-
state.currentGfi = 0;
|
|
377
|
-
state.gfiBySource = {};
|
|
378
|
-
state.lastErrorSource = '';
|
|
379
|
-
state.consecutiveErrors = 0;
|
|
380
|
-
state.lastErrorHash = '';
|
|
381
401
|
touchActivity(state, 'control');
|
|
382
|
-
|
|
383
|
-
// Schedule persistence
|
|
402
|
+
|
|
384
403
|
schedulePersistence(state);
|
|
385
|
-
|
|
386
404
|
return state;
|
|
387
405
|
}
|
|
388
406
|
|
|
@@ -538,15 +556,9 @@ export function resetDailyStats(sessionId: string): void {
|
|
|
538
556
|
}
|
|
539
557
|
|
|
540
558
|
/**
|
|
541
|
-
* Apply time-based decay to GFI using
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
* - GFI >= 70 (severe): 3%/min - fast recovery to avoid prolonged blocking
|
|
545
|
-
* - GFI 40-70 (moderate): 2%/min - medium decay
|
|
546
|
-
* - GFI < 40 (mild): 1%/min - slow decay to retain as warning
|
|
547
|
-
*
|
|
548
|
-
* Formula: GFI_new = GFI * (1 - λ)^elapsedMinutes
|
|
549
|
-
*
|
|
559
|
+
* Apply time-based decay to GFI using stage-aware linear decay.
|
|
560
|
+
* Delegates to core GFI kernel.
|
|
561
|
+
*
|
|
550
562
|
* @param sessionId - The session to decay
|
|
551
563
|
* @param elapsedMinutes - Minutes since last decay
|
|
552
564
|
* @returns Updated session state, or undefined if session not found or GFI is 0
|
|
@@ -554,49 +566,23 @@ export function resetDailyStats(sessionId: string): void {
|
|
|
554
566
|
export function decayGfi(sessionId: string, elapsedMinutes: number): SessionState | undefined {
|
|
555
567
|
const state = sessions.get(sessionId);
|
|
556
568
|
if (!state || state.currentGfi <= 0 || elapsedMinutes <= 0) return undefined;
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
if (state.currentGfi >= 70) {
|
|
562
|
-
decayRate = 0.03; // 3%/min for severe friction
|
|
563
|
-
} else if (state.currentGfi >= 40) {
|
|
564
|
-
decayRate = 0.02; // 2%/min for moderate friction
|
|
565
|
-
} else {
|
|
566
|
-
decayRate = 0.01; // 1%/min for mild friction
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// Exponential decay: GFI_new = GFI * (1-λ)^Δt
|
|
570
|
-
const decayFactor = Math.pow(1 - decayRate, elapsedMinutes);
|
|
569
|
+
|
|
570
|
+
const coreState = toGfiState(state);
|
|
571
|
+
const stage = classifyGfiStage(state.currentGfi, DEFAULT_GFI_POLICY);
|
|
572
|
+
const nextCore = coreApplyDecay(coreState, elapsedMinutes, DEFAULT_GFI_POLICY, stage, Date.now());
|
|
571
573
|
const previousGfi = state.currentGfi;
|
|
572
|
-
state
|
|
573
|
-
|
|
574
|
-
// Apply same decay factor to all sources
|
|
575
|
-
const ledger = ensureGfiLedger(state);
|
|
576
|
-
for (const source of Object.keys(ledger)) {
|
|
577
|
-
ledger[source] = Math.max(0, ledger[source] * decayFactor);
|
|
578
|
-
// Remove sources that have decayed below 0.1
|
|
579
|
-
if (ledger[source] < 0.1) {
|
|
580
|
-
delete ledger[source];
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
// Round to 1 decimal place
|
|
585
|
-
state.currentGfi = Math.round(state.currentGfi * 10) / 10;
|
|
586
|
-
|
|
587
|
-
// Update last decay timestamp
|
|
588
|
-
state.lastGfiDecayAt = Date.now();
|
|
589
|
-
|
|
574
|
+
applyGfiResult(state, nextCore);
|
|
575
|
+
|
|
590
576
|
// Log if significant decay
|
|
591
577
|
const decayedAmount = previousGfi - state.currentGfi;
|
|
592
578
|
if (decayedAmount >= 1) {
|
|
593
579
|
SystemLogger.log(
|
|
594
580
|
state.workspaceDir,
|
|
595
581
|
'GFI_DECAY',
|
|
596
|
-
`GFI decayed by ${decayedAmount.toFixed(1)} (${elapsedMinutes}min
|
|
582
|
+
`GFI decayed by ${decayedAmount.toFixed(1)} (${elapsedMinutes}min). ${previousGfi.toFixed(1)} → ${state.currentGfi.toFixed(1)}`
|
|
597
583
|
);
|
|
598
584
|
}
|
|
599
|
-
|
|
585
|
+
|
|
600
586
|
schedulePersistence(state);
|
|
601
587
|
return state;
|
|
602
588
|
}
|