@causyne/agent 1.10.21 → 1.11.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.
Files changed (55) hide show
  1. package/native-skills/media/professional-document-publishing/SKILL.md +44 -0
  2. package/native-skills/media/screenplay-and-shotlist-forge/SKILL.md +42 -0
  3. package/package.json +1 -1
  4. package/src/acp/client.js +235 -0
  5. package/src/acp/client.ts +282 -0
  6. package/src/acp/peerProfiles.js +122 -0
  7. package/src/acp/peerProfiles.ts +128 -0
  8. package/src/acp/protocol.js +54 -0
  9. package/src/acp/protocol.ts +434 -0
  10. package/src/acp/server.js +609 -0
  11. package/src/acp/server.ts +676 -0
  12. package/src/agentLoop.d.ts +43 -0
  13. package/src/agentLoop.js +142 -8
  14. package/src/commands.js +203 -0
  15. package/src/compiledSkills.js +10 -0
  16. package/src/documentEngine/antiSlopAuditor.js +156 -0
  17. package/src/documentEngine/antiSlopAuditor.ts +221 -0
  18. package/src/documentEngine/documentForge.js +443 -0
  19. package/src/documentEngine/documentForge.ts +519 -0
  20. package/src/documentEngine/screenplayFormatter.js +336 -0
  21. package/src/documentEngine/screenplayFormatter.ts +407 -0
  22. package/src/entrypoint.js +13 -0
  23. package/src/execSafe.js +10 -13
  24. package/src/execSafe.ts +25 -0
  25. package/src/modelRouter.js +56 -123
  26. package/src/modelRouter.ts +134 -0
  27. package/src/observer/advisory.js +164 -0
  28. package/src/observer/advisory.ts +171 -0
  29. package/src/observer/coldObserver.js +462 -0
  30. package/src/observer/coldObserver.ts +540 -0
  31. package/src/observer/evidence.js +435 -0
  32. package/src/observer/evidence.ts +499 -0
  33. package/src/perception.js +303 -118
  34. package/src/perception.ts +387 -0
  35. package/src/procSignal.js +15 -16
  36. package/src/procSignal.ts +31 -0
  37. package/src/promptMentions.js +170 -0
  38. package/src/promptMentions.ts +212 -0
  39. package/src/sandbox.js +1 -1
  40. package/src/textDiff.js +117 -109
  41. package/src/textDiff.ts +169 -0
  42. package/src/toolPacks.js +5 -2
  43. package/src/tools/documentForge.js +215 -0
  44. package/src/tools/documentForge.ts +265 -0
  45. package/src/tools/peerAgent.js +102 -0
  46. package/src/tools/peerAgent.ts +116 -0
  47. package/src/tools/registry.js +3 -1
  48. package/src/tui/completion.js +97 -41
  49. package/src/tui/prompt.js +17 -17
  50. package/src/uncertainty.js +93 -205
  51. package/src/uncertainty.ts +255 -0
  52. package/src/utils/paths.js +15 -18
  53. package/src/utils/paths.ts +36 -0
  54. package/src/vfeTracker.js +122 -181
  55. package/src/vfeTracker.ts +242 -0
@@ -1,6 +1,5 @@
1
- import os from 'os';
2
- import path from 'path';
3
-
1
+ import os from 'node:os';
2
+ import path from 'node:path';
4
3
  /**
5
4
  * Resolve a path the model supplied, expanding a leading `~`.
6
5
  *
@@ -15,23 +14,21 @@ import path from 'path';
15
14
  * look at its own work, which is the one step that separates a good render from
16
15
  * a bad one.
17
16
  *
18
- * @param {string} input A path, possibly starting with `~`.
19
- * @returns {string} An absolute path.
17
+ * @param input A path, possibly starting with `~`.
18
+ * @returns An absolute path.
20
19
  */
