principles-disciple 1.5.4
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/capabilities.d.ts +3 -0
- package/dist/commands/capabilities.js +73 -0
- package/dist/commands/evolver.d.ts +9 -0
- package/dist/commands/evolver.js +26 -0
- package/dist/commands/pain.d.ts +5 -0
- package/dist/commands/pain.js +114 -0
- package/dist/commands/strategy.d.ts +3 -0
- package/dist/commands/strategy.js +29 -0
- package/dist/commands/thinking-os.d.ts +2 -0
- package/dist/commands/thinking-os.js +162 -0
- package/dist/commands/trust.d.ts +4 -0
- package/dist/commands/trust.js +95 -0
- package/dist/core/agent-loader.d.ts +44 -0
- package/dist/core/agent-loader.js +147 -0
- package/dist/core/config-service.d.ts +15 -0
- package/dist/core/config-service.js +26 -0
- package/dist/core/config.d.ts +103 -0
- package/dist/core/config.js +186 -0
- package/dist/core/detection-funnel.d.ts +33 -0
- package/dist/core/detection-funnel.js +100 -0
- package/dist/core/detection-service.d.ts +15 -0
- package/dist/core/detection-service.js +28 -0
- package/dist/core/dictionary-service.d.ts +15 -0
- package/dist/core/dictionary-service.js +26 -0
- package/dist/core/dictionary.d.ts +36 -0
- package/dist/core/dictionary.js +136 -0
- package/dist/core/event-log.d.ts +53 -0
- package/dist/core/event-log.js +196 -0
- package/dist/core/evolution-engine.d.ts +119 -0
- package/dist/core/evolution-engine.js +542 -0
- package/dist/core/evolution-types.d.ts +126 -0
- package/dist/core/evolution-types.js +56 -0
- package/dist/core/hygiene/tracker.d.ts +22 -0
- package/dist/core/hygiene/tracker.js +106 -0
- package/dist/core/init.d.ts +12 -0
- package/dist/core/init.js +117 -0
- package/dist/core/migration.d.ts +6 -0
- package/dist/core/migration.js +90 -0
- package/dist/core/pain.d.ts +4 -0
- package/dist/core/pain.js +70 -0
- package/dist/core/path-resolver.d.ts +43 -0
- package/dist/core/path-resolver.js +259 -0
- package/dist/core/paths.d.ts +60 -0
- package/dist/core/paths.js +67 -0
- package/dist/core/profile.d.ts +62 -0
- package/dist/core/profile.js +210 -0
- package/dist/core/risk-calculator.d.ts +7 -0
- package/dist/core/risk-calculator.js +39 -0
- package/dist/core/session-tracker.d.ts +76 -0
- package/dist/core/session-tracker.js +286 -0
- package/dist/core/system-logger.d.ts +8 -0
- package/dist/core/system-logger.js +31 -0
- package/dist/core/trust-engine.d.ts +91 -0
- package/dist/core/trust-engine.js +284 -0
- package/dist/core/workspace-context.d.ts +64 -0
- package/dist/core/workspace-context.js +134 -0
- package/dist/hooks/gate.d.ts +6 -0
- package/dist/hooks/gate.js +487 -0
- package/dist/hooks/lifecycle.d.ts +5 -0
- package/dist/hooks/lifecycle.js +180 -0
- package/dist/hooks/llm.d.ts +4 -0
- package/dist/hooks/llm.js +153 -0
- package/dist/hooks/pain.d.ts +5 -0
- package/dist/hooks/pain.js +173 -0
- package/dist/hooks/prompt.d.ts +38 -0
- package/dist/hooks/prompt.js +285 -0
- package/dist/hooks/subagent.d.ts +2 -0
- package/dist/hooks/subagent.js +70 -0
- package/dist/i18n/commands.d.ts +26 -0
- package/dist/i18n/commands.js +88 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +204 -0
- package/dist/service/evolution-worker.d.ts +17 -0
- package/dist/service/evolution-worker.js +293 -0
- package/dist/tools/agent-spawn.d.ts +33 -0
- package/dist/tools/agent-spawn.js +170 -0
- package/dist/tools/critique-prompt.d.ts +14 -0
- package/dist/tools/critique-prompt.js +81 -0
- package/dist/tools/deep-reflect.d.ts +19 -0
- package/dist/tools/deep-reflect.js +174 -0
- package/dist/tools/model-index.d.ts +9 -0
- package/dist/tools/model-index.js +82 -0
- package/dist/types/event-types.d.ts +229 -0
- package/dist/types/event-types.js +73 -0
- package/dist/types/hygiene-types.d.ts +20 -0
- package/dist/types/hygiene-types.js +12 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.js +1 -0
- package/dist/utils/file-lock.d.ts +64 -0
- package/dist/utils/file-lock.js +270 -0
- package/dist/utils/glob-match.d.ts +28 -0
- package/dist/utils/glob-match.js +49 -0
- package/dist/utils/hashing.d.ts +9 -0
- package/dist/utils/hashing.js +25 -0
- package/dist/utils/io.d.ts +6 -0
- package/dist/utils/io.js +106 -0
- package/dist/utils/nlp.d.ts +9 -0
- package/dist/utils/nlp.js +59 -0
- package/dist/utils/plugin-logger.d.ts +39 -0
- package/dist/utils/plugin-logger.js +70 -0
- package/openclaw.plugin.json +46 -0
- package/package.json +63 -0
- package/templates/langs/en/core/AGENTS.md +206 -0
- package/templates/langs/en/core/BOOT.md +60 -0
- package/templates/langs/en/core/BOOTSTRAP.md +250 -0
- package/templates/langs/en/core/HEARTBEAT.md +74 -0
- package/templates/langs/en/core/IDENTITY.md +8 -0
- package/templates/langs/en/core/PRINCIPLES.md +10 -0
- package/templates/langs/en/core/SOUL.md +76 -0
- package/templates/langs/en/core/TOOLS.md +53 -0
- package/templates/langs/en/core/USER.md +10 -0
- package/templates/langs/en/pain/00_seed_samples.md +23 -0
- package/templates/langs/en/pain_dictionary.json +22 -0
- package/templates/langs/en/skills/admin/SKILL.md +40 -0
- package/templates/langs/en/skills/bootstrap-tools/SKILL.md +53 -0
- package/templates/langs/en/skills/deductive-audit/SKILL.md +36 -0
- package/templates/langs/en/skills/evolution-framework-update/SKILL.md +31 -0
- package/templates/langs/en/skills/evolve-system/SKILL.md +46 -0
- package/templates/langs/en/skills/evolve-task/SKILL.md +83 -0
- package/templates/langs/en/skills/feedback/SKILL.md +51 -0
- package/templates/langs/en/skills/init-strategy/SKILL.md +54 -0
- package/templates/langs/en/skills/inject-rule/SKILL.md +19 -0
- package/templates/langs/en/skills/manage-okr/SKILL.md +96 -0
- package/templates/langs/en/skills/pain/SKILL.md +19 -0
- package/templates/langs/en/skills/pd-daily/SKILL.md +199 -0
- package/templates/langs/en/skills/pd-grooming/SKILL.md +46 -0
- package/templates/langs/en/skills/pd-mentor/SKILL.md +230 -0
- package/templates/langs/en/skills/plan-script/SKILL.md +32 -0
- package/templates/langs/en/skills/profile/SKILL.md +24 -0
- package/templates/langs/en/skills/reflection/SKILL.md +40 -0
- package/templates/langs/en/skills/reflection-log/SKILL.md +37 -0
- package/templates/langs/en/skills/report/SKILL.md +13 -0
- package/templates/langs/en/skills/root-cause/SKILL.md +33 -0
- package/templates/langs/en/skills/triage/SKILL.md +29 -0
- package/templates/langs/en/skills/watch-evolution/SKILL.md +33 -0
- package/templates/langs/zh/core/AGENTS.md +207 -0
- package/templates/langs/zh/core/BOOT.md +60 -0
- package/templates/langs/zh/core/BOOTSTRAP.md +250 -0
- package/templates/langs/zh/core/HEARTBEAT.md +74 -0
- package/templates/langs/zh/core/IDENTITY.md +8 -0
- package/templates/langs/zh/core/SOUL.md +76 -0
- package/templates/langs/zh/core/TOOLS.md +53 -0
- package/templates/langs/zh/core/USER.md +10 -0
- package/templates/langs/zh/pain/00_seed_samples.md +24 -0
- package/templates/langs/zh/pain_dictionary.json +18 -0
- package/templates/langs/zh/skills/admin/SKILL.md +42 -0
- package/templates/langs/zh/skills/bootstrap-tools/SKILL.md +52 -0
- package/templates/langs/zh/skills/deductive-audit/SKILL.md +36 -0
- package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +31 -0
- package/templates/langs/zh/skills/evolve-system/SKILL.md +46 -0
- package/templates/langs/zh/skills/evolve-task/SKILL.md +83 -0
- package/templates/langs/zh/skills/feedback/SKILL.md +53 -0
- package/templates/langs/zh/skills/init-strategy/SKILL.md +54 -0
- package/templates/langs/zh/skills/inject-rule/SKILL.md +19 -0
- package/templates/langs/zh/skills/manage-okr/SKILL.md +109 -0
- package/templates/langs/zh/skills/pain/SKILL.md +19 -0
- package/templates/langs/zh/skills/pd-daily/SKILL.md +199 -0
- package/templates/langs/zh/skills/pd-grooming/SKILL.md +46 -0
- package/templates/langs/zh/skills/pd-mentor/SKILL.md +230 -0
- package/templates/langs/zh/skills/plan-script/SKILL.md +32 -0
- package/templates/langs/zh/skills/profile/SKILL.md +24 -0
- package/templates/langs/zh/skills/reflection/SKILL.md +40 -0
- package/templates/langs/zh/skills/reflection-log/SKILL.md +37 -0
- package/templates/langs/zh/skills/report/SKILL.md +13 -0
- package/templates/langs/zh/skills/root-cause/SKILL.md +33 -0
- package/templates/langs/zh/skills/triage/SKILL.md +29 -0
- package/templates/langs/zh/skills/watch-evolution/SKILL.md +33 -0
- package/templates/pain_dictionary.json +36 -0
- package/templates/pain_settings.json +77 -0
- package/templates/workspace/.principles/00-kernel.md +51 -0
- package/templates/workspace/.principles/DECISION_POLICY.json +44 -0
- package/templates/workspace/.principles/PRINCIPLES.md +20 -0
- package/templates/workspace/.principles/PROFILE.json +52 -0
- package/templates/workspace/.principles/PROFILE.schema.json +56 -0
- package/templates/workspace/.principles/THINKING_OS.md +64 -0
- package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +7 -0
- package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +9 -0
- package/templates/workspace/.principles/models/_INDEX.md +27 -0
- package/templates/workspace/.principles/models/first_principles.md +62 -0
- package/templates/workspace/.principles/models/marketing_4p.md +52 -0
- package/templates/workspace/.principles/models/porter_five.md +63 -0
- package/templates/workspace/.principles/models/swot.md +60 -0
- package/templates/workspace/.principles/models/user_story_map.md +63 -0
- package/templates/workspace/.state/WORKBOARD.json +4 -0
- package/templates/workspace/AUDIT.md +15 -0
- package/templates/workspace/PLAN.md +2 -0
- package/templates/workspace/okr/RECOVERY_PROTOCOL.md +56 -0
- package/templates/workspace/okr/TASK_CHANGES.jsonl +6 -0
- package/templates/workspace/okr/WEEK_TASKS.json +6 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { isRisky } from '../utils/io.js';
|
|
2
|
+
export function estimateLineChanges(modification) {
|
|
3
|
+
const { toolName, params } = modification;
|
|
4
|
+
if (toolName === 'write_file' || toolName === 'write') {
|
|
5
|
+
const content = params.content || '';
|
|
6
|
+
return content.split('\n').length;
|
|
7
|
+
}
|
|
8
|
+
if (toolName === 'replace' || toolName === 'edit') {
|
|
9
|
+
const newContent = params.new_string || params.newText || '';
|
|
10
|
+
return newContent.split('\n').length;
|
|
11
|
+
}
|
|
12
|
+
if (toolName === 'apply_patch' || toolName === 'patch') {
|
|
13
|
+
const patch = params.patch || '';
|
|
14
|
+
// Rough estimate for patch files
|
|
15
|
+
return patch.split('\n').filter((l) => l.startsWith('+') || l.startsWith('-')).length;
|
|
16
|
+
}
|
|
17
|
+
if (toolName === 'delete_file') {
|
|
18
|
+
// Deleting a file is considered a significant change, but we don't know the size.
|
|
19
|
+
// We'll treat it as a medium-to-large size change.
|
|
20
|
+
return 50;
|
|
21
|
+
}
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
export function assessRiskLevel(filePath, modification, riskPaths) {
|
|
25
|
+
const isRiskPath = isRisky(filePath, riskPaths);
|
|
26
|
+
const estimatedLines = estimateLineChanges(modification);
|
|
27
|
+
if (isRiskPath) {
|
|
28
|
+
if (estimatedLines > 100)
|
|
29
|
+
return 'CRITICAL';
|
|
30
|
+
return 'HIGH';
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
if (estimatedLines > 100)
|
|
34
|
+
return 'HIGH';
|
|
35
|
+
if (estimatedLines > 10)
|
|
36
|
+
return 'MEDIUM';
|
|
37
|
+
return 'LOW';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { PainConfig } from './config.js';
|
|
2
|
+
export interface TokenUsage {
|
|
3
|
+
input?: number;
|
|
4
|
+
output?: number;
|
|
5
|
+
cacheRead?: number;
|
|
6
|
+
cacheWrite?: number;
|
|
7
|
+
total?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface SessionState {
|
|
10
|
+
sessionId: string;
|
|
11
|
+
workspaceDir?: string;
|
|
12
|
+
toolReadsByFile: Record<string, number>;
|
|
13
|
+
llmTurns: number;
|
|
14
|
+
blockedAttempts: number;
|
|
15
|
+
lastActivityAt: number;
|
|
16
|
+
totalInputTokens: number;
|
|
17
|
+
totalOutputTokens: number;
|
|
18
|
+
cacheHits: number;
|
|
19
|
+
stuckLoops: number;
|
|
20
|
+
currentGfi: number;
|
|
21
|
+
lastErrorHash: string;
|
|
22
|
+
consecutiveErrors: number;
|
|
23
|
+
dailyToolCalls: number;
|
|
24
|
+
dailyToolFailures: number;
|
|
25
|
+
dailyPainSignals: number;
|
|
26
|
+
dailyGfiPeak: number;
|
|
27
|
+
lastThinkingTimestamp: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Initialize persistence for session state.
|
|
31
|
+
* Call this once during plugin startup.
|
|
32
|
+
*/
|
|
33
|
+
export declare function initPersistence(stateDir: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Force persist all sessions immediately.
|
|
36
|
+
*/
|
|
37
|
+
export declare function flushAllSessions(): void;
|
|
38
|
+
export declare function trackToolRead(sessionId: string, filePath: string, workspaceDir?: string): SessionState;
|
|
39
|
+
export declare function trackLlmOutput(sessionId: string, usage: TokenUsage | undefined, config?: PainConfig, workspaceDir?: string): SessionState;
|
|
40
|
+
/**
|
|
41
|
+
* Tracks physical friction based on tool execution failures.
|
|
42
|
+
*/
|
|
43
|
+
export declare function trackFriction(sessionId: string, deltaF: number, hash: string, workspaceDir?: string): SessionState;
|
|
44
|
+
/**
|
|
45
|
+
* Resets the friction index upon successful action.
|
|
46
|
+
*/
|
|
47
|
+
export declare function resetFriction(sessionId: string, workspaceDir?: string): SessionState;
|
|
48
|
+
/**
|
|
49
|
+
* Records that deep thinking (Thinking OS) was performed in this session.
|
|
50
|
+
* Used by the Thinking OS checkpoint to allow high-risk operations.
|
|
51
|
+
*/
|
|
52
|
+
export declare function recordThinkingCheckpoint(sessionId: string, workspaceDir?: string): SessionState;
|
|
53
|
+
/**
|
|
54
|
+
* Checks if deep thinking was performed recently (within the given window).
|
|
55
|
+
* @param sessionId - The session to check
|
|
56
|
+
* @param windowMs - How recent the thinking must be (default: 5 minutes)
|
|
57
|
+
* @returns true if thinking was recorded within the window
|
|
58
|
+
*/
|
|
59
|
+
export declare function hasRecentThinking(sessionId: string, windowMs?: number): boolean;
|
|
60
|
+
export declare function trackBlock(sessionId: string): SessionState;
|
|
61
|
+
export declare function getSession(sessionId: string): SessionState | undefined;
|
|
62
|
+
export declare function clearSession(sessionId: string): void;
|
|
63
|
+
export declare function garbageCollectSessions(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Get daily statistics summary for a session.
|
|
66
|
+
*/
|
|
67
|
+
export declare function getDailySummary(sessionId: string): {
|
|
68
|
+
toolCalls: number;
|
|
69
|
+
toolFailures: number;
|
|
70
|
+
painSignals: number;
|
|
71
|
+
gfiPeak: number;
|
|
72
|
+
} | null;
|
|
73
|
+
/**
|
|
74
|
+
* Reset daily statistics (call at midnight or on new day).
|
|
75
|
+
*/
|
|
76
|
+
export declare function resetDailyStats(sessionId: string): void;
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import { SystemLogger } from './system-logger.js';
|
|
4
|
+
const sessions = new Map();
|
|
5
|
+
/** Directory for persisting session state */
|
|
6
|
+
let persistDir = null;
|
|
7
|
+
/** Debounce timer for persistence */
|
|
8
|
+
let persistTimer = null;
|
|
9
|
+
/**
|
|
10
|
+
* Initialize persistence for session state.
|
|
11
|
+
* Call this once during plugin startup.
|
|
12
|
+
*/
|
|
13
|
+
export function initPersistence(stateDir) {
|
|
14
|
+
persistDir = path.join(stateDir, 'sessions');
|
|
15
|
+
if (!fs.existsSync(persistDir)) {
|
|
16
|
+
fs.mkdirSync(persistDir, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
// Load all existing sessions
|
|
19
|
+
loadAllSessions();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get the file path for a session's persisted state.
|
|
23
|
+
*/
|
|
24
|
+
function getSessionPath(sessionId) {
|
|
25
|
+
if (!persistDir)
|
|
26
|
+
return '';
|
|
27
|
+
// Sanitize sessionId for filesystem
|
|
28
|
+
const safeId = sessionId.replace(/[/\\:]/g, '_');
|
|
29
|
+
return path.join(persistDir, `${safeId}.json`);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Load all persisted sessions from disk.
|
|
33
|
+
*/
|
|
34
|
+
function loadAllSessions() {
|
|
35
|
+
if (!persistDir || !fs.existsSync(persistDir))
|
|
36
|
+
return;
|
|
37
|
+
try {
|
|
38
|
+
const files = fs.readdirSync(persistDir).filter(f => f.endsWith('.json'));
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
const twoHoursAgo = now - 2 * 60 * 60 * 1000;
|
|
41
|
+
for (const file of files) {
|
|
42
|
+
try {
|
|
43
|
+
const content = fs.readFileSync(path.join(persistDir, file), 'utf-8');
|
|
44
|
+
const state = JSON.parse(content);
|
|
45
|
+
// Skip abandoned sessions
|
|
46
|
+
if (state.lastActivityAt < twoHoursAgo) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
sessions.set(state.sessionId, state);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Ignore corrupted files
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
// Ignore errors during load
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Persist a single session to disk.
|
|
62
|
+
*/
|
|
63
|
+
function persistSession(state) {
|
|
64
|
+
if (!persistDir)
|
|
65
|
+
return;
|
|
66
|
+
const sessionPath = getSessionPath(state.sessionId);
|
|
67
|
+
if (!sessionPath)
|
|
68
|
+
return;
|
|
69
|
+
try {
|
|
70
|
+
fs.writeFileSync(sessionPath, JSON.stringify(state, null, 2), 'utf-8');
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Ignore persistence errors
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Schedule persistence with debounce.
|
|
78
|
+
*/
|
|
79
|
+
function schedulePersistence(state) {
|
|
80
|
+
if (persistTimer) {
|
|
81
|
+
clearTimeout(persistTimer);
|
|
82
|
+
}
|
|
83
|
+
persistTimer = setTimeout(() => {
|
|
84
|
+
persistSession(state);
|
|
85
|
+
persistTimer = null;
|
|
86
|
+
}, 1000); // 1 second debounce
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Force persist all sessions immediately.
|
|
90
|
+
*/
|
|
91
|
+
export function flushAllSessions() {
|
|
92
|
+
for (const state of sessions.values()) {
|
|
93
|
+
persistSession(state);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function getOrCreateSession(sessionId, workspaceDir) {
|
|
97
|
+
let state = sessions.get(sessionId);
|
|
98
|
+
if (!state) {
|
|
99
|
+
state = {
|
|
100
|
+
sessionId,
|
|
101
|
+
workspaceDir,
|
|
102
|
+
toolReadsByFile: {},
|
|
103
|
+
llmTurns: 0,
|
|
104
|
+
blockedAttempts: 0,
|
|
105
|
+
lastActivityAt: Date.now(),
|
|
106
|
+
totalInputTokens: 0,
|
|
107
|
+
totalOutputTokens: 0,
|
|
108
|
+
cacheHits: 0,
|
|
109
|
+
stuckLoops: 0,
|
|
110
|
+
currentGfi: 0,
|
|
111
|
+
lastErrorHash: '',
|
|
112
|
+
consecutiveErrors: 0,
|
|
113
|
+
dailyToolCalls: 0,
|
|
114
|
+
dailyToolFailures: 0,
|
|
115
|
+
dailyPainSignals: 0,
|
|
116
|
+
dailyGfiPeak: 0,
|
|
117
|
+
lastThinkingTimestamp: 0,
|
|
118
|
+
};
|
|
119
|
+
sessions.set(sessionId, state);
|
|
120
|
+
}
|
|
121
|
+
if (workspaceDir && !state.workspaceDir) {
|
|
122
|
+
state.workspaceDir = workspaceDir;
|
|
123
|
+
}
|
|
124
|
+
return state;
|
|
125
|
+
}
|
|
126
|
+
export function trackToolRead(sessionId, filePath, workspaceDir) {
|
|
127
|
+
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
128
|
+
const normalizedPath = path.posix.normalize(filePath.replace(/\\/g, '/'));
|
|
129
|
+
state.toolReadsByFile[normalizedPath] = (state.toolReadsByFile[normalizedPath] || 0) + 1;
|
|
130
|
+
state.lastActivityAt = Date.now();
|
|
131
|
+
return state;
|
|
132
|
+
}
|
|
133
|
+
export function trackLlmOutput(sessionId, usage, config, workspaceDir) {
|
|
134
|
+
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
135
|
+
state.llmTurns += 1;
|
|
136
|
+
state.lastActivityAt = Date.now();
|
|
137
|
+
if (usage) {
|
|
138
|
+
state.totalInputTokens += usage.input || 0;
|
|
139
|
+
state.totalOutputTokens += usage.output || 0;
|
|
140
|
+
state.cacheHits += usage.cacheRead || 0;
|
|
141
|
+
// Use thresholds from config or defaults
|
|
142
|
+
const minTurns = 5; // Increased from 3 to 5 to prevent false positives on short tasks
|
|
143
|
+
const outputThreshold = 30; // Decreased from 50. Only penalize truly stunted outputs.
|
|
144
|
+
const inputThreshold = config ? config.get('thresholds.cognitive_paralysis_input') : 8000; // Increased base to 8k
|
|
145
|
+
// Very rough heuristic for empty/paralysis loops: high input context, tiny output, multiple turns
|
|
146
|
+
if (state.llmTurns > minTurns) {
|
|
147
|
+
const isTinyOutput = (usage.output || 0) < outputThreshold;
|
|
148
|
+
const isLargeInput = (usage.input || 0) > inputThreshold;
|
|
149
|
+
if (isTinyOutput && isLargeInput) {
|
|
150
|
+
state.stuckLoops += 1;
|
|
151
|
+
SystemLogger.log(state.workspaceDir, 'EFFICIENCY_ALARM', `Stuck loop detected (Turn ${state.llmTurns}). Input: ${usage.input}, Output: ${usage.output}. Consecutive: ${state.stuckLoops}`);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// Reset if we broke out of the tiny output loop
|
|
155
|
+
if (state.stuckLoops > 0) {
|
|
156
|
+
SystemLogger.log(state.workspaceDir, 'EFFICIENCY_OK', `Broke out of stuck loop after ${state.stuckLoops} turns.`);
|
|
157
|
+
}
|
|
158
|
+
state.stuckLoops = Math.max(0, state.stuckLoops - 1);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return state;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Tracks physical friction based on tool execution failures.
|
|
166
|
+
*/
|
|
167
|
+
export function trackFriction(sessionId, deltaF, hash, workspaceDir) {
|
|
168
|
+
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
169
|
+
if (hash && hash === state.lastErrorHash) {
|
|
170
|
+
state.consecutiveErrors++;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
state.lastErrorHash = hash;
|
|
174
|
+
state.consecutiveErrors = 1;
|
|
175
|
+
}
|
|
176
|
+
// GFI formula with multiplier: GFI = GFI + (Delta_F * 1.5^(n-1))
|
|
177
|
+
const multiplier = Math.pow(1.5, state.consecutiveErrors - 1);
|
|
178
|
+
const addedFriction = deltaF * multiplier;
|
|
179
|
+
state.currentGfi = (state.currentGfi || 0) + addedFriction;
|
|
180
|
+
state.lastActivityAt = Date.now();
|
|
181
|
+
SystemLogger.log(state.workspaceDir, 'GFI_INC', `Friction added: +${addedFriction.toFixed(1)} (Base: ${deltaF}, Mult: ${multiplier.toFixed(2)}). Total GFI: ${state.currentGfi.toFixed(1)}`);
|
|
182
|
+
// Update daily stats
|
|
183
|
+
state.dailyToolFailures++;
|
|
184
|
+
state.dailyGfiPeak = Math.max(state.dailyGfiPeak, state.currentGfi);
|
|
185
|
+
// Schedule persistence
|
|
186
|
+
schedulePersistence(state);
|
|
187
|
+
return state;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Resets the friction index upon successful action.
|
|
191
|
+
*/
|
|
192
|
+
export function resetFriction(sessionId, workspaceDir) {
|
|
193
|
+
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
194
|
+
if (state.currentGfi > 0) {
|
|
195
|
+
SystemLogger.log(state.workspaceDir, 'GFI_RESET', `Friction reset to 0 (Was: ${state.currentGfi.toFixed(1)}). Action successful.`);
|
|
196
|
+
}
|
|
197
|
+
state.currentGfi = 0;
|
|
198
|
+
state.consecutiveErrors = 0;
|
|
199
|
+
state.lastErrorHash = '';
|
|
200
|
+
// Schedule persistence
|
|
201
|
+
schedulePersistence(state);
|
|
202
|
+
return state;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Records that deep thinking (Thinking OS) was performed in this session.
|
|
206
|
+
* Used by the Thinking OS checkpoint to allow high-risk operations.
|
|
207
|
+
*/
|
|
208
|
+
export function recordThinkingCheckpoint(sessionId, workspaceDir) {
|
|
209
|
+
const state = getOrCreateSession(sessionId, workspaceDir);
|
|
210
|
+
state.lastThinkingTimestamp = Date.now();
|
|
211
|
+
SystemLogger.log(state.workspaceDir, 'THINKING_CHECKPOINT', `Deep thinking recorded at ${new Date(state.lastThinkingTimestamp).toISOString()}`);
|
|
212
|
+
schedulePersistence(state);
|
|
213
|
+
return state;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Checks if deep thinking was performed recently (within the given window).
|
|
217
|
+
* @param sessionId - The session to check
|
|
218
|
+
* @param windowMs - How recent the thinking must be (default: 5 minutes)
|
|
219
|
+
* @returns true if thinking was recorded within the window
|
|
220
|
+
*/
|
|
221
|
+
export function hasRecentThinking(sessionId, windowMs = 5 * 60 * 1000) {
|
|
222
|
+
const state = sessions.get(sessionId);
|
|
223
|
+
if (!state || !state.lastThinkingTimestamp)
|
|
224
|
+
return false;
|
|
225
|
+
return (Date.now() - state.lastThinkingTimestamp) < windowMs;
|
|
226
|
+
}
|
|
227
|
+
export function trackBlock(sessionId) {
|
|
228
|
+
const state = getOrCreateSession(sessionId);
|
|
229
|
+
state.blockedAttempts += 1;
|
|
230
|
+
state.lastActivityAt = Date.now();
|
|
231
|
+
return state;
|
|
232
|
+
}
|
|
233
|
+
export function getSession(sessionId) {
|
|
234
|
+
return sessions.get(sessionId);
|
|
235
|
+
}
|
|
236
|
+
export function clearSession(sessionId) {
|
|
237
|
+
sessions.delete(sessionId);
|
|
238
|
+
}
|
|
239
|
+
// Memory cleanup for abandoned sessions (older than 2 hours)
|
|
240
|
+
export function garbageCollectSessions() {
|
|
241
|
+
const twoHoursAgo = Date.now() - 2 * 60 * 60 * 1000;
|
|
242
|
+
for (const [id, state] of sessions.entries()) {
|
|
243
|
+
if (state.lastActivityAt < twoHoursAgo) {
|
|
244
|
+
sessions.delete(id);
|
|
245
|
+
// Also delete persisted file
|
|
246
|
+
if (persistDir) {
|
|
247
|
+
const sessionPath = getSessionPath(id);
|
|
248
|
+
if (sessionPath && fs.existsSync(sessionPath)) {
|
|
249
|
+
try {
|
|
250
|
+
fs.unlinkSync(sessionPath);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
// Ignore deletion errors
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Get daily statistics summary for a session.
|
|
262
|
+
*/
|
|
263
|
+
export function getDailySummary(sessionId) {
|
|
264
|
+
const state = sessions.get(sessionId);
|
|
265
|
+
if (!state)
|
|
266
|
+
return null;
|
|
267
|
+
return {
|
|
268
|
+
toolCalls: state.dailyToolCalls,
|
|
269
|
+
toolFailures: state.dailyToolFailures,
|
|
270
|
+
painSignals: state.dailyPainSignals,
|
|
271
|
+
gfiPeak: state.dailyGfiPeak,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Reset daily statistics (call at midnight or on new day).
|
|
276
|
+
*/
|
|
277
|
+
export function resetDailyStats(sessionId) {
|
|
278
|
+
const state = sessions.get(sessionId);
|
|
279
|
+
if (state) {
|
|
280
|
+
state.dailyToolCalls = 0;
|
|
281
|
+
state.dailyToolFailures = 0;
|
|
282
|
+
state.dailyPainSignals = 0;
|
|
283
|
+
state.dailyGfiPeak = 0;
|
|
284
|
+
schedulePersistence(state);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System Logger for Principles Disciple
|
|
3
|
+
* Writes critical evolutionary events to the project's memory/logs/SYSTEM.log
|
|
4
|
+
* Uses asynchronous writing to avoid blocking the Node.js event loop.
|
|
5
|
+
*/
|
|
6
|
+
export declare const SystemLogger: {
|
|
7
|
+
log(workspaceDir: string | undefined, eventType: string, message: string): void;
|
|
8
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { resolvePdPath } from './paths.js';
|
|
4
|
+
/**
|
|
5
|
+
* System Logger for Principles Disciple
|
|
6
|
+
* Writes critical evolutionary events to the project's memory/logs/SYSTEM.log
|
|
7
|
+
* Uses asynchronous writing to avoid blocking the Node.js event loop.
|
|
8
|
+
*/
|
|
9
|
+
export const SystemLogger = {
|
|
10
|
+
log(workspaceDir, eventType, message) {
|
|
11
|
+
if (!workspaceDir)
|
|
12
|
+
return;
|
|
13
|
+
try {
|
|
14
|
+
const logFile = resolvePdPath(workspaceDir, 'SYSTEM_LOG');
|
|
15
|
+
const logDir = path.dirname(logFile);
|
|
16
|
+
if (!fs.existsSync(logDir)) {
|
|
17
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
const timestamp = new Date().toISOString();
|
|
20
|
+
// Format: [YYYY-MM-DDTHH:mm:ss.sssZ] [EVENT_TYPE] Message
|
|
21
|
+
const logEntry = `[${timestamp}] [${eventType.padEnd(15)}] ${message}\n`;
|
|
22
|
+
// Use fire-and-forget async append to prevent blocking
|
|
23
|
+
fs.appendFile(logFile, logEntry, 'utf8', (err) => {
|
|
24
|
+
// Silently drop errors (e.g. disk full) to not crash the gateway
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
// Silently fail if we can't setup the log
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trust Engine V2.1 - Recalibrated for Action Classification
|
|
3
|
+
* Differentiates between Exploratory (safe) and Constructive (risky) actions.
|
|
4
|
+
*/
|
|
5
|
+
export interface TrustScorecard {
|
|
6
|
+
trust_score: number;
|
|
7
|
+
success_streak: number;
|
|
8
|
+
failure_streak: number;
|
|
9
|
+
exploratory_failure_streak: number;
|
|
10
|
+
grace_failures_remaining?: number;
|
|
11
|
+
last_updated: string;
|
|
12
|
+
cold_start_end?: string;
|
|
13
|
+
first_activity_at?: string;
|
|
14
|
+
history: Array<{
|
|
15
|
+
type: 'success' | 'failure' | 'penalty' | 'info';
|
|
16
|
+
delta: number;
|
|
17
|
+
reason: string;
|
|
18
|
+
timestamp: string;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
export type TrustStage = 1 | 2 | 3 | 4;
|
|
22
|
+
export declare const EXPLORATORY_TOOLS: string[];
|
|
23
|
+
export declare const CONSTRUCTIVE_TOOLS: string[];
|
|
24
|
+
export declare class TrustEngine {
|
|
25
|
+
private scorecard;
|
|
26
|
+
private workspaceDir;
|
|
27
|
+
private stateDir;
|
|
28
|
+
constructor(workspaceDir: string);
|
|
29
|
+
private get config();
|
|
30
|
+
private get trustSettings();
|
|
31
|
+
private loadScorecard;
|
|
32
|
+
private saveScorecard;
|
|
33
|
+
getScore(): number;
|
|
34
|
+
getScorecard(): TrustScorecard;
|
|
35
|
+
getStage(): TrustStage;
|
|
36
|
+
isColdStart(): boolean;
|
|
37
|
+
recordSuccess(reason: string, context?: {
|
|
38
|
+
sessionId?: string;
|
|
39
|
+
api?: any;
|
|
40
|
+
toolName?: string;
|
|
41
|
+
}, isSubagent?: boolean): void;
|
|
42
|
+
recordFailure(type: 'tool' | 'risky' | 'bypass', context: {
|
|
43
|
+
sessionId?: string;
|
|
44
|
+
api?: any;
|
|
45
|
+
toolName?: string;
|
|
46
|
+
}): void;
|
|
47
|
+
private updateScore;
|
|
48
|
+
resetTrust(newScore?: number): void;
|
|
49
|
+
getStatusSummary(): {
|
|
50
|
+
stage: TrustStage;
|
|
51
|
+
successRate: number;
|
|
52
|
+
isInColdStart: boolean;
|
|
53
|
+
graceRemaining: number;
|
|
54
|
+
currentStreak: {
|
|
55
|
+
type: string;
|
|
56
|
+
count: number;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
private calculateSuccessRate;
|
|
60
|
+
}
|
|
61
|
+
export declare function recordSuccess(workspaceDir: string, reason: string, context?: {
|
|
62
|
+
sessionId?: string;
|
|
63
|
+
api?: any;
|
|
64
|
+
toolName?: string;
|
|
65
|
+
}, isSubagent?: boolean): void;
|
|
66
|
+
export declare function recordFailure(type: 'tool' | 'risky' | 'bypass', workspaceDir: string, ctx: {
|
|
67
|
+
sessionId?: string;
|
|
68
|
+
api?: any;
|
|
69
|
+
toolName?: string;
|
|
70
|
+
}): void;
|
|
71
|
+
export declare function getAgentScorecard(workspaceDir: string): TrustScorecard;
|
|
72
|
+
export declare function getTrustStats(scorecard: TrustScorecard): {
|
|
73
|
+
stage: TrustStage;
|
|
74
|
+
successRate: any;
|
|
75
|
+
isInColdStart: boolean;
|
|
76
|
+
graceRemaining: number;
|
|
77
|
+
currentStreak: {
|
|
78
|
+
type: string;
|
|
79
|
+
count: number;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
export declare function getTrustStatus(workspaceDir: string): {
|
|
83
|
+
stage: TrustStage;
|
|
84
|
+
successRate: number;
|
|
85
|
+
isInColdStart: boolean;
|
|
86
|
+
graceRemaining: number;
|
|
87
|
+
currentStreak: {
|
|
88
|
+
type: string;
|
|
89
|
+
count: number;
|
|
90
|
+
};
|
|
91
|
+
};
|