principles-disciple 1.6.0 → 1.7.1
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/dist/commands/context.js +7 -3
- package/dist/commands/evolution-status.d.ts +4 -0
- package/dist/commands/evolution-status.js +134 -0
- package/dist/commands/export.d.ts +2 -0
- package/dist/commands/export.js +45 -0
- package/dist/commands/focus.js +9 -6
- package/dist/commands/pain.js +8 -0
- package/dist/commands/principle-rollback.d.ts +4 -0
- package/dist/commands/principle-rollback.js +22 -0
- package/dist/commands/rollback.js +9 -3
- package/dist/commands/samples.d.ts +2 -0
- package/dist/commands/samples.js +55 -0
- package/dist/commands/trust.js +64 -81
- package/dist/core/config.d.ts +5 -0
- package/dist/core/control-ui-db.d.ts +68 -0
- package/dist/core/control-ui-db.js +274 -0
- package/dist/core/detection-funnel.d.ts +1 -1
- package/dist/core/detection-funnel.js +4 -0
- package/dist/core/dictionary.d.ts +2 -0
- package/dist/core/dictionary.js +13 -0
- package/dist/core/event-log.d.ts +7 -1
- package/dist/core/event-log.js +10 -0
- package/dist/core/evolution-engine.d.ts +5 -5
- package/dist/core/evolution-engine.js +18 -18
- package/dist/core/evolution-migration.d.ts +5 -0
- package/dist/core/evolution-migration.js +65 -0
- package/dist/core/evolution-reducer.d.ts +69 -0
- package/dist/core/evolution-reducer.js +369 -0
- package/dist/core/evolution-types.d.ts +103 -0
- package/dist/core/path-resolver.js +75 -36
- package/dist/core/paths.d.ts +7 -8
- package/dist/core/paths.js +48 -40
- package/dist/core/profile.js +1 -1
- package/dist/core/session-tracker.d.ts +14 -2
- package/dist/core/session-tracker.js +75 -9
- package/dist/core/thinking-models.d.ts +38 -0
- package/dist/core/thinking-models.js +170 -0
- package/dist/core/trajectory.d.ts +184 -0
- package/dist/core/trajectory.js +817 -0
- package/dist/core/trust-engine.d.ts +6 -0
- package/dist/core/trust-engine.js +50 -29
- package/dist/core/workspace-context.d.ts +13 -0
- package/dist/core/workspace-context.js +50 -7
- package/dist/hooks/gate.js +171 -87
- package/dist/hooks/llm.js +119 -71
- package/dist/hooks/pain.js +105 -5
- package/dist/hooks/prompt.d.ts +11 -14
- package/dist/hooks/prompt.js +283 -57
- package/dist/hooks/subagent.js +69 -28
- package/dist/hooks/trajectory-collector.d.ts +32 -0
- package/dist/hooks/trajectory-collector.js +256 -0
- package/dist/http/principles-console-route.d.ts +2 -0
- package/dist/http/principles-console-route.js +257 -0
- package/dist/i18n/commands.js +16 -0
- package/dist/index.js +105 -4
- package/dist/service/control-ui-query-service.d.ts +217 -0
- package/dist/service/control-ui-query-service.js +537 -0
- package/dist/service/empathy-observer-manager.d.ts +2 -0
- package/dist/service/empathy-observer-manager.js +43 -1
- package/dist/service/evolution-worker.d.ts +27 -0
- package/dist/service/evolution-worker.js +256 -41
- package/dist/service/runtime-summary-service.d.ts +79 -0
- package/dist/service/runtime-summary-service.js +319 -0
- package/dist/service/trajectory-service.d.ts +2 -0
- package/dist/service/trajectory-service.js +15 -0
- package/dist/tools/agent-spawn.d.ts +27 -6
- package/dist/tools/agent-spawn.js +339 -87
- package/dist/tools/deep-reflect.d.ts +27 -7
- package/dist/tools/deep-reflect.js +210 -121
- package/dist/types/event-types.d.ts +10 -2
- package/dist/types.d.ts +10 -0
- package/dist/types.js +5 -0
- package/openclaw.plugin.json +43 -11
- package/package.json +14 -4
- package/templates/langs/zh/skills/pd-daily/SKILL.md +97 -13
package/dist/core/paths.js
CHANGED
|
@@ -1,67 +1,75 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
+
const posixJoin = (...parts) => path.posix.join(...parts);
|
|
3
|
+
function isWindowsPath(input) {
|
|
4
|
+
return /^[A-Za-z]:[\\/]/.test(input) || input.startsWith('\\\\');
|
|
5
|
+
}
|
|
6
|
+
function joinWorkspacePath(workspaceDir, relativePath) {
|
|
7
|
+
if (isWindowsPath(workspaceDir)) {
|
|
8
|
+
return path.win32.join(workspaceDir, relativePath);
|
|
9
|
+
}
|
|
10
|
+
if (workspaceDir.startsWith('/')) {
|
|
11
|
+
return path.posix.join(workspaceDir, relativePath);
|
|
12
|
+
}
|
|
13
|
+
return path.join(workspaceDir, relativePath);
|
|
14
|
+
}
|
|
2
15
|
/**
|
|
3
16
|
* Principles Disciple Directory Constants
|
|
4
17
|
* Establishing a logical separation between Identity, State, and Memory.
|
|
5
18
|
*/
|
|
6
19
|
export const PD_DIRS = {
|
|
7
|
-
/** 🧬 Core configuration, identity, and kernel rules (hidden) */
|
|
8
20
|
IDENTITY: '.principles',
|
|
9
|
-
|
|
10
|
-
MODELS: path.join('.principles', 'models'),
|
|
11
|
-
/** ⚡ Volatile operational data, queues, and task status (hidden) */
|
|
21
|
+
MODELS: posixJoin('.principles', 'models'),
|
|
12
22
|
STATE: '.state',
|
|
13
|
-
/** 💾 Historical records, logs, and long-term memory */
|
|
14
23
|
MEMORY: 'memory',
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
SESSIONS: path.join('.state', 'sessions'),
|
|
21
|
-
/** 🩹 Semantic pain samples for L3 retrieval */
|
|
22
|
-
PAIN_SAMPLES: path.join('memory', 'pain'),
|
|
24
|
+
OKR: posixJoin('memory', 'okr'),
|
|
25
|
+
LOGS: posixJoin('memory', 'logs'),
|
|
26
|
+
SESSIONS: posixJoin('.state', 'sessions'),
|
|
27
|
+
PAIN_SAMPLES: posixJoin('memory', 'pain'),
|
|
28
|
+
LOCKS: posixJoin('memory', '.locks'),
|
|
23
29
|
};
|
|
24
30
|
/**
|
|
25
31
|
* Standard File Path Mappings
|
|
26
32
|
*/
|
|
27
33
|
export const PD_FILES = {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
DECISION_POLICY: path.join(PD_DIRS.IDENTITY, 'DECISION_POLICY.json'),
|
|
34
|
+
PROFILE: posixJoin(PD_DIRS.IDENTITY, 'PROFILE.json'),
|
|
35
|
+
PRINCIPLES: posixJoin(PD_DIRS.IDENTITY, 'PRINCIPLES.md'),
|
|
36
|
+
THINKING_OS: posixJoin(PD_DIRS.IDENTITY, 'THINKING_OS.md'),
|
|
37
|
+
KERNEL: posixJoin(PD_DIRS.IDENTITY, '00-kernel.md'),
|
|
38
|
+
DECISION_POLICY: posixJoin(PD_DIRS.IDENTITY, 'DECISION_POLICY.json'),
|
|
34
39
|
MODELS_DIR: PD_DIRS.MODELS,
|
|
35
|
-
// State Layer
|
|
36
40
|
STATE_DIR: PD_DIRS.STATE,
|
|
37
|
-
EVOLUTION_QUEUE:
|
|
38
|
-
EVOLUTION_DIRECTIVE:
|
|
39
|
-
WORKBOARD:
|
|
40
|
-
AGENT_SCORECARD:
|
|
41
|
-
PAIN_FLAG:
|
|
42
|
-
SYSTEM_CAPABILITIES:
|
|
43
|
-
PAIN_SETTINGS:
|
|
44
|
-
PAIN_CANDIDATES:
|
|
45
|
-
THINKING_OS_USAGE:
|
|
41
|
+
EVOLUTION_QUEUE: posixJoin(PD_DIRS.STATE, 'evolution_queue.json'),
|
|
42
|
+
EVOLUTION_DIRECTIVE: posixJoin(PD_DIRS.STATE, 'evolution_directive.json'),
|
|
43
|
+
WORKBOARD: posixJoin(PD_DIRS.STATE, 'WORKBOARD.json'),
|
|
44
|
+
AGENT_SCORECARD: posixJoin(PD_DIRS.STATE, 'AGENT_SCORECARD.json'),
|
|
45
|
+
PAIN_FLAG: posixJoin(PD_DIRS.STATE, '.pain_flag'),
|
|
46
|
+
SYSTEM_CAPABILITIES: posixJoin(PD_DIRS.STATE, 'SYSTEM_CAPABILITIES.json'),
|
|
47
|
+
PAIN_SETTINGS: posixJoin(PD_DIRS.STATE, 'pain_settings.json'),
|
|
48
|
+
PAIN_CANDIDATES: posixJoin(PD_DIRS.STATE, 'pain_candidates.json'),
|
|
49
|
+
THINKING_OS_USAGE: posixJoin(PD_DIRS.STATE, 'thinking_os_usage.json'),
|
|
50
|
+
TRAJECTORY_DB: posixJoin(PD_DIRS.STATE, 'trajectory.db'),
|
|
51
|
+
TRAJECTORY_BLOBS_DIR: posixJoin(PD_DIRS.STATE, 'blobs'),
|
|
52
|
+
EXPORTS_DIR: posixJoin(PD_DIRS.STATE, 'exports'),
|
|
46
53
|
SESSION_DIR: PD_DIRS.SESSIONS,
|
|
47
|
-
DICTIONARY:
|
|
48
|
-
|
|
54
|
+
DICTIONARY: posixJoin(PD_DIRS.STATE, 'pain_dictionary.json'),
|
|
55
|
+
PRINCIPLE_BLACKLIST: posixJoin(PD_DIRS.STATE, 'principle_blacklist.json'),
|
|
49
56
|
PLAN: 'PLAN.md',
|
|
50
57
|
MEMORY_MD: 'MEMORY.md',
|
|
51
58
|
HEARTBEAT: 'HEARTBEAT.md',
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
USER_CONTEXT: path.join(PD_DIRS.MEMORY, 'USER_CONTEXT.md'),
|
|
59
|
+
SYSTEM_LOG: posixJoin(PD_DIRS.LOGS, 'SYSTEM.log'),
|
|
60
|
+
REFLECTION_LOG: posixJoin(PD_DIRS.MEMORY, 'reflection-log.md'),
|
|
61
|
+
USER_CONTEXT: posixJoin(PD_DIRS.MEMORY, 'USER_CONTEXT.md'),
|
|
56
62
|
OKR_DIR: PD_DIRS.OKR,
|
|
57
|
-
CURRENT_FOCUS:
|
|
58
|
-
WEEK_STATE:
|
|
59
|
-
THINKING_OS_CANDIDATES:
|
|
60
|
-
SEMANTIC_PAIN:
|
|
63
|
+
CURRENT_FOCUS: posixJoin(PD_DIRS.OKR, 'CURRENT_FOCUS.md'),
|
|
64
|
+
WEEK_STATE: posixJoin(PD_DIRS.OKR, 'WEEK_STATE.json'),
|
|
65
|
+
THINKING_OS_CANDIDATES: posixJoin(PD_DIRS.MEMORY, 'THINKING_OS_CANDIDATES.md'),
|
|
66
|
+
SEMANTIC_PAIN: posixJoin(PD_DIRS.PAIN_SAMPLES, 'confusion_samples.md'),
|
|
67
|
+
EVOLUTION_STREAM: posixJoin(PD_DIRS.MEMORY, 'evolution.jsonl'),
|
|
68
|
+
EVOLUTION_LOCK: posixJoin(PD_DIRS.LOCKS, 'evolution'),
|
|
61
69
|
};
|
|
62
70
|
/**
|
|
63
71
|
* Resolves a PD file path within a given workspace.
|
|
64
72
|
*/
|
|
65
73
|
export function resolvePdPath(workspaceDir, fileKey) {
|
|
66
|
-
return
|
|
74
|
+
return joinWorkspacePath(workspaceDir, PD_FILES[fileKey]);
|
|
67
75
|
}
|
package/dist/core/profile.js
CHANGED
|
@@ -51,7 +51,7 @@ export const PROFILE_DEFAULTS = {
|
|
|
51
51
|
thinking_checkpoint: {
|
|
52
52
|
enabled: false, // Default OFF to avoid blocking new users
|
|
53
53
|
window_ms: 5 * 60 * 1000, // 5 minute window
|
|
54
|
-
high_risk_tools: ['run_shell_command', 'delete_file', 'move_file', '
|
|
54
|
+
high_risk_tools: ['run_shell_command', 'delete_file', 'move_file', 'pd_run_worker'],
|
|
55
55
|
},
|
|
56
56
|
custom_guards: [],
|
|
57
57
|
};
|
|
@@ -18,6 +18,8 @@ export interface SessionState {
|
|
|
18
18
|
cacheHits: number;
|
|
19
19
|
stuckLoops: number;
|
|
20
20
|
currentGfi: number;
|
|
21
|
+
gfiBySource?: Record<string, number>;
|
|
22
|
+
lastErrorSource?: string;
|
|
21
23
|
lastErrorHash: string;
|
|
22
24
|
consecutiveErrors: number;
|
|
23
25
|
dailyToolCalls: number;
|
|
@@ -25,6 +27,7 @@ export interface SessionState {
|
|
|
25
27
|
dailyPainSignals: number;
|
|
26
28
|
dailyGfiPeak: number;
|
|
27
29
|
lastThinkingTimestamp: number;
|
|
30
|
+
injectedProbationIds?: string[];
|
|
28
31
|
}
|
|
29
32
|
/**
|
|
30
33
|
* Initialize persistence for session state.
|
|
@@ -40,11 +43,16 @@ export declare function trackLlmOutput(sessionId: string, usage: TokenUsage | un
|
|
|
40
43
|
/**
|
|
41
44
|
* Tracks physical friction based on tool execution failures.
|
|
42
45
|
*/
|
|
43
|
-
export declare function trackFriction(sessionId: string, deltaF: number, hash: string, workspaceDir?: string
|
|
46
|
+
export declare function trackFriction(sessionId: string, deltaF: number, hash: string, workspaceDir?: string, options?: {
|
|
47
|
+
source?: string;
|
|
48
|
+
}): SessionState;
|
|
44
49
|
/**
|
|
45
50
|
* Resets the friction index upon successful action.
|
|
46
51
|
*/
|
|
47
|
-
export declare function resetFriction(sessionId: string, workspaceDir?: string
|
|
52
|
+
export declare function resetFriction(sessionId: string, workspaceDir?: string, options?: {
|
|
53
|
+
source?: string;
|
|
54
|
+
amount?: number;
|
|
55
|
+
}): SessionState;
|
|
48
56
|
/**
|
|
49
57
|
* Records that deep thinking (Thinking OS) was performed in this session.
|
|
50
58
|
* Used by the Thinking OS checkpoint to allow high-risk operations.
|
|
@@ -58,7 +66,11 @@ export declare function recordThinkingCheckpoint(sessionId: string, workspaceDir
|
|
|
58
66
|
*/
|
|
59
67
|
export declare function hasRecentThinking(sessionId: string, windowMs?: number): boolean;
|
|
60
68
|
export declare function trackBlock(sessionId: string): SessionState;
|
|
69
|
+
export declare function setInjectedProbationIds(sessionId: string, ids: string[], workspaceDir?: string): SessionState;
|
|
70
|
+
export declare function getInjectedProbationIds(sessionId: string, workspaceDir?: string): string[];
|
|
71
|
+
export declare function clearInjectedProbationIds(sessionId: string, workspaceDir?: string): SessionState;
|
|
61
72
|
export declare function getSession(sessionId: string): SessionState | undefined;
|
|
73
|
+
export declare function listSessions(workspaceDir?: string): SessionState[];
|
|
62
74
|
export declare function clearSession(sessionId: string): void;
|
|
63
75
|
export declare function garbageCollectSessions(): void;
|
|
64
76
|
/**
|
|
@@ -6,6 +6,11 @@ const sessions = new Map();
|
|
|
6
6
|
let persistDir = null;
|
|
7
7
|
/** Debounce timer for persistence */
|
|
8
8
|
let persistTimer = null;
|
|
9
|
+
function logSessionTrackerWarning(message, error) {
|
|
10
|
+
const detail = error instanceof Error ? error.message : error ? String(error) : '';
|
|
11
|
+
const suffix = detail ? `: ${detail}` : '';
|
|
12
|
+
console.warn(`[PD:SessionTracker] ${message}${suffix}`);
|
|
13
|
+
}
|
|
9
14
|
/**
|
|
10
15
|
* Initialize persistence for session state.
|
|
11
16
|
* Call this once during plugin startup.
|
|
@@ -48,13 +53,13 @@ function loadAllSessions() {
|
|
|
48
53
|
}
|
|
49
54
|
sessions.set(state.sessionId, state);
|
|
50
55
|
}
|
|
51
|
-
catch {
|
|
52
|
-
|
|
56
|
+
catch (error) {
|
|
57
|
+
logSessionTrackerWarning(`Failed to load session snapshot ${file}`, error);
|
|
53
58
|
}
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
catch (err) {
|
|
57
|
-
|
|
62
|
+
logSessionTrackerWarning('Failed to load persisted sessions', err);
|
|
58
63
|
}
|
|
59
64
|
}
|
|
60
65
|
/**
|
|
@@ -69,8 +74,8 @@ function persistSession(state) {
|
|
|
69
74
|
try {
|
|
70
75
|
fs.writeFileSync(sessionPath, JSON.stringify(state, null, 2), 'utf-8');
|
|
71
76
|
}
|
|
72
|
-
catch {
|
|
73
|
-
|
|
77
|
+
catch (error) {
|
|
78
|
+
logSessionTrackerWarning(`Failed to persist session ${state.sessionId}`, error);
|
|
74
79
|
}
|
|
75
80
|
}
|
|
76
81
|
/**
|
|
@@ -108,6 +113,8 @@ function getOrCreateSession(sessionId, workspaceDir) {
|
|
|
108
113
|
cacheHits: 0,
|
|
109
114
|
stuckLoops: 0,
|
|
110
115
|
currentGfi: 0,
|
|
116
|
+
gfiBySource: {},
|
|
117
|
+
lastErrorSource: '',
|
|
111
118
|
lastErrorHash: '',
|
|
112
119
|
consecutiveErrors: 0,
|
|
113
120
|
dailyToolCalls: 0,
|
|
@@ -115,6 +122,7 @@ function getOrCreateSession(sessionId, workspaceDir) {
|
|
|
115
122
|
dailyPainSignals: 0,
|
|
116
123
|
dailyGfiPeak: 0,
|
|
117
124
|
lastThinkingTimestamp: 0,
|
|
125
|
+
injectedProbationIds: [],
|
|
118
126
|
};
|
|
119
127
|
sessions.set(sessionId, state);
|
|
120
128
|
}
|
|
@@ -123,6 +131,12 @@ function getOrCreateSession(sessionId, workspaceDir) {
|
|
|
123
131
|
}
|
|
124
132
|
return state;
|
|
125
133
|
}
|
|
134
|
+
function ensureGfiLedger(state) {
|
|
135
|
+
if (!state.gfiBySource || typeof state.gfiBySource !== 'object') {
|
|
136
|
+
state.gfiBySource = {};
|
|
137
|
+
}
|
|
138
|
+
return state.gfiBySource;
|
|
139
|
+
}
|
|
126
140
|
export function trackToolRead(sessionId, filePath, workspaceDir) {
|
|
127
141
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
128
142
|
const normalizedPath = path.posix.normalize(filePath.replace(/\\/g, '/'));
|
|
@@ -164,12 +178,14 @@ export function trackLlmOutput(sessionId, usage, config, workspaceDir) {
|
|
|
164
178
|
/**
|
|
165
179
|
* Tracks physical friction based on tool execution failures.
|
|
166
180
|
*/
|
|
167
|
-
export function trackFriction(sessionId, deltaF, hash, workspaceDir) {
|
|
181
|
+
export function trackFriction(sessionId, deltaF, hash, workspaceDir, options) {
|
|
168
182
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
183
|
+
const ledger = ensureGfiLedger(state);
|
|
169
184
|
if (hash && hash === state.lastErrorHash) {
|
|
170
185
|
state.consecutiveErrors++;
|
|
171
186
|
}
|
|
172
187
|
else {
|
|
188
|
+
state.lastErrorSource = options?.source || (hash ? `unattributed:${hash}` : 'unattributed:unknown');
|
|
173
189
|
state.lastErrorHash = hash;
|
|
174
190
|
state.consecutiveErrors = 1;
|
|
175
191
|
}
|
|
@@ -177,6 +193,8 @@ export function trackFriction(sessionId, deltaF, hash, workspaceDir) {
|
|
|
177
193
|
const multiplier = Math.pow(1.5, state.consecutiveErrors - 1);
|
|
178
194
|
const addedFriction = deltaF * multiplier;
|
|
179
195
|
state.currentGfi = (state.currentGfi || 0) + addedFriction;
|
|
196
|
+
const sourceKey = options?.source || (hash ? `unattributed:${hash}` : 'unattributed:unknown');
|
|
197
|
+
ledger[sourceKey] = (ledger[sourceKey] || 0) + addedFriction;
|
|
180
198
|
state.lastActivityAt = Date.now();
|
|
181
199
|
SystemLogger.log(state.workspaceDir, 'GFI_INC', `Friction added: +${addedFriction.toFixed(1)} (Base: ${deltaF}, Mult: ${multiplier.toFixed(2)}). Total GFI: ${state.currentGfi.toFixed(1)}`);
|
|
182
200
|
// Update daily stats
|
|
@@ -189,12 +207,36 @@ export function trackFriction(sessionId, deltaF, hash, workspaceDir) {
|
|
|
189
207
|
/**
|
|
190
208
|
* Resets the friction index upon successful action.
|
|
191
209
|
*/
|
|
192
|
-
export function resetFriction(sessionId, workspaceDir) {
|
|
210
|
+
export function resetFriction(sessionId, workspaceDir, options) {
|
|
193
211
|
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
212
|
+
const ledger = ensureGfiLedger(state);
|
|
213
|
+
if (options?.source) {
|
|
214
|
+
const sourceKey = options.source;
|
|
215
|
+
const currentSource = ledger[sourceKey] || 0;
|
|
216
|
+
const requestedAmount = Number.isFinite(options.amount) ? Number(options.amount) : currentSource;
|
|
217
|
+
const amountToRemove = Math.max(0, Math.min(currentSource, requestedAmount));
|
|
218
|
+
if (amountToRemove > 0) {
|
|
219
|
+
ledger[sourceKey] = Math.max(0, currentSource - amountToRemove);
|
|
220
|
+
if (ledger[sourceKey] === 0) {
|
|
221
|
+
delete ledger[sourceKey];
|
|
222
|
+
}
|
|
223
|
+
state.currentGfi = Math.max(0, (state.currentGfi || 0) - amountToRemove);
|
|
224
|
+
SystemLogger.log(state.workspaceDir, 'GFI_SLICE_RESET', `Friction slice reset for ${sourceKey}: -${amountToRemove.toFixed(1)}. Total GFI: ${state.currentGfi.toFixed(1)}`);
|
|
225
|
+
if (state.lastErrorSource === sourceKey) {
|
|
226
|
+
state.consecutiveErrors = 0;
|
|
227
|
+
state.lastErrorHash = '';
|
|
228
|
+
state.lastErrorSource = '';
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
schedulePersistence(state);
|
|
232
|
+
return state;
|
|
233
|
+
}
|
|
194
234
|
if (state.currentGfi > 0) {
|
|
195
235
|
SystemLogger.log(state.workspaceDir, 'GFI_RESET', `Friction reset to 0 (Was: ${state.currentGfi.toFixed(1)}). Action successful.`);
|
|
196
236
|
}
|
|
197
237
|
state.currentGfi = 0;
|
|
238
|
+
state.gfiBySource = {};
|
|
239
|
+
state.lastErrorSource = '';
|
|
198
240
|
state.consecutiveErrors = 0;
|
|
199
241
|
state.lastErrorHash = '';
|
|
200
242
|
// Schedule persistence
|
|
@@ -230,9 +272,33 @@ export function trackBlock(sessionId) {
|
|
|
230
272
|
state.lastActivityAt = Date.now();
|
|
231
273
|
return state;
|
|
232
274
|
}
|
|
275
|
+
export function setInjectedProbationIds(sessionId, ids, workspaceDir) {
|
|
276
|
+
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
277
|
+
state.injectedProbationIds = [...ids];
|
|
278
|
+
state.lastActivityAt = Date.now();
|
|
279
|
+
schedulePersistence(state);
|
|
280
|
+
return state;
|
|
281
|
+
}
|
|
282
|
+
export function getInjectedProbationIds(sessionId, workspaceDir) {
|
|
283
|
+
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
284
|
+
return [...(state.injectedProbationIds || [])];
|
|
285
|
+
}
|
|
286
|
+
export function clearInjectedProbationIds(sessionId, workspaceDir) {
|
|
287
|
+
return setInjectedProbationIds(sessionId, [], workspaceDir);
|
|
288
|
+
}
|
|
233
289
|
export function getSession(sessionId) {
|
|
234
290
|
return sessions.get(sessionId);
|
|
235
291
|
}
|
|
292
|
+
export function listSessions(workspaceDir) {
|
|
293
|
+
return [...sessions.values()]
|
|
294
|
+
.filter((state) => !workspaceDir || !state.workspaceDir || state.workspaceDir === workspaceDir)
|
|
295
|
+
.map((state) => ({
|
|
296
|
+
...state,
|
|
297
|
+
toolReadsByFile: { ...state.toolReadsByFile },
|
|
298
|
+
gfiBySource: state.gfiBySource ? { ...state.gfiBySource } : undefined,
|
|
299
|
+
injectedProbationIds: state.injectedProbationIds ? [...state.injectedProbationIds] : undefined,
|
|
300
|
+
}));
|
|
301
|
+
}
|
|
236
302
|
export function clearSession(sessionId) {
|
|
237
303
|
sessions.delete(sessionId);
|
|
238
304
|
}
|
|
@@ -249,8 +315,8 @@ export function garbageCollectSessions() {
|
|
|
249
315
|
try {
|
|
250
316
|
fs.unlinkSync(sessionPath);
|
|
251
317
|
}
|
|
252
|
-
catch {
|
|
253
|
-
|
|
318
|
+
catch (error) {
|
|
319
|
+
logSessionTrackerWarning(`Failed to delete session snapshot for ${id}`, error);
|
|
254
320
|
}
|
|
255
321
|
}
|
|
256
322
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface ThinkingModelDefinition {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
patterns: RegExp[];
|
|
6
|
+
baselineScenarios: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface ThinkingModelMatch {
|
|
9
|
+
modelId: string;
|
|
10
|
+
matchedPattern: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ThinkingScenarioContext {
|
|
13
|
+
recentToolCalls?: Array<{
|
|
14
|
+
toolName: string;
|
|
15
|
+
outcome: 'success' | 'failure' | 'blocked';
|
|
16
|
+
errorType?: string | null;
|
|
17
|
+
}>;
|
|
18
|
+
recentPainEvents?: Array<{
|
|
19
|
+
source: string;
|
|
20
|
+
score: number;
|
|
21
|
+
}>;
|
|
22
|
+
recentGateBlocks?: Array<{
|
|
23
|
+
toolName: string;
|
|
24
|
+
reason: string;
|
|
25
|
+
}>;
|
|
26
|
+
recentUserCorrections?: Array<{
|
|
27
|
+
correctionCue?: string | null;
|
|
28
|
+
}>;
|
|
29
|
+
recentPrincipleEvents?: Array<{
|
|
30
|
+
eventType: string;
|
|
31
|
+
principleId?: string | null;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
export declare const THINKING_MODEL_MAP: Map<string, ThinkingModelDefinition>;
|
|
35
|
+
export declare function listThinkingModels(): ThinkingModelDefinition[];
|
|
36
|
+
export declare function getThinkingModel(modelId: string): ThinkingModelDefinition | undefined;
|
|
37
|
+
export declare function detectThinkingModelMatches(text: string): ThinkingModelMatch[];
|
|
38
|
+
export declare function deriveThinkingScenarios(modelId: string, context: ThinkingScenarioContext): string[];
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const THINKING_MODELS = [
|
|
2
|
+
{
|
|
3
|
+
id: 'T-01',
|
|
4
|
+
name: 'Survey Before Acting',
|
|
5
|
+
description: 'Understand the structure first before making changes.',
|
|
6
|
+
baselineScenarios: ['exploration', 'discovery'],
|
|
7
|
+
patterns: [
|
|
8
|
+
/let me (first )?(understand|map|outline|survey|review the (structure|architecture|dependencies))/i,
|
|
9
|
+
/before (changing|editing|touching) anything/i,
|
|
10
|
+
/让我先(梳理|理解|看看|盘点).*(结构|架构|依赖|全貌)/i,
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: 'T-02',
|
|
15
|
+
name: 'Respect Constraints',
|
|
16
|
+
description: 'Explicitly reason about contracts, tests, schemas, and requirements.',
|
|
17
|
+
baselineScenarios: ['constraint-check', 'contract-verification'],
|
|
18
|
+
patterns: [
|
|
19
|
+
/(type|test|contract|schema|interface) (constraint|requirement|check|validation)/i,
|
|
20
|
+
/we (must|need to) (respect|follow|adhere to) the/i,
|
|
21
|
+
/(必须|需要).*(遵守|符合|满足).*(类型|测试|契约|接口|规范)/i,
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'T-03',
|
|
26
|
+
name: 'Evidence Over Assumption',
|
|
27
|
+
description: 'Use logs, code, and outputs before inferring causes.',
|
|
28
|
+
baselineScenarios: ['evidence-gathering', 'verification'],
|
|
29
|
+
patterns: [
|
|
30
|
+
/based on (the |this )?(evidence|logs?|output|error|stack trace|test result)/i,
|
|
31
|
+
/let me (check|verify|confirm|read|look at) (the |)(actual|source|code|file|log)/i,
|
|
32
|
+
/根据(日志|证据|输出|报错|堆栈|测试结果)/i,
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'T-04',
|
|
37
|
+
name: 'Reversible First',
|
|
38
|
+
description: 'Prefer changes that are safe to roll back when risk is high.',
|
|
39
|
+
baselineScenarios: ['risk-management', 'reversibility'],
|
|
40
|
+
patterns: [
|
|
41
|
+
/this (is|would be) (irreversible|destructive|permanent|not easily undone)/i,
|
|
42
|
+
/(reversible|can be undone|safely roll back)/i,
|
|
43
|
+
/(不可逆|破坏性|无法回滚|可以回滚|安全撤销)/i,
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'T-05',
|
|
48
|
+
name: 'Safety Rails',
|
|
49
|
+
description: 'Call out guardrails, prohibitions, and failure-prevention constraints.',
|
|
50
|
+
baselineScenarios: ['guardrails', 'safety-rails'],
|
|
51
|
+
patterns: [
|
|
52
|
+
/we (must|should) (not|never|avoid|prevent|ensure we don't)/i,
|
|
53
|
+
/(critical|important) (not to|that we don't|to avoid)/i,
|
|
54
|
+
/(绝不能|必须避免|不可|禁止|确保不会)/i,
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: 'T-06',
|
|
59
|
+
name: 'Simplicity First',
|
|
60
|
+
description: 'Prefer the smallest understandable solution over over-engineering.',
|
|
61
|
+
baselineScenarios: ['simplification', 'pragmatism'],
|
|
62
|
+
patterns: [
|
|
63
|
+
/(simpl(er|est|ify)|minimal|straightforward|lean) (approach|solution|fix|implementation)/i,
|
|
64
|
+
/(simple is better|keep it simple|no need to over)/i,
|
|
65
|
+
/(最简|更简单|精简|没必要过度设计)/i,
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: 'T-07',
|
|
70
|
+
name: 'Minimal Change Surface',
|
|
71
|
+
description: 'Limit the blast radius and touch only what is necessary.',
|
|
72
|
+
baselineScenarios: ['minimal-diff', 'blast-radius-control'],
|
|
73
|
+
patterns: [
|
|
74
|
+
/(minimal|smallest|narrowest|least) (change|diff|modification|impact)/i,
|
|
75
|
+
/only (change|modify|touch|edit) (the |what)/i,
|
|
76
|
+
/(最小改动|最小变更|只改|只动必要部分)/i,
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'T-08',
|
|
81
|
+
name: 'Pain As Signal',
|
|
82
|
+
description: 'Treat failures and friction as clues to step back and rethink.',
|
|
83
|
+
baselineScenarios: ['reflection', 'pain-response'],
|
|
84
|
+
patterns: [
|
|
85
|
+
/this (error|failure|issue) (tells us|indicates|signals|suggests|means)/i,
|
|
86
|
+
/let me (stop|pause|step back|reconsider|rethink)/i,
|
|
87
|
+
/这个(错误|失败|问题).*(说明|意味着|提示)/i,
|
|
88
|
+
/让我(停下|暂停|退一步|重新考虑|重新审视)/i,
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'T-09',
|
|
93
|
+
name: 'Divide And Conquer',
|
|
94
|
+
description: 'Split the task into smaller phases before execution.',
|
|
95
|
+
baselineScenarios: ['decomposition', 'phased-execution'],
|
|
96
|
+
patterns: [
|
|
97
|
+
/(break|split|decompose|divide) (this |the task |it )?(into|down)/i,
|
|
98
|
+
/(step 1|first,? (we|i|let's)|phase 1)/i,
|
|
99
|
+
/(拆分|分解|分步|分阶段|第一步)/i,
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
export const THINKING_MODEL_MAP = new Map(THINKING_MODELS.map((model) => [model.id, model]));
|
|
104
|
+
export function listThinkingModels() {
|
|
105
|
+
return THINKING_MODELS.slice();
|
|
106
|
+
}
|
|
107
|
+
export function getThinkingModel(modelId) {
|
|
108
|
+
return THINKING_MODEL_MAP.get(modelId);
|
|
109
|
+
}
|
|
110
|
+
export function detectThinkingModelMatches(text) {
|
|
111
|
+
if (!text)
|
|
112
|
+
return [];
|
|
113
|
+
const matches = [];
|
|
114
|
+
for (const model of THINKING_MODELS) {
|
|
115
|
+
for (const pattern of model.patterns) {
|
|
116
|
+
if (pattern.test(text)) {
|
|
117
|
+
matches.push({
|
|
118
|
+
modelId: model.id,
|
|
119
|
+
matchedPattern: pattern.source,
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return matches;
|
|
126
|
+
}
|
|
127
|
+
export function deriveThinkingScenarios(modelId, context) {
|
|
128
|
+
const scenarios = new Set(getThinkingModel(modelId)?.baselineScenarios ?? []);
|
|
129
|
+
if ((context.recentToolCalls ?? []).some((call) => call.outcome === 'failure')) {
|
|
130
|
+
scenarios.add('after-tool-failure');
|
|
131
|
+
}
|
|
132
|
+
// after-recovery: success that follows a failure (not just any success)
|
|
133
|
+
const calls = context.recentToolCalls ?? [];
|
|
134
|
+
const hasFailure = calls.some((call) => call.outcome === 'failure');
|
|
135
|
+
const hasSuccess = calls.some((call) => call.outcome === 'success');
|
|
136
|
+
if (hasFailure && hasSuccess) {
|
|
137
|
+
scenarios.add('after-recovery');
|
|
138
|
+
}
|
|
139
|
+
if ((context.recentToolCalls ?? []).some((call) => call.outcome === 'blocked')) {
|
|
140
|
+
scenarios.add('blocked-execution');
|
|
141
|
+
}
|
|
142
|
+
if ((context.recentToolCalls ?? []).some((call) => Boolean(call.errorType))) {
|
|
143
|
+
scenarios.add('incident-response');
|
|
144
|
+
}
|
|
145
|
+
if ((context.recentPainEvents ?? []).length > 0) {
|
|
146
|
+
scenarios.add('user-friction');
|
|
147
|
+
}
|
|
148
|
+
if ((context.recentGateBlocks ?? []).length > 0) {
|
|
149
|
+
scenarios.add('gate-block');
|
|
150
|
+
}
|
|
151
|
+
if ((context.recentUserCorrections ?? []).length > 0) {
|
|
152
|
+
scenarios.add('user-correction');
|
|
153
|
+
}
|
|
154
|
+
if ((context.recentPrincipleEvents ?? []).length > 0) {
|
|
155
|
+
scenarios.add('principle-feedback');
|
|
156
|
+
}
|
|
157
|
+
if (modelId === 'T-03') {
|
|
158
|
+
scenarios.add('root-cause-analysis');
|
|
159
|
+
}
|
|
160
|
+
if (modelId === 'T-04' || modelId === 'T-05') {
|
|
161
|
+
scenarios.add('risk-review');
|
|
162
|
+
}
|
|
163
|
+
if (modelId === 'T-08') {
|
|
164
|
+
scenarios.add('reflection-loop');
|
|
165
|
+
}
|
|
166
|
+
if (modelId === 'T-09') {
|
|
167
|
+
scenarios.add('task-planning');
|
|
168
|
+
}
|
|
169
|
+
return Array.from(scenarios);
|
|
170
|
+
}
|