21
20
  export function resolveUserPath(input) {
22
- const raw = String(input ?? '').trim();
23
- if (raw === '~') return os.homedir();
24
- if (raw.startsWith('~/') || raw.startsWith('~\\')) {
25
- return path.resolve(path.join(os.homedir(), raw.slice(2)));
26
- }
27
- return path.resolve(raw);
21
+ const raw = String(input ?? '').trim();
22
+ if (raw === '~')
23
+ return os.homedir();
24
+ if (raw.startsWith('~/') || raw.startsWith('~\\')) {
25
+ return path.resolve(path.join(os.homedir(), raw.slice(2)));
26
+ }
27
+ return path.resolve(raw);
28
28
  }
29
-
30
-
31
- const STATE_ROOT = process.env.CAUSYNE_HOME && String(process.env.CAUSYNE_HOME).trim()
32
- ? path.resolve(String(process.env.CAUSYNE_HOME).trim())
33
- : path.join(os.homedir(), '.causyne');
34
-
29
+ const STATE_ROOT = process.env['CAUSYNE_HOME'] && String(process.env['CAUSYNE_HOME']).trim()
30
+ ? path.resolve(String(process.env['CAUSYNE_HOME']).trim())
31
+ : path.join(os.homedir(), '.causyne');
35
32
  export function causyneHome() {
36
- return STATE_ROOT;
33
+ return STATE_ROOT;
37
34
  }
@@ -0,0 +1,36 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+
4
+ /**
5
+ * Resolve a path the model supplied, expanding a leading `~`.
6
+ *
7
+ * WHY THIS EXISTS. `path.resolve('~/x')` does NOT expand the tilde — it treats
8
+ * `~` as an ordinary directory name and returns `<cwd>/~/x`. Models write `~/`
9
+ * constantly, because that is how paths are written everywhere they have ever
10
+ * read one, and because Causyne's own transcript SHOWS paths that way.
11
+ *
12
+ * It cost a real run: `view_image ~/Causyne/pen_cinematic/preview.png` failed in
13
+ * 17ms with "Image not found at /Users/example/project/~/project/..."
14
+ * on a file that existed — the agent had just rendered it and was trying to
15
+ * look at its own work, which is the one step that separates a good render from
16
+ * a bad one.
17
+ *
18
+ * @param input A path, possibly starting with `~`.
19
+ * @returns An absolute path.
20
+ */
21
+ export function resolveUserPath(input: string): string {
22
+ const raw = String(input ?? '').trim();
23
+ if (raw === '~') return os.homedir();
24
+ if (raw.startsWith('~/') || raw.startsWith('~\\')) {
25
+ return path.resolve(path.join(os.homedir(), raw.slice(2)));
26
+ }
27
+ return path.resolve(raw);
28
+ }
29
+
30
+ const STATE_ROOT = process.env['CAUSYNE_HOME'] && String(process.env['CAUSYNE_HOME']).trim()
31
+ ? path.resolve(String(process.env['CAUSYNE_HOME']).trim())
32
+ : path.join(os.homedir(), '.causyne');
33
+
34
+ export function causyneHome(): string {
35
+ return STATE_ROOT;
36
+ }
package/src/vfeTracker.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @file vfeTracker.js
2
+ * @file vfeTracker.ts
3
3
  * Variational Free Energy (VFE) Tracker & Active Inference Plateau Detector.
4
4
  *
5
5
  * Implements Active Inference principles (Friston et al. / Research Section D):
@@ -17,204 +17,145 @@
17
17
  *
18
18
  * Zero LLM calls. Computed purely from execution signals already tracked.
19
19
  */
20
-
21
20
  import { failureClass } from './planCache.js';
22
-
23
21
  /** Window size for rolling VFE metrics (default N=10 steps). */
24
22
  export const VFE_WINDOW_SIZE = 10;
25
-
26
23
  /** Minimum observations needed before plateau detection can trigger. */
27
24
  export const MIN_SAMPLES_FOR_PLATEAU = 4;
28
-
29
25
  /** Upper bound on variance to qualify as a "plateau" (stuck with no progress). */
30
26
  export const VARIANCE_EPSILON = 0.01;
31
-
32
27
  /** Lower bound on mean error to qualify as high-error plateau. */
33
28
  export const MEAN_ERROR_THRESHOLD = 0.50;
34
-
35
29
  /** Minimum step interval between paradigm shift triggers to prevent spamming. */
36
30
  export const SHIFT_COOLDOWN_STEPS = 5;
37
-
38
31
  /** Hard route-breaking failure categories from uncertainty taxonomy. */
39
32
  const ROUTE_BREAKING_FAILURES = new Set(['not_found', 'permission', 'syntax', 'invalid_args']);
40
-
41
33
  export class VFETracker {
42
- /**
43
- * @param {Object} [options]
44
- * @param {number} [options.windowSize=10]
45
- * @param {number} [options.varianceEpsilon=0.01]
46
- * @param {number} [options.meanThreshold=0.50]
47
- */
48
- constructor(options = {}) {
49
- this.windowSize = options.windowSize || VFE_WINDOW_SIZE;
50
- this.varianceEpsilon = options.varianceEpsilon || VARIANCE_EPSILON;
51
- this.meanThreshold = options.meanThreshold || MEAN_ERROR_THRESHOLD;
52
-
53
- /** @type {Array<{ step: number, vfe: number, tool: string, success: boolean }>} */
54
- this.history = [];
55
- this.lastShiftStep = -SHIFT_COOLDOWN_STEPS;
56
- this.totalSteps = 0;
57
- }
58
-
59
- /**
60
- * Reset tracker state for a fresh task.
61
- */
62
- reset() {
63
- this.history = [];
64
- this.lastShiftStep = -SHIFT_COOLDOWN_STEPS;
65
- this.totalSteps = 0;
66
- }
67
-
68
- /**
69
- * Compute the VFE (prediction error proxy) for a single tool execution step.
70
- *
71
- * @param {Object} stepData
72
- * @param {boolean} stepData.success
73
- * @param {Object} [stepData.result]
74
- * @param {number} [stepData.consecutiveFailures=0]
75
- * @param {boolean} [stepData.isThrashing=false]
76
- * @returns {number} VFE score in [0.0, 1.0]
77
- */
78
- computeStepVFE({ success, result, consecutiveFailures = 0, isThrashing = false }) {
79
- if (success) {
80
- // Nominal observation matches expected state: minimal free energy
81
- return 0.05;
34
+ windowSize;
35
+ varianceEpsilon;
36
+ meanThreshold;
37
+ history;
38
+ lastShiftStep;
39
+ totalSteps;
40
+ constructor(options = {}) {
41
+ this.windowSize = options.windowSize || VFE_WINDOW_SIZE;
42
+ this.varianceEpsilon = options.varianceEpsilon || VARIANCE_EPSILON;
43
+ this.meanThreshold = options.meanThreshold || MEAN_ERROR_THRESHOLD;
44
+ this.history = [];
45
+ this.lastShiftStep = -SHIFT_COOLDOWN_STEPS;
46
+ this.totalSteps = 0;
82
47
  }
83
-
84
- // Base prediction error for an unexpected negative outcome
85
- let vfe = 0.65;
86
-
87
- // Consecutive repetitions of failure increase prediction mismatch
88
- if (consecutiveFailures > 1) {
89
- vfe += Math.min(0.20, (consecutiveFailures - 1) * 0.10);
48
+ /**
49
+ * Reset tracker state for a fresh task.
50
+ */
51
+ reset() {
52
+ this.history = [];
53
+ this.lastShiftStep = -SHIFT_COOLDOWN_STEPS;
54
+ this.totalSteps = 0;
90
55
  }
91
-
92
- // Hard route errors (missing file, invalid syntax) mean mental model is wrong
93
- if (result) {
94
- const fClass = failureClass(result);
95
- if (fClass && ROUTE_BREAKING_FAILURES.has(fClass)) {
96
- vfe += 0.15;
97
- }
56
+ /**
57
+ * Compute the VFE (prediction error proxy) for a single tool execution step.
58
+ */
59
+ computeStepVFE({ success, result, consecutiveFailures = 0, isThrashing = false }) {
60
+ if (success) {
61
+ // Nominal observation matches expected state: minimal free energy
62
+ return 0.05;
63
+ }
64
+ // Base prediction error for an unexpected negative outcome
65
+ let vfe = 0.65;
66
+ // Consecutive repetitions of failure increase prediction mismatch
67
+ if (consecutiveFailures > 1) {
68
+ vfe += Math.min(0.20, (consecutiveFailures - 1) * 0.10);
69
+ }
70
+ // Hard route errors (missing file, invalid syntax) mean mental model is wrong
71
+ if (result) {
72
+ const fClass = failureClass(result);
73
+ if (fClass && ROUTE_BREAKING_FAILURES.has(fClass)) {
74
+ vfe += 0.15;
75
+ }
76
+ }
77
+ // Thrashing tool calls mean cyclic un-converging belief state
78
+ if (isThrashing) {
79
+ vfe += 0.15;
80
+ }
81
+ return Math.min(1.0, Math.max(0.0, vfe));
98
82
  }
99
-
100
- // Thrashing tool calls mean cyclic un-converging belief state
101
- if (isThrashing) {
102
- vfe += 0.15;
83
+ /**
84
+ * Record a completed tool step and compute rolling window statistics.
85
+ */
86
+ recordStep({ tool, success, result, consecutiveFailures = 0, isThrashing = false }) {
87
+ this.totalSteps += 1;
88
+ const currentStep = this.totalSteps;
89
+ const vfe = this.computeStepVFE({ success, result, consecutiveFailures, isThrashing });
90
+ this.history.push({ step: currentStep, vfe, tool, success });
91
+ if (this.history.length > this.windowSize) {
92
+ this.history.shift();
93
+ }
94
+ const stats = this.computeWindowStats();
95
+ const isPlateau = (this.history.length >= MIN_SAMPLES_FOR_PLATEAU &&
96
+ stats.mean >= this.meanThreshold &&
97
+ stats.variance <= this.varianceEpsilon);
98
+ const canShift = (currentStep - this.lastShiftStep) >= SHIFT_COOLDOWN_STEPS;
99
+ const paradigmShift = isPlateau && canShift;
100
+ if (paradigmShift) {
101
+ this.lastShiftStep = currentStep;
102
+ }
103
+ return {
104
+ vfe,
105
+ mean: stats.mean,
106
+ variance: stats.variance,
107
+ isPlateau,
108
+ paradigmShift
109
+ };
103
110
  }
104
-
105
- return Math.min(1.0, Math.max(0.0, vfe));
106
- }
107
-
108
- /**
109
- * Record a completed tool step and compute rolling window statistics.
110
- *
111
- * @param {Object} params
112
- * @param {string} params.tool
113
- * @param {boolean} params.success
114
- * @param {Object} [params.result]
115
- * @param {number} [params.consecutiveFailures=0]
116
- * @param {boolean} [params.isThrashing=false]
117
- * @returns {{
118
- * vfe: number,
119
- * mean: number,
120
- * variance: number,
121
- * isPlateau: boolean,
122
- * paradigmShift: boolean
123
- * }}
124
- */
125
- recordStep({ tool, success, result, consecutiveFailures = 0, isThrashing = false }) {
126
- this.totalSteps += 1;
127
- const currentStep = this.totalSteps;
128
-
129
- const vfe = this.computeStepVFE({ success, result, consecutiveFailures, isThrashing });
130
-
131
- this.history.push({ step: currentStep, vfe, tool, success });
132
- if (this.history.length > this.windowSize) {
133
- this.history.shift();
111
+ /**
112
+ * Calculate mean and sample variance over the current sliding window.
113
+ */
114
+ computeWindowStats() {
115
+ const n = this.history.length;
116
+ if (n === 0)
117
+ return { mean: 0, variance: 0, count: 0 };
118
+ if (n === 1)
119
+ return { mean: this.history[0].vfe, variance: 0, count: 1 };
120
+ const sum = this.history.reduce((acc, item) => acc + item.vfe, 0);
121
+ const mean = sum / n;
122
+ const sumSqDiff = this.history.reduce((acc, item) => {
123
+ const diff = item.vfe - mean;
124
+ return acc + (diff * diff);
125
+ }, 0);
126
+ const variance = sumSqDiff / (n - 1);
127
+ return {
128
+ mean: Number(mean.toFixed(4)),
129
+ variance: Number(variance.toFixed(6)),
130
+ count: n
131
+ };
134
132
  }
135
-
136
- const stats = this.computeWindowStats();
137
- const isPlateau = (
138
- this.history.length >= MIN_SAMPLES_FOR_PLATEAU &&
139
- stats.mean >= this.meanThreshold &&
140
- stats.variance <= this.varianceEpsilon
141
- );
142
-
143
- const canShift = (currentStep - this.lastShiftStep) >= SHIFT_COOLDOWN_STEPS;
144
- const paradigmShift = isPlateau && canShift;
145
-
146
- if (paradigmShift) {
147
- this.lastShiftStep = currentStep;
133
+ /**
134
+ * Get the current status for telemetry or debugging.
135
+ */
136
+ getStatus() {
137
+ const stats = this.computeWindowStats();
138
+ const isPlateau = (this.history.length >= MIN_SAMPLES_FOR_PLATEAU &&
139
+ stats.mean >= this.meanThreshold &&
140
+ stats.variance <= this.varianceEpsilon);
141
+ return {
142
+ totalSteps: this.totalSteps,
143
+ windowCount: this.history.length,
144
+ mean: stats.mean,
145
+ variance: stats.variance,
146
+ isPlateau
147
+ };
148
+ }
149
+ /**
150
+ * Format the high-priority paradigm shift prompt directive.
151
+ */
152
+ getParadigmShiftDirective() {
153
+ return (`\n\n🚨 [PARADIGM SHIFT REQUIRED — ACTIVE INFERENCE DEADLOCK DETECTED]\n` +
154
+ `Variational Free Energy tracking detected a persistent error plateau with zero convergence.\n` +
155
+ `Your current mental model and execution route are trapped in a local minimum.\n` +
156
+ `DO NOT retry previous variations, tweak parameters, or repeat failed commands.\n` +
157
+ `1. Explicitly identify which core assumption failed.\n` +
158
+ `2. State your revised hypothesis about the environment/codebase.\n` +
159
+ `3. Choose a fundamentally different tool, strategy, or alternative path.\n`);
148
160
  }
149
-
150
- return {
151
- vfe,
152
- mean: stats.mean,
153
- variance: stats.variance,
154
- isPlateau,
155
- paradigmShift
156
- };
157
- }
158
-
159
- /**
160
- * Calculate mean and sample variance over the current sliding window.
161
- * @returns {{ mean: number, variance: number, count: number }}
162
- */
163
- computeWindowStats() {
164
- const n = this.history.length;
165
- if (n === 0) return { mean: 0, variance: 0, count: 0 };
166
- if (n === 1) return { mean: this.history[0].vfe, variance: 0, count: 1 };
167
-
168
- const sum = this.history.reduce((acc, item) => acc + item.vfe, 0);
169
- const mean = sum / n;
170
-
171
- const sumSqDiff = this.history.reduce((acc, item) => {
172
- const diff = item.vfe - mean;
173
- return acc + (diff * diff);
174
- }, 0);
175
-
176
- const variance = sumSqDiff / (n - 1);
177
-
178
- return {
179
- mean: Number(mean.toFixed(4)),
180
- variance: Number(variance.toFixed(6)),
181
- count: n
182
- };
183
- }
184
-
185
- /**
186
- * Get the current status for telemetry or debugging.
187
- */
188
- getStatus() {
189
- const stats = this.computeWindowStats();
190
- const isPlateau = (
191
- this.history.length >= MIN_SAMPLES_FOR_PLATEAU &&
192
- stats.mean >= this.meanThreshold &&
193
- stats.variance <= this.varianceEpsilon
194
- );
195
- return {
196
- totalSteps: this.totalSteps,
197
- windowCount: this.history.length,
198
- mean: stats.mean,
199
- variance: stats.variance,
200
- isPlateau
201
- };
202
- }
203
-
204
- /**
205
- * Format the high-priority paradigm shift prompt directive.
206
- *
207
- * @returns {string}
208
- */
209
- getParadigmShiftDirective() {
210
- return (
211
- `\n\n🚨 [PARADIGM SHIFT REQUIRED — ACTIVE INFERENCE DEADLOCK DETECTED]\n` +
212
- `Variational Free Energy tracking detected a persistent error plateau with zero convergence.\n` +
213
- `Your current mental model and execution route are trapped in a local minimum.\n` +
214
- `DO NOT retry previous variations, tweak parameters, or repeat failed commands.\n` +
215
- `1. Explicitly identify which core assumption failed.\n` +
216
- `2. State your revised hypothesis about the environment/codebase.\n` +
217
- `3. Choose a fundamentally different tool, strategy, or alternative path.\n`
218
- );
219
- }
220
161
  }
@@ -0,0 +1,242 @@
1
+ /**
2
+ * @file vfeTracker.ts
3
+ * Variational Free Energy (VFE) Tracker & Active Inference Plateau Detector.
4
+ *
5
+ * Implements Active Inference principles (Friston et al. / Research Section D):
6
+ * In language-mediated active inference, VFE represents the prediction error
7
+ * between the agent's expected environmental state transitions and actual
8
+ * tool execution outcomes.
9
+ *
10
+ * Detection Rule (Research Section D5):
11
+ * When Var(VFE_recent) < VARIANCE_EPSILON and Mean(VFE_recent) > MEAN_ERROR_THRESHOLD
12
+ * over a sliding window (N=10), the agent has converged onto a flat error plateau
13
+ * (a local minimum). Retrying local step variations will waste tokens. Instead,
14
+ * the agent must execute a PARADIGM SHIFT: re-evaluate underlying assumptions,
15
+ * invalidate the current operational hypothesis, and select a fundamentally
16
+ * different route.
17
+ *
18
+ * Zero LLM calls. Computed purely from execution signals already tracked.
19
+ */
20
+
21
+ import { failureClass } from './planCache.js';
22
+
23
+ /** Window size for rolling VFE metrics (default N=10 steps). */
24
+ export const VFE_WINDOW_SIZE = 10;
25
+
26
+ /** Minimum observations needed before plateau detection can trigger. */
27
+ export const MIN_SAMPLES_FOR_PLATEAU = 4;
28
+
29
+ /** Upper bound on variance to qualify as a "plateau" (stuck with no progress). */
30
+ export const VARIANCE_EPSILON = 0.01;
31
+
32
+ /** Lower bound on mean error to qualify as high-error plateau. */
33
+ export const MEAN_ERROR_THRESHOLD = 0.50;
34
+
35
+ /** Minimum step interval between paradigm shift triggers to prevent spamming. */
36
+ export const SHIFT_COOLDOWN_STEPS = 5;
37
+
38
+ /** Hard route-breaking failure categories from uncertainty taxonomy. */
39
+ const ROUTE_BREAKING_FAILURES = new Set(['not_found', 'permission', 'syntax', 'invalid_args']);
40
+
41
+ export interface VFETrackerOptions {
42
+ windowSize?: number | undefined;
43
+ varianceEpsilon?: number | undefined;
44
+ meanThreshold?: number | undefined;
45
+ }
46
+
47
+ export interface StepVFEParams {
48
+ success: boolean;
49
+ result?: unknown;
50
+ consecutiveFailures?: number | undefined;
51
+ isThrashing?: boolean | undefined;
52
+ }
53
+
54
+ export interface RecordStepParams extends StepVFEParams {
55
+ tool: string;
56
+ }
57
+
58
+ export interface WindowStats {
59
+ mean: number;
60
+ variance: number;
61
+ count: number;
62
+ }
63
+
64
+ export interface StepRecordResult {
65
+ vfe: number;
66
+ mean: number;
67
+ variance: number;
68
+ isPlateau: boolean;
69
+ paradigmShift: boolean;
70
+ }
71
+
72
+ export interface VFEStatus {
73
+ totalSteps: number;
74
+ windowCount: number;
75
+ mean: number;
76
+ variance: number;
77
+ isPlateau: boolean;
78
+ }
79
+
80
+ interface VFEHistoryItem {
81
+ step: number;
82
+ vfe: number;
83
+ tool: string;
84
+ success: boolean;
85
+ }
86
+
87
+ export class VFETracker {
88
+ windowSize: number;
89
+ varianceEpsilon: number;
90
+ meanThreshold: number;
91
+ history: VFEHistoryItem[];
92
+ lastShiftStep: number;
93
+ totalSteps: number;
94
+
95
+ constructor(options: VFETrackerOptions = {}) {
96
+ this.windowSize = options.windowSize || VFE_WINDOW_SIZE;
97
+ this.varianceEpsilon = options.varianceEpsilon || VARIANCE_EPSILON;
98
+ this.meanThreshold = options.meanThreshold || MEAN_ERROR_THRESHOLD;
99
+
100
+ this.history = [];
101
+ this.lastShiftStep = -SHIFT_COOLDOWN_STEPS;
102
+ this.totalSteps = 0;
103
+ }
104
+
105
+ /**
106
+ * Reset tracker state for a fresh task.
107
+ */
108
+ reset(): void {
109
+ this.history = [];
110
+ this.lastShiftStep = -SHIFT_COOLDOWN_STEPS;
111
+ this.totalSteps = 0;
112
+ }
113
+
114
+ /**
115
+ * Compute the VFE (prediction error proxy) for a single tool execution step.
116
+ */
117
+ computeStepVFE({ success, result, consecutiveFailures = 0, isThrashing = false }: StepVFEParams): number {
118
+ if (success) {
119
+ // Nominal observation matches expected state: minimal free energy
120
+ return 0.05;
121
+ }
122
+
123
+ // Base prediction error for an unexpected negative outcome
124
+ let vfe = 0.65;
125
+
126
+ // Consecutive repetitions of failure increase prediction mismatch
127
+ if (consecutiveFailures > 1) {
128
+ vfe += Math.min(0.20, (consecutiveFailures - 1) * 0.10);
129
+ }
130
+
131
+ // Hard route errors (missing file, invalid syntax) mean mental model is wrong
132
+ if (result) {
133
+ const fClass = failureClass(result);
134
+ if (fClass && ROUTE_BREAKING_FAILURES.has(fClass)) {
135
+ vfe += 0.15;
136
+ }
137
+ }
138
+
139
+ // Thrashing tool calls mean cyclic un-converging belief state
140
+ if (isThrashing) {
141
+ vfe += 0.15;
142
+ }
143
+
144
+ return Math.min(1.0, Math.max(0.0, vfe));
145
+ }
146
+
147
+ /**
148
+ * Record a completed tool step and compute rolling window statistics.
149
+ */
150
+ recordStep({ tool, success, result, consecutiveFailures = 0, isThrashing = false }: RecordStepParams): StepRecordResult {
151
+ this.totalSteps += 1;
152
+ const currentStep = this.totalSteps;
153
+
154
+ const vfe = this.computeStepVFE({ success, result, consecutiveFailures, isThrashing });
155
+
156
+ this.history.push({ step: currentStep, vfe, tool, success });
157
+ if (this.history.length > this.windowSize) {
158
+ this.history.shift();
159
+ }
160
+
161
+ const stats = this.computeWindowStats();
162
+ const isPlateau = (
163
+ this.history.length >= MIN_SAMPLES_FOR_PLATEAU &&
164
+ stats.mean >= this.meanThreshold &&
165
+ stats.variance <= this.varianceEpsilon
166
+ );
167
+
168
+ const canShift = (currentStep - this.lastShiftStep) >= SHIFT_COOLDOWN_STEPS;
169
+ const paradigmShift = isPlateau && canShift;
170
+
171
+ if (paradigmShift) {
172
+ this.lastShiftStep = currentStep;
173
+ }
174
+
175
+ return {
176
+ vfe,
177
+ mean: stats.mean,
178
+ variance: stats.variance,
179
+ isPlateau,
180
+ paradigmShift
181
+ };
182
+ }
183
+
184
+ /**
185
+ * Calculate mean and sample variance over the current sliding window.
186
+ */
187
+ computeWindowStats(): WindowStats {
188
+ const n = this.history.length;
189
+ if (n === 0) return { mean: 0, variance: 0, count: 0 };
190
+ if (n === 1) return { mean: this.history[0]!.vfe, variance: 0, count: 1 };
191
+
192
+ const sum = this.history.reduce((acc, item) => acc + item.vfe, 0);
193
+ const mean = sum / n;
194
+
195
+ const sumSqDiff = this.history.reduce((acc, item) => {
196
+ const diff = item.vfe - mean;
197
+ return acc + (diff * diff);
198
+ }, 0);
199
+
200
+ const variance = sumSqDiff / (n - 1);
201
+
202
+ return {
203
+ mean: Number(mean.toFixed(4)),
204
+ variance: Number(variance.toFixed(6)),
205
+ count: n
206
+ };
207
+ }
208
+
209
+ /**
210
+ * Get the current status for telemetry or debugging.
211
+ */
212
+ getStatus(): VFEStatus {
213
+ const stats = this.computeWindowStats();
214
+ const isPlateau = (
215
+ this.history.length >= MIN_SAMPLES_FOR_PLATEAU &&
216
+ stats.mean >= this.meanThreshold &&
217
+ stats.variance <= this.varianceEpsilon
218
+ );
219
+ return {
220
+ totalSteps: this.totalSteps,
221
+ windowCount: this.history.length,
222
+ mean: stats.mean,
223
+ variance: stats.variance,
224
+ isPlateau
225
+ };
226
+ }
227
+
228
+ /**
229
+ * Format the high-priority paradigm shift prompt directive.
230
+ */
231
+ getParadigmShiftDirective(): string {
232
+ return (
233
+ `\n\n🚨 [PARADIGM SHIFT REQUIRED — ACTIVE INFERENCE DEADLOCK DETECTED]\n` +
234
+ `Variational Free Energy tracking detected a persistent error plateau with zero convergence.\n` +
235
+ `Your current mental model and execution route are trapped in a local minimum.\n` +
236
+ `DO NOT retry previous variations, tweak parameters, or repeat failed commands.\n` +
237
+ `1. Explicitly identify which core assumption failed.\n` +
238
+ `2. State your revised hypothesis about the environment/codebase.\n` +
239
+ `3. Choose a fundamentally different tool, strategy, or alternative path.\n`
240
+ );
241
+ }
242
+ }